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.