Reputation: 53
As for now I'm working on a blog application that has article/comment models connected via has_many/belongs_to associations. To create nested comments functionality I use ancestry gem. However, I would like to eager load all of the descendants of a comment. Is there any ideas of how to approach this? I tried using join and where but it seems like they produce n + 1 queries. Here is how I call the method to display them in the view.
<%= nested_comments_display comments.arrange(:order => :created_at) %>
And here is nested_comments_display method
def nested_comments_display(comments)
comments.map do |comment, sub_comments|
render(comment) + content_tag(:div,nested_comments_display(sub_comments),
:class => "nested_comment")
end.join.html_safe
end
I also use decent_exposure gem and my CommentsController looks like this
class CommentsController < ApplicationController
expose(:article)
expose(:comments, ancestor: :article)
expose(:comment, attributes: :comment_params)
....
end
Upvotes: 4
Views: 1438
Reputation: 9742
The easiest way to solve this (that I know of), would be to create an object that holds the entire subtree collection preloaded, and then just ask for children from that in-memory object...
class CachedAncestryCollection
def initialize(collection)
@collection = collection.to_a
end
def children_for(parent_id = nil)
@collection.select do |node|
parent_id ? (node.ancestry && node.ancestry.match(/#{parent_id}$/)) : node.ancestry.nil?
end
end
end
# ...
preloaded_subtree = Comment.where(:id => comments.map(&:subtree_ids))
cached = CachedAncestryCollection.new(preloaded_subtree)
def nested_comments_display(cached, parent_id = nil)
content_tag(:div) do
cached.children_for(parent_id).map do |child|
nested_comments_display(cached, child.id)
end
end
end
Upvotes: 2