Counter Cache для столбца с условиями?
Я новичок в концепции кэширования счетчиков и с некоторыми астрономическими временами загрузки на одной из основных страниц приложения, я считаю, что мне нужно идти по ней.
Большинство кешей-счетчиков, которые мне нужно реализовать, имеют определенные (простые) условия. Например, вот общий запрос:
@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
После миграции... это все, что мне нужно сделать?
Как я могу работать в условиях, о которых я говорил, чтобы минимизировать время загрузки?
Ответы
Ответ 1
Что касается условий с 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. После этого вам должно быть хорошо идти.
Ответ 2
Проверьте counter_culture gem:
counter_culture :category, column_name: Proc.new {|project| project.complete? ? 'complete_count' : nil }
Ответ 3
Вы не должны использовать "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
, чтобы получить к нему доступ
Ответ 4
Вместо 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')")