Как отсортировать Rails AR.find по количеству объектов в отношении has_many - PullRequest
10 голосов
/ 11 июня 2009

Как мне написать запрос на поиск AR, чтобы упорядочить результаты по количеству записей в ассоциации has_many?

class User < ActiveRecord::Base
  has_many :photos
end

Я хочу сделать что-то вроде ...

User.find(:all, :order => photos.count)

Я понимаю, что моя находка не является допустимым кодом. Скажем, у меня есть следующие данные.

User 1, which has 3 photos 
User 2, which has 5 photos 
User 3, which has 2 photos 

Я хочу, чтобы моя находка вернула мне пользователей в порядке ...

User 2, 
User 1, 
User 3 

на основе количества фотографий пользователей

Ответы [ 7 ]

19 голосов
/ 11 июня 2009

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

class Photo < ActiveRecord::Base
  belongs_to :user, :counter_cache => true
end

И обязательно добавьте в свою таблицу users столбец с именем photos_count.

Тогда вы сможете ...

User.find(:all, :order => 'photos_count')
13 голосов
/ 12 июня 2009

Если вам не нужен дополнительный столбец, вы всегда можете запросить дополнительный столбец в возвращенном наборе результатов:

User.all(:select => "#{User.table_name}.*, COUNT(#{Photo.table_name}.id) number_of_photos",
         :joins => :photos,
         :order => "number_of_photos")

Это генерирует следующий SQL:

SELECT users.*, COUNT(photos.id) number_of_photos
FROM `users` INNER JOIN `photos` ON photos.user_id = users.id
ORDER BY number_of_photos
7 голосов
/ 11 июня 2009

Если вы не хотите добавлять столбец кэша счетчика, вы можете отсортировать только после поиска. Если у вас :include ассоциация в вашей находке, вам не понадобится дополнительная работа с базой данных.

users = User.find(:all, :include => :photos).sort_by { |u| -u.photos.size }

Обратите внимание на отрицательный знак в блоке sort_by для сортировки от высокого к низкому.

5 голосов
/ 23 мая 2013

Я бы посоветовал вам не писать прямой SQL, поскольку его реализация может варьироваться от магазина к магазину. К счастью, у вас есть арель:

User.joins(:photos).group(Photo.arel_table[:user_id]).
     order(Photo.arel_table[:user_id].count)
1 голос
/ 11 июня 2009

Счетчик кеша поможет, но вам понадобится дополнительный столбец в БД.

0 голосов
/ 16 февраля 2011

Я бы добавил это как комментарий к верхнему ответу, но не могу по какой-то причине. Согласно этому посту:

http://m.onkey.org/active-record-query-interface

Метод User.all(options) будет устаревшим после Rails 3.0.3 и заменен кучей других (удобных, цепных) активных типов записей, но из-за этого очень трудно понять, как собрать один и тот же вид запроса.

В результате я реализовал метод кэширования счетчиков. Это было довольно легко и безболезненно, за исключением того, что вам нужно помнить, чтобы обновить информацию о столбцах в вашей миграции, иначе все существующие записи будут иметь «0».

Вот что я использовал в своей миграции:

class AddUserCountToCollections < ActiveRecord::Migration
  def self.up
    add_column :collections, :collectionusers_count, :integer, :default => 0
    Collection.reset_column_information
    Collection.all.each do |c| 
      Collection.update_counters c.id, :collectionusers_count => c.collectionusers.count
    end
  end

  def self.down
    remove_column :collections, :collectionusers_count
  end
end

В теории это тоже должно быть быстрее. Надеюсь, это будет полезно в будущем.

0 голосов
/ 11 июня 2009

Ваш вопрос не имеет смысла. Параметр :order указывает имя столбца и необязательное направление упорядочения, то есть asc (конец) или desc (конец).

Какого результата вы пытаетесь достичь?

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