问题
i am trying to toggle a boolean value inside a object, which is a subdocument, i am having hard time to update a particular object within an array.
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
}
],
Controller:
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);
});
i tried using $set but it didnt worked out as whole Invitation array got replaced with 'Accepted = 1' How can i toggle 'Accepted' field of the document with particular 'ID'.
Invitation.$.Accepted
Positional operator doesnot apply to field containing array so can't iterate to Accepted field
EDIT:
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);
});
});
The control is not getting inside second async.each, error is thrown on first async here is the error:
error-function () {
if (called) throw new Error("Callback was already called.");
called = true;
fn.apply(root, arguments);
}
回答1:
I really don't think that even as a feeder query the aggregation framework is the right operation to use here. All you are doing is "denormalizing" the array as individual documents. There really should be no need. Just fetch the document instead:
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);
});
});
So there is no problem iterating the list of documents in a response for what you are doing, it's just that you also want to iterate the array members as well. The catch is when issuing any kind of .update()
that you need to be aware then the asynchronous call is complete.
So I'm using async.each but you probably want async.eachLimit to control the looping. The matching of the element comes from the positional $ operator, corresponding to the matched array element in the query.
It's just JavaScript code, so simply "toggle" the value with !invite.accepted
which will inverse it. For additional fun, return the "results" array by pushing the modified document from .findOneAndUpdate().
回答2:
Use the positional update operator for this: 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
}
);
Note : you don't need the aggregation step since it actually does nothing.
来源:https://stackoverflow.com/questions/27960568/update-on-aggregate-in-mongodb