首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >原子地更新数组项

原子地更新数组项
EN

Stack Overflow用户
提问于 2014-09-17 01:41:15
回答 1查看 1.7K关注 0票数 1

基本问题:

我如何检查文档的子数组,以查看条目是否存在,如果存在则更新它,如果不存在,如何以原子方式添加它?我还想原子地调整计数器(使用$inc),这取决于第一次操作的结果。

充分解释:

我正在尝试编写一种方法,为任意mongodb文档提供原子性的“向上表决”或“否决”功能。

我希望发生的事情如下:

  • 将“选票”和"voteScore“项添加到给定架构中
  • “投票”是由投票类型(向上或向下)和投票者的userId组成的对象数组。
  • "voteScore“是所有选票的运行总和(+1表示向上投票,-1表示否决)

我试图实现的逻辑是:

  • 当用户进行向上或向下投票时,"voteScore“就会相应地使用原子$inc操作符进行更新,并使用该用户的ID和投票类型向数组中添加新的投票。
  • 当用户更改他/她的投票时,就会原子地更新voteScore,或者从数组中删除投票(如果voteType"none"),或者更新voteType以反映所做的更改。

我该怎么做呢?我想我将不得不使用醚document.update()model.findOneAndUpdate(),因为传统的MongooseArray方法 (后面跟着save())不会是原子的。

到目前为止,我有以下代码,但它不起作用:

代码语言:javascript
复制
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;
  }
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2014-09-17 02:21:56

要实现这一点,最好将数组分离为“向上/向下”投票。通过这种方式,您可以同时从每个字段中提取$push$pull。这在很大程度上是因为MongoDB无法在文档中同一字段路径上的单个更新中执行这些操作。尝试这样做会导致错误。

简化的模式表示可能如下所示:

代码语言:javascript
复制
var questionSchema = new Schema({
    "score": Number,
    "upVotes": [{ type: Schema.Types.ObjectId }],
    "downVotes": [{ type: Schema.Types.ObjectId }]
]);

module.exports = mongoose.model( 'Question', questionSchema );

这是一个通用的表示,所以不要把它看得太字面意思,但重点是两个数组。

在处理“向上投票”时,您真正需要做的就是确保没有将“up票”数组添加到已经存在“用户”的位置:

代码语言:javascript
复制
Question.findOneAndUpdate(
    { "_id": question_id, "upVotes": { "$ne": user_id } },
    {
        "$inc": { "score": 1 },
        "$push": { "upVotes": user_id },
        "$pull": { "downVotes": user_id }
    },
    function(err,doc) {

    }
);

而反过来处理“否决”:

代码语言:javascript
复制
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独立于其他操作符操作。因此,您不希望“选择”对更新无效的文档。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/25880978

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档