Rails 3 ActiveRecord: порядок по количеству ассоциаций - PullRequest
59 голосов
/ 02 января 2012

У меня есть модель с именем Song.У меня также есть модель с именем Listen.A Listen belongs_to :song и песня :has_many listens (может быть прослушана много раз).

В моей модели я хочу определить метод self.top, который должен возвращать 5 лучших прослушанных песенбольшинство.Как мне добиться этого с помощью отношения has_many?

Я использую Rails 3.1.

Спасибо!

Ответы [ 3 ]

93 голосов
/ 02 января 2012

Использование именованных областей :

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
32 голосов
/ 02 марта 2014

Еще лучше, используйте counter_cache, что будет быстрее, потому что вы будете использовать только одну таблицу в своем запросе

Вот ваш класс песни:

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
0 голосов
/ 17 ноября 2017

Для 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")
  }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...