Eager Loading Polymorphic Associations in Ruby on Rails

Paulo Carvalho
3 min readOct 27, 2020

For when .includes is not enough.

Photo by Paul Smith on Unsplash

Basic Rails Associations

Rails’ ActiveRecord associations permits relating one model to another. Take the example below:

class Post < ApplicationRecord
has_many :comments
end
class Comment < ApplicationRecord
belongs_to :post
end

A Post has many Comments. This allows us to call Post.first.comments to get all the comments associated with a post! ActiveRecord internally generates our required SQL queries:

SELECT "posts".* FROM "posts" WHERE "posts".id = 1
SELECT "comments".* FROM "comments" WHERE "comments".post_id

N+1 Queries and .includes

However, imagine we are serializing this data to return as part of a controller for the /posts index. In that case, instead of getting the comments for just one post, we would get it for multiple posts: Post.all.each { |p| puts p.comments }. What happens to our SQL queries?

SELECT "posts".* FROM "posts"
SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = 1
SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = 2
...
SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = N

As seen above, we end up getting one query to get all the posts and then N queries to get the comments for each of the N posts. Sounds familiar? This is what is often called N+1 queries and is a major slow down in large applications.

Luckily, Rails provides a built in solution for this. Just add the .includes method on the call to the parent object: Post.all.includes(:comments).each { |p| puts p.comments }. This informs Rails to eager load the comments resulting in only 2 total queries:

SELECT "posts".* FROM "posts"
SELECT "comments".* FROM "comments" WHERE "comments"."post_id" IN (1, 2, ..., N)

In case you want to preemptively detect N+1 queries in your code before it makes it into production, I recommend using the Bullet Gem that I go into more detail in this other article:

--

--

Paulo Carvalho

Want to chat about startups, consulting or engineering? Just send me an email on paulo@avantsoft.com.br.