Reputation: 1249
I'm working with mongoose, and when I do an update in the following way
model.update({_id: id}, {$addToSet: {refs: refId}}, function(err, numAffected){
console.log(numAffected);
})
This is working, my references are not updated if the element is already in the array but numAffected is always 1..
Is this the correct behaviour? because it doesn't have sense to me. If the element wasn't modified so the numAffected should be 0.
Is there a way of accomplish or using this library as I want ?
Thanks in advance.
Upvotes: 3
Views: 2507
Reputation: 151122
The reason for this is that mongoose has yet to update to do the same sort of thing the mongo shell is doing as of the MongoDB 2.6 release.
The MongoDB 2.6 release introduces the bulk operations API which actually has extended statistics on the write concern response. The shell commands such as "update", "insert" and "remove" have been updated to actually use this API in their implementation. You can in fact see this in the shell by issuing a call to a method without the ending brackets:
db.collection.update
That shows the code that wraps the API methods implemented as well as the "fallback" to the "legacy" write concern response mode that is currently used by mongoose.
If your version of mongoose is recent enough, the node native driver which is bundled will support these operations. But you do have to reference the raw collection object by accessing the .collection
method from the Model or other means. You also do have to make sure that the database connection is actually active. Here I am forcing this for example by placing the code within the "open" event for the connection:
var mongoose = require('mongoose'),
async = require('async'),
Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost');
var testSchema = new Schema({
user: String,
list: []
});
var Test = mongoose.model( "Test", testSchema, "mytest" );
mongoose.connection.on("open",function(err,conn) {
var batch = Test.collection.initializeOrderedBulkOp();
batch.find({ "user": "me"}).upsert().updateOne({ "$addToSet": { "list": 7 }});
batch.execute(function(err,result) {
console.log( JSON.stringify( result, undefined, 4 ) );
});
});
The response from this on the first update action, and assuming there is a document already existing that will match, shows the extended statistics from the operation with both a singular counter in "mMatched" and "nModified":
{
"ok": 1,
"writeErrors": [],
"writeConcernErrors": [],
"nInserted": 0,
"nUpserted": 0,
"nMatched": 1,
"nModified": 1,
"nRemoved": 0,
"upserted": []
}
If that were an "upsert" the counter would be on "nUpserted" and the "upserted" array in the stats holds the _id
of the document that was created.
On the second iteration the response will indicate that the document was in fact "matched" but no update was performed:
{
"ok": 1,
"writeErrors": [],
"writeConcernErrors": [],
"nInserted": 0,
"nUpserted": 0,
"nMatched": 1,
"nModified": 0,
"nRemoved": 0,
"upserted": []
}
This is what the new statistics from this API give you. The "legacy" implementation currently used by the "mongoose" methods does not make this distinction. So whether any "update/Modification" actually occurred is not returned.
So if you have a version that is recent enough to support this you can implement your code to do exactly as is shown above. I would expect though that the current maintainers would be aware of the implementation change and will make an effort to make the mongoose methods do exactly this "under the hood" in much the same way as the shell helper methods are implemented.
Upvotes: 4