Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CouchDB - hierarchical comments with ranking. Hacker News style

I'm trying to implement a basic way of displaying comments in the way that Hacker News provides, using CouchDB. Not only ordered hierarchically, but also, each level of the tree should be ordered by a "points" variable.

The idea is that I want a view to return it in the order I except, and not make many Ajax calls for example, to retrieve them and make them look like they're ordered correctly.

This is what I got so far:

  • Each document is a "comment".
  • Each comment has a property path which is an ordered list containing all its parents.

So for example, imagine I have 4 comments (with _id 1, 2, 3 and 4). Comment 2 is children of 1, comment 3 is children of 2, and comment 4 is also children of 1. This is what the data would look like:

{ _id: 1, path: ["1"] },
{ _id: 2, path: ["1", "2"] },
{ _id: 3, path: ["1", "2", "3"] }
{ _id: 4, path: ["1", "4"] }

This works quite well for the hierarchy. A simple view will already return things ordered the way I want it.

The issue comes when I want to order each "level" of the tree independently. So for example documents 2 and 4 belong to the same branch, but are ordered, on that level, by their ID. Instead I want them ordered based on a "points" variable that I want to add to the path - but can't seem to understand where I could be adding this variable for it to work the way I want it.

Is there a way to do this? Consider that the "points" variable will change in time.

like image 926
Luca Matteis Avatar asked May 14 '12 07:05

Luca Matteis


2 Answers

Because each level needs to be sorted recursively by score, Couch needs to know the score of each parent to make this work the way you want it to.

Taking your example with the following scores (1: 10, 2: 10, 3: 10, 4: 20)

In this case you'd want the ordering to come out like the following:

.1
.1.4
.1.2
.1.2.3

Your document needs a scores array like this:

{ _id: 1, path: [1], scores: [10] },
{ _id: 2, path: [1, 2], scores: [10,10] },
{ _id: 3, path: [1, 2, 3], scores: [10,10,10] },
{ _id: 4, path: [1, 4], scores: [10,20] }

Then you'll use the following sort key in your view.

emit([doc.scores, doc.path], doc)

The path gets used as a tiebreaker because there will be cases where sibling comments have the exact same score. Without the tiebreaker, their descendants could lose their grouping (by chain of ancestry).

Note: This approach will return scores from low-to-high, whereas you probably want scores (high to low) and path/tiebreaker(low to high). So a workaround for this would be to populate the scores array with the inverse of each score like this:

{ _id: 1, path: [1], scores: [0.1] },
{ _id: 2, path: [1, 2], scores: [0.1,0.1] },
{ _id: 3, path: [1, 2, 3], scores: [0.1,0.1,0.1] },
{ _id: 4, path: [1, 4], scores: [0.1,0.2] }

and then use descending=true when you request the view.

like image 198
Peter Dixon-Moses Avatar answered Oct 30 '22 19:10

Peter Dixon-Moses


Maybe anybody interestingly the thread on this question with variants of solutions:

http://mail-archives.apache.org/mod_mbox/couchdb-dev/201205.mbox/thread -> theme "Hierarchical comments Hacker News style" 16/05/2012

like image 20
Andrii Syrokomskyi Avatar answered Oct 30 '22 19:10

Andrii Syrokomskyi