【问题标题】:Update on Aggregate in MongodbMongodb 中的聚合更新
【发布时间】:2015-03-13 16:15:05
【问题描述】:

我正在尝试在一个对象(它是一个子文档)内切换布尔值,我很难更新数组中的特定对象。

文档:

"_id" : ObjectId("54afaabd88694dc019d3b628")
"Invitation" : [ 
    {
        "__v" : 0,
        "ID" : ObjectId("54af6ce091324fd00f97a15f"),
        "__t" : "USER",
        "_id" : ObjectId("54b4ceb50fc380001bea1752"),
        "Accepted" : false
    }, 
    {
        "__v" : 0,
        "ID" : ObjectId("54afac5412f5fdcc007a5c4d"),
        "__t" : "USER",
        "_id" : ObjectId("54b4cebe0fc380001bea1753"),
        "Accepted" : false
    }
],

控制器:

User.aggregate([{$match: {_id: ObjectId(54afaabd88694dc019d3b628)}},{$unwind: '$Invitation'},{$project: {_id: '$_id',Invitation: '$Invitation'}}],function(err,results){

    function updateInvitation(_id){

                var query = {'_id': _id, 'Invitation.ID': ObjectId("54af6ce091324fd00f97a15f")};
                var operator = {$inc: {'Invitation.Accepted': 1}}; 
                User.update(query,operator,{multi:true},function(err,updated){
                    if(err){
                        console.log(err);
                    }
                    console.log('updating'+updated);
                });
            }
            res.jsonp(results);
            updateInvitation(results[0]._id);
});

我尝试使用 $set 但没有成功,因为整个 Invitation 数组被替换为 'Accepted = 1' 如何使用特定的“ID”切换文档的“Accepted”字段。

Invitation.$.Accepted

位置运算符不适用于包含数组的字段,因此无法迭代到 Accepted 字段

编辑:

User.find({_id: req.user._id},'Invitation',function(err,docs){
    if(err){
        console.log(err);
    }
    console.log(docs);
    var results = [];
    async.each(docs,function(doc,err) {
        if(err){
            console.log('error'+ err);
        }
        async.each(docs.Invitation,function(invite,callback) {
console.log('second async');
            User.update(
                { '_id': doc._id, 'Invitation._id': invite._id },
                { '$set': {'Invitation.$.Accepted': !invite.Accepted}},
                function(err,doc) {
                    results.push(doc);
                    console.log('updated'+doc);
                    callback(err);
                }
            );
        });
    },function(err) {
        if (err)
            console.log(err);
        console.log(results);
    });

});

控件没有进入第二个 async.each,在第一个 async 上抛出错误,这是错误:

error-function () {
        if (called) throw new Error("Callback was already called.");
        called = true;
        fn.apply(root, arguments);
    }

【问题讨论】:

    标签: javascript node.js mongodb mongoose aggregation-framework


    【解决方案1】:

    我真的不认为即使作为馈线查询,聚合框架也不是在这里使用的正确操作。您所做的就是将数组“非规范化”为单个文档。真的应该没有必要。只需获取文档即可:

    var query = {}; // whatever criteria
    
    Users.find(query,"Invitation",function(err,docs) {
        if (err) {
            console.log(err);
    
        var results = [];        
    
        async.each(docs,function(doc,callback) {
            async.each(docs.Invitation,function(invite,callback) {
                Users.findOneAndUpdate(
                    { "_id": doc._id, "Invitation._id": invite._id },
                    { "$set": { "Invitation.$.Accepted": !invite.Accepted } },
                    function(err,doc) {
                       results.push( doc );
                       callback(err);
                    }
                );
            },callback);
        },function(err) {
            if (err)
                console.log(err);
    
            console.log(results);
        });    
    
    });
    

    因此,在响应您正在做的事情时迭代文档列表没有问题,只是您还想迭代数组成员。问题是,当您发出任何需要注意的.update() 时,异步调用就完成了。

    所以我使用async.each,但您可能希望async.eachLimit 控制循环。元素的匹配来自positional $运算符,对应查询中匹配的数组元素。

    这只是 JavaScript 代码,所以只需用!invite.accepted“切换”该值,这将反转它。为了获得更多乐趣,请通过从 .findOneAndUpdate() 推送修改后的文档来返回“结果”数组。

    【讨论】:

    • 感谢您的回答,但我在 async.each github.com/caolan/async/issues/610 上收到此错误
    • @shaileshshekhawat 那个问题是关于一个未定义的数组对象?那么这里未定义哪个数组对象呢? .find() 的结果应该是返回文档或者查询错误。如果内部“邀请”数组未定义,那么您可以在提交到 async.each 之前测试该值。
    • @NeilLunn-query on .find() 返回已定义的邀请数组我在console.log(docs) 上对其进行了测试,但仍然出现这个奇怪的错误
    • @shaileshshekhawat 听起来您正在做与清单中的代码不同的事情。尝试首先使用给定的列表,看看还有什么地方可能出错。
    • @NeilLunn-你能解释一下使用两个 async.each 的逻辑吗?
    【解决方案2】:

    为此使用位置更新运算符: http://docs.mongodb.org/manual/reference/operator/update/positional/

    User.update(
        {
            "_id": ObjectId("54afaabd88694dc019d3b628"),
            "Invitation": {"$elemMatch": {"ID" : ObjectId("54af6ce091324fd00f97a15f"), "Accepted":false}}
        },
        {
            "$set" : {
                "Invitation.$.Accepted" : true
            }
        },{multi:false, upsert:false, safe:true}, function (err, numAffectedDocuments){
          // TODO
        }
    );
    

    注意:您不需要聚合步骤,因为它实际上什么都不做。

    【讨论】:

    • 我已经尝试使用位置运算符,但收到此错误“无法应用包含数组的位置运算符字段”
    • @shaileshshekhawat 发生这种情况是因为要更新的字段也需要在查询中。我编辑了答案中的代码来纠正这个问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-06-14
    • 1970-01-01
    • 2021-11-04
    • 1970-01-01
    • 2017-09-03
    • 2022-12-02
    • 2021-11-23
    相关资源
    最近更新 更多