Find in ActiveRecord object - ruby-on-rails

I have a page where I can either display all customers' details, or if specified in the params, just one customer's details.
My controller looks something like this.
def display_customers
#all_customers = Customer.all
if params[:customer_id]
#customers = Customer.find(:all, conditions: ["id = ?", params[:customer_id]])
else
#customers = #all_customers
end
end
I use #all_customers to populate a dropdown of customers.
I use #customers to perform an each loop through each customer.
Then, if the customer_id param is specified, #customers will just be the single customer.
This works just fine but #customers = Customer.find(...) is an extra DB query.
I already have all the customers in #all_customers so I though there would be a better way to grab the one record I need from there - instead of going back to the DB again.

You can use the find which is built in to all Enumerables...
if params[:customer_id]
#customer = #all_customers.find { |c| c.id == params[:customer_id] }
else
#...
... but probably shouldn't.
Depending on the number of records, this may not be any faster, as your database query will use an index, where the above code uses a linear search through all the returned records. Chances are a second database query is the way to go.
You should also rewrite your query. Rails is smart enough that you don't have to pass conditions when finding a record by its ID; you just find it. These are identical:
# BAD:
#customers = Customer.find(:all, conditions: ["id = ?", params[:customer_id]])
# BETTER:
#customers = Customer.find_by_id(params[:customer_id])
# BEST:
#customer = Customer.find(params[:customer_id])
If you really want it to be an array, just wrap the result in [].

How about this
def display_customers
#all_customers = []
if params[:customer_id].empty?
#all_customers = Customer.all
else
#all_customers << Customer.find(params[customer_id])
end
#all_customers
end
this way
your "select * from customers" query will only be executed when there is no customer_id param (not always). I believe its more efficient rather than selecting all from DB and then filtering

Related

Neo4j.rb How to make one query when filtering parameter may or may not be present?

User and Place are the two nodes having has_many realtionship
#places = User.find(4).places
#places = #places.where(dog: false) if params[:no_dogs]
can I make these two into one so that if param comes then it involve where condition other wise ignore it
I don't believe that the find() method takes zero arguments. Maybe you're wanting this?
#places = User.places # Calling the association gives an unexecuted proxy to a query to build
#places = #places.where(dog: false) if params[:no_dogs]
Or if you wanted to work with the User model:
#users = User.all
#users = #users.where(dog: false) if params[:no_dogs]

Sorting a Rails active record

I have a table A(:name, :address, :country_id). I run this query
ids = [1,2,3]
#result = A.where(country_id: ids)
But, I want to get the results in this order : records containing country_id = 2 first, then 1 and then 3.
How should I write this query?
Edit :
#people = []
#result.each do |r|
#people.push([r,r.name])
end
I have an array #people of arrays now and want to sort it. What should be the query now?
Another Edit :
#people.sort_by! {|p| preferred_order.index(p.first.country_id)}
This worked.
You can use sort_by :
ids = [1,2,3]
preferred_order = [2,1,3]
#result = A.where(country_id: ids)
#result.sort_by! { |u| preferred_order.index(u.country_id) }
One option would be adding an extra column
rails g migration add_sortorder_to_A sort_order:integer
Then add a
before_save :update_sort_order
def update_sort_order
if self.country_id == 1
self.update_attributes(sort_order:2)
elsif self.country_id ==2
self.update_attributes(sort_order:1)
........
end
Alternatively, put your sort_order in the countries and sort on that.
Alternatively, create a dynamic field which will give you the sort_order.
There is probably a more elegant way of doing this, but this should work in a quick and dirty way and allow you to use standard activerecord queries and sorting. (i.e. you can just forget about it once you've done it the once)

Returning a list with ActiveRecord query interface

My Program class has_many Patients
and Patient class has a field like salary
And the json I pass to JBuilder with respond_to :json , respond_with(#org) is like this:
#org = Org.includes(programs: [{hospitals: :nurses}, :patients]).find(params[:id])
Well now if my database has 200 programs that meed the org_id = params[:id] condition it will return them all. BUT this is Not what I want. I want to tell it to return "5" programs and those "five" programs that the "salary" field of their :'patients" table is the highest.
How can I implement this limitation in active record query?
Something like
Program.includes([{hospitals: :nurses}, :patients]).where("organization_id = ?", params[:id]).order("patients.salary desc").limit(5)
Or you can do:
#program_ids = Program.select("id").includes([{hospitals: :nurses}, :patients]).where("organization_id = ?", params[:id]).order("patients.salary desc").limit(5)
#org = Org.includes(programs: [{hospitals: :nurses}, :patients]).where("programs.id = ?", program_ids.collect(&:id)).find(params[:id])
You can also refactor with a subquery in one step (still 2 queries), by checking this question:
subqueries in activerecord
I'm not sure if you can get that information in just one single query

Ruby on Rails. Searching ActiveRecord objects where condition is the presence of one of the associations

I have a Rails code that queries database for employee objects, which have many social_url_for_service (for various services). The way it is implemented, it first gets all employees from database, and then serches for ones with Twitter. Is there any way to look for this association directly (with Employee.find() metod for example) ?
#e = Employee.all
#employees = []
#tweets = []
#e.each do |employee|
if employee.social_url_for_service(:twitter)
#employees << employee
#tweets.concat(Twitter.user_timeline(employee.social_url_for_service(:twitter).split('/').last, count: 3))
end
end
Assuming social_url_for_service is a method that grabs a social_service_link association with a service_name field:
Employee.joins(:social_service_links).where('social_service_links.service_name = ?', "Twitter")
You'll need to update this for your exact table and field names. You can also drop the .where call to return all employees with a service of any kind.

Fastest way to search two models connected via join table in rails given large data set

I have a user model and a cd model connected through a join table 'cds_users'. I'm trying to return a hash of users plus each cd they have in common with the original user.
#user.users_with_similar_cds(1,4,5)
# => {:bob => [4], :tim => [1,5]}
Is there a better/faster way of doing this without looping so much? Maybe a more direct way?
def users_with_similar_cds(*args)
similar_users = {}
Cd.find(:all, :conditions => ["cds.id IN (?)", args]).each do |cd|
cd.users.find(:all, :conditions => ["users.id != ?", self.id]).each do |user|
if similar_users[user.name]
similar_users[user.name] << cd.id
else
similar_users[user.name] = [cd.id]
end
end
end
similar_users
end
[addition]
Taking the join model idea, I could do something like this. I'll call the model 'joined'.
def users_with_similar_cds(*args)
similar_users = {}
Joined.find(:all, :conditions => ["user_id != ? AND cd_id IN (?)", self.id, args]).each do |joined|
if similar_users[joined.user_id]
similar_users[joined.user_id] << cd_id
else
similar_users[joined.user_id] = [cd_id]
end
end
similar_users
end
Would this be the fastest way on large data sets?
You could use find_by_sql on the Users model, and Active Record will dynamically add methods for any extra fields returned by the query. For example:
similar_cds = Hash.new
peeps = Users.find_by_sql("SELECT Users.*, group_concat(Cds_Users.cd_id) as cd_ids FROM Users, Cds_Users GROUP BY Users.id")
peeps.each { |p| similar_cds[p.name] = p.cd_ids.split(',') }
I haven't tested this code, and this particular query will only work if your database supports group_concat (eg, MySQL, recent versions of Oracle, etc), but you should be able to do something similar with whatever database you use.
Yap, you can, with only 2 selects:
Make a join table model named CdUser (use has_many.. through)
# first select
cd_users = CdUser.find(:all, :conditions => ["cd_id IN (?)", args])
cd_users_by_cd_id = cd_users.group_by{|cd_user| cd_user.cd_id }
users_ids = cd_users.collect{|cd_user| cd_user.user_id }.uniq
#second select
users_by_id = User.find_all_by_id(users_ids).group_by{|user| user.id}
cd_users_by_cd_id.each{|cd_id, cd_user_hash|
result_hash[:cd_id] = cd_users_hash.collect{|cd_user| users_by_id[cd_user.user_id]}
}
This is just an ideea, haven't tested :)
FYI: http://railscasts.com/episodes/47-two-many-to-many

Resources