Как установить значение вновь добавленного кэша счетчика ActiveRecord?

У меня есть объект модели, в котором раньше не было кеша счетчика, и я добавил его с помощью миграции. Дело в том, что я пытался и не смог установить начальное значение кеша счетчика на основе количества дочерних объектов, которые у меня уже были в миграции. Любая попытка обновить значение кэша не записывалась в базу данных. Я даже пытался сделать это с консоли, но у меня ничего не вышло. Любая попытка записи непосредственно в это значение родителя игнорировалась.

Изменение количества дочерних элементов обновило кеш счетчика (как и должно быть), а удаление «:counter_cache => true» из дочернего элемента позволило бы мне обновить значение родителя. Но это обман. Мне нужно было иметь возможность добавить кеш-счетчик, а затем установить его начальное значение на количество дочерних элементов в миграции, чтобы затем я мог начать с правильных значений для страниц, которые будут его отображать.

Как правильно это сделать, чтобы ActiveRecord не переопределял меня?


person John Munsch    schedule 20.04.2009    source источник


Ответы (2)


Вы хотите использовать метод update_counters, в этом сообщении блога есть более подробная информация:

josh.the-owens.com добавить кэш счетчика в существующую db-таблицу

Этот RailsCasts по теме также является хорошим ресурсом:

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

person James Avery    schedule 20.04.2009

Каноническим способом является использование reset_counter_cache, т.е.:

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

... и именно так вы должны это сделать, если эти таблицы имеют скромный размер, т.е. е. ‹= 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 не позволяет устанавливать абсолютное значение, а только увеличивать/уменьшать его. В противном случае столбец помечается только для чтения.

person Matthias Winkelmann    schedule 16.09.2018