-
Type:
Improvement
-
Resolution: Won't Do
-
Priority:
Minor - P4
-
None
-
Affects Version/s: None
-
Component/s: Querying
-
None
-
None
-
None
-
None
-
None
-
None
-
None
-
None
Currently, executing the following operations in the shell will produce error code 66, "After applying the update, the (immutable) field '_id' was found to have been altered to _id..."
var tm = { "_id": ObjectId("571fc8d290f5282a50778960"), "parentId": null, "name": "test", "containerType" : "IP" }; db.Containers.findOneAndReplace({"parentId": null, "containerType": "IP"}, tm, { "upsert": true, "returnNewDocument": true } );var tm2 = { "_id": ObjectId("571fc8d290f5282a50778961"), "parentId": null, "name": "test2", "containerType" : "IP" }; db.Containers.findOneAndReplace({"parentId": null, "containerType": "IP"}, tm2, { "upsert": true, "returnNewDocument": true } );
I understand, based on the documentation, that this is by design as the documentation states "The <replacement> document cannot specify an _id value that differs from the replaced document." as well as "MongoDB will add the _id field to the replacement document if it is not specified in either the filter or replacement documents. If _id is present in both, the values must be equal."
However, this is flawed in my opinion. Why not have the "_id" property ignored on the replacement document?
The use case for this that based on the example above, we don't want to change the "_id" but instead what we want is only one entry where the containerType is "IP" and the parentId is null. However, due to the source of these commands, when we call the command we will have an assigned "_id" from a javascript web client.
Expected Result:
The first call in which there is no document matching
inserts the document with the specified _id and returns the new document (returnNewDocument = true).
Subsequent calls that may come in with different _id's should replace all other fields except for the _id field and return the new document with the existing _id instead of the replacement document's _id.
Workaround(s):
1. Query first to see if a document exists that matches
and if it does, return it, otherwise insert the new document. This, however, would not be atomic without using a transaction which would not be preferable.
2. Somehow avoid sending the _id. While this works, we want the _id to be known/specified ahead of time in the case of an insert.
3. Use standard update with upsert using setOnInsert for _id then query for the resulting document. This is essentially a reverse of workaround #1 and is not atomic.
This is by no means a deal breaker but to me it's a little bit counter-intuitive that when upserting in a situation where you do not use _id in the filter criteria that _id would be assumed to be something the user is trying to replace.
Thank you for your time and consideration!