summaryrefslogtreecommitdiff
path: root/jstests/aggregation/sources/lookup
diff options
context:
space:
mode:
Diffstat (limited to 'jstests/aggregation/sources/lookup')
-rw-r--r--jstests/aggregation/sources/lookup/lookup_absorb_match.js104
-rw-r--r--jstests/aggregation/sources/lookup/lookup_collation.js45
-rw-r--r--jstests/aggregation/sources/lookup/lookup_foreign_collation.js121
3 files changed, 112 insertions, 158 deletions
diff --git a/jstests/aggregation/sources/lookup/lookup_absorb_match.js b/jstests/aggregation/sources/lookup/lookup_absorb_match.js
index be589615f5c..4220cbcb71a 100644
--- a/jstests/aggregation/sources/lookup/lookup_absorb_match.js
+++ b/jstests/aggregation/sources/lookup/lookup_absorb_match.js
@@ -136,7 +136,7 @@ expected =
[{_id: "dog", locationId: "doghouse", location: {_id: "doghouse", coordinates: [25.0, 60.0]}}];
assert.eq(result, expected);
-// Test that a $match with $jsonSchema works as expected although ineligable for absorbtion by a
+// Test that a $match with $jsonSchema works as expected although ineligible for absorbtion by a
// $lookup.
result = testDB.animals
.aggregate([
@@ -167,7 +167,7 @@ expected =
[{_id: "bull", locationId: "bullpen", location: {_id: "bullpen", coordinates: [-25.0, -60.0]}}];
assert.eq(result, expected);
-// Test that a more complex $match with $jsonSchema works as expected although ineligable for
+// Test that a more complex $match with $jsonSchema works as expected although ineligible for
// absorbtion by a $lookup.
result = testDB.animals
.aggregate([
@@ -194,8 +194,100 @@ expected =
[{_id: "bull", locationId: "bullpen", location: {_id: "bullpen", coordinates: [-25.0, -60.0]}}];
assert.eq(result, expected);
-// Test that a $match with $alwaysTrue works as expected although ineligable for absorbtion by a
-// $lookup.
+// Test that $match with a $jsonSchema property that will internally translate to a match
+// expression node that has a path that is prefixed by the 'as' field in the lookup and that has
+// children that can operate on that path (in this case, $_internalSchemaAllElemMatchFromIndex)
+// works as expected although ineligible for absorbtion by a $lookup. Note that the jsonSchema below
+// ensures that all elements of 'location.coordinates' are above 0 since the 'items' field is an
+// object.
+result = testDB.animals
+ .aggregate([
+ {
+ $lookup: {
+ from: "locations",
+ localField: "locationId",
+ foreignField: "_id",
+ as: "location"
+ }
+ },
+ {$unwind: "$location"},
+ {
+ $match: {
+ $jsonSchema: {
+ properties: {"location.coordinates": {items: {minimum: 0}}}
+ }
+ }
+ },
+ {$project: {"location.extra": false, "colors": false}}
+ ])
+ .toArray();
+
+expected =
+ [{_id: "dog", locationId: "doghouse", location: {_id: "doghouse", coordinates: [25.0, 60.0]}}];
+assert.eq(result, expected);
+
+// Test that $match with a $jsonSchema property that will internally translate to a match
+// expression node that has a path that is prefixed by the 'as' field in the lookup and that has
+// children that can operate on that path (in this case, $_internalSchemaMatchArrayIndex) works as
+// expected although ineligible for absorbtion by a $lookup. Note that the jsonSchema below ensures
+// that the first element of 'location.coordinates' is above 0 since the 'items' field is an array.
+result = testDB.animals
+ .aggregate([
+ {
+ $lookup: {
+ from: "locations",
+ localField: "locationId",
+ foreignField: "_id",
+ as: "location"
+ }
+ },
+ {$unwind: "$location"},
+ {
+ $match: {
+ $jsonSchema: {
+ properties: {"location.coordinates": {items: [{minimum: 0}]}}
+ }
+ }
+ },
+ {$project: {"location.extra": false, "colors": false}}
+ ])
+ .toArray();
+
+expected =
+ [{_id: "dog", locationId: "doghouse", location: {_id: "doghouse", coordinates: [25.0, 60.0]}}];
+assert.eq(result, expected);
+
+// Test that $match with a $jsonSchema property that will internally translate to a match
+// expression node that has a path that is prefixed by the 'as' field in the lookup and that has
+// children that can operate that path (in this case, $_internalSchemaObjectMatch) works as expected
+// although ineligible for absorbtion by a $lookup.
+result = testDB.animals
+ .aggregate([
+ {
+ $lookup: {
+ from: "locations",
+ localField: "locationId",
+ foreignField: "_id",
+ as: "location"
+ }
+ },
+ {$unwind: "$location"},
+ {
+ $match: {
+ $jsonSchema: {
+ properties: {"location.extra": {type: 'object', properties: {"breeds": {type: 'string'}}}}
+ }
+ }
+ },
+ {$project: {"location.extra": false, "colors": false}}
+ ])
+ .toArray();
+expected =
+ [{_id: "bull", locationId: "bullpen", location: {_id: "bullpen", coordinates: [-25.0, -60.0]}}];
+assert.eq(result, expected);
+
+// Test that a $match with $alwaysTrue works as expected although ineligible for absorbtion
+// by a $lookup. The $sort is to guarantee records are returned in the expected order.
result = testDB.animals
.aggregate([
{
@@ -220,7 +312,7 @@ expected = [
];
assert.eq(result, expected);
-// Test that a $match with $alwaysFalse works as expected although ineligable for absorbtion by a
+// Test that a $match with $alwaysFalse works as expected although ineligible for absorbtion by a
// $lookup.
result = testDB.animals
.aggregate([
@@ -242,7 +334,7 @@ result = testDB.animals
expected = [];
assert.eq(result, expected);
-// Test that a $match with $expr works as expected although ineligable for absorbtion by a $lookup.
+// Test that a $match with $expr works as expected although ineligible for absorbtion by a $lookup.
result = testDB.animals
.aggregate([
{
diff --git a/jstests/aggregation/sources/lookup/lookup_collation.js b/jstests/aggregation/sources/lookup/lookup_collation.js
index 50ec057ae79..36f18b7e44c 100644
--- a/jstests/aggregation/sources/lookup/lookup_collation.js
+++ b/jstests/aggregation/sources/lookup/lookup_collation.js
@@ -3,14 +3,11 @@
* when performing comparisons on a foreign collection with a different default collation. Exercises
* the fix for SERVER-43350.
*
- * Collation can be set at three different levels for $lookup stage
+ * Collation can be set at two different levels for $lookup stage
* 1. on the local collection (collation on the foreign collection is always ignored)
- * 2. on the $lookup stage via '_internalCollation' property
- * 3. on the aggregation command via 'collation' property in options
+ * 2. on the aggregation command via 'collation' property in options
*
- * The three settings have the following precedence:
- * 1. '_internalCollation' overrides all others
- * 2. 'collation' option overrides local collection's collation
+ * The 'collation' command option overrides local collection's collation.
*/
load("jstests/aggregation/extras/utils.js"); // For anyEq.
load("jstests/libs/analyze_plan.js"); // For getAggPlanStages, getWinningPlan.
@@ -137,29 +134,6 @@ let explain;
}
})();
-// Collation set on $lookup stage with '_internalCollation' should override collation of the local
-// collection and on the command.
-(function testStageCollationPrecedence() {
- for (let lookupInto of [lookupWithPipeline, lookupNoPipeline]) {
- let lookupStage = lookupInto(collAa);
- lookupStage.$lookup._internalCollation = caseInsensitive;
- results = collAa.aggregate([lookupStage], {collation: caseSensitive}).toArray();
- assertArrayEq({
- actual: results,
- expected: resultCaseInsensitive,
- extraErrorMsg: " Case-insensitive collation on stage, running: " + tojson(lookupInto)
- });
-
- lookupStage.$lookup._internalCollation = caseSensitive;
- results = collAA.aggregate([lookupStage], {collation: caseInsensitive}).toArray();
- assertArrayEq({
- actual: results,
- expected: resultCaseSensistive,
- extraErrorMsg: " Case-sensitive collation on stage, running: " + tojson(lookupInto)
- });
- }
-})();
-
// In presense of indexes lookup might choose a different strategy for the join, that relies on the
// index (INLJ). It should respect the effective collation of $lookup.
(function testCollationWithIndexes() {
@@ -213,19 +187,6 @@ let explain;
explain = collAa.explain().aggregate([lookupInto(collAa_indexed)],
{collation: {locale: "fr"}, allowDiskUse: false});
assertNestedLoopJoinStrategy(explain);
-
- // Stage-level collation overrides collection-level and command-level collations.
- let lookupStage = lookupInto(collAa_indexed);
- lookupStage.$lookup._internalCollation = caseInsensitive;
- results = collAa.aggregate([lookupStage], {collation: caseSensitive}).toArray();
- assertArrayEq({
- actual: results,
- expected: resultCaseInsensitive,
- extraErrorMsg: " Case-insensitive collation on stage, foreign is indexed, running: " +
- tojson(lookupInto)
- });
- explain = collAa.explain().aggregate([lookupStage], {collation: caseSensitive});
- assertIndexJoinStrategy(explain);
}
})();
})();
diff --git a/jstests/aggregation/sources/lookup/lookup_foreign_collation.js b/jstests/aggregation/sources/lookup/lookup_foreign_collation.js
index 6937fe42854..7d6e9a4578e 100644
--- a/jstests/aggregation/sources/lookup/lookup_foreign_collation.js
+++ b/jstests/aggregation/sources/lookup/lookup_foreign_collation.js
@@ -96,12 +96,10 @@ function setup() {
// localColl: Local Collection
// foreignColl: Foreign Collection
// commandCollation: Collation set on the aggregate command. Pass null for default collation.
- // lookupCollation: Collation specified in the $lookup stage. Pass null for default
- // collation. expectedResults: Results expected from the aggregate invocation
+ // expectedResults: Results expected from the aggregate invocation
//
- function assertExpectedResultSet(
- localColl, foreignColl, commandCollation, lookupCollation, expectedResults) {
- const lookupWithPipeline = {$lookup: {from: foreignColl.getName(),
+ function assertExpectedResultSet(localColl, foreignColl, commandCollation, expectedResults) {
+ const lookupWithPipeline = {$lookup: {from: foreignColl.getName(),
as: "foreignMatch",
let: {l_id: "$_id"},
pipeline: [{$match: {$expr: {$eq: ["$_id", "$$l_id"]}}}]}};
@@ -111,11 +109,6 @@ function setup() {
foreignField: "_id",
as: "foreignMatch"}};
- if (lookupCollation) {
- lookupWithPipeline.$lookup._internalCollation = lookupCollation;
- lookupWithLocalForeignField.$lookup._internalCollation = lookupCollation;
- }
-
const aggOptions = {};
if (commandCollation) {
aggOptions.collation = commandCollation;
@@ -129,112 +122,20 @@ function setup() {
}
// Baseline test, confirming simple binary comparison when no collation has been specified on
- // the command, $lookup stage or collections.
- assertExpectedResultSet(localColl, foreignColl, null, null, resultSetCaseSensitive);
+ // the command or the collection.
+ assertExpectedResultSet(localColl, foreignColl, null, resultSetCaseSensitive);
// When a collation has been specified on the $lookup stage, it will always be used to join
// local and foreign collections.
for (const local of [localColl, localCaseInsensitiveColl]) {
for (const foreign of [foreignColl, foreignCaseInsensitiveColl]) {
- for (const command of [null, simpleCollation, caseInsensitiveCollation]) {
- // Case insensitive collation specified in the $lookup stage results in a case
- // insensitive join.
- assertExpectedResultSet(
- local, foreign, command, caseInsensitiveCollation, resultSetCaseInsensitive);
-
- // Simple collation specified in the $lookup stage results in a case sensitive join.
- assertExpectedResultSet(
- local, foreign, command, simpleCollation, resultSetCaseSensitive);
- }
+ // Case insensitive collation results in a case insensitive join.
+ assertExpectedResultSet(
+ local, foreign, caseInsensitiveCollation, resultSetCaseInsensitive);
+
+ // Simple collation results in a case sensitive join.
+ assertExpectedResultSet(local, foreign, simpleCollation, resultSetCaseSensitive);
}
}
})();
-
-(function testNestedLookupStagesWithDifferentCollations() {
- setup();
-
- const lookupWithPipeline = {$lookup: {from: foreignColl.getName(),
- as: "foreignMatch",
- let: {l_id: "$_id"},
- pipeline: [{$match: {$expr: {$eq: ["$_id", "$$l_id"]}}},
- {$lookup: {from: localColl.getName(),
- as: "foreignMatch2",
- let: {l_id: "$_id"},
- pipeline: [{$match: {$expr: {$eq: ["$_id", "$$l_id"]}}}],
- _internalCollation: simpleCollation}}],
- _internalCollation: caseInsensitiveCollation}};
-
- const resultSet = [
- {_id: "a", foreignMatch: [{_id: "a", "foreignMatch2": [{"_id": "a"}]}]},
- {_id: "b", foreignMatch: [{_id: "B", "foreignMatch2": []}]},
- {_id: "c", foreignMatch: [{_id: "c", "foreignMatch2": [{"_id": "c"}]}]},
- {_id: "d", foreignMatch: [{_id: "D", "foreignMatch2": []}]},
- {_id: "e", foreignMatch: [{_id: "e", "foreignMatch2": [{"_id": "e"}]}]}
- ];
-
- const results = localColl.aggregate([lookupWithPipeline]).toArray();
- assert(anyEq(results, resultSet), tojson(results));
-})();
-
-(function testMatchOnUnwoundAsFieldAbsorptionOptimization() {
- setup();
-
- // A $lookup stage with a collation that differs from the collection and command collation
- // will not absorb a $match on unwound results.
- let pipeline = [{$lookup: {from: foreignColl.getName(),
- as: "foreignMatch",
- let: {l_id: "$_id"},
- pipeline: [{$match: {$expr: {$eq: ["$_id", "$$l_id"]}}}],
- _internalCollation: caseInsensitiveCollation}},
- {$unwind: "$foreignMatch"},
- {$match: {"foreignMatch._id": "b"}}];
-
- let results = localColl.aggregate(pipeline).toArray();
- assert.eq(0, results.length);
-
- let explain = localColl.explain().aggregate(pipeline);
- let lastStage = explain.stages[explain.stages.length - 1];
- assert(lastStage.hasOwnProperty("$match"), tojson(explain));
- assert.eq({$match: {"foreignMatch._id": {$eq: "b"}}},
- lastStage,
- "The $match stage should not be optimized into the $lookup stage" + tojson(explain));
-
- // A $lookup stage with a collation that matches the command collation will absorb a $match
- // stage.
- pipeline = [{$lookup: {from: foreignColl.getName(),
- as: "foreignMatch",
- let: {l_id: "$_id"},
- pipeline: [{$match: {$expr: {$eq: ["$_id", "$$l_id"]}}}],
- _internalCollation: caseInsensitiveCollation}},
- {$unwind: "$foreignMatch"},
- {$match: {"foreignMatch._id": "b"}}];
-
- let expectedResults = [{"_id": "b", "foreignMatch": {"_id": "B"}}];
-
- results = localColl.aggregate(pipeline, {collation: caseInsensitiveCollation}).toArray();
- assert(anyEq(results, expectedResults), tojson(results));
-
- explain = localColl.explain().aggregate(pipeline, {collation: caseInsensitiveCollation});
- lastStage = explain.stages[explain.stages.length - 1];
- assert(lastStage.hasOwnProperty("$lookup"), tojson(explain));
-
- // A $lookup stage with a collation that matches the local collection collation will absorb
- // a $match stage.
- pipeline = [{$lookup: {from: foreignColl.getName(),
- as: "foreignMatch",
- let: {l_id: "$_id"},
- pipeline: [{$match: {$expr: {$eq: ["$_id", "$$l_id"]}}}],
- _internalCollation: caseInsensitiveCollation}},
- {$unwind: "$foreignMatch"},
- {$match: {"foreignMatch._id": "b"}}];
-
- expectedResults = [{"_id": "b", "foreignMatch": {"_id": "B"}}];
-
- results = localCaseInsensitiveColl.aggregate(pipeline).toArray();
- assert(anyEq(results, expectedResults), tojson(results));
-
- explain = localCaseInsensitiveColl.explain().aggregate(pipeline);
- lastStage = explain.stages[explain.stages.length - 1];
- assert(lastStage.hasOwnProperty("$lookup"), tojson(explain));
-})();
})();