summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYoonsoo Kim <yoonsoo.kim@mongodb.com>2023-02-09 22:47:09 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2023-02-10 02:16:16 +0000
commitbf7c308ffe91ae183fc9507b7297e183e5cff4cf (patch)
tree8aafdd76d1d0d52da34565ddc6c1cc0351ff0b03
parentc835341f9706373fb46ba7a6fc37d9892c42a967 (diff)
downloadmongo-bf7c308ffe91ae183fc9507b7297e183e5cff4cf.tar.gz
SERVER-73535 Add support for $jsonSchema scalar fields in splitMatchExpressionBy()
-rw-r--r--src/mongo/db/matcher/expression_algo_test.cpp210
-rw-r--r--src/mongo/db/matcher/expression_type.h4
-rw-r--r--src/mongo/db/pipeline/pipeline_test.cpp30
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);
}