# Eager Loading ### Resolving the N+1 query problem N+1 query is a common anti-pattern which happens when you call a relation inside a collection of model. Let's take this example: ```ruby Post.query.each do |post| puts "Post category: #{post.category.name}" end # Output: # SELECT * FROM posts; # SELECT * FROM categories WHERE post_id = 1 # SELECT * FROM categories WHERE post_id = 2 # SELECT * FROM categories WHERE post_id = 3 # SELECT * FROM categories WHERE post_id = 4 # SELECT * FROM categories WHERE post_id = 5 # .... ``` Since it's faster to query once 100 models than to query 100 times for each model, we could optimize it by calling two requests: one for the posts then one for the related categories. Clear offers convenient methods called `with_[relation]` which build the query and cache the related model. Let's try it: ```ruby Post.query.with_category.each do |post| puts "Post category: #{post.category.name}" end # Output: # SELECT * FROM category WHERE post_id IN (SELECT id FROM posts); # SELECT * FROM posts; ``` We just resolved our problem, and we will execute only two requests. ## Deep inclusion `with_[relation]` helper allows you to pass a block, which can refine the related objects. Therefore, it's easy to include far-related model like in this example: ```ruby User.query.with_posts(&.with_category).each do |user| puts "User #{user.id}'s posts:" user.posts.each do |post| puts "Post category: #{post.category.name}" end end ``` Since the `with_[relation]` helper return a collection in the block, you can apply filtering over the query: ```ruby User.query.with_posts{ |p| p.where(published: true).with_category{ |c| c.select("name") } }.each do |user| puts "User #{user.id}'s published posts:" user.posts.each do |post| puts "Post category: #{post.category.name}" end end # Note: we encourage using &.xxx notation and scopes for the query above. # It would then be rewritten like this: User.query.with_posts(&.published.with_category(&.select("name"))).each do |user| puts "User #{user.id}'s published posts:" user.posts.each do |post| puts "Post category: #{post.category.name}" end end ```