基本问题:
我如何检查文档的子数组,以查看条目是否存在,如果存在则更新它,如果不存在,如何以原子方式添加它?我还想原子地调整计数器(使用$inc),这取决于第一次操作的结果。
充分解释:
我正在尝试编写一种方法,为任意mongodb文档提供原子性的“向上表决”或“否决”功能。
我希望发生的事情如下:
我试图实现的逻辑是:
$inc操作符进行更新,并使用该用户的ID和投票类型向数组中添加新的投票。voteType是"none"),或者更新voteType以反映所做的更改。我该怎么做呢?我想我将不得不使用醚document.update()或model.findOneAndUpdate(),因为传统的MongooseArray方法 (后面跟着save())不会是原子的。
到目前为止,我有以下代码,但它不起作用:
var mongoose = require('mongoose');
var voteTypes = ['up', 'down'];
var voteSchema = new mongoose.Schema({
userId: { type: Schema.Types.ObjectId, ref: 'User', },
voteType: { type: String, enum: voteTypes }
});
exports.schema = voteSchema;
exports.plugin = function votesPlugin(schema, options) {
schema.add({ voteScore: Number });
schema.add({ votes: [voteSchema] });
schema.methods.atomicVote = function(args, callback) {
var item = this;
var voteType = (args.voteType && args.voteType.toLowerCase()) || 'none';
var incrementScore, query;
switch (voteType) {
case 'none':
incrementScore = -voteTypeValue(voteRemoved.voteType);
query = this.update({ $inc: {voteScore: incrementScore}, $pull: { votes: { userId: args.userId } } });
break;
case 'up':
case 'down':
var newVoteVal = voteTypeValue(args.voteType);
if (/* Vote does not already exist in array */) {
incrementScore = newVoteVal;
query = this.update({$inc: {voteScore: incrementScore}, $addToSet: { userId: args.userId, voteType: args.voteType });
} else {
var vote = /* existing vote */;
if (vote.voteType === args.voteType) return callback(null); // no-op
var prevVoteVal = voteTypeValue(vote.voteType);
incrementScore = (-prevVoteVal) + newVoteVal;
vote.voteType = args.voteType;
// This likely won't work because document.update() doesn't have the right query param
// to infer the index of '$'
query = this.update({$inc: {voteScore: incrementScore}, $set: { 'votes.$': { userId: args.userId, voteType: args.voteType } });
}
break;
default:
return callback(new Error('Invalid or missing "voteType" argument (possible values are "up", "down", or "none").'));
}
query.exec(callback);
};
};
function voteTypeValue(voteType) {
switch (voteType) {
case 'up': return 1;
case 'down': return -1;
default: return 0;
}
}发布于 2014-09-17 02:21:56
要实现这一点,最好将数组分离为“向上/向下”投票。通过这种方式,您可以同时从每个字段中提取$push或$pull。这在很大程度上是因为MongoDB无法在文档中同一字段路径上的单个更新中执行这些操作。尝试这样做会导致错误。
简化的模式表示可能如下所示:
var questionSchema = new Schema({
"score": Number,
"upVotes": [{ type: Schema.Types.ObjectId }],
"downVotes": [{ type: Schema.Types.ObjectId }]
]);
module.exports = mongoose.model( 'Question', questionSchema );这是一个通用的表示,所以不要把它看得太字面意思,但重点是两个数组。
在处理“向上投票”时,您真正需要做的就是确保没有将“up票”数组添加到已经存在“用户”的位置:
Question.findOneAndUpdate(
{ "_id": question_id, "upVotes": { "$ne": user_id } },
{
"$inc": { "score": 1 },
"$push": { "upVotes": user_id },
"$pull": { "downVotes": user_id }
},
function(err,doc) {
}
);而反过来处理“否决”:
Question.findOneAndUpdate(
{ "_id": question_id, "downVotes": { "$ne": user_id } },
{
"$inc": { "score": -1 },
"$push": { "downVotes": user_id },
"$pull": { "upVotes": user_id }
},
function(err,doc) {
}
);有了逻辑扩展,您就可以通过从两个数组中“提取”来抵消所有的选票。但是,您确实可以在这方面“聪明”,并在应用程序中维护“状态”信息,这样您就可以“知道”您是否也在增加或减少“分数”。
因此,对于我来说,我会这样做,并在发送响应时处理“数组”结果,只需过滤掉状态信息的当前“用户”,这样您就可以在取消甚至不向服务器发送请求时做出明智的选择,因为在服务器上,用户已经投出了他们的“向上投票”或“否决”。
还请注意,如果没有查询,$addToSet在这里不是一个选项,因为$inc独立于其他操作符操作。因此,您不希望“选择”对更新无效的文档。
https://stackoverflow.com/questions/25880978
复制相似问题