Upvote和Downvote主干,快车和mongoose

我正在尝试实现类似于stackoverflow或reddit的投票系统,用户只能在给定的职位上投票一次。

遵循这里给出的build议

在mongodb中存储upvotes / downvotes

我创build了两个模式来存储upvotes和downvotes。 对于每个用户,我都跟踪用户投票的post。

邮政架构:

var postSchema = new Schema({ name: String, votes: Number, votetype: Number, postedBy: { type: String, ref: 'User' }, }); 

用户架构:

 var userSchema = new Schema({ twittername: String, twitterID: Number, votedPosts : [{ _id : mongoose.Schema.Types.ObjectId , votetype: Number }] }); 

取决于当前用户,每个post将有不同的视图,如果用户在upvotebutton或downvotebutton将变为橙色之前投票(与stackoverflow类似),所以我有以下(简化)主干模型的职位:

 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); } }); }, }); 

因此,如果用户之前没有投过票,那么votetype以“0”开始,而根据投票计算,它的“1”或“-1”。 在upvote函数中,当我更新并保存该post的votetypetypes时,我还发送了一个ajax请求,将该post添加到post控制器中用户的投票post数组中,如下所示:

 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(); }; 

我可能有一些不好的devise决定,但到目前为止,似乎这工作正常。 请告诉我,如果我可以对我的代码或我的devise上的其他任何改进。

现在来了棘手的部分。 不知何故,我必须查看这两个模式,并在做一个collection.fetch()之前更改每个职位的“votetype”。我想出了一个丑陋的解决scheme,像这样:

https://gist.github.com/gorkemyurt/6042558

(我把它放在一个合适的,所以也许它更可读,对于丑陋的代码抱歉..)

一旦我更新每个职位的投票types取决于用户,我把它传递给我的骨干视图,在我的模板中,我做了一些非常基本的事情:

 <div class="post-container"> <div id="arrow-container"> <% if (votetype == 1 ) { %> <p><img id="arrowup" src="/images/arrow-up-orange.jpg"></p> <p><img id="arrowdown" src="/images/arrow-down.jpg"></p> <% } %> <% if ( votetype == 0 ) { %> <p><img id="arrowup" src="/images/arrow-up.jpg"></p> <p><img id="arrowdown" src="/images/arrow-down.jpg"></p> <% } %> <% if ( votetype == -1 ) { %> <p><img id="arrowup" src="/images/arrow-up.jpg"></p> <p><img id="arrowdown" src="/images/arrow-down-orange.jpg"></p> <% } %> </div> <div id="text-container"> <p><h2><%- name %></h2></p> <p><%- dateCreated %></p> <p>Posted by: <%- postedBy %></p> </div> </div> 

这个解决scheme的工作原理,但我不认为它真的很有效的查找所有的post和用户投票的所有post,每当用户打开页面呈现自定义视图的post..任何人都可以想到一个更好的方法来做到这一点? 我对任何意见或批评我的代码开放..预先感谢

有很多事情可以改进:

首先,你的客户端代码对于攻击者来说是一个低级的成果 – 你用两个请求做一个primefaces操作(upvote / downvote),第一个请求不仅发送投票types,而且发送总票数:

 this.set ({votetype : 1 }, {votes : this.get('votes') + 1}); this.save(); // btw this looks broken, the second argument for `set` is options, so // if you want to set votes, you should move them to the first argument: this.set ({votetype : 1, votes : this.get('votes') + 1}); 

但是,如果攻击者发送100甚至1000张选票,你的应用程序将如何回应? 此操作应该是primefaces性的,并且当您向/upvote端点发出POST请求时,您应该在服务器上增加投票。

其次,您并不需要在邮件本身上存储votetypetypes – 每当用户投票时,您都会更改所有用户可见的votetypetypes,但是之后您会将其隐藏起来,并且存储最后一个投票types在明确需要具有特定用户的投票types的post上,因此,在模式中不需要它,并且可以远程执行它。 您可以从post中删除votetype,您可以通过在post本身存储投票历史logging来删除循环,所以无论何时显示post或post列表,您都可以轻松地过滤数组以包含给定的投票用户,所以你的模式将如下所示:

 var postSchema = new Schema({ name: String, votesCount: Number, votes: [{ user_id : mongoose.Schema.Types.ObjectId , type: Number }], postedBy: { type: String, ref: 'User' }, }); 

然后你可以得到一个post,并用类似的东西过滤投票:

 Post.findOne({_id:<post-id>)}, function(err, post){ post.vote = post.votes.filter(function(vote){ return vote.user_id === req.body.userID; })[0].type; res.send(post) } ) // or list of posts Post.find(function(err, posts){ posts.forEach(function(post){ post.vote = post.votes.filter(function(vote){ return vote.user_id === req.body.userID; })[0].type; }); res.send(posts) } ) // you can move vote finding logic in a function: function findVote(post) { var vote = post.votes.filter(function(vote){ return vote.user_id === req.body.userID; })[0] if(vote) return vote.type; } 

如果您需要在用户个人资料中显示最新的投票post,则可以过滤用户投票的post:

 Post.find({'votes.user_id': req.body.userID}, function(err, posts){ posts.forEach(function(post){ // using findVote defined above post.vote = findVote(post); }); res.send(posts) } ) 

客户端的模板代码应该保持几乎相同。