Как отсортировать авторов по количеству книг в ActiveRecord? - PullRequest
6 голосов
/ 06 июня 2009

Допустим, у меня есть модель Book и модель Author. Я хочу перечислить всех авторов, отсортированных по количеству книг. Какой лучший способ сделать это?

Я знаю, как сделать это в SQL, выполнив where .. in с помощью вложенного выбора или с помощью некоторого объединения Но я хотел бы знать, как это сделать с ActiveRecord.

Ответы [ 4 ]

9 голосов
/ 06 июня 2009

Взято из http://www.ruby -forum.com / topic / 164155

Book.find(:all, :select => "author_id, count(id) as book_count", :group => "author_id", :order => "book_count")
9 голосов
/ 06 июня 2009

Как предположил Кевин, counter_cache - самый простой вариант, определенно то, что я бы использовал.

class Author < ActiveRecord::Base
  has_many :books, :counter_cache => true
end

class Book < ActiveRecord::Base
  belongs_to :author
end

И если вы используете Rails 2.3 и хотите, чтобы это был порядок по умолчанию, вы можете использовать новый метод default_scope:

class Author < ActiveRecord::Base
  has_many :books, :counter_cache => true

  default_scope :order => "books_count DESC"
end

books_count - это поле, которое выполняет поведение кэширования счетчика, и, вероятно, есть лучший способ, чем использовать его непосредственно в области действия по умолчанию, но оно дает вам идею и выполнит работу.

EDIT:

В ответ на комментарий, спрашивающий, будет ли counter_cache работать, если приложение, не относящееся к rails, изменяет данные, это вполне возможно, но не по умолчанию, так как Rails увеличивает и уменьшает счетчик во время сохранения. Что вы можете сделать, это написать свою собственную реализацию в обратном вызове after_save.

class Author < ActiveRecord::Base
  has_many :books

  after_save :update_counter_cache

  private
    def update_counter_cache
      update_attribute(:books_count, self.books.length) unless self.books.length == self.books_count
    end
end

Теперь у вас не установлен counter_cache, но если вы назовете поле в базе данных books_count в соответствии с соглашением counter_cache, то при поиске:

@Author = Author.find(1)
puts @author.books.size

Он будет по-прежнему использовать счетный номер счетчика вместо выполнения поиска в базе данных. Конечно, это будет работать только тогда, когда приложение rails обновит таблицу, поэтому, если другое приложение что-то делает, ваши цифры могут быть не синхронизированы, пока приложение rails не вернется и не сохранит данные. Единственный способ обойти это, что я могу придумать, - это использовать cron для синхронизации чисел, если ваше приложение rails не выполняет поиск достаточно часто, чтобы это не имело значения.

7 голосов
/ 06 июня 2009

Я предлагаю счетчик, кэширующий счетчик книг, в качестве другого атрибута для автора (Rails поддерживает это с опцией для ассоциации). На сегодняшний день это самый быстрый метод, и Rails очень хорошо следит за тем, чтобы счетчик синхронизировался.

3 голосов
/ 07 января 2012

Принимая рельсы 3

@authors=Authors.include(:books).sort do |a,b| 
  a.books.size <=> b.books.size
end

Примечание. Возвращается массив записей, а не ActiveRecord: Relation.

Примечание 2: Используйте размер, а не счетчик, так как счетчик будет выполнять вызовы sql в БД.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...