Adding a LIKE criteria to a Rails Conditions block - ruby-on-rails

Consider the following code which is to be thrown at an AR find:
conditions = []
conditions[:age] = params[:age] if params[:age].present?
conditions[:gender] = params[:gender] if params[:gender].present?
I need to add another condition which is a LIKE criteria on a 'profile' attribute. How can I do this, as obviously a LIKE is usually done via an array, not a hash key.

You can scope your model with hash conditions, and then perform find on scope with array conditions:
YourModel.scoped(:conditions => conditions).all(:conditions => ["profile like ?", profile])

Follwing is ugly but it works
conditions = {} #This should be Hash
conditions[:age] = params[:age] if params[:age].present?
conditions[:gender] = params[:gender] if params[:gender].present?
conditions[:profile] = '%params[:profile]%' if params[:profile].present?
col_str ="" #this is our column names string for conditions array
col_str = "age=:age" if params[:age].present?
col_str+= (col_str.blank?)? "gender=:gender" :" AND gender=:gender" if params[:gender].present?
col_str += (col_str.blank?) 'profile like :profile' : ' AND profile like :profile' if params[:profile].present?
:conditions=>[col_str , conditions]

When you call your active record find, you send your conditions string first, then the hash with the values like :
:conditions => [ "age = :age AND gender = :gender AND profile LIKE :profile", conditions ]
that way you can keep doing what you are doing :)

Related

Query on Params

I want to get records based on params received. Sometimes I receive 2 params or sometimes 3. I have written a code to get results when all 3 params received but when I receive only 1 param am getting 0 results.
Example
Office.where(state: params[:state], type: params[:type]).where("name like ?","%#{params[:name]}%")
When i get values like
{state: "KA", type: "private", name: "google"}
But when I get the only {name: "google"} I get no records
I have tried with condition
If params[:name].present? && params[:state].present? && params[:type].present?
query
elsif condition
query
end
Let me know how can I solve this or any better way
You can do something like this
In controller
filter_params = params.slice(:state, :type, :name)
Office.filter(filter_params)
In Office model
scope :state, -> (state) { where(state: state) }
scope :type, -> (type) { where(type: type) }
scope :name, -> (name) { where("name LIKE ?", "%#{name}%") }
def self.filter(filter_params)
results = where(nil)
filter_params.each do |key, value|
results = results.public_send(key, value) if value.present?
end
results
end
PS: It runs a single query irrespective of the number of params!
Hope that helps!
If a parameter is missing, it will probably be blank. If you pass them all in this will result in clauses like type = ''. For example, if only name is passed in you'll get something like...
where name like '%google%' and type = '' and state = ''
You need to strip out the blank fields. There are various ways to do this. Since you have a special case, the name clause, one good way to handle this is to build the query piece by piece.
query = Office.all
query = query.where(state: params[:state]) if params[:state].present?
query = query.where(type: params[:type]) if params[:type].present?
query = query.where("name like ?","%#{params[:name]}%") if params[:name].present?
The query does not execute until you fetch the values from query.
If there's a lot of simple parameters you can make a Hash and remove the pairs with a blank value.
qparams = {
state: params[:state],
type: params[:type]
}.select { |k,v|
v.present?
}
query = Office.where(qparams)
query = query.where("name like ?","%#{params[:name]}%") if params[:name].present?
Or use the handy compact_blank gem.
using CompactBlank
qparams = {
state: params[:state],
type: params[:type]
}.compact_blank
query = Office.where(qparams)
query = query.where("name like ?","%#{params[:name]}%") if params[:name].present?

Combine two ActiveRecord results and sort by a shared joined table attribute

I have a Convo table and a GroupMeeting table that both are associated with a Msg table.
I want to find all the instances where the current_user has convos or group_meetings with msgs, combine the two, and then show both together to the user in order of the last msg.created_at
Here I have defined both:
#convos = Convo.includes(:msgs).where("sender_id = ? OR recipient_id = ?", current_user, current_user).where.not(:msgs => { :id => nil }).merge(Msg.order(created_at: :desc))
#group_meetings = current_user.group_meetings.includes(:msgs).where.not(:msgs => { :id => nil }).merge(Msg.order(created_at: :desc))
And then combined them together:
#convos = #convos + #group_meetings
What I can't figure out is how to now sort them by msg.created_at
I have tried the following:
#convos = (#convos + #group_meetings).sort_by(&:"#{msg.created_at}")
#convos.order('msg.created_at DESC')
These all seem to be server-side sorting though. How can I sort these based off the join table, after the array has been created?
Please let me know if I need to supply any other details. Thank you!!
You can try the following:
(#convos + #group_meetings).sort_by { |item| item.msgs.minimum(:created_at) }

Rails get multiple values from db

I'm trying to retrieve multiple values from a database into a single variable and return the whole thing. Here is what I am doing
my_hash = {
'name' => 'John'
'current_location' => 'Sweden'
}
Now I need to go into database and retrieve all records and store them into a single variable, and then i need to add that variable into my_hash so I can return the whole thing. How would I do that?
Example:
last_names = Names.where('first_name = ?', 'John').select('last_name').last_name
my_hash.add(last_names)
return my_hash
Now that above does not works, can somebody tell me proper way to achieve this?
are you trying to do the following?
my_hash = {
'name' => 'John'
'current_location' => 'Sweden'
}
my_hash['last_names'] = Names.where('first_name = ?', 'John')
.select('last_name')
.map { |name| name.last_name }
# or shorthand version .map(&:last_name)
return my_hash
Updated
# get name objects from the database
# add select as an optimization if desired
last_name_list = Names.where('first_name = ?', 'John')
# get an array of only the last_name fields
last_names = last_name_list.map { |name| name.last_name }
# assign the array to the new hash key 'last_names'
my_hash['last_names'] = last_names
see http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-map for documentation on map, note that map and collect are the same
another example
names = Names.where('updated_at >= ?', Date.parse('2013-01-01'))
# get an array of 'full names'
full_names = names.map do |name|
"#{name.first_name} #{name.last_name}"
end

How to have condition in ActiveRecord update

I am trying to update a record. Am using the following code.
Proddiscount.update({:prodid => params[:id]}, {:discount=>params[:discount]})
Query Im trying to have is:
update proddiscount set discount = '' where prodid = ''
If prodif is not a primary key, use update_all (read more here)
Proddiscount.update_all("discount = #{params[:discount]}", ["prodid = ?", params[:prodid])
But it won't trigger validations.
How about doing this:
#prod = Proddiscount.find(:first, :conditions => {prodid => params[:id]})
#prod.update_attributes({:discount=>params[:discount]})

Ruby on Rails: Sum table column row values based on other column data

I have a table with columns 'id', 'resource_id', 'read_time', 'value' where 'value' is a float
What I am trying to accomplish is to return a list of records such that the 'value' of each record is the sum of all the records at a specific 'read_time' but having differing 'resource_id' values.
I am wondering if there is a clever way (ie not looping through all the entries) to accomplish this. Currently I am implementing something along these lines:
#aggregate_meters = []
#res_one_meters = Meter.find(:all, :conditions => ["resource_id = ?", 1])
#res_one_meters.each do |meter|
read_time = meter.read_time
value = meter.value
if res_two_meter = Meter.find(:first, :conditions => ["resource_id = ? AND read_time = ?", 2, read_time ])
value = value + res_two_meter.value
end
aggregate_meter = Meter.new(:read_time => read_time, :value => value, :resource_id => 3)
#aggregate_meters.push(aggregate_meter)
end
Thank you.
ActiveRecord::Calculate is your friend here. Letting you do exactly what you want with one database call. It returns a hash using the unique values in the column used in the group as keys.
Here's the code you wrote, rewritten to use sum.
values = Meter.sum(:value, :group => :read_time)
values.each do |read_time, value|
aggregate_meter = Meter.new(:read_time => read_time, :value => value, :resource_id => 3)
#aggregates_meter.push(aggregate_meter)
end

Resources