I am using Ruby on Rails 4 and I would like to understand why during the eager loading process are run further SQL queries even if data is eager loaded. That is, I have the following code that eager loads :comments
the right way:
@articles = @current_user.articles.includes(:comments)
When the above code runs and I "track" what is happening in the logger with the following code:
@articles.each do |article|
logger.debug article.comments
end
Then the logger says:
Article Load (0.4ms) SELECT ...
Comment Load (0.5ms) SELECT ... WHERE `articles`.`id` IN (...)
#<ActiveRecord::Associations::CollectionProxy [#<Comment id: 1, title: "Hello A">, #<Comment id: 2, title: "Hello B">]>
#<ActiveRecord::Associations::CollectionProxy [#<Comment id: 3, title: "Hello A">, #<Comment id: 4, title: "Hello C">]>
#<ActiveRecord::Associations::CollectionProxy [#<Comment id: 5, title: "Hello D">, #<Comment id: 6, title: "Hello E">]>
...
The above output indicates that the eager loading is working as expected: no N+1 problem since ActiveRecord::Associations::CollectionProxy
objects are loaded when running article.comments
.
However when I try to run code as like the following (note the find_by
clause):
@articles.each do |article|
logger.debug article.comments.find_by(:title => "Hello A")
end
Then the logger says:
Article Load (0.4ms) SELECT ...
Comment Load (0.5ms) SELECT ... WHERE `articles`.`id` IN (...)
Comment Load (0.4ms) SELECT ... AND `comments`.`title` = 'HELLO A'
#<Comment id: 1, title: "Hello A">
Comment Load (0.4ms) SELECT ... AND `comments`.`title` = 'HELLO A'
#<Comment id: 3, title: "Hello A">
Comment Load (0.4ms) SELECT ... AND `comments`.`title` = 'HELLO A'
nil
...
The above output indicates that the eager loading is not working as expected: a SQL query runs for each comment.
So, my questions/doubts are:
- Why in the last case the
find_by
clause makes the eager loading to do not work (note: it happens even in cases when I "filter"article.comments
by using a clause other thanfind_by
)? - Should Ruby on Rails handle data already loaded in
ActiveRecord::Associations::CollectionProxy
objects as array so that it avoids to hit the database?! - How can I solve the problem in order to avoid the N+1 problem in the last case?