1

我正在使用 Ruby on Rails 3.2.2,我想将计数器缓存值设置为“自定义”值。也就是说,此时(在我的迁移文件中)我正在尝试使用以下代码:

def up
  add_column :articles, :comments_count, :integer, :default => 0

  Article.reset_column_information

  Article.find_each do |article|
    # Note: The following code doesn't work (when I migrate the database it   
    # raises the error "comments_count is marked as readonly").
    Article.update_column(:comments_count, article.custom_comments.count)
  end
end

换句话说,我想将:comments_count(计数器缓存数据库表列)设置为自定义值(在我的情况下,该值是article.custom_comments.count-注意:这custom_comments不是 ActiveRecord 关联,而是Article模型类中声明的方法;它返回一个整数值),与关联无关has_many

也许,我可以/应该使用类似的东西

Article.reset_column_information

Article.find_each do |article|
  Article.reset_counters(article.id, ...)
end

但似乎该方法没有关联reset_counters就无法工作。has_many

如何将:comments_count计数器缓存值设置为与“自定义关联”相关的给定值?

4

2 回答 2

1

接受答案包括迭代方法,这对于:comment_count以外的现有值是错误的,对于它的当前值设置计数器。要设置绝对值,请执行以下操作:0update_counter

Article.update_counters(article.id, comments_count: comments.count - article.comments_count)

如果您无论如何都必须获取每一行的正确计数,您还可以更轻松地使用Article.reset_counters(article.id, :comments)

要使用更少的查询来做到这一点,请使用以下命令:

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
于 2018-09-16T12:23:37.563 回答
0

您将 comments_count 描述为计数器缓存,但计数器缓存被严格定义为 has_many 关系中关联记录的数量,您说这不是。

如果获得所需值的唯一方法是通过 Article 上的方法,那么您将不得不遍历所有 Article 对象并更新每个对象。

Article.find_each do |article|
  article.update_attribute(:comments_count, article.custom_comments.count)
end

这是非常低效的,因为它正在加载和保存每个对象。如果 custom_comments 的定义(您实际上并没有解释)是您可以用 SQL 表达的东西,那么在数据库中进行此更新无疑会更快。这可能看起来像这样:

CREATE TEMP TABLE custom_comment_counts_temp AS
  SELECT articles.id as id, count(comments.id) as custom_comments 
    FROM articles 
    LEFT JOIN comments ON articles.id = comments.article_id
    WHERE <whatever condition indicates custom comments>
    GROUP BY articles.id;

CREATE INDEX ON custom_comments_counts_temp(id);

UPDATE articles SET comments_count = (SELECT custom_comments FROM custom_comment_counts_temp WHERE custom_comment_counts_temp.id = articles.id);

DROP TABLE custom_comment_counts_temp;

(这假设 postgresql - 如果您使用的是 MySQL 或其他数据库,它可能看起来不同。如果您根本不使用关系数据库,则可能不可能)

此外,由于根据 Rails 相当狭窄的定义,它不是计数器缓存,因此您需要编写一些回调来保持这些值更新 - 可能是after_save评论回调,如下所示:

评论.rb:

after_save :set_article_custom_comments


def set_article_custom_comments
  a = self.article
  a.update_attribute(:comments_count, a.custom_comments.count)
end
于 2012-10-09T14:28:13.847 回答