views:

358

answers:

3

I'm considering using CouchDB for an upcoming site, but I'm a little confused as far as far as how to implement a system of user ratings for the site. Basically, each item of content can be rated by a given user. What way of doing this makes the most sense in the CouchDB model? I would think the DRYest and most logical way would be to have 3 different document types, Content, Users, and a user_rating doc that looks something like this.

{
  user_id: "USERID"
  content_id: "CONTENTID"
  rating: 6
}

Then, I'd create a view where the map was the set of all content docs and user_rating docs keyed by content doc ids, and where the reduce tallied the mean of the ratings and returned the content doc keyed by content doc id.

Is that the best way of doing this? I haven't yet found much in the way of resources on CouchDB best practices so I'm pretty unsure of all this stuff.

My Conclusion: The accepted answer below, which is what I pretty much was going to implement does work, but beware, the docs need to be keyed by content doc id which makes advanced queries based on other document properties troublesome. I'm going back to SQL for my needs in this app.

+1  A: 

Well Damien Katz, one of the Couchdb developers, gives a description of a similar process, so you might be doing it the way that the Couchdb folks intend.

Joe Soul-bringer
Thanks for the feedback, but I was looking more for a specific example for this task. I understand the principles he talked about there, and I used them to derive the implementation I proposed above. I'm just not sure if my conclusion is correct.
Andrew Cholakian
+8  A: 

Sounds like you've got a reasonable idea going. CouchDB is so new that I think it'll take awhile for best practices to shake out.

A map/reduce pair like this might form a reasonable starting point.

map:

function(doc) {
   if(doc.type='rating' && doc.content_id) {
     emit(doc.content_id, doc.rating);
   }
}

reduce:

function(keys, values) {
   return sum(values)/values.length
}

NB: That map function requires adding the proper type to your Rating model:

{
  type: 'rating',
  user_id: "USERID",
  content_id: "CONTENTID",
  rating: 6
}
Hank
Paul J. Davis
A: 

I wrote about a similar situation (although simpler than your example). I was adding article ratings to my blog and decided to use CouchDB to store the ratings themselves. I think you've got the right idea.

Here's a thought, though. Do you care who rated what, like for display somewhere or tracking? If so, carry on :)

If not then why not just update the content document's rating attribute to += 1 (and perhaps the user document's rated attribute to .push( doc._id ) if you want to prevent a user from rating content more than once).

This would greatly simplify your document handling and give better performance when 'reading' ratings to display on pages (since you'll already have the content document assumingly)... This would be at the cost of making the actual process of rating more expensive (bigger documents going to the server, etc).

Seems to me that sometimes CouchDB (and other key-value databases) are at their best when things aren't totally normalized.

thenduks
When you update the document you will run into conflicts if two users rate the same document at the same time.
mdorseif
It can be handled because of the revs. But you're right, it wouldn't exactly scale to high-traffic stuff. But really... how many people are rating at the same time?
thenduks
If you're writing reddit . . . lots!
Eric Normand