Thursday, January 10, 2008

Eager Loading

There are many instances where you need to find an object, or several objects, then require their associated objects afterwards. In the simple case, this would require (1 + n) SQL queries: one for the object(s) and n for the associated object(s) for each object. To solve this problem, RoR allows you to do eager loading of associations when performing a find. This means that the (1 + n) SQL queries can be squashed into 1 and RoR handles the placing of the data correctly. Examples of eager loading can be found here (under Eager loading of associations).

I thought I would take this one step further. Using the example of Post, I thought I would have something like:

class Post
has_many :comments, :include => :author, :order => "comments.date ASC, authors.name ASC"
end

This would eager load the authors of each comment in the post, ordered by the date created and then by alphabetical order of name.

This works great when doing:

@comments = post.comments

and later on using @comments in the view to display.

However, I started to have major problems when I attempted a find on the collection such as:

comment = post.comments.find_by_author_id(author.id)

When executing such a command, I got a database error. The error reported that the authors table was not included in the FROM clause of the SQL query (due to the order by author.name ASC). I checked the query in the output console, and sure enough, there was no join to the authors table. I tried looking around for explanations, but ended up with none.

I finally gave up and removed the :include and :order from the has_many relationship and ended up doing this instead when I required eager loading and ordering:

@comments = post.comments.find(:all, :include => :author, :order => "comments.date ASC, authors.name ASC")

Hopefully, if anyone knows the explanation as to why the previous did not work, I would love to hear it. Otherwise, I hope this solution works for you.


W

No comments: