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誤り0です。絶対値を設定するには、次のようにします。update_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コメントのコールバックのようになります。

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
于 2012-10-09T14:28:13.847 に答える