我正在使用Ruby on Rails 3.2.2,我想将一个计数器缓存值设置为" custom"一。也就是说,此时(在我的迁移文件中)我试图使用以下代码:
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
- note :custom_comments
不是ActiveRecord关联,而是Article
模型类中声明的方法;它还返回一个整数值)与<{1}}关联相关的 。
也许,我可以/应该使用像
这样的东西has_many
但似乎没有Article.reset_column_information
Article.find_each do |article|
Article.reset_counters(article.id, ...)
end
关联,reset_counters
方法无效。
如何将has_many
计数器缓存值设置为与&#34;自定义关联相关的给定值&#34;?
答案 0 :(得分:0)
您将comments_count描述为计数器缓存,但计数器缓存严格定义为has_many关系中的关联记录数,您说这不是。
如果获得所需值的唯一方法是通过Article上的方法,那么您将不得不迭代所有Article对象并更新每个对象。
Article.find_each do |article|
article.update_attribute(:comments_count, article.custom_comments.count)
end
这是非常低效的,因为它正在加载并保存每个对象。 如果您可以在SQL中表达custom_comments的定义(您实际上没有解释),那么在数据库中进行此更新无疑会更快。可能看起来像这样:
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
回调,如下所示:
comment.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
答案 1 :(得分:0)
接受答案包括迭代方法,这对于除comment_count
之外的0
的现有值是错误的:update_counter
将计数器 relative 设置为当前值价值观。要设置绝对值,请执行以下操作:
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