diff options
-rw-r--r-- | src/mongo/db/matcher/expression_algo_test.cpp | 210 | ||||
-rw-r--r-- | src/mongo/db/matcher/expression_type.h | 4 | ||||
-rw-r--r-- | src/mongo/db/pipeline/pipeline_test.cpp | 30 |
3 files changed, 225 insertions, 19 deletions
diff --git a/src/mongo/db/matcher/expression_algo_test.cpp b/src/mongo/db/matcher/expression_algo_test.cpp index 3c8fb1c27fc..9962da9d4c0 100644 --- a/src/mongo/db/matcher/expression_algo_test.cpp +++ b/src/mongo/db/matcher/expression_algo_test.cpp @@ -1525,6 +1525,216 @@ TEST(SplitMatchExpression, ShouldSplitOutAndRenameJsonSchemaRequiredByIsOnlyDepe ASSERT_BSONOBJ_EQ(residualBob.obj(), fromjson("{b: {$eq: 1}}")); } +// Verifies that $jsonSchema 'type' is supported by splitMatchExpressionBy(). +TEST(SplitMatchExpression, ShouldSplitOutAndRenameJsonSchemaTypeByIsOnlyDependentOn) { + ParsedMatchExpression matcher(R"({$jsonSchema: {properties: {a: {type: "number"}}}})"); + + // $jsonSchema expression will be split out by the meta field "a" and the meta field "a" will be + // renamed to "meta". + auto [splitOutExpr, residualExpr] = expression::splitMatchExpressionBy( + matcher.extractExpr(), {"a"}, {{"a", "meta"}}, expression::isOnlyDependentOn); + + ASSERT_TRUE(splitOutExpr.get()); + ASSERT_BSONOBJ_EQ(splitOutExpr->serialize(), fromjson(R"( +{ + $and: [{ + $and: [{ + $or: [ + {meta: {$not: {$exists: true}}}, + {$and: [{meta: {$_internalSchemaType: ["number"]}}]} + ] + }] + }] +} + )")); + + ASSERT_FALSE(residualExpr.get()); +} + +// Verifies that $jsonSchema 'bsonType' is supported by splitMatchExpressionBy(). +TEST(SplitMatchExpression, ShouldSplitOutAndRenameJsonSchemaBsonTypeByIsOnlyDependentOn) { + ParsedMatchExpression matcher(R"({$jsonSchema: {properties: {a: {bsonType: "long"}}}})"); + + // $jsonSchema expression will be split out by the meta field "a" and the meta field "a" will be + // renamed to "meta". + auto [splitOutExpr, residualExpr] = expression::splitMatchExpressionBy( + matcher.extractExpr(), {"a"}, {{"a", "meta"}}, expression::isOnlyDependentOn); + + ASSERT_TRUE(splitOutExpr.get()); + ASSERT_BSONOBJ_EQ(splitOutExpr->serialize(), fromjson(R"( +{ + $and: [{ + $and: [ + {$or: [{meta: {$not: {$exists: true}}}, {$and: [{meta: {$_internalSchemaType: [18]}}]}]} + ] + }] +} + )")); + + ASSERT_FALSE(residualExpr.get()); +} + +// Verifies that $jsonSchema 'enum' is supported by splitMatchExpressionBy(). +TEST(SplitMatchExpression, ShouldSplitOutAndRenameJsonSchemaEnumByIsOnlyDependentOn) { + ParsedMatchExpression matcher(R"({$jsonSchema: {properties: {a: {enum: [1,2]}}}})"); + + // $jsonSchema expression will be split out by the meta field "a" and the meta field "a" will be + // renamed to "meta". + auto [splitOutExpr, residualExpr] = expression::splitMatchExpressionBy( + matcher.extractExpr(), {"a"}, {{"a", "meta"}}, expression::isOnlyDependentOn); + + ASSERT_TRUE(splitOutExpr.get()); + ASSERT_BSONOBJ_EQ(splitOutExpr->serialize(), fromjson(R"( +{ + $and: [{ + $and: [{ + $or: [ + {meta: {$not: {$exists: true}}}, + {$and: [{$or: [{meta: {$_internalSchemaEq: 1}}, {meta: {$_internalSchemaEq: 2}}]}]} + ] + }] + }] +} + )")); + + ASSERT_FALSE(residualExpr.get()); +} + +// Verifies that $jsonSchema 'minimum' and 'maximum' are supported by splitMatchExpressionBy(). +TEST(SplitMatchExpression, ShouldSplitOutAndRenameJsonSchemaMinMaxByIsOnlyDependentOn) { + ParsedMatchExpression matcher("{$jsonSchema: {properties: {a: {minimum: 1, maximum: 10}}}}"); + + // $jsonSchema expression will be split out by the meta field "a" and the meta field "a" will be + // renamed to "meta". + auto [splitOutExpr, residualExpr] = expression::splitMatchExpressionBy( + matcher.extractExpr(), {"a"}, {{"a", "meta"}}, expression::isOnlyDependentOn); + + ASSERT_TRUE(splitOutExpr.get()); + ASSERT_BSONOBJ_EQ(splitOutExpr->serialize(), fromjson(R"( +{ + $and: [{ + $and: [{ + $or: [ + {meta: {$not: {$exists: true}}}, + { + $and: [ + { + $or: [ + {meta: {$not: {$_internalSchemaType: ["number"]}}}, + {meta: {$lte: 10}} + ] + }, + { + $or: [ + {meta: {$not: {$_internalSchemaType: ["number"]}}}, + {meta: {$gte: 1}} + ] + } + ] + } + ] + }] + }] +} + )")); + + ASSERT_FALSE(residualExpr.get()); +} + +// Verifies that $jsonSchema 'minLength' and 'maxLength' are supported by splitMatchExpressionBy(). +TEST(SplitMatchExpression, ShouldSplitOutAndRenameJsonSchemaMinMaxLengthByIsOnlyDependentOn) { + ParsedMatchExpression matcher( + "{$jsonSchema: {properties: {a: {minLength: 1, maxLength: 10}}}}"); + + // $jsonSchema expression will be split out by the meta field "a" and the meta field "a" will be + // renamed to "meta". + auto [splitOutExpr, residualExpr] = expression::splitMatchExpressionBy( + matcher.extractExpr(), {"a"}, {{"a", "meta"}}, expression::isOnlyDependentOn); + + ASSERT_TRUE(splitOutExpr.get()); + ASSERT_BSONOBJ_EQ(splitOutExpr->serialize(), fromjson(R"( +{ + $and: [{ + $and: [{ + $or: [ + {meta: {$not: {$exists: true}}}, + { + $and: [ + { + $or: [ + {meta: {$not: {$_internalSchemaType: [2]}}}, + {meta: {$_internalSchemaMaxLength: 10}} + ] + }, + { + $or: [ + {meta: {$not: {$_internalSchemaType: [2]}}}, + {meta: {$_internalSchemaMinLength: 1}} + ] + } + ] + } + ] + }] + }] +} + )")); + + ASSERT_FALSE(residualExpr.get()); +} + +// Verifies that $jsonSchema 'multipleOf' is supported by splitMatchExpressionBy(). +TEST(SplitMatchExpression, ShouldSplitOutAndRenameJsonSchemaMultipleOfByIsOnlyDependentOn) { + ParsedMatchExpression matcher("{$jsonSchema: {properties: {a: {multipleOf: 10}}}}"); + + // $jsonSchema expression will be split out by the meta field "a" and the meta field "a" will be + // renamed to "meta". + auto [splitOutExpr, residualExpr] = expression::splitMatchExpressionBy( + matcher.extractExpr(), {"a"}, {{"a", "meta"}}, expression::isOnlyDependentOn); + + ASSERT_TRUE(splitOutExpr.get()); + ASSERT_BSONOBJ_EQ(splitOutExpr->serialize(), fromjson(R"( +{ + $and: [{ + $and: [{ + $or: [ + {meta: {$not: {$exists: true}}}, + { + $and: [{ + $or: [ + {meta: {$not: {$_internalSchemaType: ["number"]}}}, + {meta: {$_internalSchemaFmod: [10, 0]}} + ] + }] + } + ] + }] + }] +} + )")); + + ASSERT_FALSE(residualExpr.get()); +} + +// Verifies that $jsonSchema 'pattern' is supported by splitMatchExpressionBy(). +TEST(SplitMatchExpression, ShouldSplitOutAndRenameJsonSchemaPatternByIsOnlyDependentOn) { + ParsedMatchExpression matcher(R"({$jsonSchema: {properties: {a: {pattern: "[0-9]*"}}}})"); + auto originalExpr = matcher.extractExpr(); + auto originalExprCopy = originalExpr->shallowClone(); + + // $jsonSchema expression will be split out by the meta field "a" and the meta field "a" will be + // renamed to "meta". + auto [splitOutExpr, residualExpr] = expression::splitMatchExpressionBy( + std::move(originalExpr), {"a"}, {{"a", "meta"}}, expression::isOnlyDependentOn); + + ASSERT_TRUE(splitOutExpr.get()); + // 'splitOutExpr' must be same as the expression after renaming 'a' to 'meta'. + expression::applyRenamesToExpression(originalExprCopy.get(), {{"a", "meta"}}); + ASSERT_BSONOBJ_EQ(splitOutExpr->serialize(), originalExprCopy->serialize()); + + ASSERT_FALSE(residualExpr.get()); +} + TEST(ApplyRenamesToExpression, ShouldApplyBasicRenamesForAMatchWithExpr) { BSONObj matchPredicate = fromjson("{$expr: {$eq: ['$a.b', '$c']}}"); boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); diff --git a/src/mongo/db/matcher/expression_type.h b/src/mongo/db/matcher/expression_type.h index 17dbb3a36d3..95e1416486e 100644 --- a/src/mongo/db/matcher/expression_type.h +++ b/src/mongo/db/matcher/expression_type.h @@ -199,10 +199,6 @@ public: return expr; } - MatchCategory getCategory() const final { - return MatchCategory::kOther; - } - void acceptVisitor(MatchExpressionMutableVisitor* visitor) final { visitor->visit(this); } diff --git a/src/mongo/db/pipeline/pipeline_test.cpp b/src/mongo/db/pipeline/pipeline_test.cpp index bfc12d09eb8..f32007de26b 100644 --- a/src/mongo/db/pipeline/pipeline_test.cpp +++ b/src/mongo/db/pipeline/pipeline_test.cpp @@ -2594,40 +2594,40 @@ TEST(PipelineOptimizationTest, MatchOnRootDocEqShouldNotSwapSinceCategoryIsOther assertPipelineOptimizesTo(inputPipe, inputPipe); } -// Descriptive test. The following internal match expression *could* participate in pipeline -// optimizations, but it currently does not. -TEST(PipelineOptimizationTest, MatchOnInternalSchemaTypeShouldNotSwapSinceCategoryIsOther) { +// Descriptive test. The following internal match expression can participate in pipeline +// optimizations. +TEST(PipelineOptimizationTest, MatchOnInternalSchemaTypeShouldSwap) { string inputPipe = "[{$project: {_id: true, a: '$b'}}, " "{$match: {a: {$_internalSchemaType: 1}}}]"; string outputPipe = - "[{$project: {_id: true, a: '$b'}}, " - " {$match: {a: {$_internalSchemaType: [1]}}}]"; + "[{$match: {b: {$_internalSchemaType: [1]}}}, " + "{$project: {_id: true, a: '$b'}}]"; string serializedPipe = - "[{$project: {_id: true, a: '$b'}}, " - "{$match: {a: {$_internalSchemaType: 1}}}]"; + "[{$match: {b: {$_internalSchemaType: [1]}}}, " + "{$project: {_id: true, a: '$b'}}]"; assertPipelineOptimizesAndSerializesTo(inputPipe, outputPipe, serializedPipe); inputPipe = "[{$project: {redacted: false}}, " "{$match: {a: {$_internalSchemaType: 1}}}]"; outputPipe = - "[{$project: {redacted: false, _id: true}}, " - " {$match: {a: {$_internalSchemaType: [1]}}}]"; + "[{$match: {a: {$_internalSchemaType: [1]}}}, " + "{$project: {redacted: false, _id: true}}]"; serializedPipe = - "[{$project: {redacted: false, _id: true}}, " - "{$match: {a: {$_internalSchemaType: 1}}}]"; + "[{$match: {a: {$_internalSchemaType: 1}}}, " + "{$project: {redacted: false, _id: true}}]"; assertPipelineOptimizesAndSerializesTo(inputPipe, outputPipe, serializedPipe); inputPipe = "[{$addFields : {a : {$const: 1}}}, " "{$match: {b: {$_internalSchemaType: 1}}}]"; outputPipe = - "[{$addFields : {a : {$const: 1}}}, " - " {$match: {b: {$_internalSchemaType: [1]}}}]"; + "[{$match: {b: {$_internalSchemaType: [1]}}}, " + "{$addFields : {a : {$const: 1}}}]"; serializedPipe = - "[{$addFields : {a : {$const: 1}}}, " - "{$match: {b: {$_internalSchemaType: 1}}}]"; + "[{$match: {b: {$_internalSchemaType: 1}}}, " + "{$addFields : {a : {$const: 1}}}]"; assertPipelineOptimizesAndSerializesTo(inputPipe, outputPipe, serializedPipe); } |