# How to ? Rails 3 ActiveRecord eager loading and AREL

**URL:** <https://www.ruby-forum.com/t/how-to-rails-3-activerecord-eager-loading-and-arel/192793>\
**Category:** Rails\
**Created:** [August 5, 2010, 4:48pm UTC](https://www.ruby-forum.com/t/how-to-rails-3-activerecord-eager-loading-and-arel/192793 "2010-08-05T16:48:59Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![m-ryan](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/e0b2c6/32.png) [@m-ryan](https://www.ruby-forum.com/u/m-ryan)\
**Post date:** [August 5, 2010, 4:48pm UTC](https://www.ruby-forum.com/t/how-to-rails-3-activerecord-eager-loading-and-arel/192793/1 "2010-08-05T16:48:59Z")

</div>

Hello everyone,

I would like to eager load scoped records to avoid queries executed in a  
loop (huge performance impact).

The “scoped” part is what is giving me a hard time.

I’m using Rails3 RC.

Does anyone have any idea how I can do it?

Here is a test case : we have an “Article” and a “Comment” Activerecord  
models,

article has many comments  
comment belongs to article

Comment has a scope defined like this (app/models/comment.rb) :

* * *

def recent  
where [“published\_at \>= ?”, 2.days.ago]  
end

* * *

In the ArticlesController (app/controllers/comments\_controller.rb) :

* * *

def index  
@articles = Article.includes(:comments)  
end

* * *

In the view (haml) (app/views/articles/index.html.haml):

* * *

- @articles.each do |article|  
= article.title
  - article.comments.each do |comment|  
= comment.body

* * *

this works fine and no query is executed when accessing the comments for  
each articles (“includes” call in the controller handled the eager  
loading).

But if you try to display only the recent comments for each article :

* * *

- @articles.each do |article|  
= article.title
  - article.comments.recent.each do |recent\_comment|  
= recent\_comment.body

* * *

Now a query is executed for every article in order to fetch it’s recent  
articles.

What can I write (presumably in the “include” call) to get the eager  
loading to work in this situation ?

Cheers,

M-Ryan

---

<div class="post-metadata">

**Author:** ![m-ryan](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/e0b2c6/32.png) [@m-ryan](https://www.ruby-forum.com/u/m-ryan)\
**Post date:** [August 5, 2010, 7:30pm UTC](https://www.ruby-forum.com/t/how-to-rails-3-activerecord-eager-loading-and-arel/192793/2 "2010-08-05T19:30:59Z")

</div>

On Aug 5, 9:48 am, M- Ryan [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> Hello everyone,
> 
> I would like to eager load scoped records to avoid queries executed in a  
> loop (huge performance impact).
> 
> The “scoped” part is what is giving me a hard time.

Can you define a scope at class level, instead of defining a method?

scope :recent, where [“published\_at \>= ?”, 2.days.ago]

Then when you later do

article.comments.recent

the join/include clauses should be chained together _first_, resulting  
in max one query per article.

Jeff

---

<div class="post-metadata">

**Author:** ![m-ryan](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/e0b2c6/32.png) [@m-ryan](https://www.ruby-forum.com/u/m-ryan)\
**Post date:** [August 6, 2010, 9:03am UTC](https://www.ruby-forum.com/t/how-to-rails-3-activerecord-eager-loading-and-arel/192793/3 "2010-08-06T09:03:32Z")

</div>

Thanks for your replay Jeff.

Unfortunately, declaring my scope with the “scope” method instead of a  
regular method returning an ActiveRecord::Relation doesn’t solve my  
problem. (it also creates a new one since “2.days.ago” is only evaluated  
when the app loads the classes and not every time the method is called)

I think the includes method should support relations as well as  
associations (since associations/scopes/relations are so well integrated  
with eachother in rails 3).

That way the following :

@articles = Article.includes(:comments =\> :recent)

would build the relation in a way that recent comments are eagerly  
loaded (instead of complaining that comments have no association named  
“recent”)

Cheers,

M-Ryan

---

<div class="post-metadata">

**Author:** ![m-ryan](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/m/e0b2c6/32.png) [@m-ryan](https://www.ruby-forum.com/u/m-ryan)\
**Post date:** [August 6, 2010, 8:40pm UTC](https://www.ruby-forum.com/t/how-to-rails-3-activerecord-eager-loading-and-arel/192793/4 "2010-08-06T20:40:46Z")

</div>

On Aug 6, 3:03 am, M- Ryan [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> Unfortunately, declaring my scope with the “scope” method instead of a  
> regular method returning an ActiveRecord::Relation doesn’t solve my  
> problem. (it also creates a new one since “2.days.ago” is only evaluated  
> when the app loads the classes and not every time the method is called)

He made a mistake. It should be:

scope :recent, lambda { where [“published\_at \>= ?”, 2.days.ago] }

> I think the includes method should support relations as well as  
> associations (since associations/scopes/relations are so well integrated  
> with eachother in rails 3).  
> That way the following :
> 
> @articles = Article.includes(:comments =\> :recent)

Ok, I see. You want to scope the eager-loaded objects. I guess this is  
a case where Datamapper’s identity map comes in handy.

I haven’t tried this yet, but you might be able to put add scopes to  
the association itself.

has\_many :recent\_comments, :class\_name =\> “Comment”, :conditions  
=\> …

Hm, though that won’t do the lazy eval. I checked the code for  
has\_many. The finder\_sql is constructed the old way, not Arel all the  
way down.

The compromise I can think of is to extend the collection code so it  
loads all the comments in one batch and insert them into your  
articles. The other one I can think of is to use AJAX to load the  
recent comments and have a dedicated controller handle that. It’s  
probably a better design than trying to load a set of articles and  
recent comments for all of them, in a single go, then rendering them  
to the page.

Ho-Sheng H.

> **[lambda { }](http://ruby-lambda.blogspot.com)**
>
> lambda { put(self).in(:pipe).and.call() }
