Eager loading not working well for me

Man my app is causing me so much grief lately.

Okay let’s say I have:

class Discussion
has_many :posts
end

class Post
belongs_to :discussion
acts_as_tree
end

Then let’s say I grab a discussion object and try to eager load all of
its posts:

discussion = Discussion.find(:first, :include => [:posts])

Seems simple enough and it grabs an entire tree of posts. However:

discussion.posts.first.children

will run a MySQL query instead of (more smartly) simply accessing the
already eager-loaded posts.

I’m getting pretty aggravated. I’ve been trying to properly handle going
about my app for ages and nothing seems to work optimally. Any ideas?

More importantly: should I even bother.

Eleo wrote:

discussion = Discussion.find(:first, :include => [:posts])

Seems simple enough and it grabs an entire tree of posts. However:

discussion.posts.first.children

will run a MySQL query instead of (more smartly) simply accessing the
already eager-loaded posts.

To avoid a query you have to eager load all the levels you’ll be
accessing:

discussion = Discussion.find(:first, :include => {:posts => :children})


We develop, watch us RoR, in numbers too big to ignore.

That doesn’t make any sense to me though. When I do
Dicussion.find(:first, :include => [:posts]) I have effectively grabbed
all the posts, including the children and those children and so on. All
of the posts, including children, have the same discussion_id, after
all.

Another problem with :include => {:posts => :children} is that it only
grabs one level of children. I can make the include argument more
complex but there’s no way to know how deep a tree is.

I’ve found a better way to get children without queries is something
like:

discussion.posts.collect {|post| if post.parent_id = num}.compact

Which will grab the children of a certain parent. This of course only
works if I indeed have an entire tree of posts pre-loaded.

Eleo wrote:

I’ve found a better way to get children without queries is something
like:

discussion.posts.collect {|post| if post.parent_id = num}.compact

Which will grab the children of a certain parent. This of course only
works if I indeed have an entire tree of posts pre-loaded.

OK, I didn’t realise discussion.posts contained every post in
the discussion, rather than just the top-level posts.

Perhaps you should use Better Nested Set:
http://opensource.symetrie.com/trac/better_nested_set/

This will allow you to fetch the posts in depth-first order,
so you can render whole discussion trees without searching,
sorting, or re-querying.


We develop, watch us RoR, in numbers too big to ignore.

I am using better nested set right now, but I don’t see precisely how
I’m supposed to render without searching/sorting/querying. As it is,
foo.children or the like will still query the database. Can you
elaborate on how this is supposed to work, please?

Eleo wrote:

I am using better nested set right now, but I don’t see precisely how
I’m supposed to render without searching/sorting/querying. As it is,
foo.children or the like will still query the database. Can you
elaborate on how this is supposed to work, please?

Fetch all posts using discussion.posts.find(:all, :order => ‘lft’),
then write a recursive rendering function that loops through these,
watching for the parent_id to change, increasing the depth by one when
it changes to the id of the previous object, otherwise popping back up
the levels until the parent_ids match.


We develop, watch us RoR, in numbers too big to ignore.

This is the best I could come up with

def show_thread(posts, parent_id = nil)
#result = ‘’
result = “

    \n”
    posts.each do |post|
    if post.parent_id == parent_id
    result += “
  • \n”
    result += render(:partial => ‘post’, :object => post) + “\n”
    result += show_thread(posts, post.id) unless posts.select {|p|
    p.parent_id == post.id }.empty?
    result += “
  • \n”
    end
    end
    result += ‘

end

It’s a massive waste of CPU ticks since it iterates through the array
once for every item in the array and uses Array#select each time, but
everything else I tried wasn’t working. The larger the thread, the
dumber this method gets. If anyone has anything better please let me
know.

I tried something along the lines of what you suggested. While I
figured out how to determine if the depth changed I ultimately couldn’t
figure out how to close off all the tags properly.

That makes sense (I think). Thanks.