Rails 3 ActiveRecord:按关联计数排序

Chr*_*jer 59 mysql activerecord ruby-on-rails ruby-on-rails-3

我有一个名为的模型Song.我也有一个名为的模型Listen.A Listen belongs_to :song,和一首歌:has_many listens(可以多次收听).

在我的模型中,我想定义一个方法self.top,该方法应该返回最多听过的前5首歌曲.如何使用这种has_many关系实现这一目标?

我正在使用Rails 3.1.

谢谢!

cly*_*yfe 93

使用命名范围:

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
Run Code Online (Sandbox Code Playgroud)

  • 谢谢.我必须在连接和顺序之间添加组("songs.id").:) (11认同)
  • 我猜你正在使用MySQL.在Postgres中你需要聚合所有属性:.group("listens.id,songs.#{Song.column_names.join(",songs.")}") (11认同)
  • SUre,从作用域定义中删除limit语句,然后在控制器调用中如下:`Song.where(:user_id => current_user.id).top.limit(5)` (5认同)

Nea*_*eal 32

更好的是,使用counter_cache哪个会更快,因为你只会因为在查询中使用一个表

这是你的歌曲课程:

class Song < ActiveRecord::Base
  has_many :listens

  def self.top
    order('listens_count DESC').limit(5)
  end
end
Run Code Online (Sandbox Code Playgroud)

然后,你的听课:

class Listen < ActiveRecord::Base
  belongs_to :song, counter_cache: true
end
Run Code Online (Sandbox Code Playgroud)

确保添加迁移:

add_column :comments, :likes_count, :integer, default: 0
Run Code Online (Sandbox Code Playgroud)

奖励积分,添加测试:

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
Run Code Online (Sandbox Code Playgroud)

或者,如果你想使用上面的方法,这里更简单,并使用类方法而不是 scope

def self.top
    select('comments.*, COUNT(listens.id) AS listens_count').
      joins(:listens).                                                   
      group('comments.id').
      order('listens_count DESC').
      limit(5)
end
Run Code Online (Sandbox Code Playgroud)

  • 在迁移中,您的意思是"listens_count"而不是"likes_count"吗? (7认同)