0

I read the Voting with Atomic Operators article. In this article, the voters field is an array of ObjectIds. But I would like to store the voters as an array of embedded documents with the following format:

{
  user: ObjectId,
  date: Date,
  isDownvote: Boolean,  // If false, it's an upvote.
}

The user can upvote or downvote the post just like the voting system provided by Stack Overflow. So, for example, when the user want to upvote a post, there are cases to consider:

  • If a downvote by the user already exists, then update the vote's isDownvote to false.
  • Else push a new vote with isDownvote being false.

How can I push/pull a vote in single query with this format of votes?

Trantor Liu
  • 8,770
  • 8
  • 44
  • 64

2 Answers2

1
$push : { voters : {
  user: ObjectId,
  date: Date
}}
Abhishek Kumar
  • 3,328
  • 2
  • 18
  • 31
1

Another way of doing this fully atomically is:

db.posts.update({'_id':post_id}, {
    '$pull': {'votes.user':ObjectId},
    '$addToSet': {votes: {
        user: objectId,
        date: Date,
        isdownvote: isdownvote
    }}
})

As already said, there is no upsert for subdocuments so this kind of cheats and deletes the old vote and writes it again. The date field should still be ok because if the user votes again then the date field should change to the last time that user voted so in my mind that part makes sense.

Sammaye
  • 43,242
  • 7
  • 104
  • 146
  • Thank you. This should be the answer. The queries in the [Voting with Atomic Operators](http://cookbook.mongodb.org/patterns/votes/) article also update the counters, but I think there's no way to achieve this in a single query in this case. – Trantor Liu Jun 26 '13 at 08:19
  • @TrantorLiu Hmm, no that one does require two queries even in this case which sucks – Sammaye Jun 26 '13 at 08:20