Rails 3 ActiveRecord: Order by count on association
Using named scopes:
class Song has_many :listens scope :top5, select("songs.id, OTHER_ATTRS_YOU_NEED, count(listens.id) AS listens_count"). joins(:listens). group("songs.id"). order("listens_count DESC"). limit(5)Song.top5 # top 5 most listened songs
Even better, use counter_cache
which will be faster because you'll only because using one table in your query
Here is your song class:
class Song < ActiveRecord::Base has_many :listens def self.top order('listens_count DESC').limit(5) endend
Then, your listen class:
class Listen < ActiveRecord::Base belongs_to :song, counter_cache: trueend
Make sure you add a migration:
add_column :comments, :likes_count, :integer, default: 0
Bonus points, add test:
describe '.top' do it 'shows most listened songs first' do song_one = create(:song) song_three = create(:song, listens_count: 3) song_two = create(:song, listens_count: 2) popular_songs = Song.top expect(popular_songs).to eq [song_three, song_two, song_one] endend
Or, if you want to go with the above method, here it is a bit more simply, and using a class method rather than scope
def self.top select('comments.*, COUNT(listens.id) AS listens_count'). joins(:listens). group('comments.id'). order('listens_count DESC'). limit(5)end
For rails 4.x try this if your rows without any association matters:
scope :order_by_my_association, lambda { select('comments.*, COUNT(listens.id) AS listens_total') .joins("LEFT OUTER JOIN listens ON listens.comment_id = comments.id") .group('comments.id') .order("listens_total DESC") }