2
votes

I am just experimenting with mongodb/redis for work. I am considering this scenario, assume a blog for example.

I plan on doing the following queries, primarily:

  1. List posts by a specifc user
  2. List comments by a specific user
  3. List posts based on a specific category
  4. View a single post and its comments

Note: Redis is used to store userid<->username mapping.

Is this a good/flexible schema design? The future plan say would be to add post/comment rating.

Post

  • _id
  • author (db-ref: user._id; index)
  • content
  • category (index)

User

  • _id
  • username (unique index)
  • passwordhash

Comments

  • _id
  • post (db-ref: post._id; index)
  • author (db-ref: author._id; index)

adding Rating/votes

Post

  • _id
  • author (db-ref: user._id; index)
  • content
  • category (index)
  • votes (number)

User

  • _id
  • username (unique index)
  • passwordhash

Comments

  • _id
  • post (db-ref: post._id; index)
  • author (db-ref: author._id; index)
  • votes (number)

CommentVotes

  • _id = author.Id (key)
  • voted: [comment._id, comment._id, ...]

PostVotes

  • _id = author.Id (key)
  • voted: [post._id, post._id]

What do you think? Or am I just nuts?

2
Why is Redis involved here? It seems to be adding complexity for no reason. I'd pick Mongo or Redis and build your db in that. - jcollum
Well, I am not embedding the username in the Comments collection, so to get the username, I would have to query the Users collection. That means if a post has 50 comments, then I am querying the user collection 50 times. I figured, I might just as well use redis and pull usernames from the userids. - States
I think we can address that by keeping the comments in the Post object and not really caring right away about who made the comment. See my answer. If you really really need to know which users 'liked' a comment (Massively.com does this), then I'd make that an occasional update via map/reduce. The end user doesn't need to know right now who 'likes' a post. Eventual consistency is something that comes with the territory in doc dbs. Check out the CAP Theorem. - jcollum
Okay thanks. But what about knowing who posted the comment. Should I be embedding username into the comment and post, beside their userid? - States
you're right, you do need that and probably don't want to make a join, just for speed sake. You could put the username in both places (User, Comment) and update the Comment object if a user ever changes their username (this probably happens infrequently or never, depending on how the app is built). I think in the Mongo world speed is more important than compactness. Disk space is cheaper than having a slow web page. - jcollum

2 Answers

2
votes

I think you're still thinking relationally. I like to think of document data objects as Russian dolls with links (threads?) that can link one doll to another. In your case:

  • Users doll
    • Username
    • Email
    • PasswordHash
    • PostUpvotes (list of Post IDs)
    • CommentUpvotes (list of Comment IDs)
  • Posts doll
    • Comments doll (list of comments)
      • Comment
        • User ID
        • Username
        • TotalVotes (int)
        • Comment ID
    • TotalVotes (int)
    • Category IDs (list) (surely a post can have more than one?)
    • Author ID
  • Categories Doll

You might want to put the votes on comments and posts into the user object then use map/reduce to get the counts every 15 mins or so (this is the 'canonical' use of a map/reduce). The important thing about upvotes is that a User doesn't get to do them twice, so keeping them in the User object makes more sense (no hitting of an index to see if the User has voted on a post already). Since the actual number of upvotes per Post isn't as critical, it can be kept in the Post and updated periodically.