web-dev-qa-db-ja.com

Rails 3 ActiveRecord:関連付けのカウント順

Songという名前のモデルがあります。 Listenという名前のモデルもあります。 Listenbelongs_to :song、および歌:has_many listens(何度も聴くことができます)。

私のモデルでは、self.topメソッドを定義します。このメソッドは、最も多く聴かれた上位5曲を返します。 has_many関係を使用してそれを達成するにはどうすればよいですか?

私はRails 3.1。

ありがとう!

59

名前付きスコープ の使用:

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
93
clyfe

さらに良いことに、counter_cacheクエリで1つのテーブルを使用するだけなので、高速になります

歌のクラスは次のとおりです。

class Song < ActiveRecord::Base
  has_many :listens

  def self.top
    order('listens_count DESC').limit(5)
  end
end

次に、リスニングクラス:

class Listen < ActiveRecord::Base
  belongs_to :song, counter_cache: true
end

必ず移行を追加してください。

add_column :comments, :likes_count, :integer, default: 0

ボーナスポイント、テストの追加:

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

または、上記のメソッドを使用する場合は、ここでもう少し簡単になり、scopeではなくクラスメソッドを使用します

def self.top
    select('comments.*, COUNT(listens.id) AS listens_count').
      joins(:listens).                                                   
      group('comments.id').
      order('listens_count DESC').
      limit(5)
end
32
Neal

Rails 4.xの場合、関連付けのない行が重要な場合は、これを試してください。

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")
  }
0
Bruno Casali