How to use trim in select query rails or in sql query - mysql

User.select(:name).group(:name).having("count(*) > 1")
this query works fine to select records having duplicate user name. But problem am facing is when there is space in name.
For example.
recoed1 = "Username"
record2 = "Username "
This are the two records but they are having same name, but above query consider them as different records because space is there in the second record. So while selecting I did not get this record.
Any solution using normal mysql query or rails will do.
OR
How I can strip or trim all the column data first from table using rails/mysql query. Then I can apply above query.

What i would do here is make sure your data is tidy in the first place.
You could put in a pre-validation method to call strip on usernames. You could do it like so
#in lib/trimmer.rb
module Trimmer
# Make a class method available to define space-trimming behavior.
def self.included base
base.extend(ClassMethods)
end
module ClassMethods
# Register a before-validation handler for the given fields to
# trim leading and trailing spaces.
def trimmed_fields *field_list
before_validation do |model|
field_list.each do |field|
model.send("#{field}=", model.send("#{field}").strip) if model.send("#{field}").respond_to?('strip')
end
end
end
end
end
Make sure this module is required, wherever you require things in lib in your config.
Now, you can say, in any models, like so (in this example i'm doing some other fields besides username)
class User < ActiveRecord::Base
include Trimmer
trimmed_fields :username, :email, :first_name, :last_name
...
So, that will fix you going forward. The remaining step is to tidy up your existing data. I would do this in a migration. (again, i'm doing some other fields as an example)
tables_and_cols = {"users" => %w(username email first_name last_name), "foos" => %w(bar baz)}
tables_and_cols.each do |table, cols|
cols.each do |col|
ActiveRecord::Base.connection.execute("update #{tablename} set #{col} = trim(#{col})")
end
end
Now, after doing this trim, you may have some duplicate usernames. You will need to decide how you are going to deal with that, since the records involved are no longer valid. If you haven't publically deployed this yet, ie if you don't have any active real users, then it doesn't matter: you can change the usernames to something else. But if you do have real people using it you will probably need to change the username for some of them and inform them. This is unavoidable if you want to maintain a situation where people can't have spaces at the start or end of their username.

You can use mysql's string functions:
User.select("lower(trim(name))").group("lower(trim(name))").having("count(*) > 1")

Related

Copying Parent model attribute to all of its children with same attribute in mysql or Rails as single Query

I know we need to use the following sudo code in case of Rails
Parent.all.each do |parent|
parent.childrens.update_all(:price => parent.price)
end
But I have like 5 Million Parent records and I know this would take a lot of time.
Is there a easy way to do the above through Rails or MySQL the fastest way (in a single query)
Parent.includes(:childrens).find_in_batches.find_in_batches do |group|
sleep(50)
group.each { |parent| parent.childrens.update_all(price: parent.price) }
end
This is the best you can come up with rails atleast..it will avoid n+1 also, since the records are huge, find_in_batches will help you, otherwise there is a possibility that your db/dyno gets locked..
I think you can use ActiveRecord callback functionality to achieve this.
Example code would look like this:
class Parent < ActiveRecord::Base
after_update :denormalize
has_many :children
private
def denormalize
children.update_all(price: price)
end
end
This will ensure that, whenever a parent object is modified the child will also be updated.

Use a custom query when generating CSV in Activeadmin

I have additional calculation columns (based on joins) I want to include in my CSV.
If I open and calculate it individually for every record
csv do
column :complicated_calculation {  |r| r.calculate_things }
end
it's going to take a long time to generate with thousands of records.
I need to customize the SELECT query for when my CSV is generated and then use the columns in that query. How do I do that?
Customizing resource retrieval, in documentation, shows you how without rewriting the whole csv builder: modifying scoped_collection.
So if you have your query nicely waiting in your model:
class Person < ActiveRecord::Base
def self.with_calculation
select("people.*, (mumbo + jumbo * fumbo) "\
"AS complicated_calculation") # notice we name the attribute here
.joins("LEFT JOIN games ON person_id = people.id")
.group("people.id")
end
end
with your calculation you can do:
controller do
def scoped_collection
super.with_calculation
end
end
and then your CSV will have the attribute for free:
csv do
column :complicated_calculation
end

ActiveRecord: retrieve mySQL data based on one field in URL

I have a model called "blog", which has several columns, including one called "token".
I want to retrieve a row based on the token that's in a url - for instance, if the user goes to /blog/post1, I want to retrieve the row where token = post1
In my app setup I'm doing:
get '/blog/:id' do
#postID = params[:id]
#thePost = Blog.where(token: #postID)
render 'blog/index'
end
When I try to access <%= #thePost %> in my .erb file, I get:
#<Sequel::Mysql2::Dataset:0x007fdec1777318>
How do I access the actual data from the row here?
You are returning a relation, what you want to return is an actual record. To do this, use first at the end of your where call.
Blog.where(token: #postID).first
Blog.where(token: #postID) returns a list with all matches - even if the list contains only one elemen. OP is using the Sequel ORM instead of ActiveRecord (Hint is the return type Sequel::Mysql2::Dataset), therefore I would suggest to use first (or first! depending on your usecase) instead of where:
#thePost = Blog.first(token: #postID)
From the docs:
An alias for calling first on the model's dataset, but with optimized handling of the single argument case.

Update column of another table from another table model rails

I have a table called Application and another table called Application_Case_Status
in my code, I create the applications and i want to update the application_case_status table column "application-source".
Upon creating an application, one of the column is :case_code ="OF-123" or "ON-123"
In my Application_case_Status table, i hv a column :loan_application_id and :application_source
My code in application.rb
after_create :generate_source_id
def generate_source_id
application_object = Application.find(self.id)
if application_object.case_code.include? "OF-"
update_attribute(:application_source, "Manual Upload")
end
if self.case_code.include? "ON-"
update_attribute(:application_source, "Website")
end
end
I get error that it cant find column :application_source how can i get it to update this column which is Application_Case_Status table
As per your code, it seems that it tries to update column of table Application but Application table doesn't have column named "application_source".
You need to update table Application_case_Status
First of all: You don't need application_object = Application.find(self.id) because self is the application_object already.
Furthermore, you have two if statements but it looks like they can't be both true at the same time so you should convert them to one if...else.
So, assuming:
class Application
has_one :application_case_status
end
class ApplicationCaseStatus
belongs_to :application
end
your method should look like this:
def generate_source_id
# we are going to check "case_code" **once** and feed the "application_source" variable
application_source =
if self.case_code.include? "OF-"
"Manual Upload"
else
"Website"
end
# and then we are going to create the "ApplicationCaseStatus" entity
ApplicationCaseStatus.create!(:application_id = self.id, :application_source, application_source)
end
Notice, as per #Tien Nguyen's remark, that we are creating an ApplicationCaseStatus entity because we have assumed it's not created already. If that's not the case, you should just update it (self.application_case_status.update_attribute!(:application_source, application_source))
Let me know if something doesn't work quite as you want it or doesn't make sense.

Why is the first element always blank in my Rails multi-select, using an embedded array?

I'm using Rails 3.2.0.rc2. I've got a Model, in which I have a static Array which I'm offering up through a form such that users may select a subset of Array and save their selection to the database, stored in a single column in Model. I've used serialize on the database column which stores the Array and Rails is correctly converting the users' selections into Yaml (and back to an array when reading that column). I'm using a multi-select form input to make selections.
My problem is that, the way I currently have it, everything works as I would expect except that the user's subset array always has a blank first element when it's sent to the server.
This isn't a big deal, and I could write code to cut that out after the fact, but I feel like I'm just making some kind of syntactical error as it doesn't seem to me that the default Rails behaviour would intentionally add this blank element without some reason. I must have missed something or forgot to disable some kind of setting. Please help me understand what I'm missing (or point me in to some good documentation that describes this with more depth than what I've been able to find on the intertubes).
MySQL Database Table 'models':
includes a column named subset_array which is a TEXT field
Class Model includes the following settings:
serialize :subset_array
ALL_POSSIBLE_VALUES = [value1, value2, value3, ...]
Form for editing Models includes the following input option:
f.select :subset_array, Model::ALL_POSSIBLE_VALUES, {}, :multiple => true, :selected => #model.subset_array
PUT to server from client looks something like this:
assuming only value1 and value3 are selected
"model" => { "subset_array" => ["", value1, value3] }
Database update looks like this:
UPDATE 'models' SET 'subset_array' = '--- \n- \"\"\n- value1\n- value3\n'
As you can see, there's this extra, blank, element in the array being sent and set in the database. How do I get rid of that? Is there a parameter I'm missing from my f.select call?
Much thanks appreciated :)
EDIT: This is the generated HTML code from the f.select statement. It looks as though there is a hidden input being generated which may be the cause of my issue? Why is that there?
<input name="model[subset_array][]" type="hidden" value>
<select id="model_subset_array" multiple="multiple" name="model[subset_array][]" selected="selected">
<option value="value1" selected="selected">Value1</option>
<option value="value2">Value2</option>
<option value="value3" selected="selected">Value3</option>
<option...>...</option>
</select>
In Rails 4:
You will be able to pass :include_hidden option. https://github.com/rails/rails/pull/5414/files
As a quick fix for now: you can use right now in your model:
before_validation do |model|
model.subset_array.reject!(&:blank?) if model.subset_array
end
This will just delete all blank values at model level.
The hidden field is what is causing the issue. But it is there for a good reason: when all values are deselected, you still receive a subset_array parameter. From the Rails docs (you may have to scroll to the right to see all of this):
# The HTML specification says when +multiple+ parameter passed to select and all options got deselected
# web browsers do not send any value to server. Unfortunately this introduces a gotcha:
# if an +User+ model has many +roles+ and have +role_ids+ accessor, and in the form that edits roles of the user
# the user deselects all roles from +role_ids+ multiple select box, no +role_ids+ parameter is sent. So,
# any mass-assignment idiom like
#
# #user.update_attributes(params[:user])
#
# wouldn't update roles.
#
# To prevent this the helper generates an auxiliary hidden field before
# every multiple select. The hidden field has the same name as multiple select and blank value.
#
# This way, the client either sends only the hidden field (representing
# the deselected multiple select box), or both fields. Since the HTML specification
# says key/value pairs have to be sent in the same order they appear in the
# form, and parameters extraction gets the last occurrence of any repeated
# key in the query string, that works for ordinary forms.
EDIT: The last paragraph suggests that you shouldn't be seeing the empty one in the case when something is selected, but I think it is wrong. The person who made this commit to Rails (see https://github.com/rails/rails/commit/faba406fa15251cdc9588364d23c687a14ed6885) is trying to do the same trick that Rails uses for checkboxes (as mentioned here: https://github.com/rails/rails/pull/1552), but I don't think it can work for a multiple select box because the parameters sent over form an array in this case and so no value is ignored.
So my feeling is that this is a bug.
In Rails 4+ set :include_hidden on select_tag to false
<%= form.grouped_collection_select :employee_id, Company.all, :employees, :name, :id, :name, { include_hidden: false }, { size: 6, multiple: true } %>
Another quick fix is to use this controller filter:
def clean_select_multiple_params hash = params
hash.each do |k, v|
case v
when Array then v.reject!(&:blank?)
when Hash then clean_select_multiple_params(v)
end
end
end
This way can be reused across controllers without touching the model layer.
http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-check_box
Gotcha
The HTML specification says unchecked check boxes or selects are not successful,
and thus web browsers do not send them. Unfortunately this introduces
a gotcha: if an Invoice model has a paid flag, and in the form that
edits a paid invoice the user unchecks its check box, no paid
parameter is sent. So, any mass-assignment idiom like
#invoice.update(params[:invoice]) wouldn't update the flag.
To prevent this the helper generates an auxiliary hidden field before
the very check box. The hidden field has the same name and its
attributes mimic an unchecked check box.
This way, the client either sends only the hidden field (representing
the check box is unchecked), or both fields. Since the HTML
specification says key/value pairs have to be sent in the same order
they appear in the form, and parameters extraction gets the last
occurrence of any repeated key in the query string, that works for
ordinary forms.
To remove blank values:
def myfield=(value)
value.reject!(&:blank?)
write_attribute(:myfield, value)
end
In the controller:
arr = arr.delete_if { |x| x.empty? }
I fixed it using the params[:review][:staff_ids].delete("") in the controller before the update.
In my view:
= form_for #review do |f|
= f.collection_select :staff_ids, #business.staff, :id, :full_name, {}, {multiple:true}
= f.submit 'Submit Review'
In my controller:
class ReviewsController < ApplicationController
def create
....
params[:review][:staff_ids].delete("")
#review.update_attribute(:staff_ids, params[:review][:staff_ids].join(","))
....
end
end
I make it work by writing this in the Javascript part of the page:
$("#model_subset_array").val( <%= #model.subset_array %> );
Mine looks more like following:
$("#modela_modelb_ids").val( <%= #modela.modelb_ids %> );
Not sure if this is going to get me headache in the future but now it works fine.
Use jQuery:
$('select option:empty').remove();
Option to remove blank options from drop down.