Rails 3 ActiveRecord: Order by count on association Rails 3 ActiveRecord: Order by count on association mysql mysql

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")  }