...
Full document replacement can change the type of immutable fields in 3.6. It cannot change the value. Modifier-style updates cannot change the type.
david.storch commented on Thu, 23 Feb 2023 18:09:36 +0000: Closing as "Gone Away" per Charlie's recommendation above. charlie.swanson commented on Thu, 16 Feb 2023 16:08:19 +0000: Yeah I think this is a non-issue. I checked with max.hirschhorn@mongodb.com's example to be doubly sure. But it seems to replicate OK and show the same post-image on the secondaries. This makes sense because the query system should treat them as equivalent for find-by-_id, and then they will apply the same update logic to end up with the same post-image. I think this was probably an upgrade/downgrade bug from 3.4 to 3.6 where this type of update on a 3.6 primary would replicate to a 3.4 secondary and result in a differently-typed _id between the two nodes. But that ship has long sailed. I'm going to put this back into "Needs Scheduling" for attention, but my recommendation would be to close this as either "Gone Away" (upgrade/downgrade concern is in the past) or "Won't Fix" or something like that. max.hirschhorn@10gen.com commented on Thu, 26 Jan 2023 15:11:08 +0000: I noticed that pipeline updates also allow changing the _id value to an equivalent but not binary-equal value (e.g. a different BSON type). Flagging this for Needs Scheduling to have someone from the Query team take a look and evaluate whether there are any deeper concerns (e.g. potential for data inconsistency) about the current behavior. I do suspect there won't be any concerns. rs:PRIMARY> db.mycoll.insert({_id: new NumberDecimal("0E3"), padding: "x".repeat(10000)}) WriteResult({ "nInserted" : 1 }) rs:PRIMARY> db.mycoll.update({_id: 0}, [{$set: {_id: 0, a: 1}}]) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) rs:PRIMARY> db.getSiblingDB("local").oplog.rs.find().sort({$natural: -1}).limit(-1).pretty() { "op" : "u", "ns" : "test.mycoll", "ui" : UUID("6dbd7cb1-c176-4930-8e28-9ba14edec67d"), "o" : { "$v" : 2, "diff" : { "u" : { "_id" : 0 }, "i" : { "a" : 1 } } }, "o2" : { "_id" : NumberDecimal("0E+3") }, "ts" : Timestamp(1674712156, 1), "t" : NumberLong(70), "v" : NumberLong(2), "wall" : ISODate("2023-01-26T05:49:16.353Z") } asya commented on Thu, 22 Feb 2018 23:56:41 +0000: Making it an error when type is different in replacement document (for _id) would definitely be a backwards breaking change. justin.seyster commented on Sat, 17 Feb 2018 01:27:07 +0000: Both the 3.4 and 3.6 update path use compareWithBSONElement() to compare the old and new _id values, which considers equal values to be equal even when their types are different. In the 3.4 code path, object replacement works by stripping everything except the _id element out of the input document and then repopulating it with the elements from the replacement document (again, except for the _id element). In the new update system, object replacement always strips the entire document including _id, and always copies _id from the replacement document (unless the replacement document doesn't have _id). We should probably explicitly check the type of the old and new field when validating an update to prevent this problem. Would something like this be considered a backwards breaking change? A little more worrying, the check in 3.6 to ensure _id does not change is also used to check other immutable paths, so it should be possible to mutate the shard key type as well. This problem probably also existed in 3.4. david.storch commented on Tue, 30 Jan 2018 00:28:53 +0000: justin.seyster@mongodb.com, I'm assigning this to you in order to investigate. Once you have an understanding of what's going on, you can put this back on the Needs Triage, Backlog - Query Team queue with a comment explaining the root cause. tess.avitabile commented on Mon, 29 Jan 2018 15:46:22 +0000: Yes, this looks like a bug in the update system. Assigning this to the Query team.
> db.c.insert({_id: 1}) WriteResult({ "nInserted" : 1 }) > db.c.update({_id: 1}, {_id: NumberDecimal(1)}) WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) > db.c.find() { "_id" : NumberDecimal("1.00000000000000") }