- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
我正在尝试实现一个类似于 stackoverflow 或 reddit 的投票系统,其中用户只能在给定的帖子上投票一次。
遵循此处给出的建议后
storing upvotes/downvotes in mongodb
我创建了两个模式来存储赞成票和反对票。对于每个用户,我都会跟踪用户投票的帖子。
发布架构:
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 }]
});
根据当前用户,每个帖子都会有不同的 View ,如果用户在 upvote 按钮或 downvote 按钮变为橙色(类似于 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 函数中,当我更新并保存该帖子的投票类型时,我还发送了一个 ajax 请求以将该帖子添加到帖子 Controller 中用户的投票帖子数组中,如下所示:
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();
};
我可能有一些糟糕的设计决定,但到目前为止,这似乎工作正常。请告诉我是否可以对我的代码或我的设计进行任何改进。
现在是棘手的部分。不知何故,在执行 collection.fetch() 之前,我必须查看这两种模式并更改每个帖子的“投票类型”。我想出了一个像这样的丑陋解决方案:
https://gist.github.com/gorkemyurt/6042558
(我把它放在一个 gits 中,所以它可能更具可读性,对不起丑陋的代码..)
一旦我根据用户更新每个帖子的投票类型,我会将其传递给我的主干 View ,并在我的模板中执行一些非常基本的操作,例如:
<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>
此解决方案有效,但我认为每次用户打开页面以呈现帖子的自定义 View 时查找所有帖子和用户投票的所有帖子并不是很有效。任何人都可以想到更好的方法来做到这一点?我愿意接受有关我的代码的任何建议或批评。在此先感谢
最佳答案
有很多地方可以改进:
首先,您的客户端代码对于攻击者来说是唾手可得的成果 - 您使用两个请求执行原子操作(upvote/downvote),第一个请求不仅发送投票类型,还发送投票总数:
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 票,您的应用程序将如何响应?这个操作应该是原子的,当你向 /upvote
端点发出 POST 请求时,你应该增加服务器上的投票。
其次,您实际上并不需要在帖子本身上存储 votetype - 每当用户投票时,您都会更改对所有用户可见的 votetype,但您稍后会使用循环将其隐藏,并且存储一个 votetype 很奇怪帖子上的最后一次投票,您显然需要拥有特定用户的投票类型,因此,您不需要它在架构中并且可以远程它。您可以从帖子中删除投票类型,并且可以通过在帖子本身上存储投票历史记录来删除循环,因此无论何时要显示帖子或帖子列表,您都可以轻松过滤数组以仅包含给定的投票用户,因此您的架构将如下所示:
var postSchema = new Schema({
name: String,
votesCount: Number,
votes: [{ user_id : mongoose.Schema.Types.ObjectId , type: Number }],
postedBy: { type: String, ref: 'User' },
});
然后您可以通过以下方式获得帖子并过滤投票:
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.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)
}
)
客户端的模板代码应该几乎保持不变。
关于node.js - 使用 Backbone、Express 和 Mongoose 支持和反对,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17756076/
我的 Web 应用程序在后端使用 Node.js 和 Express。当违反内容安全策略 (CSP) 时,报告 URI 报告空对象。我的后台代码如下: app.use(bodyParser.urlen
在服务器端提供静态服务的方式在 Express 中似乎非常简单: To serve static files such as images, CSS files, and JavaScript fil
var express = require('express'); var app = express(); 这就是我们创建快速应用程序的方式。但是这个'express()'是什么?它是方法还是构造函
我在尝试安装时收到以下错误 express : npm ERR! code ERR_OSSL_PEM_NO_START_LINE npm ERR! errno ERR_OSSL_PEM_NO_STAR
如 express 所述routing guide和 this answer ,我们可以创建“迷你应用程序”并从主应用程序使用它。但是我看到一段代码,它在模块中使用 app 而不是 router ap
我正在写一个 NestJS应用。现在我想安装 Express中间件 express-openapi-validator . 但是,我无法让它工作。有一个 description for how to
我看过很多类似的帖子,似乎我声明的 var1 似乎需要在其他地方传递,但我似乎无法弄清楚。 public Expression> CreateEqualNameExpression(string ma
Express(或 Connect 的)bodyParser 中间件被标记为已弃用,建议用户改用: app.use(connect.urlencoded()) app.use(connect.json
我只是想知道这种看似尴尬的配置的原因是什么(来自 Getting Started w/ Apollo Server ), const server = new ApolloServer({ //
我正在尝试在表单组中写入表单控件特定的验证错误消息。我在网上找到了几个教程和示例 ( such as this one ),概述了一个看似简单的 *ngIf div,如果在控件上检测到错误,则显示错误
我有一个简单的 Express 应用程序,托管在 AWS 上,使用无服务器框架。 我正在使用 serverless-http 包装 express 应用程序以部署到 AWS lambda 函数,并使用
我最近在 mozilla 教程的帮助下安装了 node 和 express。我正在安装应用程序生成器的下一步,但是当我运行时 npm install express-generator -g 在我的终
我遇到过两种不同的方式来定义 express、use() 中间件,我想知道它们之间是否有任何区别,或者它是否只是语法糖? 一个 const app = express(); app.use(cors(
我试图让我的 Jade 模板编写一个相对于当前 URL 的超链接 ( )。 例如,我的 View 是从 http://localhost/cats 调用的它看起来像这样: extends layou
检查 Express 文档我在下面看到了这种解决方案: app.all('/*', function(req, res) { console.log('Intercepting request
我似乎无法弄清楚如何包含多个模型。 我有三个模型。Tabs, Servers, and PointsTabs hasMany ServerServers belongsTo Tabs and hasM
我已使用Web PI安装IIS Express。在托盘中,没有IIS Express图标。如何在不使用命令行的情况下启动IIS Express?我希望IIS永久运行,因此没有命令行。 最佳答案 参见R
我不想在我的网站上使用 Jade 或 EJS。如何在不默认使用 Jade 模板的情况下创建快速站点?谢谢 最佳答案 如果您想要的是直接为静态 html 文件提供缓存资源的可能性,同时仍然能够点击“/”
Express是否支持HTTP动词“PATCH”,例如: app.patch("/api/resource", function(req, res){ ... }); 我检查了文档,对我来说似乎还不清
我正在快速服务器中运行 vue SPA。问题是当使用历史模式并刷新页面时,我得到一个 404 not found 异常。我尝试使用 connect-history-api-fallback 但不起作用
我是一名优秀的程序员,十分优秀!