|
In versions prior to 3.4, it was possible to issue a single-element $in query with {upsert: true} as shown below:
> db.c.drop()
|
false
|
> db.c.update({a: {$in: [1]}}, {$addToSet: {a: 2}}, {upsert: true})
|
WriteResult({
|
"nMatched" : 0,
|
"nUpserted" : 1,
|
"nModified" : 0,
|
"_id" : ObjectId("58bdb00eb39e8f87607e9222")
|
})
|
> db.c.find()
|
{ "_id" : ObjectId("58bdb00eb39e8f87607e9222"), "a" : [ 2 ] }
|
This is no longer possible in 3.4:
> db.c.drop()
|
true
|
> db.c.update({a: {$in: [1]}}, {$addToSet: {a: 2}}, {upsert: true})
|
WriteResult({
|
"nMatched" : 0,
|
"nUpserted" : 0,
|
"nModified" : 0,
|
"writeError" : {
|
"code" : 16836,
|
"errmsg" : "Cannot apply $addToSet to a non-array field. Field named 'a' has a non-array type double in the document INVALID-MUTABLE-ELEMENT"
|
}
|
})
|
This is a breaking change which should be added to the section on "Compatibility Changes in MongoDB 3.4": https://docs.mongodb.com/manual/release-notes/3.4-compatibility/.
The reasoning is that single-element $in predicates are logically equivalent to equality predicates. Therefore, like any equality predicate, a single-element $in should seed the document being inserted when an upsert matches no documents. In the example above, the predicate {a: {$in: [1]}} seeds the document to insert with the fields {a: 1}. We then attempt to apply the $addToSet predicate to this document, which is invalid since field a does not contain an array. Users which want this behavior must wrap $in inside an $elemMatch:
> db.c.drop();
|
true
|
> db.c.update({a: {$elemMatch: {$in: [2]}}}, {$addToSet: {a: 3}}, {upsert: true});
|
WriteResult({
|
"nMatched" : 0,
|
"nUpserted" : 1,
|
"nModified" : 0,
|
"_id" : ObjectId("58bda2706b1609466de3d5dc")
|
})
|
> db.c.find();
|
{ "_id" : ObjectId("58bda2706b1609466de3d5dc"), "a" : [ 3 ] }
|
See SERVER-27707 for further details. I'd be happy to review a draft of the new documentation.
|