Rails: Order custom model using custom method - ruby-on-rails

I have a custom model called product, and it has many reviews.
i have a method that calculates the review
def rating
total = 0
reviews_count = reviews.count
return 0 if reviews_count == 0
reviews.each do |review|
total += review.grade
end
total.to_f/reviews_count
end
i would like to know how could i use this method to Order my products.
At products_controller.rb, if i use:
#products = Product.all.order("price")
its easy, it gives me the products list ordered by price. But, if i use, for example:
#products = Product.all.sort_by{|p| p.rating}
it gives me an array and not a "ActiveRecord::Relation"
I would like to know how could i order my product using a custom method that returns a value.

In general, you can't. Ordering happens in your database, which has no knowledge about any method you typed in your application. What you need is a way of translating your method into a valid sql. In your case, you can do:
Product.joins(:reviews).group('products.id').order('AVG(reviews.grade)')
That will give you sorted results and the relation object. However, relations with join are not that nice to work with, especially if you try to add another join. Also this might get quite slow when your database grows.

What you're doing in your example is running the query then using sort_by to sort the result set.
If you want to get back an activerecord collection instead of an array, and potentially chain this with other scopes, you should move the logic from your method into SQL, and put it in a scope.

Related

sum method alternatives for active records

I am in a legacy Ruby on Rails project. Rails v2.3.9.
I have a model class product, in database there is products table.
class Product < ActiveRecord::Base
...
end
There is price attribute which is a integer type.
I got all products (ActiveRecords) at some point. I need to calculate the total price of all products I got. I know I can do:
total_price = all_products.sum(&:price)
It works.
But it also triggers a database query SELECT sum(...). Is there an alternative way to calculate the summation of price of all products without triggering any database query? I know I can use for loop, but I wonder any other ways?
sum with block is delegated to Enumerable and it will always hit the database if all_products is not previously loaded, so you have to make sure it is not being lazy loaded.
In terms of performance, SUM query would be the fastest way to get the result as it doesn't need to load all records and makes the operation in the database and not in memory.
In your case, if you have the collection loaded and still creating a query, you can use
total_price = all_products.map(&:price).sum
which will default to kindof rockusbacchus solution
.inject { |sum, element| sum + element }
If you already have the products loaded into all_products, you can use Ruby's inject method to sum the prices like so:
total_price = all_products.inject(0){|accumulator, product| accumulator + product.price}
However, you will likely find that this takes longer than just running the extra query. You might want to familiarize yourself with the other "*ect" methods in Ruby such as select and reject. Here's a decent article reviewing them:
https://blog.abushady.com/2014/08/27/select-reject-collect-detect-inject.html

Rails 5 get specific field/value from table

I have a table called group. I want this method to return just the content of the relevant record's ID field. At the moment it returns an active record object ID.
def get_group_name(group_id)
Group.select([:name]).where("id = ?", group_id)
end
Thanks in advance.
I think you can do easier with find
def get_group_name(group_id)
Group.find(group_id).name
end
This will get you only the name of the group.
def get_group_name(group_id)
Group.where(id: group_id).limit(1).pluck(:name).first
end
It will run this query:
SELECT name
FROM groups
WHERE id = ?
LIMIT 1;
A side note is, be careful of what you’re doing. Any time you have a method to get a single field’s value, while it can be more efficient at times, it can easily be misused. If you’re looping over a collection of group ids trying to grab all of the names, then you’d be better off 1 query up front for all of the names as opposed to 1 per group id on the page. So just keep and eye on your console and pay attention to the queries you’re running.
Also, if you are looking over a collection, you may want to look into includes for your ActiveRecord queries, to include the group data in the previous query. You can benchmark this all to figure out what’s fastest for your use case.

HATBM association, how can I get pairs of associated models?

Given following models and association:
(source: rubyonrails.org)
How can I get an array of pairs (physician_name, patient_name) that are appointed for certain day (appointment_date)? You can assume that one patient will never go to the same physician twice. Never.
I already tried things like:
#appointments = Appointment.where(appointment_date: params[:date])
but I have no idea what to do further. Should I iterate through this array and get every pair like this below?
#appointments.each do |appointment|
#physician = Physicians.where(id: :appointment.physician_id)
#patient = Patients.where(id: :appointment_patient_id)
I believe there's much easier way.
I'm using Rails 4.2.5.1.
I think what you want is approximately this:
Appointment.includes([:physician, :patient]).where(:date => appointment_date).map{|a| [a.physician.name, a.patient.name]}
Since only the Physician and Patient models have the names, they'll need to be loaded in the query (ok, you could avoid it by doing some fancy SQL trickery, but this is database-agnostic, which is convenient). Hence includes, which eager-loads associated models.
Then use .where to return only the appointments on the day you want (may be more complex if you're actually setting times in those DateTime values).
And finally, iterate over the list and return an Array of Arrays (Ruby not having Tuples) containing the names.

Rails - Get records of a specific id and store it in an Array

How do I get records from a table with a specific ID and store these records in an Array?
For better understanding I try to explain it to you more clear:
Imagine a table with the following columns: (ID,FIX_ID,AMOUNT)
where ID is an unique ID which always will auto_incremented by 1.
FIX_ID is an ID which can appear multiple times in the table.
And AMOUNT is just a simple type which represents the amount of some "things".
So all I want to get now is every record from the table which have the FIX_ID that I am looking for.
Remember: The FIX_ID is not unique => it can appear multiple times.
And that's it. So imagine that I want to get all records with the FIX_ID of 10. All these records which I will get should be stored in an Array.
My question to you: Is it possible to realize this "request" to the database with ActiveRecord?
If so, then how?
You can get an ActiveRecord::Relation object like this:
ModelName.where('FIX_ID = ?', 10)
Relation object will perform a database query as soon as you call methods like all, each or any? on it. If you really need an array, call to_a on that Relation:
ModelName.where('FIX_ID = ?', 10).to_a

Extraction of ids from a collection of objects

I have a collection of objects students. I want to get only ids of all the students. Student model is implemented in Datamapper. I am new to Ruby on Rails and Datamapper. Is there any way so that I can get id of all the students in collection students. So basically I want the following thing:
students = Student.all
ids = students.get_ids
I don't know how to implement get_ids.
Variable "students" is an array, you should not apply get_ids on it.
The function should get no parameters.
def get_ids
Student.all.map { |student| student.id }
end
If you really need all the student objects, use students.map(&:id) which is short for students.map{|s| s.id} and returns an array of all ids.
To get the ids directly from your database, use Student.where(...).pluck(:id) without all, which is much faster and less memory intensive than instantiating all the student objects.
Edit:
Sorry, the pluck method is ActiveRecord only. But the other answers have an alternative with the fields option.
If you just want to get an active record relation, try this:
Student.select(:id)
That should return you an ActiveRecord relation with all the objects and IDs in each object.
If what you want is not an ActiveRecord relation, but rather just the ids, go with the simple "pluck" method
Student.pluck(:id)
You need fields
Student.all(:fields=>[:id])
See more : How to fetch only specified fields of model with DataMapper?

Resources