Explain Codes LogoExplain Codes Logo

Rails includes with conditions

sql
eager-loading
performance
joins
Alex KataevbyAlex Kataev·Aug 14, 2024
TLDR

Quickly fetch associated records in Rails where only certain conditions are met:

User.includes(:posts).where(posts: { published: true })

Eager loading Users with their published posts becomes a breeze. If you run into SQL errors due to conditions on joins, use references(:posts) as a lifeline.

Selective eager loading

Let's selectively include important posts when loading User records:

class User < ApplicationRecord # Biologically, the mother of all posts — but we only care about the important ones 😅 has_many :important_posts, -> { where(important: true) }, class_name: 'Post' end

Now let's meet our VIP users with their VIP posts:

User.includes(:important_posts)

Party tip: Always gatecrash with test-first ethos when it comes to performance. Watch out for the uninvited guest at parties: SQL injection when user input is involved!

"To preload, or not to preload... That's the query!"

Diving deeper into preload, eager_load, and includes, can optimize performance:

  • preload: Fires two queries, one for the main model, and one for the associated model. It's a peaceful pick for when you don't need conditions on associations.
  • eager_load: A solo-artist that performs a LEFT OUTER JOIN. This rockstar springs into action when conditions are required.
  • includes: This one is a quick-change artist, behaves like preload or eager_load based on the situation.

Nesting and joining made easy

Got nested associations? No worries, includes method got you covered:

User.includes(notes: [:grades]).where('grades.passed = ?', true)

Need more SQL control? You may call upon joins:

User.joins(:posts).where(posts: { published: true })

It gives you absolute power over the SQL but beware of the N+1 query menace that could hit you harder than Thanos' snap!

Goodbye to deprecated .conditions method

That old bag :conditions is deprecated. Here's how you safely update:

# "Conditions", I don't feel so good... 🕷️ # Deprecated style (Older than iPhone 4 ), avoid this: has_many :published_posts, conditions: ['published = ?', true] # Here's iPhone 12 (or Rails 5+), use this: has_many :published_posts, -> { where(published: true) }