Mongodb中的聚合更新

时间:2015-01-15 09:43:28

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

我试图在对象内部切换一个布尔值,这是一个子文档,我很难更新数组中的特定对象。

文件:

"_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但是它没有成功,因为整个邀请数组被替换为' 接受= 1 ' 我如何切换' 接受'具有特定' ID '的文档字段。

Invitation.$.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);
    }

2 个答案:

答案 0 :(得分: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()推送修改过的文档。

答案 1 :(得分:0)

使用位置更新运算符: 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
    }
);

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