Mise à jour sur l'agrégat à Mongodb

J'essaie de basculer une valeur booléenne dans un objet, qui est un sous-document, j'ai du mal à mettre à jour un objet particulier dans un tableau.

Document:

"_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 } ], 

Manette:

 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); }); 

J'ai essayé d'utiliser $ set, mais cela n'a pas fonctionné car tout le tableau d'invitation a été remplacé par ' Accepted = 1 ' Comment puis-je basculer le champ ' Accepté ' du document avec ' ID ' particulier.

 Invitation.$.Accepted 

L'opérateur positionnel ne s'applique pas au champ contenant un tableau, de sorte qu'il ne peut pas itérer dans le champ Accepté

MODIFIER:

 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); }); }); 

Le contrôle n'est pas entré dans le second async.each, l'erreur est lancée sur le premier asynch. Voici l'erreur:

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

Je ne pense vraiment pas que, même en tant que requête d'alimentation, le cadre d'agrégation est la bonne opération à utiliser ici. Tout ce que vous faites est "dénormaliser" le tableau en tant que documents individuels. Il n'y aurait vraiment aucun besoin. Il suffit d'aller chercher le document à la place:

 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); }); }); 

Donc, il n'y a pas de problème pour iterating la liste des documents dans une réponse pour ce que vous faites, c'est juste que vous souhaitez également itérer les membres du tableau aussi. La capture consiste à émettre tout type de .update() que vous devez savoir, puis l'appel asynchrone est terminé.

Donc, j'utilise async.each, mais vous voulez probablement que async.eachLimit contrôle la boucle. L'appariement de l'élément provient de l'opérateur positionnel $ , correspondant à l'élément du tableau apparié dans la requête.

C'est juste le code JavaScript, alors simplement "basculer" la valeur avec !invite.accepted qui l'inversera. Pour un plaisir supplémentaire, renvoyez le tableau "résultats" en poussant le document modifié à partir de .findOneAndUpdate() .

Utilisez l'opérateur de mise à jour de position pour cela: 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 } ); 

Remarque: vous n'avez pas besoin de l'étape d'agrégation puisqu'il ne fait réellement rien.