views:

33

answers:

2

For my blog, I want to incorporate my own commenting system without relying on the default wordpress commenting system. I need the comments module to utilize mongodb instead of mysql and the module needs support for the following:

  1. Threaded comments
  2. Comment Voting
  3. The votes on comments need to be aggregated per each comment author for the entire blog.

In this context, What is best way to represent the data in mongodb?

+1  A: 

How about:

  • A single collection for all comments. Each comment object holds a reference to a parent comment object for threading and of course the a reference to the parent blog post.

  • Each comment object also has numeric vote count that can be updated using mongo's atomic updates.

  • Each specific vote by a user would then be a reference to the comment's id in the user's object directly.

zulkamal
+2  A: 

Just store the comments as you want them represented on your blog. You want threaded/nested comments? Then store them in a nested fashion:

postId: {
  comments: [
    {
      id: "47cc67093475061e3d95369d" // ObjectId
      title: "Title of comment",
      body: "Comment body",
      timestamp: 123456789,
      author: "authorIdentifier",
      upVotes: 11,
      downVotes: 2,
      comments: [
        {
          id: "58ab67093475061e3d95a684"
          title: "Nested comment",
          body: "Hello, this is a nested/threaded comment",
          timestamp: 123456789,
          author: "authorIdentifier",
          upVotes: 11,
          downVotes: 2,
          comments: [
            // More nested comments
          ]
        }
      ]
    },
    {
      // Another top-level comment
    }
  ]
}

The postId refers to the blog post to which the comments belong and has been used as the key (or _id in MongoDB) of the document. Each comment has a unique id, in order to vote or comment on individual comments.

To get the aggregated votes, you'll need to write map-reduce functions somewhere along these lines:

function map() {
  mapRecursive(this.comments)
}

function mapRecursive(comments) {
  comments.forEach(
    function (c) {
      emit(comment.author, { upVotes: c.upVotes, downVotes: c.downVotes });
      mapRecursive(c.comments);
    }
  );
}

function reduce(key, values) {
  var upVotes = 0;
  var downVotes = 0;

  values.forEach(
    function(votes) {
      upVotes += votes.upVotes;
      downVotes += votes.downVotes;
    }
  );

  return { upVotes: upVotes, downVotes: downVotes };
}

I haven't tested these functions and they don't check for null values either. That's up to you :)

Niels van der Rest
Is there a downside to maintaining aggregated vote results of all the users in a separate table and manipulate the users votecount as and when a comment is up/downvoted?
Varma
@Varma: Not really. The only downsides are the extra maintenance and the theoretical possibility that the aggregated data is out of sync with the actual vote counts. But these are minor downsides you shouldn't have to worry about. You can always run a weekly/monthly process that recalculates the aggregates based on the actual data, to make sure they are in sync.
Niels van der Rest