views:

56

answers:

2

Hey,

I'm looking for help with Ruby optimization regarding loading of associations on demand.

This is simplified example. I have 3 models: Post, Comment, User. References are: Post has many comments and Comment has reference to User (:author). Now when I go to the post page, I expect to see post body + all comments (and their respective authors names). This requires following 2 queries:

select * from Post -- to get post data (1 row)
select * from Comment inner join User -- to get comment + usernames (N rows)

In the code I have:

Post.find(params[:id], :include => { :comments => [:author] }

But it doesn't work as expected: as I see in the back end, there're still N+1 hits (some of them are cached though). How can I optimize that?

UPD After some investigation, it looks like code was correct, but it doesn't work as expected in case I have named belongs_to in a Comment model. Once I changed from :author to :user, it worked as expected.

+1  A: 

If you are happy with 2/3 queries, you can try:

@post = Post.find params[:id]
@comments = Comments.find_by_post_id(params[:id], :include => [:author])

or

@comments = @post.comments(:include => [:author])

Edit: Have you tried with:

Post.find(params[:id], :include => { :comments => :author }

Vlad Zloteanu
I did right by the last variant, and still see that it loads authors separately (hitting database more than once, by the # of comments).
Vitaly
http://guides.rubyonrails.org/active_record_querying.html#nested-associations-hash, it looks like you can nest includes
Corey
@Corey - yes, I was following this article as you can see in my questions, but that did't work as expected.
Vitaly
@vlad - did u investigate how one can order comments by create date desc?
Vitaly
@post.comments(:include => [:author], :order => 'created_at DESC')
Vlad Zloteanu
+1  A: 

In my project I have a similar relationship to your Post, Comment, and User models. I only see three actual sql queries.

Post.find(1, :include => { :comments => [:author] })

From the debug log it shows these three queries

SELECT * FROM `posts` WHERE (`posts`.`id` = 1)
SELECT `comments`.* FROM `comments` WHERE (`comments`.`post_id` = 1)
SELECT * FROM `authors` WHERE (`authors`.`id` IN (4,8,15,16,23,42)) 
Corey
I updated, it changes the way it pulls users (authors) depending on what I have in belongs_to.
Vitaly
Did u investigate how one can order comments by create date desc?
Vitaly
so you mean this?: Post.find(1, :include => {:comments => :author}, :order => 'comments.created_at desc')
Corey
Not exactly: it translates this query as inner join instead of additional query and doesn't include author to comments.
Vitaly