Rails 3 ActiveRecord: Order by count on association
Solution 1
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
Solution 2
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)
end
end
Then, your listen class:
class Listen < ActiveRecord::Base
belongs_to :song, counter_cache: true
end
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]
end
end
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
Christoffer Reijer
Working as a security technician with a masters degree in computer science, and two small kids. So life is usually very busy with way too little sleep. But I still try to find some time to code on small hobby projects such as Stoffi Music Player. Really enjoy Ruby on Rails, Python and C#. Oh, and IT-security of course.
Updated on November 17, 2020Comments
-
Christoffer Reijer over 3 years
I have a model named
Song
. I also have a model namedListen
. AListen
belongs_to :song
, and a song:has_many listens
(can be listen to many times).In my model I want to define a method
self.top
which should return the top 5 songs listened to the most. How can I achieve that using thehas_many
relation?I'm using Rails 3.1.
Thanks!