Ruby on Rails - Bullet / N + 1

时间:2014-05-20 21:13:05

标签: ruby-on-rails performance rails-bullet

On Rails 4.我最近为我的开发环境安装了bullet gem来清理我的应用程序的N + 1查询。相关模型:

提交:属于类别和用户。有很多SubmissionDetails。

用户:有多个提交

类别:有很多提交内容。属于奖项。

奖励:包含多个类别(以及按类别提交的内容)

提交详细信息:属于提交内容

在我提交的索引页面中,我有一个each do语句来显示当前用户提交的每个提交。

<% current_user.submissions.order('created_at DESC').in_groups_of(3, false) do |group| %>
  <div class="row">
    <% group.each do |submission| %>

之后,我列出了有关提交的信息,包括其相关类别,奖励和提交详细信息。 Bullet说我在这个陈述中遇到了N + 1个问题:

N+1 Query detected Submission => [:category] Add to your finder: :include => [:category]
N+1 Query detected Submission => [:user] Add to your finder: :include => [:user]
N+1 Query detected Submission => [:submission_details] Add to your finder: :include => [:submission_details]

每当我尝试将.includes添加到所有这三个模型时,它只选择我列出的第一个(这并不奇怪)。我想我需要在涉及多个模型时采用不同的路线 - 可能是连接语句?

(当我将:category作为第一项时,它会添加此通知):

N+1 Query detected Category => [:award] Add to your finder: :include => [:award]

(因此,我还需要在声明中加入一种方法,使奖励适合那里,再次,通过类别提交了许多提交)。

所以假设我不能为三个不同的模型做一个.includes,还有另一种方法可以解决这个问题吗?感谢。

2 个答案:

答案 0 :(得分:7)

为了更清楚,让我让细节更加明显:

class User < ActiveRecord::Base
  has_many :submissions
end

class Submission < ActiveRecord::Base
  belongs_to :category
  belongs_to :user
  has_many   :submission_details
end

class SubmissionDetail < ActiveRecord::Base
  belongs_to :submission
end

class Category < ActiveRecord::Base
  belongs_to :award
  has_many   :submissions
end

class Award < ActiveRecord::Base
  has_many :categories
  has_many :submissions, through: :categories
end

如果我理解正确,那么对于您的current_user,您将列出他的提交内容。 对于每个提交,您要列出submission_details及其所属的类别。 对于每个类别,您也会列出奖项。

<% current_user.submissions.order('created_at DESC').in_groups_of(3, false) do |group| %>
  <div class="row">
    <% group.each do |submission| %>
      ...
      <div><%= submission.category %></div>
      <div><%= submission.category.award %></div>
      <%= submission.submissions_details.each do |submission_detail| %>
        ...
      <% end %>
    <% end %>
  </div>
<% end %> 

您可以通过以下方式使用includes删除N + 1问题:

current_user.submissions.includes(:submission_details, :category => :award)

有关includes的更多详情,请参阅:

  1. Rails guides eager-loading-associations
  2. Rails guides eager-loading-multiple-associations
  3. Rails api - includes
  4. 协会

答案 1 :(得分:2)

要包含这些关联,我会为提交创建一个范围。在您进行操作时,请添加latest范围。

class Submission
  scope :eager,  -> { includes(:submission_details, :category => [:award]) }
  scope :latest, -> { order("created_at DESC") }
end

然后简单地

current_user.submissions.latest.eager [...]

您不应该包含:user,但我注意到Rails对此类引用并不太聪明。