The full explaination of this is at the end so read on.
That cannot Cannot be done "atomically" in a single operation and the best you will get is "Bulk" operations which is the best way to do it.
var products = [
{name: "apple", status: 1}
{name: "notebook", status: 0}
];
var bulk = db.collection.initializeOrderedBulkOp();
products.forEach(function(product) {
// Try to update
bulk.find({
"_id" : ObjectId("53e87e239ae974e6a0a81004"),
"products.name": product.name
})
.updateOne({
"$set": { "products.$.status": product.status }
});
// Try to "push"
bulk.find({
"_id" : ObjectId("53e87e239ae974e6a0a81004"),
"products.name": { "$ne": product.name }
})
.updateOne({
"$push": { "products": product }
});
});
bulk.execute();
The other alternative is to retrieve the document via a .findOne() or similar operation, then alter the array content in client code and then .save() the altered content back.
That is what you don't want since there is no guarantee the document has not "changed" since it was read into memory. And if other members were added to the array that sort of action would "overwrite" them.
So loop the items with multiple updates. At least "Bulk" operations send these all at once to the server without waiting for responses from individual writes.
But as you point out. What if the value is still the same? For that you need to look at the "WriteResult" response from the "Bulk" operation on .execute():
WriteResult({ "nMatched" : 2, "nUpserted" : 0, "nModified" : 2 })
So there were two (2) actions here depite four (4) operations being sent in total. If the array contained one more item, say "iphone" without changes:
var products = [
{name: "apple", status: 1}
{name: "notebook", status: 0},
{name: "iphone", status: 0 }
];
Then the response would be this:
WriteResult({ "nMatched" : 3, "nUpserted" : 0, "nModified" : 2 })
Since the "Bulk" API is smart enough to see that the value for "status" on the matching "iphone" is not different to the value already present ( assuming nothing else changed that in between ) and does not report this as a modification.
So let the server do the work, because all the smarts you could code up are really already there.