如何使用ActiveRecord按作业数量对作者进行排序?

Sij*_*der 6 ruby activerecord ruby-on-rails

假设我有Book模型和Author模型.我想列出按书籍数量排序的所有作者.最好的方法是什么?

我知道如何在SQL中执行此操作,方法是使用嵌套选择或使用某些连接执行.但我想知道的是如何使用ActiveRecord很好地完成这项工作.

dpl*_*nte 9

取自http://www.ruby-forum.com/topic/164155

Book.find(:all, :select => "author_id, count(id) as book_count", :group => "author_id", :order => "book_count")
Run Code Online (Sandbox Code Playgroud)


nit*_*der 9

正如Kevin建议的那样,counter_cache是​​最简单的选择,绝对是我会用的.

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

class Book < ActiveRecord::Base
  belongs_to :author
end
Run Code Online (Sandbox Code Playgroud)

如果您使用的是Rails 2.3,并且您希望这是默认排序,则可以使用新的default_scope方法:

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

  default_scope :order => "books_count DESC"
end
Run Code Online (Sandbox Code Playgroud)

books_count是执行反缓存行为的领域,并有可能比直接在默认范围内使用它一个更好的方式,但它给你的想法,并会完成这项工作.

编辑:

响应评论询问如果非rails应用程序改变数据,counter_cache是​​否会起作用,它可以,但不是默认方式,因为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
Run Code Online (Sandbox Code Playgroud)

现在您没有安装counter_cache,但是如果您根据counter_cache约定命名数据库books_count中的字段,那么当您查找时:

@Author = Author.find(1)
puts @author.books.size
Run Code Online (Sandbox Code Playgroud)

它仍将使用计数器缓存的数字,而不是执行数据库查找.当然这仅在rails应用程序更新表时才有效,因此如果另一个应用程序执行了某些操作,那么在rails应用程序返回之前,您的数字可能会不同步,必须保存.解决这个问题的唯一方法是,如果你的rails应用程序不经常查找事情以使其无关紧要,则可以同步数字.


小智 7

我建议将计数缓存书计数作为Author的另一个属性(Rails通过关联选项支持此项).这是迄今为止最快的方法,Rails非常适合确保它保持计数同步.

  • 感谢您快速准确的回答.我用你的答案搜索了更多信息,并找到了http://railscasts.com/episodes/23-counter-cache-column,它也很有用.我选择了railsninja的答案,因为它更全面,更能帮助过路人. (2认同)