开发者

how to structure 1-level-nested comments in redis?

I've been struggling to find a data structure for nested comments (only 1 level of nesting, e.g. facebook)

In order to achieve a non-nested "feed" of comments I've been using sorted sets to track comments, using the score as a timestamp and member as a json-encoded set of attributes that contains all information needed to render the comment.

So adding a comment might look like this:

zadd 'users:1:comments', 123456789, {body : 'hello'}

And retrieving it is as simple as this:

zrevrange 'users:1:comments', 0, 20

In order to support nested comments I've tried to expand on this in some way I've brainstormed two different ways, but each has a problem:

1)

Add comment_id to the list of attributes where comment_id points to the parent comment

zadd 'users:1:comments', 123456789, {id : 1, body : 'hello'}
zadd 'comments:1:comments', 123456789, {id : 2, body : 'nested hello', comment_id : 123 }

Would look like this:

-hello
  -nested hello

The problem with this approach is when it comes to pagination. If, say, a comment has 20 nested comments, and I'm only showing the first 10 comments, then the nested tree will be cut off (the parent comment + 9 nested comments will be retrieved)

2)

Put the nested comments into a feed of its own:

This is a parent comment
zadd 'users:1:comments', 123456789, {id: 1, body : 'hello'}
this is a nested comment
zadd 'comments:1:comments' 123456789, {id: 2, body : 'nested hello'} 

However, this would resu开发者_StackOverflow中文版lt in N+1 redis queries when trying to show a user's feed:

zrevrange 'users:1:comments', 0, 20
zrevrange 'comments:1:comments', 0, 20
zrevrange 'comments:2:comments', 0, 20
etc...

...Not to mention the nested comments probably shouldn't be selected with a range.

Ideally, I would like this to work with a single redis query, but I'm not sure how to structure my data so that is possible.

Ideas?


The only way I can come up with that will result in a single redis query is to use Lists.

When adding a parent item you can simply LPUSH it to the top (left) of the list. When adding a child comment you would use something like LINSERT 'user:1:comments' AFTER parent-comment-data child-comment-data.

This causes redis to search for the parent comment data and place the child data immediately after it. This is an O(N) operation and is done top (left) to bottom (right), so the further down the list the parent is the longer this operation will take, so for extremely long lists this could prove problematic (but should be fine if you keep your list/thread sizes in the 4 or 5 digit range).

Then, a simple LRANGE can fetch you the latest comments, both parents and children, limited to any number.

You could do something similar using the score values in sorted sets, giving children a score just lower than the parent. This could significantly complicate inserts though, as you might run out of available scores between two parent comments, meaning you'll have to run an operation to reassign scores for many (or even most) of the comments. If this happens on every insert your inserts could be (needlessly) costly.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜