Last Updated: February 25, 2016
·
1.114K
· bryanmikaelian

Rails N+1 with nested has_many

Here is something I stumbled upon tonight that seems a bit off. Consider the following relationships:

class Posts < ActiveRecord::Base
  belongs_to :user
  has_many :post_images
end

class PostImage < ActiveRecord::Base
  belongs_to :post
end

class User < ActiveRecord::Base
  has_many :posts
end

Now, lets pretend we have an index action that shows 25 posts and the user who created the post. The standard way to avoid the N+1 (that we all know by heart) is as follows:

def index
  Post.includes(:user).limit(25)
end

What about the scenario where we want to show all the images and the user who created the post? Your first inclination is:

def index
  Post.includes(:user, :post_images).limit(25)
end

But actually, this doesn't quite solve the N+1 problem. The above code will eager load the post images but it will still need to select the post, per set of post images. The "correct" way is actually as follows:

def index
  Post.includes(:user, {post_images: :post}).limit(25)
end

Boom. The posts, per set of post images, will no longer need to be selected. However, this seems...off, but it works and it makes sense...kind of. I could not find an explanation for this inside the Rails docs, specifically for this scenario. I personally ran in to this scenario while debugging a N+1 issue that was occurring when a set of records were serialized into json.

Regardless, now you know how to solve a n+1 in this situation.