Upvote/Downvote logic in MongoDB via nodejs

Govindarajan D
3 min readNov 13, 2018

--

In websites where user created content is significant, the feature of upvote as well as sometimes downvote is essential. It distinguishes good posts better and provides an insight into what content most people like.

We would have seen three types of voting:

  1. Facebook — Only positive. There is a ‘like’ button only and the number of votes is always positive.
  2. Youtube — Both. There is a ‘like’ and ‘dislike’ button. Both have separate counters for them.
  3. 9GAG — Both. There is a ‘upvote’ and ‘downvote’ button, but both respectively increment and decrement the same counter .

In the following post, we see the 3rd type of voting method. The other two can be developed from the 3rd one.

We use a separate collection in MongoDB that contains documents for each post containing the list of users who upvoted/downvoted and the count. We could embed the same in the respective Post’s document itself but for the simplicity of this blog, I have restricted to separate collection.

I use Robo 3T for GUI for MongoDB. It allows easily to manage data. The screenshot from Robo 3T shows the structure for the MongoDB collection for the voting logic.

Document structure for voting — Screenshot from Robo 3T

We make all validation in back-end so that even if the request is tampered with, the data is proper. That’s the reason for the code being this long.

Operations we perform in the below embedded code:

  1. Check if the user has previously upvoted or downvoted, and now tries to do the opposite. If so remove the previous changes(incl. counter change).
  2. Check if the user is trying to cancel his upvote or downvote. The front-end passes “” (empty quotes) for this and we remove the user’s voting count and the corresponding username in the list.
  3. For values “up” and “down” (other than “”), we change the corresponding voters list and the count.

The $pull opertator removes an element from the existing array. $addToSet operator appends an element to the array and if the element already exists in the array, it returns without any modification. The result object has modifiedCount property which has value > 0, when a document gets modified. Whenever we modify using$pull or $addToSet , we check that property to know if any modifications are done i.e the username existed in the list.

Thus, even if the front-end requestBody is tampered with, the user cannot make improper changes to the data.

We can test the API using Postman, which is in my opinion, an excellent program to test REST APIs.

We remove “John Doe” from the voters list and check the corresponding data in MongoDB.

We see that “John Doe” is removed and the count becomes 0 (1 upvote and 1 downvote).

Since MongoDB maintains atomicity at document level, so the above code will work even when more than one user simultaneously upvotes/downvotes the same post. MongoDB maintains a queue of the pending operations pertaining to a document and executes it one after another.

The above code can be expanded to the fit the ‘Facebook’ and ‘Youtube’ method easily by removing and adding a counter accordingly.

--

--