diff options
author | David Storch <david.storch@10gen.com> | 2016-06-02 16:11:17 -0700 |
---|---|---|
committer | David Storch <david.storch@10gen.com> | 2016-06-03 15:54:51 -0700 |
commit | bd5ef88fd884a2cc95eab6d4b66ce09d5efacb81 (patch) | |
tree | 3b890a9df96f32b0b870beef523ae292b199e840 | |
parent | 681372e942244307067b39c5fe2b27069f5fbbe7 (diff) | |
download | mongo-bd5ef88fd884a2cc95eab6d4b66ce09d5efacb81.tar.gz |
SERVER-23791 harden integration testing for commands that accept a collation
Also fixes bugs in collation plumbing for the following commands:
- findAndModify
- group
- mapReduce
-rw-r--r-- | jstests/core/collation_shell_helpers.js | 363 | ||||
-rw-r--r-- | src/mongo/db/commands/find_and_modify.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/commands/mr.cpp | 1 | ||||
-rw-r--r-- | src/mongo/db/query/get_executor.cpp | 1 |
4 files changed, 274 insertions, 93 deletions
diff --git a/jstests/core/collation_shell_helpers.js b/jstests/core/collation_shell_helpers.js index 8c22c57512d..01755a342c8 100644 --- a/jstests/core/collation_shell_helpers.js +++ b/jstests/core/collation_shell_helpers.js @@ -1,16 +1,15 @@ -// Ensure that shell helpers correctly deliver the collation to the server. -// -// TODO SERVER-23791: Once we have an end-to-end working on mongod, we should be able to strengthen -// the assertions in this file in order to ensure that the server is correctly respecting the -// assertion. Currently we exercise the code paths in the shell that are supposed to propagate the -// collation to the server, but we don't require that the result of executing the command respects -// the collation. +// Integration tests for the collation feature. (function() { 'use strict'; + load("jstests/libs/analyze_plan.js"); + var coll = db.collation_shell_helpers; coll.drop(); + var explainRes; + var planStage; + var assertIndexHasCollation = function(keyPattern, collation) { var foundIndex = false; var indexSpecs = coll.getIndexes(); @@ -163,9 +162,45 @@ assert.commandWorked(coll.createIndexes([{e: 1}], {collation: {locale: "simple"}})); assertIndexHasCollation({e: 1}, {locale: "simple"}); - // TODO SERVER-23791: Test that queries with matching collations can use these indices, and that - // the indices contain collator-generated comparison keys rather than the verbatim indexed - // strings. + // Test that an index with a non-simple collation contains collator-generated comparison keys + // rather than the verbatim indexed strings. + if (db.getMongo().useReadCommands()) { + coll.drop(); + assert.commandWorked(coll.createIndex({a: 1}, {collation: {locale: "fr_CA"}})); + assert.commandWorked(coll.createIndex({b: 1})); + assert.writeOK(coll.insert({a: "foo", b: "foo"})); + assert.eq( + 1, coll.find({}, {_id: 0, a: 1}).collation({locale: "fr_CA"}).hint({a: 1}).itcount()); + assert.neq( + "foo", + coll.find({}, {_id: 0, a: 1}).collation({locale: "fr_CA"}).hint({a: 1}).next().a); + assert.eq( + 1, coll.find({}, {_id: 0, b: 1}).collation({locale: "fr_CA"}).hint({b: 1}).itcount()); + assert.eq("foo", + coll.find({}, {_id: 0, b: 1}).collation({locale: "fr_CA"}).hint({b: 1}).next().b); + } + + // Test that a query with a string comparison can use an index with a non-simple collation if it + // has a matching collation. + if (db.getMongo().useReadCommands()) { + coll.drop(); + assert.commandWorked(coll.createIndex({a: 1}, {collation: {locale: "fr_CA"}})); + + // Query has simple collation, but index has fr_CA collation. + explainRes = coll.find({a: "foo"}).explain(); + assert.commandWorked(explainRes); + assert(planHasStage(explainRes.queryPlanner.winningPlan, "COLLSCAN")); + + // Query has en_US collation, but index has fr_CA collation. + explainRes = coll.find({a: "foo"}).collation({locale: "en_US"}).explain(); + assert.commandWorked(explainRes); + assert(planHasStage(explainRes.queryPlanner.winningPlan, "COLLSCAN")); + + // Matching collations. + explainRes = coll.find({a: "foo"}).collation({locale: "fr_CA"}).explain(); + assert.commandWorked(explainRes); + assert(planHasStage(explainRes.queryPlanner.winningPlan, "IXSCAN")); + } // // Test helpers for operations that accept a collation. @@ -176,7 +211,10 @@ assert.writeOK(coll.insert({_id: 2, str: "bar"})); // Aggregation. - assert.eq(2, coll.aggregate([], {collation: {locale: "fr"}}).itcount()); + assert.eq(0, coll.aggregate([{$match: {str: "FOO"}}]).itcount()); + assert.eq(1, + coll.aggregate([{$match: {str: "FOO"}}], {collation: {locale: "en_US", strength: 2}}) + .itcount()); assert.commandWorked(coll.explain().aggregate([], {collation: {locale: "fr"}})); // Count command. @@ -186,7 +224,22 @@ assert.eq(0, coll.count({str: "FOO"})); assert.eq(0, coll.count({str: "FOO"}, {collation: {locale: "en_US"}})); assert.eq(1, coll.count({str: "FOO"}, {collation: {locale: "en_US", strength: 2}})); - assert.commandWorked(coll.explain().find().collation({locale: "fr"}).count()); + + explainRes = + coll.explain("executionStats").find({str: "FOO"}).collation({locale: "en_US"}).count(); + assert.commandWorked(explainRes); + planStage = getPlanStage(explainRes.executionStats.executionStages, "COLLSCAN"); + assert.neq(null, planStage); + assert.eq(0, planStage.advanced); + + explainRes = coll.explain("executionStats") + .find({str: "FOO"}) + .collation({locale: "en_US", strength: 2}) + .count(); + assert.commandWorked(explainRes); + planStage = getPlanStage(explainRes.executionStats.executionStages, "COLLSCAN"); + assert.neq(null, planStage); + assert.eq(1, planStage.advanced); // Distinct. assert.eq(["foo", "bar"], coll.distinct("str", {}, {collation: {locale: "fr"}})); @@ -218,9 +271,8 @@ .itcount()); assert.commandWorked(coll.dropIndexes()); - // With a partial index. - // {_id: 1, str: "foo"} will be indexed even though "foo" > "FOO", since the collation is - // case-insensitive. + // With a partial index. {_id: 1, str: "foo"} will be indexed even though "foo" > "FOO", + // since the collation is case-insensitive. assert.commandWorked(coll.ensureIndex({str: 1}, { partialFilterExpression: {str: {$lte: "FOO"}}, collation: {locale: "en_US", strength: 2} @@ -243,37 +295,83 @@ coll.find().collation({locale: "fr"}).itcount(); }); } - // Explain of find always uses the find command, so this will succeed regardless of readMode. - assert.commandWorked(coll.explain().find().collation({locale: "fr"}).finish()); - assert.commandWorked(coll.find().collation({locale: "fr"}).explain()); - // findAndModify. - assert.eq({_id: 2, str: "baz"}, coll.findAndModify({ - query: {str: "bar"}, + // Explain of find always uses the find command, so this will succeed regardless of readMode. + explainRes = + coll.explain("executionStats").find({str: "FOO"}).collation({locale: "en_US"}).finish(); + assert.commandWorked(explainRes); + assert.eq(0, explainRes.executionStats.nReturned); + explainRes = coll.explain("executionStats") + .find({str: "FOO"}) + .collation({locale: "en_US", strength: 2}) + .finish(); + assert.commandWorked(explainRes); + assert.eq(1, explainRes.executionStats.nReturned); + + // Update via findAndModify. + coll.drop(); + assert.writeOK(coll.insert({_id: 1, str: "foo"})); + assert.writeOK(coll.insert({_id: 2, str: "bar"})); + assert.eq({_id: 1, str: "baz"}, coll.findAndModify({ + query: {str: "FOO"}, update: {$set: {str: "baz"}}, new: true, - collation: {locale: "fr"} + collation: {locale: "en_US", strength: 2} })); - assert.commandWorked(coll.explain().findAndModify( - {query: {str: "bar"}, update: {$set: {str: "baz"}}, new: true, collation: {locale: "fr"}})); + explainRes = coll.explain("executionStats").findAndModify({ + query: {str: "BAR"}, + update: {$set: {str: "baz"}}, + new: true, + collation: {locale: "en_US", strength: 2} + }); + assert.commandWorked(explainRes); + planStage = getPlanStage(explainRes.executionStats.executionStages, "UPDATE"); + assert.neq(null, planStage); + assert.eq(1, planStage.nWouldModify); + + // Delete via findAndModify. + coll.drop(); + assert.writeOK(coll.insert({_id: 1, str: "foo"})); + assert.writeOK(coll.insert({_id: 2, str: "bar"})); + assert.eq({_id: 1, str: "foo"}, + coll.findAndModify( + {query: {str: "FOO"}, remove: true, collation: {locale: "en_US", strength: 2}})); + explainRes = coll.explain("executionStats").findAndModify({ + query: {str: "BAR"}, + remove: true, + collation: {locale: "en_US", strength: 2} + }); + assert.commandWorked(explainRes); + planStage = getPlanStage(explainRes.executionStats.executionStages, "DELETE"); + assert.neq(null, planStage); + assert.eq(1, planStage.nWouldDelete); // Group. - assert.eq([{str: "foo", count: 1}, {str: "baz", count: 1}], coll.group({ + coll.drop(); + assert.writeOK(coll.insert({_id: 1, str: "foo"})); + assert.writeOK(coll.insert({_id: 2, str: "bar"})); + assert.eq([{str: "foo", count: 1}], coll.group({ + cond: {str: "FOO"}, key: {str: 1}, initial: {count: 0}, reduce: function(curr, result) { result.count += 1; }, - collation: {locale: "fr"} + collation: {locale: "en_US", strength: 2} })); - assert.commandWorked(coll.explain().group({ + explainRes = coll.explain("executionStats").group({ + cond: {str: "FOO"}, key: {str: 1}, initial: {count: 0}, reduce: function(curr, result) { result.count += 1; }, - collation: {locale: "fr"} - })); + collation: {locale: "en_US", strength: 2} + }); + assert.commandWorked(explainRes); + planStage = getPlanStage(explainRes.executionStats.executionStages, "GROUP"); + assert.neq(null, planStage); + assert.eq(planStage.nGroups, 1); // mapReduce. var mapReduceOut = coll.mapReduce( @@ -283,24 +381,35 @@ function(key, values) { return Array.sum(values); }, - {out: {inline: 1}, collation: {locale: "fr"}}); + {out: {inline: 1}, query: {str: "FOO"}, collation: {locale: "en_US", strength: 2}}); assert.commandWorked(mapReduceOut); - assert.eq(mapReduceOut.results.length, 2); + assert.eq(mapReduceOut.results.length, 1); // Remove. coll.drop(); assert.writeOK(coll.insert({_id: 1, str: "foo"})); assert.writeOK(coll.insert({_id: 2, str: "foo"})); if (db.getMongo().writeMode() === "commands") { - assert.commandWorked( - coll.explain().remove({str: "foo"}, {justOne: true, collation: {locale: "fr"}})); - assert.writeOK(coll.remove({str: "foo"}, {justOne: true, collation: {locale: "fr"}})); + explainRes = coll.explain("executionStats").remove({str: "FOO"}, { + justOne: true, + collation: {locale: "en_US", strength: 2} + }); + assert.commandWorked(explainRes); + planStage = getPlanStage(explainRes.executionStats.executionStages, "DELETE"); + assert.neq(null, planStage); + assert.eq(1, planStage.nWouldDelete); + + var writeRes = + coll.remove({str: "FOO"}, {justOne: true, collation: {locale: "en_US", strength: 2}}); + assert.writeOK(writeRes); + assert.eq(1, writeRes.nRemoved); } else { assert.throws(function() { - coll.remove({str: "foo"}, {justOne: true, collation: {locale: "fr"}}); + coll.remove({str: "FOO"}, {justOne: true, collation: {locale: "en_US", strength: 2}}); }); assert.throws(function() { - coll.explain().remove({str: "foo"}, {justOne: true, collation: {locale: "fr"}}); + coll.explain().remove({str: "FOO"}, + {justOne: true, collation: {locale: "en_US", strength: 2}}); }); } @@ -309,18 +418,29 @@ assert.writeOK(coll.insert({_id: 1, str: "foo"})); assert.writeOK(coll.insert({_id: 2, str: "foo"})); if (db.getMongo().writeMode() === "commands") { - assert.commandWorked(coll.explain().update( - {str: "foo"}, {$set: {other: 99}}, {multi: true, collation: {locale: "fr"}})); - assert.writeOK(coll.update( - {str: "foo"}, {$set: {other: 99}}, {multi: true, collation: {locale: "fr"}})); + explainRes = coll.explain("executionStats").update({str: "FOO"}, {$set: {other: 99}}, { + multi: true, + collation: {locale: "en_US", strength: 2} + }); + assert.commandWorked(explainRes); + planStage = getPlanStage(explainRes.executionStats.executionStages, "UPDATE"); + assert.neq(null, planStage); + assert.eq(2, planStage.nWouldModify); + + var writeRes = coll.update({str: "FOO"}, + {$set: {other: 99}}, + {multi: true, collation: {locale: "en_US", strength: 2}}); + assert.eq(2, writeRes.nModified); } else { assert.throws(function() { - coll.update( - {str: "foo"}, {$set: {other: 99}}, {multi: true, collation: {locale: "fr"}}); + coll.update({str: "FOO"}, + {$set: {other: 99}}, + {multi: true, collation: {locale: "en_US", strength: 2}}); }); assert.throws(function() { - coll.explain().update( - {str: "foo"}, {$set: {other: 99}}, {multi: true, collation: {locale: "fr"}}); + coll.explain().update({str: "FOO"}, + {$set: {other: 99}}, + {multi: true, collation: {locale: "en_US", strength: 2}}); }); } @@ -506,62 +626,96 @@ var bulk; if (db.getMongo().writeMode() !== "commands") { + coll.drop(); + assert.writeOK(coll.insert({_id: 1, str: "foo"})); + assert.writeOK(coll.insert({_id: 2, str: "foo"})); + // Can't use the bulk API to set a collation when using legacy write ops. bulk = coll.initializeUnorderedBulkOp(); assert.throws(function() { - bulk.find({str: "foo"}).collation({locale: "fr"}); + bulk.find({str: "FOO"}).collation({locale: "en_US", strength: 2}); }); bulk = coll.initializeOrderedBulkOp(); assert.throws(function() { - bulk.find({str: "foo"}).collation({locale: "en_US"}); + bulk.find({str: "FOO"}).collation({locale: "en_US", strength: 2}); }); } else { + var writeRes; + // update(). coll.drop(); assert.writeOK(coll.insert({_id: 1, str: "foo"})); assert.writeOK(coll.insert({_id: 2, str: "foo"})); bulk = coll.initializeUnorderedBulkOp(); - bulk.find({str: "foo"}).collation({locale: "fr"}).update({$set: {other: 99}}); - assert.writeOK(bulk.execute()); + bulk.find({str: "FOO"}).collation({locale: "en_US", strength: 2}).update({ + $set: {other: 99} + }); + writeRes = bulk.execute(); + assert.writeOK(writeRes); + assert.eq(2, writeRes.nModified); // updateOne(). coll.drop(); assert.writeOK(coll.insert({_id: 1, str: "foo"})); assert.writeOK(coll.insert({_id: 2, str: "foo"})); bulk = coll.initializeUnorderedBulkOp(); - bulk.find({str: "foo"}).collation({locale: "fr"}).updateOne({$set: {other: 99}}); - assert.writeOK(bulk.execute()); + bulk.find({str: "FOO"}).collation({locale: "en_US", strength: 2}).updateOne({ + $set: {other: 99} + }); + writeRes = bulk.execute(); + assert.writeOK(writeRes); + assert.eq(1, writeRes.nModified); // replaceOne(). coll.drop(); assert.writeOK(coll.insert({_id: 1, str: "foo"})); assert.writeOK(coll.insert({_id: 2, str: "foo"})); bulk = coll.initializeUnorderedBulkOp(); - bulk.find({str: "foo"}).collation({locale: "fr"}).replaceOne({str: "oof"}); - assert.writeOK(bulk.execute()); + bulk.find({str: "FOO"}).collation({locale: "en_US", strength: 2}).replaceOne({str: "oof"}); + writeRes = bulk.execute(); + assert.writeOK(writeRes); + assert.eq(1, writeRes.nModified); // replaceOne() with upsert(). coll.drop(); + assert.writeOK(coll.insert({_id: 1, str: "foo"})); + assert.writeOK(coll.insert({_id: 2, str: "foo"})); bulk = coll.initializeUnorderedBulkOp(); - bulk.find({str: "foo"}).collation({locale: "fr"}).upsert().replaceOne({str: "foo"}); - assert.writeOK(bulk.execute()); + bulk.find({str: "FOO"}).collation({locale: "en_US"}).upsert().replaceOne({str: "foo"}); + writeRes = bulk.execute(); + assert.writeOK(writeRes); + assert.eq(1, writeRes.nUpserted); + assert.eq(0, writeRes.nModified); + + bulk = coll.initializeUnorderedBulkOp(); + bulk.find({str: "FOO"}).collation({locale: "en_US", strength: 2}).upsert().replaceOne({ + str: "foo" + }); + writeRes = bulk.execute(); + assert.writeOK(writeRes); + assert.eq(0, writeRes.nUpserted); + assert.eq(1, writeRes.nModified); // removeOne(). coll.drop(); assert.writeOK(coll.insert({_id: 1, str: "foo"})); assert.writeOK(coll.insert({_id: 2, str: "foo"})); bulk = coll.initializeUnorderedBulkOp(); - bulk.find({str: "foo"}).collation({locale: "fr"}).removeOne(); - assert.writeOK(bulk.execute()); + bulk.find({str: "FOO"}).collation({locale: "en_US", strength: 2}).removeOne(); + writeRes = bulk.execute(); + assert.writeOK(writeRes); + assert.eq(1, writeRes.nRemoved); // remove(). coll.drop(); assert.writeOK(coll.insert({_id: 1, str: "foo"})); assert.writeOK(coll.insert({_id: 2, str: "foo"})); bulk = coll.initializeUnorderedBulkOp(); - bulk.find({str: "foo"}).collation({locale: "fr"}).remove(); - assert.writeOK(bulk.execute()); + bulk.find({str: "FOO"}).collation({locale: "en_US", strength: 2}).remove(); + writeRes = bulk.execute(); + assert.writeOK(writeRes); + assert.eq(2, writeRes.nRemoved); } // @@ -573,11 +727,11 @@ assert.writeOK(coll.insert({_id: 1, str: "foo"})); assert.writeOK(coll.insert({_id: 2, str: "foo"})); if (db.getMongo().writeMode() === "commands") { - var res = coll.deleteOne({str: "foo"}, {collation: {locale: "fr"}}); + var res = coll.deleteOne({str: "FOO"}, {collation: {locale: "en_US", strength: 2}}); assert.eq(1, res.deletedCount); } else { assert.throws(function() { - coll.deleteOne({str: "foo"}, {collation: {locale: "fr"}}); + coll.deleteOne({str: "FOO"}, {collation: {locale: "en_US", strength: 2}}); }); } @@ -586,11 +740,11 @@ assert.writeOK(coll.insert({_id: 1, str: "foo"})); assert.writeOK(coll.insert({_id: 2, str: "foo"})); if (db.getMongo().writeMode() === "commands") { - var res = coll.deleteMany({str: "foo"}, {collation: {locale: "fr"}}); + var res = coll.deleteMany({str: "FOO"}, {collation: {locale: "en_US", strength: 2}}); assert.eq(2, res.deletedCount); } else { assert.throws(function() { - coll.deleteMany({str: "foo"}, {collation: {locale: "fr"}}); + coll.deleteMany({str: "FOO"}, {collation: {locale: "en_US", strength: 2}}); }); } @@ -598,22 +752,23 @@ coll.drop(); assert.writeOK(coll.insert({_id: 1, str: "foo"})); assert.eq({_id: 1, str: "foo"}, - coll.findOneAndDelete({str: "foo"}, {collation: {locale: "fr"}})); + coll.findOneAndDelete({str: "FOO"}, {collation: {locale: "en_US", strength: 2}})); assert.eq(null, coll.findOne({_id: 1})); // findOneAndReplace(). coll.drop(); assert.writeOK(coll.insert({_id: 1, str: "foo"})); assert.eq({_id: 1, str: "foo"}, - coll.findOneAndReplace({str: "foo"}, {str: "bar"}, {collation: {locale: "fr"}})); + coll.findOneAndReplace( + {str: "FOO"}, {str: "bar"}, {collation: {locale: "en_US", strength: 2}})); assert.neq(null, coll.findOne({str: "bar"})); // findOneAndUpdate(). coll.drop(); assert.writeOK(coll.insert({_id: 1, str: "foo"})); - assert.eq( - {_id: 1, str: "foo"}, - coll.findOneAndUpdate({str: "foo"}, {$set: {other: 99}}, {collation: {locale: "fr"}})); + assert.eq({_id: 1, str: "foo"}, + coll.findOneAndUpdate( + {str: "FOO"}, {$set: {other: 99}}, {collation: {locale: "en_US", strength: 2}})); assert.neq(null, coll.findOne({other: 99})); // replaceOne(). @@ -621,11 +776,13 @@ assert.writeOK(coll.insert({_id: 1, str: "foo"})); assert.writeOK(coll.insert({_id: 2, str: "foo"})); if (db.getMongo().writeMode() === "commands") { - var res = coll.replaceOne({str: "foo"}, {str: "bar"}, {collation: {locale: "fr"}}); + var res = coll.replaceOne( + {str: "FOO"}, {str: "bar"}, {collation: {locale: "en_US", strength: 2}}); assert.eq(1, res.modifiedCount); } else { assert.throws(function() { - coll.replaceOne({str: "foo"}, {str: "bar"}, {collation: {locale: "fr"}}); + coll.replaceOne( + {str: "FOO"}, {str: "bar"}, {collation: {locale: "en_US", strength: 2}}); }); } @@ -634,11 +791,13 @@ assert.writeOK(coll.insert({_id: 1, str: "foo"})); assert.writeOK(coll.insert({_id: 2, str: "foo"})); if (db.getMongo().writeMode() === "commands") { - var res = coll.updateOne({str: "foo"}, {$set: {other: 99}}, {collation: {locale: "fr"}}); + var res = coll.updateOne( + {str: "FOO"}, {$set: {other: 99}}, {collation: {locale: "en_US", strength: 2}}); assert.eq(1, res.modifiedCount); } else { assert.throws(function() { - coll.updateOne({str: "foo"}, {$set: {other: 99}}, {collation: {locale: "fr"}}); + coll.updateOne( + {str: "FOO"}, {$set: {other: 99}}, {collation: {locale: "en_US", strength: 2}}); }); } @@ -647,11 +806,13 @@ assert.writeOK(coll.insert({_id: 1, str: "foo"})); assert.writeOK(coll.insert({_id: 2, str: "foo"})); if (db.getMongo().writeMode() === "commands") { - var res = coll.updateMany({str: "foo"}, {$set: {other: 99}}, {collation: {locale: "fr"}}); + var res = coll.updateMany( + {str: "FOO"}, {$set: {other: 99}}, {collation: {locale: "en_US", strength: 2}}); assert.eq(2, res.modifiedCount); } else { assert.throws(function() { - coll.updateMany({str: "foo"}, {$set: {other: 99}}, {collation: {locale: "fr"}}); + coll.updateMany( + {str: "FOO"}, {$set: {other: 99}}, {collation: {locale: "en_US", strength: 2}}); }); } @@ -661,17 +822,20 @@ assert.writeOK(coll.insert({_id: 2, str: "foo"})); if (db.getMongo().writeMode() === "commands") { var res = coll.bulkWrite([{ - updateOne: - {filter: {str: "foo"}, update: {$set: {other: 99}}, collation: {locale: "fr"}} + updateOne: { + filter: {str: "FOO"}, + update: {$set: {other: 99}}, + collation: {locale: "en_US", strength: 2} + } }]); assert.eq(1, res.matchedCount); } else { assert.throws(function() { coll.bulkWrite([{ updateOne: { - filter: {str: "foo"}, + filter: {str: "FOO"}, update: {$set: {other: 99}}, - collation: {locale: "fr"} + collation: {locale: "en_US", strength: 2} } }]); }); @@ -683,17 +847,20 @@ assert.writeOK(coll.insert({_id: 2, str: "foo"})); if (db.getMongo().writeMode() === "commands") { var res = coll.bulkWrite([{ - updateMany: - {filter: {str: "foo"}, update: {$set: {other: 99}}, collation: {locale: "fr"}} + updateMany: { + filter: {str: "FOO"}, + update: {$set: {other: 99}}, + collation: {locale: "en_US", strength: 2} + } }]); assert.eq(2, res.matchedCount); } else { assert.throws(function() { coll.bulkWrite([{ updateMany: { - filter: {str: "foo"}, + filter: {str: "FOO"}, update: {$set: {other: 99}}, - collation: {locale: "fr"} + collation: {locale: "en_US", strength: 2} } }]); }); @@ -705,15 +872,21 @@ assert.writeOK(coll.insert({_id: 2, str: "foo"})); if (db.getMongo().writeMode() === "commands") { var res = coll.bulkWrite([{ - replaceOne: - {filter: {str: "foo"}, replacement: {str: "bar"}, collation: {locale: "fr"}} + replaceOne: { + filter: {str: "FOO"}, + replacement: {str: "bar"}, + collation: {locale: "en_US", strength: 2} + } }]); assert.eq(1, res.matchedCount); } else { assert.throws(function() { coll.bulkWrite([{ - replaceOne: - {filter: {str: "foo"}, replacement: {str: "bar"}, collation: {locale: "fr"}} + replaceOne: { + filter: {str: "FOO"}, + replacement: {str: "bar"}, + collation: {locale: "en_US", strength: 2} + } }]); }); } @@ -723,11 +896,13 @@ assert.writeOK(coll.insert({_id: 1, str: "foo"})); assert.writeOK(coll.insert({_id: 2, str: "foo"})); if (db.getMongo().writeMode() === "commands") { - var res = coll.bulkWrite([{deleteOne: {filter: {str: "foo"}, collation: {locale: "fr"}}}]); + var res = coll.bulkWrite( + [{deleteOne: {filter: {str: "FOO"}, collation: {locale: "en_US", strength: 2}}}]); assert.eq(1, res.deletedCount); } else { assert.throws(function() { - coll.bulkWrite([{deleteOne: {filter: {str: "foo"}, collation: {locale: "fr"}}}]); + coll.bulkWrite( + [{deleteOne: {filter: {str: "FOO"}, collation: {locale: "en_US", strength: 2}}}]); }); } @@ -736,11 +911,13 @@ assert.writeOK(coll.insert({_id: 1, str: "foo"})); assert.writeOK(coll.insert({_id: 2, str: "foo"})); if (db.getMongo().writeMode() === "commands") { - var res = coll.bulkWrite([{deleteMany: {filter: {str: "foo"}, collation: {locale: "fr"}}}]); + var res = coll.bulkWrite( + [{deleteMany: {filter: {str: "FOO"}, collation: {locale: "en_US", strength: 2}}}]); assert.eq(2, res.deletedCount); } else { assert.throws(function() { - coll.bulkWrite([{deleteMany: {filter: {str: "foo"}, collation: {locale: "fr"}}}]); + coll.bulkWrite( + [{deleteMany: {filter: {str: "FOO"}, collation: {locale: "en_US", strength: 2}}}]); }); } @@ -750,15 +927,15 @@ assert.writeOK(coll.insert({_id: 2, str: "bar"})); if (db.getMongo().writeMode() === "commands") { var res = coll.bulkWrite([ - {deleteOne: {filter: {str: "foo"}, collation: {locale: "fr"}}}, - {deleteOne: {filter: {str: "bar"}, collation: {locale: "en_US"}}} + {deleteOne: {filter: {str: "FOO"}, collation: {locale: "fr", strength: 2}}}, + {deleteOne: {filter: {str: "BAR"}, collation: {locale: "en_US", strength: 2}}} ]); assert.eq(2, res.deletedCount); } else { assert.throws(function() { coll.bulkWrite([ - {deleteOne: {filter: {str: "foo"}, collation: {locale: "fr"}}}, - {deleteOne: {filter: {str: "bar"}, collation: {locale: "en_US"}}} + {deleteOne: {filter: {str: "FOO"}, collation: {locale: "fr", strength: 2}}}, + {deleteOne: {filter: {str: "BAR"}, collation: {locale: "en_US", strength: 2}}} ]); }); } diff --git a/src/mongo/db/commands/find_and_modify.cpp b/src/mongo/db/commands/find_and_modify.cpp index dd1e04f311b..6efdb157ad8 100644 --- a/src/mongo/db/commands/find_and_modify.cpp +++ b/src/mongo/db/commands/find_and_modify.cpp @@ -143,6 +143,7 @@ void makeUpdateRequest(const FindAndModifyRequest& args, requestOut->setProj(args.getFields()); requestOut->setUpdates(args.getUpdateObj()); requestOut->setSort(args.getSort()); + requestOut->setCollation(args.getCollation()); requestOut->setUpsert(args.isUpsert()); requestOut->setReturnDocs(args.shouldReturnNew() ? UpdateRequest::RETURN_NEW : UpdateRequest::RETURN_OLD); @@ -156,6 +157,7 @@ void makeDeleteRequest(const FindAndModifyRequest& args, bool explain, DeleteReq requestOut->setQuery(args.getQuery()); requestOut->setProj(args.getFields()); requestOut->setSort(args.getSort()); + requestOut->setCollation(args.getCollation()); requestOut->setMulti(false); requestOut->setYieldPolicy(PlanExecutor::YIELD_AUTO); requestOut->setReturnDeleted(true); // Always return the old value. diff --git a/src/mongo/db/commands/mr.cpp b/src/mongo/db/commands/mr.cpp index b86382a7903..14acf44d7f3 100644 --- a/src/mongo/db/commands/mr.cpp +++ b/src/mongo/db/commands/mr.cpp @@ -1447,6 +1447,7 @@ public: auto qr = stdx::make_unique<QueryRequest>(nss); qr->setFilter(config.filter); qr->setSort(config.sort); + qr->setCollation(config.collation); const ExtensionsCallbackReal extensionsCallback(txn, &nss); diff --git a/src/mongo/db/query/get_executor.cpp b/src/mongo/db/query/get_executor.cpp index 22efcb696de..2b05871af47 100644 --- a/src/mongo/db/query/get_executor.cpp +++ b/src/mongo/db/query/get_executor.cpp @@ -942,6 +942,7 @@ StatusWith<unique_ptr<PlanExecutor>> getExecutorGroup(OperationContext* txn, const NamespaceString nss(request.ns); auto qr = stdx::make_unique<QueryRequest>(nss); qr->setFilter(request.query); + qr->setCollation(request.collation); qr->setExplain(request.explain); const ExtensionsCallbackReal extensionsCallback(txn, &nss); |