The following line in a list of stories an article belongs to—on an edit article page, allowing me to assign the article to stories—works but produces an n+1 query (I think).
The bit that is causing the problem is @article.stories.include?(s)
, which checks to see if an article already belongs to a story:
<%= check_box_tag "article[story_ids][]", s.id, @article.stories.include?(s) %>
Log says Rails is doing 50+ queries (all stories) because of that one bit; article_id = 266 in the example:
Story Exists (0.4ms) SELECT 1 AS one FROM "stories" INNER JOIN "articles_stories" ON "stories"."id" = "articles_stories"."story_id" WHERE "articles_stories"."article_id" = $1 AND "stories"."id" = $2 LIMIT 1 [["article_id", 266], ["id", 6]]
Story Exists (0.3ms) SELECT 1 AS one FROM "stories" INNER JOIN "articles_stories" ON "stories"."id" = "articles_stories"."story_id" WHERE "articles_stories"."article_id" = $1 AND "stories"."id" = $2 LIMIT 1 [["article_id", 266], ["id", 51]]
Story Exists (0.3ms) SELECT 1 AS one FROM "stories" INNER JOIN "articles_stories" ON "stories"."id" = "articles_stories"."story_id" WHERE "articles_stories"."article_id" = $1 AND "stories"."id" = $2 LIMIT 1 [["article_id", 266], ["id", 41]]
Etc., etc.
The actual number of stories article_id = 266 belongs to is…2:
select * from articles_stories where article_id = 266;
article_id | story_id
------------+----------
266 | 35
266 | 5
(2 rows)
How do I fix it?
UPDATE: DB, whole chunk & controller
Article.rb:
has_and_belongs_to_many :stories
Story.rb:
has_and_belongs_to_many :articles
And the whole chunk of code the check_box_tag is in—prints a list of stories by category you can assign to an article—is:
<% @stories.group_by(&:category).sort_by{|c, stories| c.category}.each do |c, stories| %>
<div class="admin-stories-in-categories">
<strong><%= c.category unless c.nil? %></strong>
<ul>
<% for s in stories %>
<li>
<%= check_box_tag "article[story_ids][]", s.id, @article.stories.include?(s) %>
<%= s.story %>
</li>
<% end %>
</ul>
</div>
<% end %>
In articles_controller.rb:
@stories = Story.all.includes(:category).order(:story)
ANSWER:
AbM's answer below makes the N+1 queries go away and instead returns a single query:
Returns `(0.8ms) SELECT "stories"."id" FROM "stories" INNER JOIN "articles_stories" ON "stories"."id" = "articles_stories"."story_id" WHERE "articles_stories"."article_id" = $1 [["article_id", 266]]`