How to sort Rails AR.find by number of objects in a has_many relationship

前端 未结 7 1974
名媛妹妹
名媛妹妹 2020-12-24 09:32

How can I write an AR find query to have the results ordered by the number of records in a has_many association?

class User < ActiveRecord::Base
  has_man         


        
相关标签:
7条回答
  • 2020-12-24 09:46

    Your question doesn't make sense. The :order parameter specifies a column name and an optional ordering direction i.e. asc(ending) or desc(ending).

    What is the result that you're trying to achieve?

    0 讨论(0)
  • 2020-12-24 09:47

    I would advise you not to write direct SQL, since implementations of it may vary from store to store. Fortunately, you have arel:

    User.joins(:photos).group(Photo.arel_table[:user_id]).
         order(Photo.arel_table[:user_id].count)
    
    0 讨论(0)
  • 2020-12-24 09:53

    I'd add this as a comment on the top answer, but I can't for some reason. According to this post:

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

    The User.all(options) method will be deprecated after Rails 3.0.3, and replaced with a bunch of other (handy, chainable) active record type stuff, but it makes it very hard to figure out how to put together the same kind of query.

    As a result, I've gone ahead and implemented the counter cache method. This was pretty easy and painless with the exception that you need to remember to update the column information in your migration, otherwise all existing records will have "0."

    Here's what I used in my migration:

    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
    

    In theory this should be faster, too. Hope that's helpful going forward.

    0 讨论(0)
  • 2020-12-24 09:54

    The easiest way to achieve this is probably to add a counter cache to that model and then sort by that column.

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

    And be sure to add a column to your users table called photos_count.

    Then you will be able to...

    User.find(:all, :order => 'photos_count')
    
    0 讨论(0)
  • 2020-12-24 09:58

    If you don't want to add a counter cache column, your only option is to sort after the find. If you :include the association in your find, you won't incur any additional database work.

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

    Note the negative sign in the sort_by block to sort from high to low.

    0 讨论(0)
  • 2020-12-24 10:01

    If you don't want an extra column, you could always ask for an extra column in the returned result set:

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

    This generates the following 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
    
    0 讨论(0)
提交回复
热议问题