Do you have a method that works fine with a single record, but when you use it on a list causes N+1 queries?
Maybe, you are building a rails app and you want to fetch a list of posts with the latest comment (or something similar)…
You have a model like this…
class Posts
has_many :comments
def latest_comment
comments.order(:created_at).last
end
end
When you are fetching a single post there is no problem…
post = Post.find(id)
puts post.latest_comment
But when you try to fetch a list of posts, the method seems to ignore the includes
and runs a query for each post to get the latest comment for each post =(.
Post.includes(:comments).each do |post|
puts post.latest_comment
end
You can write a different method to get the latest comment with ruby (and not ActiveRecord), but it feels weird, and you know that it can bring you troubles later =(.
def self.latest_comment(post)
post.comments.sort_by(&:created_at).last
end
Is this the only solution?
Well, nop… there are many ways to solve this =)
And although ther is no perfect solution, I want to tell you the one that I think that you should try first…
On the definition of your association, you will pass a scope with the default order. It looks like this…
class Post
has_many :comments, -> { order(:created_at) }
def latest_comment
comments.last
end
end
Post.includes(:comments).each do |post|
puts post.latest_comment
end
If you are not sure about the default order in the association, you can create a new association…
class Post
has_many :comments
has_many :sorted_comments, -> { order(:created_at) }, class_name: “Comment”
def latest_comment
sorted_comments.last
end
end
Post.includes(:sorted_comments).each do |post|
puts post.latest_comment
end
If you want to know more about how this part of the has_many
association work, you can check with the “Scopes” section in the docs.
I hope this tip, can help you =)
Note: As as I said, this solution is not perfect, it can cause some performance problems if you have many comments per posts, because you are preloading all the coments for the list of posts, but don’t worry for now unless you will have many (maybe more than 50) comments per post =)
If you want to review other options, you can see this post with other 4 ways to fix this problem.
Learn just enough fundamentals to be fluent preloading associations with ActiveRecord, and start helping your team to avoid n+1 queries on production.