具有条件的列的计数器缓存?

时间:2011-03-18 02:14:21

标签: ruby-on-rails ruby-on-rails-3

我对计数器缓存的概念不熟悉,并且在我的某个应用程序主页上有一些天文加载时间,我相信我需要继续使用它。

我需要实现的大多数计数器缓存都附加了某些(简单)条件。例如,这是一个常见的查询:

@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 个答案:

答案 0 :(得分:27)

关于counter_cache的条件,我会阅读此blog post

您应该做的一件事是将以下内容添加到迁移文件中:

 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

因此,您当前的项目计数可以迁移到与每个Employee对象关联的新projects_count。在那之后,你应该好好去。

答案 1 :(得分:13)

检查counter_culture gem:

counter_culture :category, column_name: Proc.new {|project| project.complete? ? 'complete_count' : nil }

答案 2 :(得分:7)

您不应使用“counter_cache”,而应使用自定义列:

rails g migration AddCompletedProjectsCountToEmployees completed_projects_count:integer

(如果需要,请将, :default => 0添加到a​​dd_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以便访问它

答案 3 :(得分:6)

而不是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')")