views:

33

answers:

2

I'm trying to write a named scope that will order my 'Products' class based on the average 'Review' value. The basic model looks like this

Product < ActiveRecord::Base
 has_many :reviews

Review < ActiveRecord::Base
  belongs_to :product
  # integer value

I've defined the following named scope on Product:

named_scope :best_reviews, 
 :select => "*, AVG(reviews.value) score", 
 :joins => "INNER JOIN (SELECT * FROM reviews GROUP BY reviews.product_id) reviews ON reviews.product_id = products.id", 
 :group => "reviews.product_id", 
 :order => "score desc"

This seems to be working properly, except that it's adding the 'score' value in the select to my Product instances, which causes problems if I try to save them, and makes comparisons return false (@BestProduct != Product.best_reviews.first, becuase Product.best_reviews.first has score=whatever).

Is there a better way to structure the named_scope? Or a way to make Rails ignore the extra field in the select?

A: 

Wow, so I should really wait before asking questions. Here's one solution (I'd love to hear if there are better approaches):

I moved the score field into the inner join. That makes it available for ordering but doesn't seem to add it to the instance:

named_scope :best_reviews, 
 :joins => "INNER JOIN (
  SELECT *, AVG(value) score FROM reviews GROUP BY reviews.product_id
  ) reviews ON reviews.product_id = products.id", 
 :group => "reviews.product_id", 
 :order => "reviews.score desc"
Kerinin
+1  A: 

I'm not a Rails developer, but I know SQL allows you to sort by a field that is not in the select-list.

Can you do this:

:select => "*", 
:joins => "INNER JOIN (SELECT * FROM reviews GROUP BY reviews.product_id) reviews ON reviews.product_id = products.id", 
:group => "reviews.product_id", 
:order => "AVG(reviews.value) desc"
Bill Karwin
This may be sqlite-specific, but I get an error using the AVG function in the order clause: SQLite3::SQLException: misuse of aggregate: AVG()
Kerinin