db.createCollection("sample");
db.sample.insert({
embedded: [{
name: "initial"
}]
});
printjson(db.sample.findOne({}));
watcher=db.sample.watch();
db.sample.update({}, {
$addToSet: {
embedded: {
name: "added to set"
}
}
});
printjson(watcher.next());
// {
// "_id" : {
// "_data" : BinData(0,"glqmxgkAAAABRmRfaWQAZFqmxbuuM8l1+kCAmQBaEASPFrTqbjBBSJoRiNbRVccuBA==")
// },
// "operationType" : "update",
// "ns" : {
// "db" : "test",
// "coll" : "sample"
// },
// "documentKey" : {
// "_id" : ObjectId("5aa6c5bbae33c975fa408099")
// },
// "updateDescription" : {
// "updatedFields" : {
// "embedded" : [
// {
// "name" : "initial"
// },
// {
// "name" : "added to set"
// }
// ]
// },
// "removedFields" : [ ]
// }
// }
db.sample.update({}, {
$push: {
embedded: {
name: "this was pushed"
}
}
});
printjson(watcher.next());
// {
// "_id" : {
// "_data" : BinData(0,"glqmxkwAAAABRmRfaWQAZFqmxbuuM8l1+kCAmQBaEASPFrTqbjBBSJoRiNbRVccuBA==")
// },
// "operationType" : "update",
// "ns" : {
// "db" : "test",
// "coll" : "sample"
// },
// "documentKey" : {
// "_id" : ObjectId("5aa6c5bbae33c975fa408099")
// },
// "updateDescription" : {
// "updatedFields" : {
// "embedded.2" : {
// "name" : "this was pushed"
// }
// },
// "removedFields" : [ ]
// }
// }
watcher.close();
I’m wondering if this is by design and perhaps why so or maybe it’s a bug or what is the gotcha that I’m not getting?
Hi Timo,
The updateDescription
field returned as part of the Change Stream update event describes fields that were updated or removed by the update operation. i.e. delta or potentially affected.
Now let’s take a look at the update operators to understand the reason: $push by default just append the value to the end of the array. This is what you have observed, where it returns only the last array element index with the new value. An append does not need to see the other values in the array, it just append to the end of the array. This operation has no potential to affect the existing values in the array.
However, if you also specify $push
modifiers such as $position where the operation needs to see the other values, the whole array would be returned. This is because the other values, are shifted along the array indexes. See also update/push_node.cpp#L193-226
For example:
db.sample.update({},
{"$push":{"embedded":{"$position":0,
"$each":[ {"name": "prepend"} ]
}
}
})
The whole array would be returned in updateDescription.updatedFields
:
...
"updateDescription": {
"updatedFields": {
"embedded": [
{"name": "prepend"},
{"name": "initial"}
]
}
...
This is similar to $addToSet update operator. The operator ensures that there are no duplicate items added to the array/set by running a deduplication. It goes through any existing value in the array to make sure the newly added value does not already exist. You may also noticed that if the new value already existed in the array there are no update operation, therefore no change stream event. See also update/addtoset_node.cpp#L106-141
Regards,
Wan.