10 Tips for Eager Loading to Avoid n+1 Queries in Rails

codenode
2 min readJul 7, 2016

--

Eager loading to prevent n+1 queries is great if you know how to do it! However, the syntax and use cases can be a little nuanced. Here are 10 tips for getting it right.

1. Use the Bullet gem to identify n+1 queries to fix

Install the gem, configure the settings, then browse around your app in development. You’ll get a popup alert on every n+1 query and see an indication of how to resolve it by showing which associations need to be eager loaded. It takes about 2 minutes to install so grab it and then get to work fixing queries. You may find these queries initiated all across your MVC files so keep an eye out and try to refactor them into your model.

2. has_many associations need plural class name includes

@libraries = Library.where(size: 'large').includes(:books)

3. belongs_to / has_one associations need singular class name includes

@books = Book.all.includes(:author)

4. Load multiple associations with comma separation

@library = Library.all.includes(:books, :magazines, :scrolls)

5. Load 1-level deep nested associations as a hash

@library = Library.all.includes( books: :author )

You might alternately see this with curly brace / hash rocket syntax:

@library = Library.all.includes( :books => :author )

6. Load 2+ level deep nested associations as a nested hash

@library = Library.all.includes( books: [ author: :bio ] )

And with curly brace / hash rocket syntax:

@library = Library.all.includes( :books => { :author => :bio } )

7. Load complex associations with commas and nested hashes

@library = Library.all.includes( { :books => :author }, :scrolls, :magazines )

All hashes all the way up there, but remember you don’t need curly braces if there is no nesting:

@library = Library.all.includes( :books => :author, :scrolls => :scribe )

8. Put includes right after the query conditions but before calculations and limits

@libraries = Library.where(size: "large").includes(:books).limit(5)
@authors = Author.where(genre: "History").includes(:books).limit(3)

9. The includes method is actually a shortcut for two types of eager loading

The includes method actually shorthands preload and eager_load, two slightly differing methods of pre-fetching data from associated models.

preload: Initiates two queries, the first to fetch the primary model and the second to fetch associated models.

eager_load: Does a left outer join which initiates one query to fetch both primary and associated models.

Using includes allows Rails to handle the selection of preload versus eager_load for you! In case Rails makes an inefficient choice, however, you can override it by using one of the underlying methods.

10. The Searchkick gem provides support for eager loading, too

This is one I ran into that’s fairly specific. Result sets from elastic search queries generated by the Searchkick gem can pre-fetch their associated models. The syntax is this:

Book.search "moby dick", include: [:author, :isbn, :publisher]

That sums it up for pre-fetching associated model data! Hope your app is feeling snappy now.

--

--