I need to build a nested comments system in a Rails 3 application that allows for comments on many models (articles, posts, etc) and am debating rolling my own solution along the lines of this post. There are gems available like acts_as_commentable_with_threading with awesome_nested_set, but they feel bloated for my needs.
- I need to be able to add comments to multiple models
- I need to be able to add comments to comments, infinitely deep
- I need to be able to efficiently retrieve all descendants for a post, article, etc
- I need to be able to efficiently present the comments in their appropriate nesting
My question is, wer开发者_运维技巧e I to roll my own solution what potential hiccups I could face. I want to avoid going down one path only to reach a dead end. My initial concerns relate to efficiently querying for children. Say, for instance, getting a list of an articles descendant comments (children and children of children).
Anyone have input on this? Thanks.
There are two kinds of nesting you can do: a tree and a nested set.
acts_as_tree
stores only a parent_id
and so it is really fast to write new entries, but you have to recursively walk the chain of id numbers to get a list of all the children. This is not a good choice when you need to do lots of reads.
awesome_nested_set
records three bits of information: parent_id
, lft
and rgt
. The left and right values are calculated so that they contain all the children ids for that entry. This is very fast for read operations but slower to write.
In your case I think awesome_nested_set
is more appropriate. You might think it seems overkill, but nested sets get complicated in a hurry. You need to use the nested set pattern to efficiently query children.
You only need to use two methods to render the entire tree of comments: iterate over Comment.roots
and for each comment, render comment.children
.
class ModelController < ApplicationController
def show
@model = Model.find_by_id(params[:id])
@comments = @model.comments.roots
end
end
<ul id="comments">
<% @comments.each do |comment| %>
<%= render :partial => 'comment', :object => comment %>
<% end %>
</ul>
<!-- _comment partial -->
<li class="comment">
<!-- comment markup -->
<% if comment.children.present? %>
<ul>
<%= render :partial => 'comment', :collection => comment.children %>
</ul>
<% end %>
</li>
To save a nested comment, simply fill in the parent_id
and awesome_nested_set
will do the rest. I don't think rolling your own solution will be any more elegant than this.
Update: Looks like the awesome_nested_set
hasn't been updated in some time. Check out ancestry instead. Does basically the same things.
A tree structure, yes, is a good idea - however it is the query execution itself that is of the utmost concern and i don't think most tree implementations as a gem take this into account and whilst nested set is alright - i reckon if something really bad happened, its overcomplicated in terms of write operations.
I'd check out Recursive CTEs - whilst not database agnostic, it gives you a nice data structure to work with without having to have extra attributes to track.
I used to do something like this by adding the following fields to the comments table:
attached_to_controller (string)
attached_to_id (int)
Then when showing I would make an AJAX call to the comments index and filter based on these two fields.
Of course, when creating comments you need to pass the appropriate values for these fields.
精彩评论