如何设置新添加的ActiveRecord计数器缓存的值?

时间:2009-04-20 00:33:58

标签: ruby-on-rails activerecord

我有一个模型对象,之前没有计数器缓存,我通过迁移添加它。问题是,我试过并且未能根据我在迁移中已经拥有的子对象的数量来设置计数器缓存的起始值。任何更新缓存值的尝试都没有写入数据库。我甚至试图从控制台上做到这一点,但它永远不会发生。任何直接写入父级上的值的尝试都会被忽略。

更改计数器缓存更新的子项数(应该如此),并从子项中删除“:counter_cache => true”将允许我更新父项的值。但这是作弊。我需要能够添加计数器缓存,然后将其起始值设置为迁移中的子节点数,这样我就可以从显示它的页面的正确值开始。

这样做的正确方法是什么,以便ActiveRecord不会覆盖我?

2 个答案:

答案 0 :(得分:2)

您想使用update_counters方法,此博客文章包含更多详细信息:

http://josh.the-owens.com/archives/2007/11/03/rails-edge-change-how-to-add-a-counter-cache-to-an-existing-db-table/

关于这个主题的RailsCasts也是一个很好的资源:

http://railscasts.com/episodes/23-counter-cache-column

答案 1 :(得分:0)

规范的方法是使用reset_counter_cache,即:

Author.find_each do |author|
  Author.reset_counter_cache(author.id, :books)
end

...如果这些表的大小适中,那就应该怎么做,即。 e。 <= 1,000,000行。

但是::对于任何大的事情,这都需要几天的时间,因为它需要对每一行进行两次查询,并完全实例化模型等。

这是一种将速度提高约5个数量级的方法:

Author
  .joins(:books)
  .select("authors.id, authors.books_count, count(books.id) as count")
  .group("authors.id")
  .having("authors.books_count != count(books.id)")
  .pluck(:id, :books_count, "count(books.id)")
  .each_with_index do |(author_id, old_count, fixed_count), index| 
    puts "at index %7i: fixed author id %7i, new books_count %4i,  previous count %4i" % [index, author_id, fixed_count, old_count] if index % 1000 == 0
    Author.update_counters(author_id, books_count: fixed_count - old_count)
  end

也可以仅使用一个查询直接在SQL中执行此操作,但是以上内容对我来说已经足够好了。请注意,它使用先前计数的差值与正确计数之间的某种程度的复杂关系:这是必要的,因为update_counters不允许设置绝对值,而只能增加/减少它。将该列标记为只读。