how to ? Rails 3 ActiveRecord eager loading and AREL

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) :

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

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

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 Hsiao