Note that there are some explanatory texts on larger screens.

plurals
  1. POUpvote and Downvote with Backbone, Express and Mongoose
    text
    copied!<p>I am trying to implement a voting system similar to stackoverflow or reddit where a user would only be allowed to vote once on a given post.</p> <p>After following the advice given here</p> <p><a href="https://stackoverflow.com/questions/12147686/storing-upvotes-downvotes-in-mongodb">storing upvotes/downvotes in mongodb</a></p> <p>I have created two schemas to store the upvotes and the downvotes. For each user I am keeping track of the posts that user has voted on.</p> <p>Post Schema :</p> <pre><code>var postSchema = new Schema({ name: String, votes: Number, votetype: Number, postedBy: { type: String, ref: 'User' }, }); </code></pre> <p>User Schema : </p> <pre><code>var userSchema = new Schema({ twittername: String, twitterID: Number, votedPosts : [{ _id : mongoose.Schema.Types.ObjectId , votetype: Number }] }); </code></pre> <p>Depending on the the current user each post is going to have a different view, if the user has voted on the post before the upvote button or downvote button is going to be orange (similar to stackoverflow) so I have the following (simplified) backbone model for a post: </p> <pre><code>var PostModel = Backbone.Model.extend({ urlRoot : '/tweet', idAttribute: '_id', defaults:{ name: '', votes: 0, votetype: 0, postedBy : '', }, upvote: function(){ this.set ({votetype : 1 }, {votes : this.get('votes') + 1}); this.save(); $.ajax({ type: "POST", url:"/upvote", data : {postID : this.id , userID : window.userID , vote: 1}, success : function(result){ console.log(result); }, error: function(jqXHR, textStatus, errorThrown) { console.log(textStatus, errorThrown); } }); }, }); </code></pre> <p>So votetype starts with a "0" if the user hasn't voted on the post before, and its "1" or "-1" depending on the vote. In the upvote function, as I update and save the votetype of that post, I also send a ajax request to add that post to the user's votes posts array in the post controller like the following : </p> <pre><code>exports.upvote = function(req,res){ var postID = req.body.postID; var newvotetype = req.body.vote; User.findOne({twitterID : req.body.userID}, {votedPosts : { $elemMatch: { "_id": postID }}}, function(err, post) { if (post.votedPosts.length == 0) { //append to the array User.update({twitterID : req.body.userID} , { $push : {votedPosts : {_id : postID , votetype: newvotetype}}} ,function (err, user, raw) { if (err){console.log(err);} }); console.log(post); console.log("no one has voted on this before"); } else { //update in the existing array User.update({twitterID : req.body.userID, 'votedPosts._id': postID }, { $set : {'votedPosts.$.votetype' : newvotetype}} ,function (err, user, raw) { if (err){console.log(err);} }); } } ); res.send("success"); res.end(); }; </code></pre> <p>I might have some bad design decisions but so far it seems like this works fine. Please please tell me if I can make some improvements on my code, or anything else on my design.</p> <p>Now comes the tricky part. Somehow I have to look through both of these schemas and change the "votetype" of every post before doing a collection.fetch().. I came up with a ugly solution like this : </p> <p><a href="https://gist.github.com/gorkemyurt/6042558" rel="nofollow noreferrer">https://gist.github.com/gorkemyurt/6042558</a></p> <p>(i put it in a gits so maybe its more readable, sorry for the ugly code..)</p> <p>and once I update the vote type of each post depending on the user I pass it to my backbone view, and in my template I do something very basic like:</p> <pre><code>&lt;div class="post-container"&gt; &lt;div id="arrow-container"&gt; &lt;% if (votetype == 1 ) { %&gt; &lt;p&gt;&lt;img id="arrowup" src="/images/arrow-up-orange.jpg"&gt;&lt;/p&gt; &lt;p&gt;&lt;img id="arrowdown" src="/images/arrow-down.jpg"&gt;&lt;/p&gt; &lt;% } %&gt; &lt;% if ( votetype == 0 ) { %&gt; &lt;p&gt;&lt;img id="arrowup" src="/images/arrow-up.jpg"&gt;&lt;/p&gt; &lt;p&gt;&lt;img id="arrowdown" src="/images/arrow-down.jpg"&gt;&lt;/p&gt; &lt;% } %&gt; &lt;% if ( votetype == -1 ) { %&gt; &lt;p&gt;&lt;img id="arrowup" src="/images/arrow-up.jpg"&gt;&lt;/p&gt; &lt;p&gt;&lt;img id="arrowdown" src="/images/arrow-down-orange.jpg"&gt;&lt;/p&gt; &lt;% } %&gt; &lt;/div&gt; &lt;div id="text-container"&gt; &lt;p&gt;&lt;h2&gt;&lt;%- name %&gt;&lt;/h2&gt;&lt;/p&gt; &lt;p&gt;&lt;%- dateCreated %&gt;&lt;/p&gt; &lt;p&gt;Posted by: &lt;%- postedBy %&gt;&lt;/p&gt; &lt;/div&gt; &lt;/div&gt; </code></pre> <p>This solution works, but I dont think its really efficient to look up all the posts and all the posts that the user has voted on every-time a user opens the page to render the custom view of posts.. Can anyone think of a better way to do this? I am open to any advice or criticism about my code.. thanks in advance </p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload