diff options
Diffstat (limited to 'jstests/core/write/update/collation_update.js')
-rw-r--r-- | jstests/core/write/update/collation_update.js | 318 |
1 files changed, 318 insertions, 0 deletions
diff --git a/jstests/core/write/update/collation_update.js b/jstests/core/write/update/collation_update.js new file mode 100644 index 00000000000..72f51de48d1 --- /dev/null +++ b/jstests/core/write/update/collation_update.js @@ -0,0 +1,318 @@ +// Cannot implicitly shard accessed collections because of collection existing when none +// expected. +// @tags: [assumes_no_implicit_collection_creation_after_drop] + +// Integration tests for collation-aware updates. +(function() { +'use strict'; +var coll = db.getCollection("collation_update_test"); + +const caseInsensitive = { + collation: {locale: "en_US", strength: 2} +}; +const caseSensitive = { + collation: {locale: "en_US", strength: 3} +}; +const numericOrdering = { + collation: {locale: "en_US", numericOrdering: true} +}; + +// Update modifiers respect collection default collation on simple _id query. +coll.drop(); +assert.commandWorked(db.createCollection(coll.getName(), numericOrdering)); +assert.commandWorked(coll.insert({_id: 1, a: "124"})); +assert.commandWorked(coll.update({_id: 1}, {$min: {a: "1234"}})); +assert.eq(coll.find({a: "124"}).count(), 1); + +// Simple _id query with hint on different index should work. +assert.commandWorked(coll.createIndex({foobar: 1})); +function makeUpdateCmdWithHint(update, hint) { + return {update: coll.getName(), updates: [{q: {_id: 1}, u: update, hint: hint}]}; +} +assert.commandWorked(coll.runCommand(makeUpdateCmdWithHint({$min: {a: "49"}}, {foobar: 1}))); +assert.eq(coll.find({a: "49"}).count(), 1); +assert.commandWorked(coll.runCommand(makeUpdateCmdWithHint({$min: {a: "5"}}, {_id: 1}))); +assert.eq(coll.find({a: "5"}).count(), 1); + +// $min respects query collation. +coll.drop(); + +// 1234 > 124, so no change should occur. +assert.commandWorked(coll.insert({a: "124"})); +assert.commandWorked(coll.update({a: "124"}, {$min: {a: "1234"}}, numericOrdering)); +assert.eq(coll.find({a: "124"}).count(), 1); + +// "1234" < "124" (non-numeric ordering), so an update should occur. +assert.commandWorked(coll.update({a: "124"}, {$min: {a: "1234"}}, caseSensitive)); +assert.eq(coll.find({a: "1234"}).count(), 1); + +// $min respects collection default collation. +coll.drop(); +assert.commandWorked(db.createCollection(coll.getName(), numericOrdering)); +assert.commandWorked(coll.insert({a: "124"})); +assert.commandWorked(coll.update({a: "124"}, {$min: {a: "1234"}})); +assert.eq(coll.find({a: "124"}).count(), 1); + +// $max respects query collation. +coll.drop(); + +// "1234" < "124", so an update should not occur. +assert.commandWorked(coll.insert({a: "124"})); +assert.commandWorked(coll.update({a: "124"}, {$max: {a: "1234"}}, caseSensitive)); +assert.eq(coll.find({a: "124"}).count(), 1); + +// 1234 > 124, so an update should occur. +assert.commandWorked(coll.update({a: "124"}, {$max: {a: "1234"}}, numericOrdering)); +assert.eq(coll.find({a: "1234"}).count(), 1); + +// $max respects collection default collation. +coll.drop(); +assert.commandWorked(db.createCollection(coll.getName(), numericOrdering)); +assert.commandWorked(coll.insert({a: "124"})); +assert.commandWorked(coll.update({a: "124"}, {$max: {a: "1234"}})); +assert.eq(coll.find({a: "1234"}).count(), 1); + +// $addToSet respects query collation. +coll.drop(); + +// "foo" == "FOO" (case-insensitive), so set isn't extended. +assert.commandWorked(coll.insert({a: ["foo"]})); +assert.commandWorked(coll.update({}, {$addToSet: {a: "FOO"}}, caseInsensitive)); +var set = coll.findOne().a; +assert.eq(set.length, 1); + +// "foo" != "FOO" (case-sensitive), so set is extended. +assert.commandWorked(coll.update({}, {$addToSet: {a: "FOO"}}, caseSensitive)); +set = coll.findOne().a; +assert.eq(set.length, 2); + +coll.drop(); + +// $each and $addToSet respect collation +assert.commandWorked(coll.insert({a: ["foo", "bar", "FOO"]})); +assert.commandWorked( + coll.update({}, {$addToSet: {a: {$each: ["FOO", "BAR", "str"]}}}, caseInsensitive)); +set = coll.findOne().a; +assert.eq(set.length, 4); +assert(set.includes("foo")); +assert(set.includes("FOO")); +assert(set.includes("bar")); +assert(set.includes("str")); + +coll.drop(); +assert.commandWorked(db.createCollection(coll.getName(), caseInsensitive)); +// "foo" == "FOO" (case-insensitive), so set isn't extended. +assert.commandWorked(coll.insert({a: ["foo"]})); +assert.commandWorked(coll.update({}, {$addToSet: {a: "FOO"}})); +var set = coll.findOne().a; +assert.eq(set.length, 1); + +// $pull respects query collation. +coll.drop(); + +// "foo" != "FOO" (case-sensitive), so it is not pulled. +assert.commandWorked(coll.insert({a: ["foo", "FOO"]})); +assert.commandWorked(coll.update({}, {$pull: {a: "foo"}}, caseSensitive)); +var arr = coll.findOne().a; +assert.eq(arr.length, 1); +assert(arr.includes("FOO")); + +// "foo" == "FOO" (case-insensitive), so "FOO" is pulled. +assert.commandWorked(coll.update({}, {$pull: {a: "foo"}}, caseInsensitive)); +arr = coll.findOne().a; +assert.eq(arr.length, 0); + +// collation-aware $pull removes all instances that match. +coll.drop(); +assert.commandWorked(coll.insert({a: ["foo", "FOO"]})); +assert.commandWorked(coll.update({}, {$pull: {a: "foo"}}, caseInsensitive)); +arr = coll.findOne().a; +assert.eq(arr.length, 0); + +// collation-aware $pull with comparisons removes matching instances. +coll.drop(); + +// "124" > "1234" (case-sensitive), so it is not removed. +assert.commandWorked(coll.insert({a: ["124", "1234"]})); +assert.commandWorked(coll.update({}, {$pull: {a: {$lt: "1234"}}}, caseSensitive)); +arr = coll.findOne().a; +assert.eq(arr.length, 2); + +// 124 < 1234 (numeric ordering), so it is removed. +assert.commandWorked(coll.update({}, {$pull: {a: {$lt: "1234"}}}, numericOrdering)); +arr = coll.findOne().a; +assert.eq(arr.length, 1); +assert(arr.includes("1234")); + +// $pull respects collection default collation. +coll.drop(); +assert.commandWorked(db.createCollection(coll.getName(), caseInsensitive)); +assert.commandWorked(coll.insert({a: ["foo", "FOO"]})); +assert.commandWorked(coll.update({}, {$pull: {a: "foo"}})); +var arr = coll.findOne().a; +assert.eq(arr.length, 0); + +// $pullAll respects query collation. +coll.drop(); + +// "foo" != "FOO" (case-sensitive), so no changes are made. +assert.commandWorked(coll.insert({a: ["foo", "bar"]})); +assert.commandWorked(coll.update({}, {$pullAll: {a: ["FOO", "BAR"]}}, caseSensitive)); +var arr = coll.findOne().a; +assert.eq(arr.length, 2); + +// "foo" == "FOO", "bar" == "BAR" (case-insensitive), so both are removed. +assert.commandWorked(coll.update({}, {$pullAll: {a: ["FOO", "BAR"]}}, caseInsensitive)); +arr = coll.findOne().a; +assert.eq(arr.length, 0); + +// $pullAll respects collection default collation. +coll.drop(); +assert.commandWorked(db.createCollection(coll.getName(), caseInsensitive)); +assert.commandWorked(coll.insert({a: ["foo", "bar"]})); +assert.commandWorked(coll.update({}, {$pullAll: {a: ["FOO", "BAR"]}})); +var arr = coll.findOne().a; +assert.eq(arr.length, 0); + +// $push with $sort respects query collation. +coll.drop(); + +// "1230" < "1234" < "124" (case-sensitive) +assert.commandWorked(coll.insert({a: ["1234", "124"]})); +assert.commandWorked(coll.update({}, {$push: {a: {$each: ["1230"], $sort: 1}}}, caseSensitive)); +var arr = coll.findOne().a; +assert.eq(arr.length, 3); +assert.eq(arr[0], "1230"); +assert.eq(arr[1], "1234"); +assert.eq(arr[2], "124"); + +// "124" < "1230" < "1234" (numeric ordering) +coll.drop(); +assert.commandWorked(coll.insert({a: ["1234", "124"]})); +assert.commandWorked(coll.update({}, {$push: {a: {$each: ["1230"], $sort: 1}}}, numericOrdering)); +arr = coll.findOne().a; +assert.eq(arr.length, 3); +assert.eq(arr[0], "124"); +assert.eq(arr[1], "1230"); +assert.eq(arr[2], "1234"); + +// $push with $sort respects collection default collation. +coll.drop(); +assert.commandWorked(db.createCollection(coll.getName(), numericOrdering)); +assert.commandWorked(coll.insert({a: ["1234", "124"]})); +assert.commandWorked(coll.update({}, {$push: {a: {$each: ["1230"], $sort: 1}}})); +var arr = coll.findOne().a; +assert.eq(arr.length, 3); +assert.eq(arr[0], "124"); +assert.eq(arr[1], "1230"); +assert.eq(arr[2], "1234"); + +// $ positional operator respects query collation on $set. +coll.drop(); + +// "foo" != "FOO" (case-sensitive) so no update occurs. +assert.commandWorked(coll.insert({a: ["foo", "FOO"]})); +assert.commandWorked(coll.update({a: "FOO"}, {$set: {"a.$": "FOO"}}, caseSensitive)); +var arr = coll.findOne().a; +assert.eq(arr.length, 2); +assert.eq(arr[0], "foo"); +assert.eq(arr[1], "FOO"); + +// "foo" == "FOO" (case-insensitive) so an update occurs. +assert.commandWorked(coll.update({a: "FOO"}, {$set: {"a.$": "FOO"}}, caseInsensitive)); +var arr = coll.findOne().a; +assert.eq(arr.length, 2); +assert.eq(arr[0], "FOO"); +assert.eq(arr[1], "FOO"); + +// $ positional operator respects collection default collation on $set. +coll.drop(); +assert.commandWorked(db.createCollection(coll.getName(), caseInsensitive)); +assert.commandWorked(coll.insert({a: ["foo", "FOO"]})); +assert.commandWorked(coll.update({a: "FOO"}, {$set: {"a.$": "FOO"}})); +var arr = coll.findOne().a; +assert.eq(arr.length, 2); +assert.eq(arr[0], "FOO"); +assert.eq(arr[1], "FOO"); + +// Pipeline-style update respects collection default collation. +assert(coll.drop()); +assert.commandWorked(db.createCollection(coll.getName(), caseInsensitive)); +assert.commandWorked(coll.insert({x: [1, 2, "a", "b", "c", "B"]})); +assert.commandWorked(coll.update({}, [{$addFields: {newField: {$indexOfArray: ["$x", "B"]}}}])); +assert.eq(coll.findOne().newField, 3, `actual=${coll.findOne()}`); + +// Pipeline-style update respects query collation. +// +// Case sensitive $indexOfArray on "B" matches "B". +assert(coll.drop()); +assert.commandWorked(coll.insert({x: [1, 2, "a", "b", "c", "B"]})); +assert.commandWorked( + coll.update({}, [{$addFields: {newField: {$indexOfArray: ["$x", "B"]}}}], caseSensitive)); +assert.eq(coll.findOne().newField, 5, `actual=${coll.findOne()}`); + +assert(coll.drop()); +assert.commandWorked(coll.insert({x: [1, 2, "a", "b", "c", "B"]})); +assert.commandWorked( + coll.update({}, [{$project: {newField: {$indexOfArray: ["$x", "B"]}}}], caseSensitive)); +assert.eq(coll.findOne().newField, 5, `actual=${coll.findOne()}`); + +assert(coll.drop()); +assert.commandWorked(coll.insert({x: [1, 2, "a", "b", "c", "B"]})); +assert.commandWorked( + coll.update({}, [{$replaceWith: {newField: {$indexOfArray: ["$x", "B"]}}}], caseSensitive)); +assert.eq(coll.findOne().newField, 5, `actual=${coll.findOne()}`); + +// Case insensitive $indexOfArray on "B" matches "b". +assert(coll.drop()); +assert.commandWorked(coll.insert({x: [1, 2, "a", "b", "c", "B"]})); +assert.commandWorked( + coll.update({}, [{$addFields: {newField: {$indexOfArray: ["$x", "B"]}}}], caseInsensitive)); +assert.eq(coll.findOne().newField, 3, `actual=${coll.findOne()}`); + +assert(coll.drop()); +assert.commandWorked(coll.insert({x: [1, 2, "a", "b", "c", "B"]})); +assert.commandWorked( + coll.update({}, [{$project: {newField: {$indexOfArray: ["$x", "B"]}}}], caseInsensitive)); +assert.eq(coll.findOne().newField, 3, `actual=${coll.findOne()}`); + +assert(coll.drop()); +assert.commandWorked(coll.insert({x: [1, 2, "a", "b", "c", "B"]})); +assert.commandWorked( + coll.update({}, [{$replaceWith: {newField: {$indexOfArray: ["$x", "B"]}}}], caseInsensitive)); +assert.eq(coll.findOne().newField, 3, `actual=${coll.findOne()}`); + +// Collation is respected for pipeline-style bulk update. +assert(coll.drop()); +assert.commandWorked(coll.insert({x: [1, 2, "a", "b", "c", "B"]})); +assert.commandWorked(coll.bulkWrite([{ + updateOne: { + filter: {}, + update: [{$addFields: {newField: {$indexOfArray: ["$x", "B"]}}}], + collation: caseInsensitive.collation + } +}])); +assert.eq(coll.findOne().newField, 3, `actual=${coll.findOne()}`); + +assert(coll.drop()); +assert.commandWorked(coll.insert({x: [1, 2, "a", "b", "c", "B"]})); +assert.commandWorked(coll.bulkWrite([{ + updateOne: { + filter: {}, + update: [{$project: {newField: {$indexOfArray: ["$x", "B"]}}}], + collation: caseInsensitive.collation + } +}])); +assert.eq(coll.findOne().newField, 3, `actual=${coll.findOne()}`); + +assert(coll.drop()); +assert.commandWorked(coll.insert({x: [1, 2, "a", "b", "c", "B"]})); +assert.commandWorked(coll.bulkWrite([{ + updateOne: { + filter: {}, + update: [{$replaceWith: {newField: {$indexOfArray: ["$x", "B"]}}}], + collation: caseInsensitive.collation + } +}])); +assert.eq(coll.findOne().newField, 3, `actual=${coll.findOne()}`); +})(); |