6

On Rails 4. I recently installed the bullet gem for my development environment to clear up my app's N+1 queries. Relevant models:

Submissions: Belongs to Categories and Users. Has many SubmissionDetails.

Users: Has many Submissions

Categories: Has many Submissions. Belongs to Awards.

Awards: Has many Categories (and Submissions through Categories)

SubmissionDetails: Belongs to Submissions

In my submission's index page, I have an each do statement to display each submission made by the current user.

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

After that, I list information about the submission, including its associated category, award, and submission details information. Bullet is saying I'm having N+1 issues with this statement:

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]

Every time I try to add .includes with all three of those models, it only picks the first one I list (this is not surprising). I figure I need to go a different route when multiple models are involved--perhaps a join statement?

(When I make :category the first item, it adds this notice):

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

(So I also need to include as part of the statement a way to make Award fit in there as well, which, again, has many Submissions through Categories).

So assuming I can't do one .includes for three different models, is there another way to go about this? Thanks.

Rachel9494
  • 849
  • 1
  • 7
  • 27

2 Answers2

9

Just to be more clear, let me make the details more visible:

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

If I understand correctly, for your current_user, you are listing his submissions. For each submission you want to list submission_details and the category it belongs. For every category you list the award too.

<% 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 %> 

You can remove N+1 problem by using includes in the following manner:

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

For more details about includes please refer to:

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

associations

Community
  • 1
  • 1
cristian
  • 8,676
  • 3
  • 38
  • 44
  • Thanks so much for that excellent formatting, that looks a lot better than what I laid out! For the record, I have discovered what my problem has been--it is a possible bug with bullet! See this URL: https://github.com/flyerhzm/bullet/issues/147 – Rachel9494 May 21 '14 at 17:33
2

To include those associations, I would create a scope for submissions. While you're at it, add a latest scope.

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

Then simply

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

You shouldn't have to include :user, but I've noticed Rails isn't too clever about such references.

Damien Roche
  • 13,189
  • 18
  • 68
  • 96
  • Thanks so much. I just found out what my problem is for this issue I've been working on! See this URL: https://github.com/flyerhzm/bullet/issues/147 Everything I tried I kept getting that "unused" message. So I'm not going insane!! – Rachel9494 May 21 '14 at 17:32
  • Great to hear @Rachel9494! – Damien Roche May 21 '14 at 17:37