Counter Cache для столбца с условиями? - PullRequest
37 голосов
/ 18 марта 2011

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

Большинство контрэшей, которые мне нужно реализовать, имеют определенные (простые) условия. Например, вот обычный запрос:

@projects = employee.projects.where("complete = ?", true).count

Я сталкиваюсь с проблемой запроса N+1, описанной выше, когда отображаю форму, в которой перечисляется количество проектов для каждого сотрудника компании.

подход

Я действительно не знаю, что я делаю, поэтому поправьте меня!

# new migration
add_column :employees, :projects_count, :integer, :default => 0, :null => false

# employee.rb
has_many :projects

# project.rb
belongs_to :employee, :counter_cache => true

После миграции ... это все, что мне нужно сделать?

Как я могу работать в упомянутых выше условиях, чтобы минимизировать время загрузки?

Ответы [ 4 ]

27 голосов
/ 18 марта 2011

Что касается условий с counter_cache, я бы прочитал это сообщение в блоге .

Единственное, что вы должны сделать, это добавить следующее в файл миграции:

 add_column :employees, :projects_count, :integer, :default => 0, :null => false

 Employee.reset_column_information

 Employee.all.each do |e|
   Employee.update_counters e.id, :projects_count => e.projects.length
 end

Таким образом, счетчик текущих проектов может быть перенесен на новый projects_count, связанный с каждым объектом Employee.После этого вам следует идти.

13 голосов
/ 15 ноября 2014

Чек counter_culture камень:

counter_culture :category, column_name: Proc.new {|project| project.complete? ? 'complete_count' : nil }
7 голосов
/ 26 августа 2015

Вы не должны использовать "counter_cache", а вместо пользовательского столбца:

rails g migration AddCompletedProjectsCountToEmployees completed_projects_count:integer

(добавьте , :default => 0 к строке add_column, если хотите)

rake db:migrate

затем используйте обратные вызовы

class Project < ActiveRecord::Base
  belongs_to :employee

  after_save :refresh_employee_completed_projects_count
  after_destroy :refresh_employee_completed_projects_count

  def refresh_employee_completed_projects_count
    employee.refresh_completed_projects_count
  end
end

class Employee
  has_many :projects

  def refresh_completed_projects_count
    update(completed_projects_count:projects.where(completed:true).size)
  end
end

После добавления столбца вы должны выполнить инициализацию в консоли или в файле миграции (в def up):

Employee.all.each &:refresh_completed_projects_count

Тогда в вашем коде вы должны позвонить employee.completed_projects_count, чтобы получить к нему доступ

6 голосов
/ 09 февраля 2015

Вместо update_counters я использую update_all

Вам не нужна строка Employee.reset_column_information И она быстрее, потому что вы делаете один вызов базы данных

Employee.update_all("projects_count = (
   SELECT COUNT(projects.id) FROM projects 
   WHERE projects.employee_id = employees.id AND projects.complete = 't')")
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...