0

I have a bucket with posts on the following format:

{title:"foo", 
description:"bar", 
votes: {John:1, Leo:-1, ...}}

I will be continuously querying for posts that have less than N votes and that weren't voted by a specific user yet. The problem is: I can't make views for every specific user, so I have to set one to filter posts with <50 votes and then programatically filter those that weren't voted by the specific user. Is that the correct way to approach this issue?

Kim Stebel
  • 41,826
  • 12
  • 125
  • 142
MaiaVictor
  • 51,090
  • 44
  • 144
  • 286
  • Why do you save votes in some object? Is it possible in your case to save votes for each user (may be it will be easier and more useful): `{"type":"vote", "voterId":123, "votedForId": 1 }` and have `{"type":"item","title":"foo"}`. And if you use your approach you should handle "simultaneous" votes and if there are many users that can vote it can become a problem. – m03geek Jun 22 '13 at 18:12
  • @Alex Uh huh, that's something that was confusing me: I thought I was actually supposed to avoid separating objects like that. That is better, then. But I really need a guide/tutorial that has those things! There are many (simple) things unclear and I'm not really finding on the official docs! And yes, there is many users that can vote. The whole thing is actually just a page where you vote in posts continuously, as fast as possible. – MaiaVictor Jun 22 '13 at 18:41

1 Answers1

3

If you have many users that can vote, you shouldn't use your approach because to add a vote you need to:

  1. Get document
  2. Parse json into some structure
  3. Add some vote to vote array
  4. Write object back to base

So if someone else vote between 1 and 4 steps, and you don't check CAS on write back, that vote will be lost. And if you check CAS and votes come very fast you can get very slow performance.

The solution is, as I said before, to save votes separatly in JSON like:

{
  "type":"vote",
  "voterId": 123,
  "votedFor": 321,
  "timestamp": 131321321
}

And store your items without votes, like this:

{
  "type":"item",
  "itemId": 321,
  "title": foo
}

With this scheme you work only with votes when you need to count them, see what users voted for some item, etc.

Another trick: if you need to display votes, i.e. on your website, you can also have "fast_voutes_count". This means that you can create separate variable that will store only votes count per item: votes:count:for:<itemId>. And if someone votes item you should:

  1. Increment item vote count value by key: votes:count:for:<itemId>
  2. Store user's vote as vote document ({"type":"vote","voterId": 123,"votedFor": 321, "timestamp": 131321321}).

So first value will be used to display votes on a website. And the second one will be used for counting your statistics.

For creating view that you need (map,reduce functions) you can refer to this manual and it's examples. If you're new to map/reduce firstly try to create a view that will display votes only for specific itemId, here is small example:

map: 
function(){
  if (meta.type === "json" && doc.type === "vote"){
    emit(doc.votedFor, null);
  }
}

Then if you want to count (sum) that votes just use _count as reduce function.

PS: In my application, we use such approach to count views for videos: one key-value called "fast_clip_view" is used for displaying info on website and another detailed clipview with userId, clipId and timestamp for detailed stats.

By the way, if you'll need something like "Top 10 items by votes" refer to this question.

Community
  • 1
  • 1
m03geek
  • 2,508
  • 1
  • 21
  • 41