summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMilitsa Sotirova <militsa.sotirova@mongodb.com>2022-09-29 12:58:50 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-09-29 14:04:48 +0000
commit0b24ce9b359574b8f695fce8b4d9b80fdcaa4a10 (patch)
tree5d6ceb3b8f3776a02dabc4f027a410a19688c3d1
parent44d7b27d4ba081d27e4ae4826ecda9ef05323cb5 (diff)
downloadmongo-0b24ce9b359574b8f695fce8b4d9b80fdcaa4a10.tar.gz
SERVER-66928 convert ABT pipeline tests to golden testing
-rw-r--r--src/mongo/db/pipeline/SConscript3
-rw-r--r--src/mongo/db/pipeline/abt/abt_optimization_test.cpp361
-rw-r--r--src/mongo/db/pipeline/abt/abt_pipeline_test.cpp2680
-rw-r--r--src/mongo/db/pipeline/abt/abt_translation_test.cpp174
-rw-r--r--src/mongo/db/query/optimizer/utils/unit_test_utils.cpp173
-rw-r--r--src/mongo/db/query/optimizer/utils/unit_test_utils.h19
-rw-r--r--src/mongo/db/test_output/pipeline/abt/a_b_t_translate/basic_tests.txt1686
-rw-r--r--src/mongo/db/test_output/pipeline/abt/a_b_t_translate/optimize_pipeline_tests.txt1438
-rw-r--r--src/mongo/db/test_output/pipeline/abt/a_b_t_translate/partial_index.txt148
9 files changed, 4001 insertions, 2681 deletions
diff --git a/src/mongo/db/pipeline/SConscript b/src/mongo/db/pipeline/SConscript
index be13b5fc366..8c65edab339 100644
--- a/src/mongo/db/pipeline/SConscript
+++ b/src/mongo/db/pipeline/SConscript
@@ -490,7 +490,8 @@ env.Library(
env.CppUnitTest(
target='db_pipeline_test',
source=[
- 'abt/abt_pipeline_test.cpp',
+ 'abt/abt_optimization_test.cpp',
+ 'abt/abt_translation_test.cpp',
'accumulator_js_test.cpp' if get_option('js-engine') != 'none' else [],
'accumulator_test.cpp',
'aggregation_request_test.cpp',
diff --git a/src/mongo/db/pipeline/abt/abt_optimization_test.cpp b/src/mongo/db/pipeline/abt/abt_optimization_test.cpp
new file mode 100644
index 00000000000..2d74409e4ee
--- /dev/null
+++ b/src/mongo/db/pipeline/abt/abt_optimization_test.cpp
@@ -0,0 +1,361 @@
+/**
+ * Copyright (C) 2022-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+
+#include "mongo/db/pipeline/abt/utils.h"
+#include "mongo/db/query/optimizer/cascades/cost_derivation.h"
+#include "mongo/db/query/optimizer/explain.h"
+#include "mongo/db/query/optimizer/opt_phase_manager.h"
+#include "mongo/db/query/optimizer/utils/unit_test_utils.h"
+#include "mongo/unittest/golden_test.h"
+
+namespace mongo::optimizer {
+
+unittest::GoldenTestConfig goldenTestConfigABTOptimization{"src/mongo/db/test_output/pipeline/abt"};
+
+TEST(ABTTranslate, OptimizePipelineTests) {
+ unittest::GoldenTestContext gctx(&goldenTestConfigABTOptimization);
+
+ testABTTranslationAndOptimization(
+ gctx,
+ "optimized $match with $or: pipeline is able to use a SargableNode with a disjunction of "
+ "point intervals.",
+ "[{$match: {$or: [{a: 1}, {a: 2}, {a: 3}]}}]",
+ "collection",
+ {OptPhase::MemoSubstitutionPhase},
+ {{{"collection",
+ ScanDefinition{{}, {{"index1", makeIndexDefinition("a", CollationOp::Ascending)}}}}}});
+
+ // TODO SERVER-67819 Support indexing for eqMember op type
+ testABTTranslationAndOptimization(
+ gctx,
+ "optimized $match with $in and a list of equalities becomes a comparison to an EqMember "
+ "list.",
+ "[{$match: {a: {$in: [1, 2, 3]}}}]",
+ "collection",
+ {OptPhase::MemoSubstitutionPhase},
+ {{{"collection",
+ ScanDefinition{{}, {{"index1", makeIndexDefinition("a", CollationOp::Ascending)}}}}}});
+
+ testABTTranslationAndOptimization(
+ gctx,
+ "optimized $project inclusion then $match: observe the Filter can be reordered "
+ "against the Eval node",
+ "[{$project: {a: 1, b: 1}}, {$match: {a: 2}}]",
+ "collection",
+ {OptPhase::ConstEvalPre,
+ OptPhase::PathFuse,
+ OptPhase::MemoSubstitutionPhase,
+ OptPhase::MemoExplorationPhase,
+ OptPhase::MemoImplementationPhase});
+
+ testABTTranslationAndOptimization(gctx,
+ "optimized $match basic",
+ "[{$match: {a: 1, b: 2}}]",
+ "collection",
+ {OptPhase::MemoSubstitutionPhase,
+ OptPhase::MemoExplorationPhase,
+ OptPhase::MemoImplementationPhase});
+
+ testABTTranslationAndOptimization(
+ gctx,
+ "optimized $expr filter: make sure we have a single array constant for (1, 2, 'str', ...)",
+ "[{$project: {a: {$filter: {input: [1, 2, 'str', {a: 2.0, b:'s'}, 3, 4], as: 'num', cond: "
+ "{$and: [{$gte: ['$$num', 2]}, {$lte: ['$$num', 3]}]}}}}}]",
+ "collection",
+ {OptPhase::ConstEvalPre});
+
+ testABTTranslationAndOptimization(
+ gctx,
+ "optimized $group local global",
+ "[{$group: {_id: '$a', c: {$sum: '$b'}}}]",
+ "collection",
+ {OptPhase::MemoSubstitutionPhase,
+ OptPhase::MemoExplorationPhase,
+ OptPhase::MemoImplementationPhase},
+ {{{"collection", ScanDefinition{{}, {}, {DistributionType::UnknownPartitioning}}}},
+ 5 /*numberOfPartitions*/});
+
+ testABTTranslationAndOptimization(gctx,
+ "optimized $unwind then $sort",
+ "[{$unwind: '$x'}, {$sort: {'x': 1}}]",
+ "collection",
+ OptPhaseManager::getAllRewritesSet());
+
+ testABTTranslationAndOptimization(
+ gctx,
+ "optimized $match with index",
+ "[{$match: {'a': 10}}]",
+ "collection",
+ {OptPhase::MemoSubstitutionPhase,
+ OptPhase::MemoExplorationPhase,
+ OptPhase::MemoImplementationPhase},
+ {{{"collection",
+ ScanDefinition{{}, {{"index1", makeIndexDefinition("a", CollationOp::Ascending)}}}}}});
+
+ testABTTranslationAndOptimization(
+ gctx,
+ "optimized $match index covered",
+ "[{$project: {_id: 0, a: 1}}, {$match: {'a': 10}}]",
+ "collection",
+ {OptPhase::ConstEvalPre,
+ OptPhase::PathFuse,
+ OptPhase::MemoSubstitutionPhase,
+ OptPhase::MemoExplorationPhase,
+ OptPhase::MemoImplementationPhase},
+ {{{"collection",
+ ScanDefinition{
+ {},
+ {{"index1",
+ IndexDefinition{{{{makeNonMultikeyIndexPath("a"), CollationOp::Ascending}}},
+ false /*multiKey*/}}}}}}});
+
+ testABTTranslationAndOptimization(
+ gctx,
+ "optimized $match index covered, match then project",
+ "[{$match: {'a': 10}}, {$project: {_id: 0, a: 1}}]",
+ "collection",
+ {OptPhase::ConstEvalPre,
+ OptPhase::PathFuse,
+ OptPhase::MemoSubstitutionPhase,
+ OptPhase::MemoExplorationPhase,
+ OptPhase::MemoImplementationPhase},
+ {{{"collection",
+ ScanDefinition{
+ {},
+ {{"index1",
+ IndexDefinition{{{{makeNonMultikeyIndexPath("a"), CollationOp::Ascending}}},
+ false /*multiKey*/}}}}}}});
+
+ testABTTranslationAndOptimization(
+ gctx,
+ "optimized $match index covered, match on two indexed keys then project",
+ "[{$match: {'a': 10, 'b': 20}}, {$project: {_id: 0, a: 1}}]",
+ "collection",
+ {OptPhase::ConstEvalPre,
+ OptPhase::PathFuse,
+ OptPhase::MemoSubstitutionPhase,
+ OptPhase::MemoExplorationPhase,
+ OptPhase::MemoImplementationPhase},
+ {{{"collection",
+ ScanDefinition{
+ {},
+ {{"index1",
+ IndexDefinition{{{{makeNonMultikeyIndexPath("a"), CollationOp::Ascending},
+ {makeNonMultikeyIndexPath("b"), CollationOp::Ascending}}},
+ false /*multiKey*/}}}}}}});
+
+ testABTTranslationAndOptimization(
+ gctx,
+ "optimized $match index covered, match on three indexed keys then project",
+ "[{$match: {'a': 10, 'b': 20, 'c': 30}}, {$project: {_id: 0, a: 1, b: 1, c: 1}}]",
+ "collection",
+ {OptPhase::ConstEvalPre,
+ OptPhase::PathFuse,
+ OptPhase::MemoSubstitutionPhase,
+ OptPhase::MemoExplorationPhase,
+ OptPhase::MemoImplementationPhase},
+ {{{"collection",
+ ScanDefinition{
+ {},
+ {{"index1",
+ IndexDefinition{{{{makeNonMultikeyIndexPath("a"), CollationOp::Ascending},
+ {makeNonMultikeyIndexPath("b"), CollationOp::Ascending},
+ {makeNonMultikeyIndexPath("c"), CollationOp::Ascending}}},
+ false /*multiKey*/}}}}}}});
+
+ testABTTranslationAndOptimization(
+ gctx,
+ "optimized $match index covered, inclusion project then match on three indexed keys",
+ "[{$project: {_id: 0, a: 1, b: 1, c: 1}}, {$match: {'a': 10, 'b': 20, 'c': 30}}]",
+ "collection",
+ {OptPhase::ConstEvalPre,
+ OptPhase::PathFuse,
+ OptPhase::MemoSubstitutionPhase,
+ OptPhase::MemoExplorationPhase,
+ OptPhase::MemoImplementationPhase},
+ {{{"collection",
+ ScanDefinition{
+ {},
+ {{"index1",
+ IndexDefinition{{{{makeNonMultikeyIndexPath("a"), CollationOp::Ascending},
+ {makeNonMultikeyIndexPath("b"), CollationOp::Ascending},
+ {makeNonMultikeyIndexPath("c"), CollationOp::Ascending}}},
+ false /*multiKey*/}}}}}}});
+
+ testABTTranslationAndOptimization(
+ gctx,
+ "optimized $match sort index",
+ "[{$match: {'a': 10}}, {$sort: {'a': 1}}]",
+ "collection",
+ {OptPhase::MemoSubstitutionPhase,
+ OptPhase::MemoExplorationPhase,
+ OptPhase::MemoImplementationPhase},
+ {{{"collection",
+ ScanDefinition{{}, {{"index1", makeIndexDefinition("a", CollationOp::Ascending)}}}}}});
+
+ testABTTranslationAndOptimization(
+ gctx,
+ "optimized range index",
+ "[{$match: {'a': {$gt: 70, $lt: 90}}}]",
+ "collection",
+ {OptPhase::MemoSubstitutionPhase,
+ OptPhase::MemoExplorationPhase,
+ OptPhase::MemoImplementationPhase},
+ {{{"collection",
+ ScanDefinition{{}, {{"index1", makeIndexDefinition("a", CollationOp::Ascending)}}}}}},
+ {},
+ true);
+
+ testABTTranslationAndOptimization(
+ gctx,
+ "optimized index on two keys",
+ "[{$match: {'a': 2, 'b': 2}}]",
+ "collection",
+ {OptPhase::MemoSubstitutionPhase,
+ OptPhase::MemoExplorationPhase,
+ OptPhase::MemoImplementationPhase},
+ {{{"collection",
+ ScanDefinition{{},
+ {{"index1",
+ IndexDefinition{{{makeIndexPath("a"), CollationOp::Ascending},
+ {makeIndexPath("b"), CollationOp::Ascending}},
+ true /*multiKey*/}}}}}}});
+
+ testABTTranslationAndOptimization(
+ gctx,
+ "optimized index on one key",
+ "[{$match: {'a': 2, 'b': 2}}]",
+ "collection",
+ {OptPhase::MemoSubstitutionPhase,
+ OptPhase::MemoExplorationPhase,
+ OptPhase::MemoImplementationPhase,
+ OptPhase::ConstEvalPost},
+ {{{"collection",
+ ScanDefinition{{}, {{"index1", makeIndexDefinition("a", CollationOp::Ascending)}}}}}});
+
+ testABTTranslationAndOptimization(
+ gctx,
+ "optimized $group eval no inline: verify that \"b\" is not inlined in the group "
+ "expression, but is coming from the physical scan",
+ "[{$group: {_id: null, a: {$first: '$b'}}}]",
+ "collection",
+ OptPhaseManager::getAllRewritesSet());
+
+ std::string scanDefA = "collA";
+ std::string scanDefB = "collB";
+ Metadata metadata{{{scanDefA, {}}, {scanDefB, {}}}};
+ testABTTranslationAndOptimization(gctx,
+ "optimized union",
+ "[{$unionWith: 'collB'}, {$match: {_id: 1}}]",
+ scanDefA,
+ {OptPhase::MemoSubstitutionPhase,
+ OptPhase::MemoExplorationPhase,
+ OptPhase::MemoImplementationPhase},
+ metadata,
+ {},
+ false,
+ {{NamespaceString("a." + scanDefB), {}}});
+
+ testABTTranslationAndOptimization(
+ gctx,
+ "optimized common expression elimination",
+ "[{$project: {foo: {$add: ['$b', 1]}, bar: {$add: ['$b', 1]}}}]",
+ "test",
+ {OptPhase::ConstEvalPre},
+ {{{"test", {{}, {}}}}});
+
+ testABTTranslationAndOptimization(
+ gctx,
+ "optimized group by dependency: demonstrate that \"c\" is set to the array size "
+ "(not the array itself coming from the group)",
+ "[{$group: {_id: {}, b: {$addToSet: '$a'}}}, {$project: "
+ "{_id: 0, b: {$size: '$b'}}}, {$project: {_id: 0, c: '$b'}}]",
+ "test",
+ {OptPhase::ConstEvalPre,
+ OptPhase::PathFuse,
+ OptPhase::MemoSubstitutionPhase,
+ OptPhase::MemoExplorationPhase,
+ OptPhase::MemoImplementationPhase},
+ {{{"test", {{}, {}}}}});
+
+ testABTTranslationAndOptimization(
+ gctx,
+ "optimized double $elemMatch",
+ "[{$match: {a: {$elemMatch: {$gte: 5, $lte: 6}}, b: {$elemMatch: {$gte: 1, $lte: 3}}}}]",
+ "test",
+ {OptPhase::MemoSubstitutionPhase},
+ {{{"test", {{}, {}}}}},
+ defaultConvertPathToInterval);
+}
+
+TEST(ABTTranslate, PartialIndex) {
+ unittest::GoldenTestContext gctx(&goldenTestConfigABTOptimization);
+
+ PrefixId prefixId;
+ std::string scanDefName = "collection";
+ ProjectionName scanProjName = prefixId.getNextId("scan");
+
+ // By default the constant is translated as "int32".
+ auto conversionResult = convertExprToPartialSchemaReq(
+ make<EvalFilter>(
+ make<PathGet>("b",
+ make<PathTraverse>(make<PathCompare>(Operations::Eq, Constant::int32(2)),
+ PathTraverse::kSingleLevel)),
+ make<Variable>(scanProjName)),
+ true /*isFilterContext*/,
+ {} /*pathToInterval*/);
+ ASSERT_TRUE(conversionResult.has_value());
+ ASSERT_FALSE(conversionResult->_retainPredicate);
+ Metadata metadata = {
+ {{scanDefName,
+ ScanDefinition{{},
+ {{"index1",
+ IndexDefinition{{{makeIndexPath("a"), CollationOp::Ascending}},
+ true /*multiKey*/,
+ {DistributionType::Centralized},
+ std::move(conversionResult->_reqMap)}}}}}}};
+
+ testABTTranslationAndOptimization(
+ gctx,
+ "optimized partial index: the expression matches the pipeline",
+ "[{$match: {'a': 3, 'b': 2}}]",
+ scanDefName,
+ OptPhaseManager::getAllRewritesSet(),
+ metadata);
+
+ testABTTranslationAndOptimization(
+ gctx,
+ "optimized partial index negative: the expression does not match the pipeline",
+ "[{$match: {'a': 3, 'b': 3}}]",
+ scanDefName,
+ OptPhaseManager::getAllRewritesSet(),
+ metadata);
+}
+} // namespace mongo::optimizer
diff --git a/src/mongo/db/pipeline/abt/abt_pipeline_test.cpp b/src/mongo/db/pipeline/abt/abt_pipeline_test.cpp
deleted file mode 100644
index 4c7c82e9910..00000000000
--- a/src/mongo/db/pipeline/abt/abt_pipeline_test.cpp
+++ /dev/null
@@ -1,2680 +0,0 @@
-/**
- * Copyright (C) 2022-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * As a special exception, the copyright holders give permission to link the
- * code of portions of this program with the OpenSSL library under certain
- * conditions as described in each individual source file and distribute
- * linked combinations including the program with the OpenSSL library. You
- * must comply with the Server Side Public License in all respects for
- * all of the code used other than as permitted herein. If you modify file(s)
- * with this exception, you may extend this exception to your version of the
- * file(s), but you are not obligated to do so. If you do not wish to do so,
- * delete this exception statement from your version. If you delete this
- * exception statement from all source files in the program, then also delete
- * it in the license file.
- */
-
-#include <boost/intrusive_ptr.hpp>
-
-#include "mongo/db/pipeline/abt/utils.h"
-#include "mongo/db/query/optimizer/cascades/cost_derivation.h"
-#include "mongo/db/query/optimizer/explain.h"
-#include "mongo/db/query/optimizer/opt_phase_manager.h"
-#include "mongo/db/query/optimizer/utils/unit_test_utils.h"
-#include "mongo/unittest/unittest.h"
-
-namespace mongo {
-namespace {
-
-using namespace optimizer;
-
-TEST(ABTTranslate, MatchWithInEmptyList) {
- // A $match with $in and an empty equalities list should not match any documents.
- ABT emptyListIn = translatePipeline("[{$match: {a: {$in: []}}}]");
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | scan_0\n"
- "| RefBlock: \n"
- "| Variable [scan_0]\n"
- "Filter []\n"
- "| EvalFilter []\n"
- "| | Variable [scan_0]\n"
- "| PathConstant []\n"
- "| Const [false]\n"
- "Scan [collection]\n"
- " BindBlock:\n"
- " [scan_0]\n"
- " Source []\n",
- emptyListIn);
-}
-
-TEST(ABTTranslate, MatchWithInSingletonList) {
- // A $match with $in and singleton equalities list should simplify to single equality.
- ABT singletonListIn = translatePipeline("[{$match: {a: {$in: [1]}}}]");
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | scan_0\n"
- "| RefBlock: \n"
- "| Variable [scan_0]\n"
- "Filter []\n"
- "| EvalFilter []\n"
- "| | Variable [scan_0]\n"
- "| PathGet [a]\n"
- "| PathTraverse [1]\n"
- "| PathCompare [Eq]\n"
- "| Const [1]\n"
- "Scan [collection]\n"
- " BindBlock:\n"
- " [scan_0]\n"
- " Source []\n",
- singletonListIn);
-}
-
-TEST(ABTTranslate, MatchWithInList) {
- // A $match with $in and a list of equalities becomes a comparison to an EqMember list.
- ABT listIn = translatePipeline("[{$match: {a: {$in: [1, 2, 3]}}}]");
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | scan_0\n"
- "| RefBlock: \n"
- "| Variable [scan_0]\n"
- "Filter []\n"
- "| EvalFilter []\n"
- "| | Variable [scan_0]\n"
- "| PathGet [a]\n"
- "| PathTraverse [1]\n"
- "| PathCompare [EqMember]\n"
- "| Const [[1, 2, 3]]\n"
- "Scan [collection]\n"
- " BindBlock:\n"
- " [scan_0]\n"
- " Source []\n",
- listIn);
-}
-
-TEST(ABTTranslate, MatchWithInDuplicateElementsRemoved) {
- // A $match with $in and a list of equalities has the duplicates removed from the list.
- ABT listIn = translatePipeline("[{$match: {a: {$in: ['abc', 'def', 'ghi', 'def']}}}]");
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | scan_0\n"
- "| RefBlock: \n"
- "| Variable [scan_0]\n"
- "Filter []\n"
- "| EvalFilter []\n"
- "| | Variable [scan_0]\n"
- "| PathGet [a]\n"
- "| PathTraverse [1]\n"
- "| PathCompare [EqMember]\n"
- "| Const [[\"abc\", \"def\", \"ghi\"]]\n"
- "Scan [collection]\n"
- " BindBlock:\n"
- " [scan_0]\n"
- " Source []\n",
- listIn);
-}
-
-TEST(ABTTranslate, EmptyElemMatch) {
- ABT emptyElemMatch = translatePipeline("[{$match: {'a': {$elemMatch: {}}}}]");
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | scan_0\n"
- "| RefBlock: \n"
- "| Variable [scan_0]\n"
- "Filter []\n"
- "| EvalFilter []\n"
- "| | Variable [scan_0]\n"
- "| PathGet [a]\n"
- "| PathComposeM []\n"
- "| | PathArr []\n"
- "| PathTraverse [1]\n"
- "| PathComposeM []\n"
- "| | PathComposeA []\n"
- "| | | PathArr []\n"
- "| | PathObj []\n"
- "| PathConstant []\n"
- "| Const [true]\n"
- "Scan [collection]\n"
- " BindBlock:\n"
- " [scan_0]\n"
- " Source []\n",
- emptyElemMatch);
-}
-
-TEST(ABTTranslate, MatchWithElemMatchAndIn) {
- ABT elemMatchIn = translatePipeline("[{$match: {'a.b': {$elemMatch: {$in: [1, 2, 3]}}}}]");
-
- // The PathGet and PathTraverse operators interact correctly when $in is under $elemMatch.
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | scan_0\n"
- "| RefBlock: \n"
- "| Variable [scan_0]\n"
- "Filter []\n"
- "| EvalFilter []\n"
- "| | Variable [scan_0]\n"
- "| PathGet [a]\n"
- "| PathTraverse [1]\n"
- "| PathGet [b]\n"
- "| PathComposeM []\n"
- "| | PathArr []\n"
- "| PathTraverse [1]\n"
- "| PathCompare [EqMember]\n"
- "| Const [[1, 2, 3]]\n"
- "Scan [collection]\n"
- " BindBlock:\n"
- " [scan_0]\n"
- " Source []\n",
- elemMatchIn);
-}
-
-TEST(ABTTranslate, MatchWithOrConvertedToIn) {
- ABT orTranslated = translatePipeline("[{$match: {$or: [{a: 1}, {a: 2}, {a: 3}]}}]");
- ABT inTranslated = translatePipeline("[{$match: {a: {$in: [1, 2, 3]}}}]");
-
- PrefixId prefixId;
- std::string scanDefName = "collection";
- Metadata metadata = {
- {{scanDefName,
- ScanDefinition{{}, {{"index1", makeIndexDefinition("a", CollationOp::Ascending)}}}}}};
- OptPhaseManager phaseManager(
- {OptPhase::MemoSubstitutionPhase}, prefixId, metadata, DebugInfo::kDefaultForTests);
-
- phaseManager.optimize(orTranslated);
- phaseManager.optimize(inTranslated);
-
- // Both pipelines are able to use a SargableNode with a disjunction of point intervals.
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | scan_0\n"
- "| RefBlock: \n"
- "| Variable [scan_0]\n"
- "Sargable [Complete]\n"
- "| | | | | requirementsMap: \n"
- "| | | | | refProjection: scan_0, path: 'PathGet [a] PathTraverse [1] "
- "PathIdentity []', intervals: {{{[Const [3], Const [3]]}} U {{[Const [1], Const [1]]}} U "
- "{{[Const [2], Const [2]]}}}\n"
- "| | | | candidateIndexes: \n"
- "| | | | candidateId: 1, index1, {}, {0}, {{{[Const [3], Const [3]]}} U "
- "{{[Const [1], Const [1]]}} U {{[Const [2], Const [2]]}}}\n"
- "| | | scanParams: \n"
- "| | | {'a': evalTemp_0}\n"
- "| | | residualReqs: \n"
- "| | | refProjection: evalTemp_0, path: 'PathTraverse [1] PathIdentity "
- "[]', intervals: {{{[Const [3], Const [3]]}} U {{[Const [1], Const [1]]}} U {{[Const [2], "
- "Const [2]]}}}, entryIndex: 0\n"
- "| | BindBlock:\n"
- "| RefBlock: \n"
- "| Variable [scan_0]\n"
- "Scan [collection]\n"
- " BindBlock:\n"
- " [scan_0]\n"
- " Source []\n",
- orTranslated);
- // TODO SERVER-67819 Support indexing for eqMember op type
- // ASSERT(orTranslated == inTranslated);
-}
-
-TEST(ABTTranslate, SortLimitSkip) {
- ABT translated = translatePipeline(
- "[{$limit: 5}, "
- "{$skip: 3}, "
- "{$sort: {a: 1, b: -1}}]");
-
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | scan_0\n"
- "| RefBlock: \n"
- "| Variable [scan_0]\n"
- "Collation []\n"
- "| | collation: \n"
- "| | sort_0: Ascending\n"
- "| | sort_1: Descending\n"
- "| RefBlock: \n"
- "| Variable [sort_0]\n"
- "| Variable [sort_1]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [sort_1]\n"
- "| EvalPath []\n"
- "| | Variable [scan_0]\n"
- "| PathGet [b]\n"
- "| PathIdentity []\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [sort_0]\n"
- "| EvalPath []\n"
- "| | Variable [scan_0]\n"
- "| PathGet [a]\n"
- "| PathIdentity []\n"
- "LimitSkip []\n"
- "| limitSkip:\n"
- "| limit: (none)\n"
- "| skip: 3\n"
- "LimitSkip []\n"
- "| limitSkip:\n"
- "| limit: 5\n"
- "| skip: 0\n"
- "Scan [collection]\n"
- " BindBlock:\n"
- " [scan_0]\n"
- " Source []\n",
- translated);
-}
-
-TEST(ABTTranslate, ProjectRetain) {
- PrefixId prefixId;
- std::string scanDefName = "collection";
- Metadata metadata = {{{scanDefName, ScanDefinition{{}, {}}}}};
- ABT translated = translatePipeline(
- metadata, "[{$project: {a: 1, b: 1}}, {$match: {a: 2}}]", scanDefName, prefixId);
-
- OptPhaseManager phaseManager({OptPhase::ConstEvalPre,
- OptPhase::PathFuse,
- OptPhase::MemoSubstitutionPhase,
- OptPhase::MemoExplorationPhase,
- OptPhase::MemoImplementationPhase},
- prefixId,
- metadata,
- DebugInfo::kDefaultForTests);
-
- ABT optimized = translated;
- phaseManager.optimize(optimized);
-
- // Observe the Filter can be reordered against the Eval node.
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | combinedProjection_0\n"
- "| RefBlock: \n"
- "| Variable [combinedProjection_0]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [combinedProjection_0]\n"
- "| EvalPath []\n"
- "| | Const [{}]\n"
- "| PathComposeM []\n"
- "| | PathField [b]\n"
- "| | PathConstant []\n"
- "| | Variable [fieldProj_2]\n"
- "| PathComposeM []\n"
- "| | PathField [a]\n"
- "| | PathConstant []\n"
- "| | Variable [fieldProj_1]\n"
- "| PathField [_id]\n"
- "| PathConstant []\n"
- "| Variable [fieldProj_0]\n"
- "Filter []\n"
- "| EvalFilter []\n"
- "| | Variable [fieldProj_1]\n"
- "| PathTraverse [1]\n"
- "| PathCompare [Eq]\n"
- "| Const [2]\n"
- "PhysicalScan [{'_id': fieldProj_0, 'a': fieldProj_1, 'b': fieldProj_2}, collection]\n"
- " BindBlock:\n"
- " [fieldProj_0]\n"
- " Source []\n"
- " [fieldProj_1]\n"
- " Source []\n"
- " [fieldProj_2]\n"
- " Source []\n",
- optimized);
-}
-
-TEST(ABTTranslate, ProjectRetain1) {
- ABT translated = translatePipeline("[{$project: {a1: 1, a2: 1, a3: 1, a4: 1, a5: 1, a6: 1}}]");
-
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | combinedProjection_0\n"
- "| RefBlock: \n"
- "| Variable [combinedProjection_0]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [combinedProjection_0]\n"
- "| EvalPath []\n"
- "| | Variable [scan_0]\n"
- "| PathKeep [_id, a1, a2, a3, a4, a5, a6]\n"
- "Scan [collection]\n"
- " BindBlock:\n"
- " [scan_0]\n"
- " Source []\n",
- translated);
-}
-
-TEST(ABTTranslate, AddFields) {
- // Since '$z' is a single element, it will be considered a renamed path.
- ABT translated = translatePipeline("[{$addFields: {a: '$z'}}]");
-
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | combinedProjection_0\n"
- "| RefBlock: \n"
- "| Variable [combinedProjection_0]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [combinedProjection_0]\n"
- "| EvalPath []\n"
- "| | Variable [scan_0]\n"
- "| PathComposeM []\n"
- "| | PathDefault []\n"
- "| | Const [{}]\n"
- "| PathField [a]\n"
- "| PathConstant []\n"
- "| Variable [projRenamedPath_0]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [projRenamedPath_0]\n"
- "| EvalPath []\n"
- "| | Variable [scan_0]\n"
- "| PathGet [z]\n"
- "| PathIdentity []\n"
- "Scan [collection]\n"
- " BindBlock:\n"
- " [scan_0]\n"
- " Source []\n",
- translated);
-}
-
-TEST(ABTTranslate, ProjectRenames) {
- // Since '$c' is a single element, it will be considered a renamed path.
- ABT translated = translatePipeline("[{$project: {'a.b': '$c'}}]");
-
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | combinedProjection_0\n"
- "| RefBlock: \n"
- "| Variable [combinedProjection_0]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [combinedProjection_0]\n"
- "| EvalPath []\n"
- "| | Variable [scan_0]\n"
- "| PathComposeM []\n"
- "| | PathField [a]\n"
- "| | PathTraverse [inf]\n"
- "| | PathComposeM []\n"
- "| | | PathDefault []\n"
- "| | | Const [{}]\n"
- "| | PathComposeM []\n"
- "| | | PathField [b]\n"
- "| | | PathConstant []\n"
- "| | | Variable [projRenamedPath_0]\n"
- "| | PathKeep [b]\n"
- "| PathKeep [_id, a]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [projRenamedPath_0]\n"
- "| EvalPath []\n"
- "| | Variable [scan_0]\n"
- "| PathGet [c]\n"
- "| PathIdentity []\n"
- "Scan [collection]\n"
- " BindBlock:\n"
- " [scan_0]\n"
- " Source []\n",
- translated);
-}
-
-TEST(ABTTranslate, ProjectPaths) {
- ABT translated = translatePipeline("[{$project: {'a.b.c': '$x.y.z'}}]");
-
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | combinedProjection_0\n"
- "| RefBlock: \n"
- "| Variable [combinedProjection_0]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [combinedProjection_0]\n"
- "| EvalPath []\n"
- "| | Variable [scan_0]\n"
- "| PathComposeM []\n"
- "| | PathField [a]\n"
- "| | PathTraverse [inf]\n"
- "| | PathComposeM []\n"
- "| | | PathField [b]\n"
- "| | | PathTraverse [inf]\n"
- "| | | PathComposeM []\n"
- "| | | | PathDefault []\n"
- "| | | | Const [{}]\n"
- "| | | PathComposeM []\n"
- "| | | | PathField [c]\n"
- "| | | | PathConstant []\n"
- "| | | | Variable [projGetPath_0]\n"
- "| | | PathKeep [c]\n"
- "| | PathKeep [b]\n"
- "| PathKeep [_id, a]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [projGetPath_0]\n"
- "| EvalPath []\n"
- "| | Variable [scan_0]\n"
- "| PathGet [x]\n"
- "| PathTraverse [inf]\n"
- "| PathGet [y]\n"
- "| PathTraverse [inf]\n"
- "| PathGet [z]\n"
- "| PathIdentity []\n"
- "Scan [collection]\n"
- " BindBlock:\n"
- " [scan_0]\n"
- " Source []\n",
- translated);
-}
-
-TEST(ABTTranslate, ProjectPaths1) {
- ABT translated = translatePipeline("[{$project: {'a.b':1, 'a.c':1, 'b':1}}]");
-
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | combinedProjection_0\n"
- "| RefBlock: \n"
- "| Variable [combinedProjection_0]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [combinedProjection_0]\n"
- "| EvalPath []\n"
- "| | Variable [scan_0]\n"
- "| PathComposeM []\n"
- "| | PathField [a]\n"
- "| | PathTraverse [inf]\n"
- "| | PathComposeM []\n"
- "| | | PathKeep [b, c]\n"
- "| | PathObj []\n"
- "| PathKeep [_id, a, b]\n"
- "Scan [collection]\n"
- " BindBlock:\n"
- " [scan_0]\n"
- " Source []\n",
- translated);
-}
-
-TEST(ABTTranslate, ProjectInclusion) {
- ABT translated = translatePipeline("[{$project: {a: {$add: ['$c.d', 2]}, b: 1}}]");
-
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | combinedProjection_0\n"
- "| RefBlock: \n"
- "| Variable [combinedProjection_0]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [combinedProjection_0]\n"
- "| EvalPath []\n"
- "| | Variable [scan_0]\n"
- "| PathComposeM []\n"
- "| | PathDefault []\n"
- "| | Const [{}]\n"
- "| PathComposeM []\n"
- "| | PathField [a]\n"
- "| | PathConstant []\n"
- "| | Variable [projGetPath_0]\n"
- "| PathKeep [_id, a, b]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [projGetPath_0]\n"
- "| BinaryOp [Add]\n"
- "| | Const [2]\n"
- "| EvalPath []\n"
- "| | Variable [scan_0]\n"
- "| PathGet [c]\n"
- "| PathTraverse [inf]\n"
- "| PathGet [d]\n"
- "| PathIdentity []\n"
- "Scan [collection]\n"
- " BindBlock:\n"
- " [scan_0]\n"
- " Source []\n",
- translated);
-}
-
-TEST(ABTTranslate, ProjectExclusion) {
- ABT translated = translatePipeline("[{$project: {a: 0, b: 0}}]");
-
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | combinedProjection_0\n"
- "| RefBlock: \n"
- "| Variable [combinedProjection_0]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [combinedProjection_0]\n"
- "| EvalPath []\n"
- "| | Variable [scan_0]\n"
- "| PathDrop [a, b]\n"
- "Scan [collection]\n"
- " BindBlock:\n"
- " [scan_0]\n"
- " Source []\n",
- translated);
-}
-
-TEST(ABTTranslate, ProjectReplaceRoot) {
- ABT translated = translatePipeline("[{$replaceRoot: {newRoot: '$a'}}]");
-
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | newRoot_0\n"
- "| RefBlock: \n"
- "| Variable [newRoot_0]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [newRoot_0]\n"
- "| EvalPath []\n"
- "| | Variable [scan_0]\n"
- "| PathGet [a]\n"
- "| PathIdentity []\n"
- "Scan [collection]\n"
- " BindBlock:\n"
- " [scan_0]\n"
- " Source []\n",
- translated);
-}
-
-TEST(ABTTranslate, MatchBasic) {
- PrefixId prefixId;
- std::string scanDefName = "collection";
-
- ABT translated = translatePipeline("[{$match: {a: 1, b: 2}}]");
-
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | scan_0\n"
- "| RefBlock: \n"
- "| Variable [scan_0]\n"
- "Filter []\n"
- "| EvalFilter []\n"
- "| | Variable [scan_0]\n"
- "| PathGet [a]\n"
- "| PathTraverse [1]\n"
- "| PathCompare [Eq]\n"
- "| Const [1]\n"
- "Filter []\n"
- "| EvalFilter []\n"
- "| | Variable [scan_0]\n"
- "| PathGet [b]\n"
- "| PathTraverse [1]\n"
- "| PathCompare [Eq]\n"
- "| Const [2]\n"
- "Scan [collection]\n"
- " BindBlock:\n"
- " [scan_0]\n"
- " Source []\n",
- translated);
-
- OptPhaseManager phaseManager({OptPhase::MemoSubstitutionPhase,
- OptPhase::MemoExplorationPhase,
- OptPhase::MemoImplementationPhase},
- prefixId,
- {{{scanDefName, ScanDefinition{{}, {}}}}},
- DebugInfo::kDefaultForTests);
-
- ABT optimized = translated;
- phaseManager.optimize(optimized);
-
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | scan_0\n"
- "| RefBlock: \n"
- "| Variable [scan_0]\n"
- "Filter []\n"
- "| EvalFilter []\n"
- "| | Variable [evalTemp_3]\n"
- "| PathTraverse [1]\n"
- "| PathCompare [Eq]\n"
- "| Const [2]\n"
- "Filter []\n"
- "| EvalFilter []\n"
- "| | Variable [evalTemp_2]\n"
- "| PathTraverse [1]\n"
- "| PathCompare [Eq]\n"
- "| Const [1]\n"
- "PhysicalScan [{'<root>': scan_0, 'a': evalTemp_2, 'b': evalTemp_3}, collection]\n"
- " BindBlock:\n"
- " [evalTemp_2]\n"
- " Source []\n"
- " [evalTemp_3]\n"
- " Source []\n"
- " [scan_0]\n"
- " Source []\n",
- optimized);
-}
-
-TEST(ABTTranslate, MatchPath1) {
- ABT translated = translatePipeline("[{$match: {$expr: {$eq: ['$a', 1]}}}]");
-
- // Demonstrate simple path is converted to EvalFilter.
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | scan_0\n"
- "| RefBlock: \n"
- "| Variable [scan_0]\n"
- "Filter []\n"
- "| EvalFilter []\n"
- "| | Variable [scan_0]\n"
- "| PathGet [a]\n"
- "| PathCompare [Eq]\n"
- "| Const [1]\n"
- "Scan [collection]\n"
- " BindBlock:\n"
- " [scan_0]\n"
- " Source []\n",
- translated);
-}
-
-TEST(ABTTranslate, MatchPath2) {
- ABT translated = translatePipeline("[{$match: {$expr: {$eq: ['$a.b', 1]}}}]");
-
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | scan_0\n"
- "| RefBlock: \n"
- "| Variable [scan_0]\n"
- "Filter []\n"
- "| EvalFilter []\n"
- "| | Variable [scan_0]\n"
- "| PathConstant []\n"
- "| BinaryOp [Eq]\n"
- "| | Const [1]\n"
- "| EvalPath []\n"
- "| | Variable [scan_0]\n"
- "| PathGet [a]\n"
- "| PathTraverse [inf]\n"
- "| PathGet [b]\n"
- "| PathIdentity []\n"
- "Scan [collection]\n"
- " BindBlock:\n"
- " [scan_0]\n"
- " Source []\n",
- translated);
-}
-
-TEST(ABTTranslate, ElemMatchPath) {
- ABT translated = translatePipeline(
- "[{$project: {a: {$literal: [1, 2, 3, 4]}}}, {$match: {a: {$elemMatch: {$gte: 2, $lte: "
- "3}}}}]");
-
- // Observe type bracketing in the filter.
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | combinedProjection_0\n"
- "| RefBlock: \n"
- "| Variable [combinedProjection_0]\n"
- "Filter []\n"
- "| EvalFilter []\n"
- "| | Variable [combinedProjection_0]\n"
- "| PathGet [a]\n"
- "| PathComposeM []\n"
- "| | PathArr []\n"
- "| PathTraverse [1]\n"
- "| PathComposeM []\n"
- "| | PathComposeM []\n"
- "| | | PathCompare [Lt]\n"
- "| | | Const [\"\"]\n"
- "| | PathCompare [Gte]\n"
- "| | Const [2]\n"
- "| PathComposeM []\n"
- "| | PathCompare [Gte]\n"
- "| | Const [nan]\n"
- "| PathCompare [Lte]\n"
- "| Const [3]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [combinedProjection_0]\n"
- "| EvalPath []\n"
- "| | Variable [scan_0]\n"
- "| PathComposeM []\n"
- "| | PathDefault []\n"
- "| | Const [{}]\n"
- "| PathComposeM []\n"
- "| | PathField [a]\n"
- "| | PathConstant []\n"
- "| | Variable [projGetPath_0]\n"
- "| PathKeep [_id, a]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [projGetPath_0]\n"
- "| Const [[1, 2, 3, 4]]\n"
- "Scan [collection]\n"
- " BindBlock:\n"
- " [scan_0]\n"
- " Source []\n",
- translated);
-}
-
-TEST(ABTTranslate, MatchProject) {
- ABT translated = translatePipeline(
- "[{$project: {s: {$add: ['$a', '$b']}, c: 1}}, "
- "{$match: {$or: [{c: 2}, {s: {$gte: 10}}]}}]");
-
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | combinedProjection_0\n"
- "| RefBlock: \n"
- "| Variable [combinedProjection_0]\n"
- "Filter []\n"
- "| EvalFilter []\n"
- "| | Variable [combinedProjection_0]\n"
- "| PathComposeA []\n"
- "| | PathGet [c]\n"
- "| | PathTraverse [1]\n"
- "| | PathCompare [Eq]\n"
- "| | Const [2]\n"
- "| PathGet [s]\n"
- "| PathTraverse [1]\n"
- "| PathComposeM []\n"
- "| | PathCompare [Lt]\n"
- "| | Const [\"\"]\n"
- "| PathCompare [Gte]\n"
- "| Const [10]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [combinedProjection_0]\n"
- "| EvalPath []\n"
- "| | Variable [scan_0]\n"
- "| PathComposeM []\n"
- "| | PathDefault []\n"
- "| | Const [{}]\n"
- "| PathComposeM []\n"
- "| | PathField [s]\n"
- "| | PathConstant []\n"
- "| | Variable [projGetPath_0]\n"
- "| PathKeep [_id, c, s]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [projGetPath_0]\n"
- "| BinaryOp [Add]\n"
- "| | EvalPath []\n"
- "| | | Variable [scan_0]\n"
- "| | PathGet [b]\n"
- "| | PathIdentity []\n"
- "| EvalPath []\n"
- "| | Variable [scan_0]\n"
- "| PathGet [a]\n"
- "| PathIdentity []\n"
- "Scan [collection]\n"
- " BindBlock:\n"
- " [scan_0]\n"
- " Source []\n",
- translated);
-}
-
-TEST(ABTTranslate, ProjectComplex) {
- ABT translated = translatePipeline("[{$project: {'a1.b.c':1, 'a.b.c.d.e':'str'}}]");
-
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | combinedProjection_0\n"
- "| RefBlock: \n"
- "| Variable [combinedProjection_0]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [combinedProjection_0]\n"
- "| EvalPath []\n"
- "| | Variable [scan_0]\n"
- "| PathComposeM []\n"
- "| | PathField [a1]\n"
- "| | PathTraverse [inf]\n"
- "| | PathComposeM []\n"
- "| | | PathField [b]\n"
- "| | | PathTraverse [inf]\n"
- "| | | PathComposeM []\n"
- "| | | | PathKeep [c]\n"
- "| | | PathObj []\n"
- "| | PathComposeM []\n"
- "| | | PathKeep [b]\n"
- "| | PathObj []\n"
- "| PathComposeM []\n"
- "| | PathField [a]\n"
- "| | PathTraverse [inf]\n"
- "| | PathComposeM []\n"
- "| | | PathField [b]\n"
- "| | | PathTraverse [inf]\n"
- "| | | PathComposeM []\n"
- "| | | | PathField [c]\n"
- "| | | | PathTraverse [inf]\n"
- "| | | | PathComposeM []\n"
- "| | | | | PathField [d]\n"
- "| | | | | PathTraverse [inf]\n"
- "| | | | | PathComposeM []\n"
- "| | | | | | PathDefault []\n"
- "| | | | | | Const [{}]\n"
- "| | | | | PathComposeM []\n"
- "| | | | | | PathField [e]\n"
- "| | | | | | PathConstant []\n"
- "| | | | | | Variable [projGetPath_0]\n"
- "| | | | | PathKeep [e]\n"
- "| | | | PathKeep [d]\n"
- "| | | PathKeep [c]\n"
- "| | PathKeep [b]\n"
- "| PathKeep [_id, a, a1]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [projGetPath_0]\n"
- "| Const [\"str\"]\n"
- "Scan [collection]\n"
- " BindBlock:\n"
- " [scan_0]\n"
- " Source []\n",
- translated);
-}
-
-TEST(ABTTranslate, ExprFilter) {
- ABT translated = translatePipeline(
- "[{$project: {a: {$filter: {input: [1, 2, 'str', {a: 2.0, b:'s'}, 3, 4], as: 'num', cond: "
- "{$and: [{$gte: ['$$num', 2]}, {$lte: ['$$num', 3]}]}}}}}]");
-
- PrefixId prefixId;
- std::string scanDefName = "collection";
- OptPhaseManager phaseManager({OptPhase::ConstEvalPre},
- prefixId,
- {{{scanDefName, ScanDefinition{{}, {}}}}},
- DebugInfo::kDefaultForTests);
-
- ABT optimized = translated;
- phaseManager.optimize(optimized);
-
- // Make sure we have a single array constant for (1, 2, 'str', ...).
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | combinedProjection_0\n"
- "| RefBlock: \n"
- "| Variable [combinedProjection_0]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [combinedProjection_0]\n"
- "| EvalPath []\n"
- "| | Variable [scan_0]\n"
- "| PathComposeM []\n"
- "| | PathDefault []\n"
- "| | Const [{}]\n"
- "| PathComposeM []\n"
- "| | PathField [a]\n"
- "| | PathConstant []\n"
- "| | EvalPath []\n"
- "| | | Const [[1, 2, \"str\", {\"a\" : 2, \"b\" : \"s\"}, 3, 4]]\n"
- "| | PathTraverse [inf]\n"
- "| | PathLambda []\n"
- "| | LambdaAbstraction [projGetPath_0_var_1]\n"
- "| | If []\n"
- "| | | | Const [Nothing]\n"
- "| | | Variable [projGetPath_0_var_1]\n"
- "| | BinaryOp [And]\n"
- "| | | BinaryOp [Gte]\n"
- "| | | | Const [2]\n"
- "| | | Variable [projGetPath_0_var_1]\n"
- "| | BinaryOp [Lte]\n"
- "| | | Const [3]\n"
- "| | Variable [projGetPath_0_var_1]\n"
- "| PathKeep [_id, a]\n"
- "Scan [collection]\n"
- " BindBlock:\n"
- " [scan_0]\n"
- " Source []\n",
- optimized);
-}
-
-TEST(ABTTranslate, GroupBasic) {
- ABT translated =
- translatePipeline("[{$group: {_id: '$a.b', s: {$sum: {$multiply: ['$b', '$c']}}}}]");
-
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | agg_project_0\n"
- "| RefBlock: \n"
- "| Variable [agg_project_0]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [agg_project_0]\n"
- "| EvalPath []\n"
- "| | Const [{}]\n"
- "| PathComposeM []\n"
- "| | PathField [s]\n"
- "| | PathConstant []\n"
- "| | Variable [s_agg_0]\n"
- "| PathField [_id]\n"
- "| PathConstant []\n"
- "| Variable [groupByProj_0]\n"
- "GroupBy []\n"
- "| | groupings: \n"
- "| | RefBlock: \n"
- "| | Variable [groupByProj_0]\n"
- "| aggregations: \n"
- "| [s_agg_0]\n"
- "| FunctionCall [$sum]\n"
- "| Variable [groupByInputProj_0]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [groupByInputProj_0]\n"
- "| BinaryOp [Mult]\n"
- "| | EvalPath []\n"
- "| | | Variable [scan_0]\n"
- "| | PathGet [c]\n"
- "| | PathIdentity []\n"
- "| EvalPath []\n"
- "| | Variable [scan_0]\n"
- "| PathGet [b]\n"
- "| PathIdentity []\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [groupByProj_0]\n"
- "| EvalPath []\n"
- "| | Variable [scan_0]\n"
- "| PathGet [a]\n"
- "| PathTraverse [inf]\n"
- "| PathGet [b]\n"
- "| PathIdentity []\n"
- "Scan [collection]\n"
- " BindBlock:\n"
- " [scan_0]\n"
- " Source []\n",
- translated);
-}
-
-TEST(ABTTranslate, GroupLocalGlobal) {
- ABT translated = translatePipeline("[{$group: {_id: '$a', c: {$sum: '$b'}}}]");
-
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | agg_project_0\n"
- "| RefBlock: \n"
- "| Variable [agg_project_0]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [agg_project_0]\n"
- "| EvalPath []\n"
- "| | Const [{}]\n"
- "| PathComposeM []\n"
- "| | PathField [c]\n"
- "| | PathConstant []\n"
- "| | Variable [c_agg_0]\n"
- "| PathField [_id]\n"
- "| PathConstant []\n"
- "| Variable [groupByProj_0]\n"
- "GroupBy []\n"
- "| | groupings: \n"
- "| | RefBlock: \n"
- "| | Variable [groupByProj_0]\n"
- "| aggregations: \n"
- "| [c_agg_0]\n"
- "| FunctionCall [$sum]\n"
- "| Variable [groupByInputProj_0]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [groupByInputProj_0]\n"
- "| EvalPath []\n"
- "| | Variable [scan_0]\n"
- "| PathGet [b]\n"
- "| PathIdentity []\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [groupByProj_0]\n"
- "| EvalPath []\n"
- "| | Variable [scan_0]\n"
- "| PathGet [a]\n"
- "| PathIdentity []\n"
- "Scan [collection]\n"
- " BindBlock:\n"
- " [scan_0]\n"
- " Source []\n",
- translated);
-
- PrefixId prefixId;
- std::string scanDefName = "collection";
- OptPhaseManager phaseManager(
- {OptPhase::MemoSubstitutionPhase,
- OptPhase::MemoExplorationPhase,
- OptPhase::MemoImplementationPhase},
- prefixId,
- {{{scanDefName, ScanDefinition{{}, {}, {DistributionType::UnknownPartitioning}}}},
- 5 /*numberOfPartitions*/},
- DebugInfo::kDefaultForTests);
-
- ABT optimized = std::move(translated);
- phaseManager.optimize(optimized);
-
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | agg_project_0\n"
- "| RefBlock: \n"
- "| Variable [agg_project_0]\n"
- "Exchange []\n"
- "| | distribution: \n"
- "| | type: Centralized\n"
- "| RefBlock: \n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [agg_project_0]\n"
- "| EvalPath []\n"
- "| | Const [{}]\n"
- "| PathComposeM []\n"
- "| | PathField [c]\n"
- "| | PathConstant []\n"
- "| | Variable [c_agg_0]\n"
- "| PathField [_id]\n"
- "| PathConstant []\n"
- "| Variable [groupByProj_0]\n"
- "GroupBy [Global]\n"
- "| | groupings: \n"
- "| | RefBlock: \n"
- "| | Variable [groupByProj_0]\n"
- "| aggregations: \n"
- "| [c_agg_0]\n"
- "| FunctionCall [$sum]\n"
- "| Variable [preagg_0]\n"
- "Exchange []\n"
- "| | distribution: \n"
- "| | type: HashPartitioning\n"
- "| | projections: \n"
- "| | groupByProj_0\n"
- "| RefBlock: \n"
- "| Variable [groupByProj_0]\n"
- "GroupBy [Local]\n"
- "| | groupings: \n"
- "| | RefBlock: \n"
- "| | Variable [groupByProj_0]\n"
- "| aggregations: \n"
- "| [preagg_0]\n"
- "| FunctionCall [$sum]\n"
- "| Variable [groupByInputProj_0]\n"
- "PhysicalScan [{'a': groupByProj_0, 'b': groupByInputProj_0}, collection, parallel]\n"
- " BindBlock:\n"
- " [groupByInputProj_0]\n"
- " Source []\n"
- " [groupByProj_0]\n"
- " Source []\n",
- optimized);
-}
-
-TEST(ABTTranslate, UnwindBasic) {
- ABT translated = translatePipeline("[{$unwind: {path: '$a.b.c'}}]");
-
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | embedProj_0\n"
- "| RefBlock: \n"
- "| Variable [embedProj_0]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [embedProj_0]\n"
- "| EvalPath []\n"
- "| | Variable [scan_0]\n"
- "| PathField [a]\n"
- "| PathTraverse [inf]\n"
- "| PathField [b]\n"
- "| PathTraverse [inf]\n"
- "| PathField [c]\n"
- "| PathConstant []\n"
- "| Variable [unwoundProj_0]\n"
- "Unwind []\n"
- "| BindBlock:\n"
- "| [unwoundPid_0]\n"
- "| Source []\n"
- "| [unwoundProj_0]\n"
- "| Source []\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [unwoundProj_0]\n"
- "| EvalPath []\n"
- "| | Variable [scan_0]\n"
- "| PathGet [a]\n"
- "| PathGet [b]\n"
- "| PathGet [c]\n"
- "| PathIdentity []\n"
- "Scan [collection]\n"
- " BindBlock:\n"
- " [scan_0]\n"
- " Source []\n",
- translated);
-}
-
-TEST(ABTTranslate, UnwindComplex) {
- ABT translated = translatePipeline(
- "[{$unwind: {path: '$a.b.c', includeArrayIndex: 'p1.pid', preserveNullAndEmptyArrays: "
- "true}}]");
-
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | embedPidProj_0\n"
- "| RefBlock: \n"
- "| Variable [embedPidProj_0]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [embedPidProj_0]\n"
- "| EvalPath []\n"
- "| | Variable [embedProj_0]\n"
- "| PathField [p1]\n"
- "| PathField [pid]\n"
- "| PathConstant []\n"
- "| If []\n"
- "| | | Const [null]\n"
- "| | Variable [unwoundPid_0]\n"
- "| BinaryOp [Gte]\n"
- "| | Const [0]\n"
- "| Variable [unwoundPid_0]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [embedProj_0]\n"
- "| EvalPath []\n"
- "| | Variable [scan_0]\n"
- "| PathField [a]\n"
- "| PathTraverse [inf]\n"
- "| PathField [b]\n"
- "| PathTraverse [inf]\n"
- "| PathField [c]\n"
- "| PathLambda []\n"
- "| LambdaAbstraction [unwoundLambdaVarName_0]\n"
- "| If []\n"
- "| | | Variable [unwoundLambdaVarName_0]\n"
- "| | Variable [unwoundProj_0]\n"
- "| BinaryOp [Gte]\n"
- "| | Const [0]\n"
- "| Variable [unwoundPid_0]\n"
- "Unwind [retainNonArrays]\n"
- "| BindBlock:\n"
- "| [unwoundPid_0]\n"
- "| Source []\n"
- "| [unwoundProj_0]\n"
- "| Source []\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [unwoundProj_0]\n"
- "| EvalPath []\n"
- "| | Variable [scan_0]\n"
- "| PathGet [a]\n"
- "| PathGet [b]\n"
- "| PathGet [c]\n"
- "| PathIdentity []\n"
- "Scan [collection]\n"
- " BindBlock:\n"
- " [scan_0]\n"
- " Source []\n",
- translated);
-}
-
-TEST(ABTTranslate, UnwindAndGroup) {
- ABT translated = translatePipeline(
- "[{$unwind:{path: '$a.b', preserveNullAndEmptyArrays: true}}, "
- "{$group:{_id: '$a.b'}}]");
-
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | agg_project_0\n"
- "| RefBlock: \n"
- "| Variable [agg_project_0]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [agg_project_0]\n"
- "| EvalPath []\n"
- "| | Const [{}]\n"
- "| PathField [_id]\n"
- "| PathConstant []\n"
- "| Variable [groupByProj_0]\n"
- "GroupBy []\n"
- "| | groupings: \n"
- "| | RefBlock: \n"
- "| | Variable [groupByProj_0]\n"
- "| aggregations: \n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [groupByProj_0]\n"
- "| EvalPath []\n"
- "| | Variable [embedProj_0]\n"
- "| PathGet [a]\n"
- "| PathTraverse [inf]\n"
- "| PathGet [b]\n"
- "| PathIdentity []\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [embedProj_0]\n"
- "| EvalPath []\n"
- "| | Variable [scan_0]\n"
- "| PathField [a]\n"
- "| PathTraverse [inf]\n"
- "| PathField [b]\n"
- "| PathLambda []\n"
- "| LambdaAbstraction [unwoundLambdaVarName_0]\n"
- "| If []\n"
- "| | | Variable [unwoundLambdaVarName_0]\n"
- "| | Variable [unwoundProj_0]\n"
- "| BinaryOp [Gte]\n"
- "| | Const [0]\n"
- "| Variable [unwoundPid_0]\n"
- "Unwind [retainNonArrays]\n"
- "| BindBlock:\n"
- "| [unwoundPid_0]\n"
- "| Source []\n"
- "| [unwoundProj_0]\n"
- "| Source []\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [unwoundProj_0]\n"
- "| EvalPath []\n"
- "| | Variable [scan_0]\n"
- "| PathGet [a]\n"
- "| PathGet [b]\n"
- "| PathIdentity []\n"
- "Scan [collection]\n"
- " BindBlock:\n"
- " [scan_0]\n"
- " Source []\n",
- translated);
-}
-
-TEST(ABTTranslate, UnwindSort) {
- ABT translated = translatePipeline("[{$unwind: '$x'}, {$sort: {'x': 1}}]");
-
- PrefixId prefixId;
- std::string scanDefName = "collection";
- OptPhaseManager phaseManager(OptPhaseManager::getAllRewritesSet(),
- prefixId,
- {{{scanDefName, ScanDefinition{{}, {}}}}},
- DebugInfo::kDefaultForTests);
-
- ABT optimized = translated;
- phaseManager.optimize(optimized);
-
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | embedProj_0\n"
- "| RefBlock: \n"
- "| Variable [embedProj_0]\n"
- "Collation []\n"
- "| | collation: \n"
- "| | sort_0: Ascending\n"
- "| RefBlock: \n"
- "| Variable [sort_0]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [sort_0]\n"
- "| Variable [unwoundProj_0]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [embedProj_0]\n"
- "| If []\n"
- "| | | Variable [scan_0]\n"
- "| | FunctionCall [setField]\n"
- "| | | | Variable [unwoundProj_0]\n"
- "| | | Const [\"x\"]\n"
- "| | Variable [scan_0]\n"
- "| BinaryOp [Or]\n"
- "| | FunctionCall [isObject]\n"
- "| | Variable [scan_0]\n"
- "| FunctionCall [exists]\n"
- "| Variable [unwoundProj_0]\n"
- "Unwind []\n"
- "| BindBlock:\n"
- "| [unwoundPid_0]\n"
- "| Source []\n"
- "| [unwoundProj_0]\n"
- "| Source []\n"
- "PhysicalScan [{'<root>': scan_0, 'x': unwoundProj_0}, collection]\n"
- " BindBlock:\n"
- " [scan_0]\n"
- " Source []\n"
- " [unwoundProj_0]\n"
- " Source []\n",
- optimized);
-}
-
-TEST(ABTTranslate, MatchIndex) {
- PrefixId prefixId;
- std::string scanDefName = "collection";
-
- Metadata metadata = {
- {{scanDefName,
- ScanDefinition{{}, {{"index1", makeIndexDefinition("a", CollationOp::Ascending)}}}}}};
- ABT translated = translatePipeline(metadata, "[{$match: {'a': 10}}]", scanDefName, prefixId);
-
- OptPhaseManager phaseManager({OptPhase::MemoSubstitutionPhase,
- OptPhase::MemoExplorationPhase,
- OptPhase::MemoImplementationPhase},
- prefixId,
- metadata,
- DebugInfo::kDefaultForTests);
-
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | scan_0\n"
- "| RefBlock: \n"
- "| Variable [scan_0]\n"
- "Filter []\n"
- "| EvalFilter []\n"
- "| | Variable [scan_0]\n"
- "| PathGet [a]\n"
- "| PathTraverse [1]\n"
- "| PathCompare [Eq]\n"
- "| Const [10]\n"
- "Scan [collection]\n"
- " BindBlock:\n"
- " [scan_0]\n"
- " Source []\n",
- translated);
-
- ABT optimized = std::move(translated);
- phaseManager.optimize(optimized);
-
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | scan_0\n"
- "| RefBlock: \n"
- "| Variable [scan_0]\n"
- "BinaryJoin [joinType: Inner, {rid_0}]\n"
- "| | Const [true]\n"
- "| LimitSkip []\n"
- "| | limitSkip:\n"
- "| | limit: 1\n"
- "| | skip: 0\n"
- "| Seek [ridProjection: rid_0, {'<root>': scan_0}, collection]\n"
- "| | BindBlock:\n"
- "| | [scan_0]\n"
- "| | Source []\n"
- "| RefBlock: \n"
- "| Variable [rid_0]\n"
- "IndexScan [{'<rid>': rid_0}, scanDefName: collection, indexDefName: index1, interval: "
- "{[Const [10], Const [10]]}]\n"
- " BindBlock:\n"
- " [rid_0]\n"
- " Source []\n",
- optimized);
-}
-
-TEST(ABTTranslate, MatchIndexCovered) {
- PrefixId prefixId;
- std::string scanDefName = "collection";
-
- Metadata metadata = {
- {{scanDefName,
- ScanDefinition{
- {},
- {{"index1",
- IndexDefinition{{{{makeNonMultikeyIndexPath("a"), CollationOp::Ascending}}},
- false /*multiKey*/}}}}}}};
- ABT translated = translatePipeline(
- metadata, "[{$project: {_id: 0, a: 1}}, {$match: {'a': 10}}]", scanDefName, prefixId);
-
- OptPhaseManager phaseManager({OptPhase::ConstEvalPre,
- OptPhase::PathFuse,
- OptPhase::MemoSubstitutionPhase,
- OptPhase::MemoExplorationPhase,
- OptPhase::MemoImplementationPhase},
- prefixId,
- metadata,
- DebugInfo::kDefaultForTests);
-
- ABT optimized = std::move(translated);
- phaseManager.optimize(optimized);
-
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | combinedProjection_0\n"
- "| RefBlock: \n"
- "| Variable [combinedProjection_0]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [combinedProjection_0]\n"
- "| EvalPath []\n"
- "| | Const [{}]\n"
- "| PathField [a]\n"
- "| PathConstant []\n"
- "| Variable [fieldProj_0]\n"
- "IndexScan [{'<indexKey> 0': fieldProj_0}, scanDefName: collection, indexDefName: index1, "
- "interval: {[Const [10], Const [10]]}]\n"
- " BindBlock:\n"
- " [fieldProj_0]\n"
- " Source []\n",
- optimized);
-}
-
-TEST(ABTTranslate, MatchIndexCovered1) {
- PrefixId prefixId;
- std::string scanDefName = "collection";
-
- Metadata metadata = {
- {{scanDefName,
- ScanDefinition{
- {},
- {{"index1",
- IndexDefinition{{{{makeNonMultikeyIndexPath("a"), CollationOp::Ascending}}},
- false /*multiKey*/}}}}}}};
- ABT translated = translatePipeline(
- metadata, "[{$match: {'a': 10}}, {$project: {_id: 0, a: 1}}]", scanDefName, prefixId);
-
- OptPhaseManager phaseManager({OptPhase::ConstEvalPre,
- OptPhase::PathFuse,
- OptPhase::MemoSubstitutionPhase,
- OptPhase::MemoExplorationPhase,
- OptPhase::MemoImplementationPhase},
- prefixId,
- metadata,
- DebugInfo::kDefaultForTests);
-
- ABT optimized = std::move(translated);
- phaseManager.optimize(optimized);
-
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | combinedProjection_0\n"
- "| RefBlock: \n"
- "| Variable [combinedProjection_0]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [combinedProjection_0]\n"
- "| EvalPath []\n"
- "| | Const [{}]\n"
- "| PathField [a]\n"
- "| PathConstant []\n"
- "| Variable [fieldProj_0]\n"
- "IndexScan [{'<indexKey> 0': fieldProj_0}, scanDefName: collection, indexDefName: index1, "
- "interval: {[Const [10], Const [10]]}]\n"
- " BindBlock:\n"
- " [fieldProj_0]\n"
- " Source []\n",
- optimized);
-}
-
-TEST(ABTTranslate, MatchIndexCovered2) {
- PrefixId prefixId;
- std::string scanDefName = "collection";
-
- Metadata metadata = {
- {{scanDefName,
- ScanDefinition{
- {},
- {{"index1",
- IndexDefinition{{{{makeNonMultikeyIndexPath("a"), CollationOp::Ascending},
- {makeNonMultikeyIndexPath("b"), CollationOp::Ascending}}},
- false /*multiKey*/}}}}}}};
- ABT translated = translatePipeline(metadata,
- "[{$match: {'a': 10, 'b': 20}}, {$project: {_id: 0, a: 1}}]",
- scanDefName,
- prefixId);
-
- OptPhaseManager phaseManager({OptPhase::ConstEvalPre,
- OptPhase::PathFuse,
- OptPhase::MemoSubstitutionPhase,
- OptPhase::MemoExplorationPhase,
- OptPhase::MemoImplementationPhase},
- prefixId,
- metadata,
- DebugInfo::kDefaultForTests);
-
- ABT optimized = std::move(translated);
- phaseManager.optimize(optimized);
-
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | combinedProjection_0\n"
- "| RefBlock: \n"
- "| Variable [combinedProjection_0]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [combinedProjection_0]\n"
- "| EvalPath []\n"
- "| | Const [{}]\n"
- "| PathField [a]\n"
- "| PathConstant []\n"
- "| Variable [fieldProj_0]\n"
- "IndexScan [{'<indexKey> 0': fieldProj_0}, scanDefName: collection, indexDefName: index1, "
- "interval: {[Const [10], Const [10]], [Const [20], Const [20]]}]\n"
- " BindBlock:\n"
- " [fieldProj_0]\n"
- " Source []\n",
- optimized);
-}
-
-TEST(ABTTranslate, MatchIndexCovered3) {
- PrefixId prefixId;
- std::string scanDefName = "collection";
-
- Metadata metadata = {
- {{scanDefName,
- ScanDefinition{
- {},
- {{"index1",
- IndexDefinition{{{{makeNonMultikeyIndexPath("a"), CollationOp::Ascending},
- {makeNonMultikeyIndexPath("b"), CollationOp::Ascending},
- {makeNonMultikeyIndexPath("c"), CollationOp::Ascending}}},
- false /*multiKey*/}}}}}}};
- ABT translated = translatePipeline(
- metadata,
- "[{$match: {'a': 10, 'b': 20, 'c': 30}}, {$project: {_id: 0, a: 1, b: 1, c: 1}}]",
- scanDefName,
- prefixId);
-
- OptPhaseManager phaseManager({OptPhase::ConstEvalPre,
- OptPhase::PathFuse,
- OptPhase::MemoSubstitutionPhase,
- OptPhase::MemoExplorationPhase,
- OptPhase::MemoImplementationPhase},
- prefixId,
- metadata,
- DebugInfo::kDefaultForTests);
-
- ABT optimized = std::move(translated);
- phaseManager.optimize(optimized);
-
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | combinedProjection_0\n"
- "| RefBlock: \n"
- "| Variable [combinedProjection_0]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [combinedProjection_0]\n"
- "| EvalPath []\n"
- "| | Const [{}]\n"
- "| PathComposeM []\n"
- "| | PathField [c]\n"
- "| | PathConstant []\n"
- "| | Variable [fieldProj_2]\n"
- "| PathComposeM []\n"
- "| | PathField [b]\n"
- "| | PathConstant []\n"
- "| | Variable [fieldProj_1]\n"
- "| PathField [a]\n"
- "| PathConstant []\n"
- "| Variable [fieldProj_0]\n"
- "IndexScan [{'<indexKey> 0': fieldProj_0, '<indexKey> 1': fieldProj_1, '<indexKey> 2': "
- "fieldProj_2}, scanDefName: collection, indexDefName: index1, interval: {[Const [10], "
- "Const [10]], [Const [20], Const [20]], [Const [30], Const [30]]}]\n"
- " BindBlock:\n"
- " [fieldProj_0]\n"
- " Source []\n"
- " [fieldProj_1]\n"
- " Source []\n"
- " [fieldProj_2]\n"
- " Source []\n",
- optimized);
-}
-
-TEST(ABTTranslate, MatchIndexCovered4) {
- PrefixId prefixId;
- std::string scanDefName = "collection";
-
- Metadata metadata = {
- {{scanDefName,
- ScanDefinition{
- {},
- {{"index1",
- IndexDefinition{{{{makeNonMultikeyIndexPath("a"), CollationOp::Ascending},
- {makeNonMultikeyIndexPath("b"), CollationOp::Ascending},
- {makeNonMultikeyIndexPath("c"), CollationOp::Ascending}}},
- false /*multiKey*/}}}}}}};
- ABT translated = translatePipeline(
- metadata,
- "[{$project: {_id: 0, a: 1, b: 1, c: 1}}, {$match: {'a': 10, 'b': 20, 'c': 30}}]",
- scanDefName,
- prefixId);
-
- OptPhaseManager phaseManager({OptPhase::ConstEvalPre,
- OptPhase::PathFuse,
- OptPhase::MemoSubstitutionPhase,
- OptPhase::MemoExplorationPhase,
- OptPhase::MemoImplementationPhase},
- prefixId,
- metadata,
- DebugInfo::kDefaultForTests);
-
- ABT optimized = std::move(translated);
- phaseManager.optimize(optimized);
-
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | combinedProjection_0\n"
- "| RefBlock: \n"
- "| Variable [combinedProjection_0]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [combinedProjection_0]\n"
- "| EvalPath []\n"
- "| | Const [{}]\n"
- "| PathComposeM []\n"
- "| | PathField [c]\n"
- "| | PathConstant []\n"
- "| | Variable [fieldProj_2]\n"
- "| PathComposeM []\n"
- "| | PathField [b]\n"
- "| | PathConstant []\n"
- "| | Variable [fieldProj_1]\n"
- "| PathField [a]\n"
- "| PathConstant []\n"
- "| Variable [fieldProj_0]\n"
- "IndexScan [{'<indexKey> 0': fieldProj_0, '<indexKey> 1': fieldProj_1, '<indexKey> 2': "
- "fieldProj_2}, scanDefName: collection, indexDefName: index1, interval: {[Const [10], "
- "Const [10]], [Const [20], Const [20]], [Const [30], Const [30]]}]\n"
- " BindBlock:\n"
- " [fieldProj_0]\n"
- " Source []\n"
- " [fieldProj_1]\n"
- " Source []\n"
- " [fieldProj_2]\n"
- " Source []\n",
- optimized);
-}
-
-TEST(ABTTranslate, MatchSortIndex) {
- PrefixId prefixId;
- std::string scanDefName = "collection";
-
- Metadata metadata = {
- {{scanDefName,
- ScanDefinition{{}, {{"index1", makeIndexDefinition("a", CollationOp::Ascending)}}}}}};
- ABT translated = translatePipeline(
- metadata, "[{$match: {'a': 10}}, {$sort: {'a': 1}}]", scanDefName, prefixId);
-
- OptPhaseManager phaseManager({OptPhase::MemoSubstitutionPhase,
- OptPhase::MemoExplorationPhase,
- OptPhase::MemoImplementationPhase},
- prefixId,
- metadata,
- DebugInfo::kDefaultForTests);
-
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | scan_0\n"
- "| RefBlock: \n"
- "| Variable [scan_0]\n"
- "Collation []\n"
- "| | collation: \n"
- "| | sort_0: Ascending\n"
- "| RefBlock: \n"
- "| Variable [sort_0]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [sort_0]\n"
- "| EvalPath []\n"
- "| | Variable [scan_0]\n"
- "| PathGet [a]\n"
- "| PathIdentity []\n"
- "Filter []\n"
- "| EvalFilter []\n"
- "| | Variable [scan_0]\n"
- "| PathGet [a]\n"
- "| PathTraverse [1]\n"
- "| PathCompare [Eq]\n"
- "| Const [10]\n"
- "Scan [collection]\n"
- " BindBlock:\n"
- " [scan_0]\n"
- " Source []\n",
- translated);
-
- ABT optimized = std::move(translated);
- phaseManager.optimize(optimized);
-
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | scan_0\n"
- "| RefBlock: \n"
- "| Variable [scan_0]\n"
- "Collation []\n"
- "| | collation: \n"
- "| | sort_0: Ascending\n"
- "| RefBlock: \n"
- "| Variable [sort_0]\n"
- "BinaryJoin [joinType: Inner, {rid_0}]\n"
- "| | Const [true]\n"
- "| LimitSkip []\n"
- "| | limitSkip:\n"
- "| | limit: 1\n"
- "| | skip: 0\n"
- "| Seek [ridProjection: rid_0, {'<root>': scan_0, 'a': sort_0}, collection]\n"
- "| | BindBlock:\n"
- "| | [scan_0]\n"
- "| | Source []\n"
- "| | [sort_0]\n"
- "| | Source []\n"
- "| RefBlock: \n"
- "| Variable [rid_0]\n"
- "IndexScan [{'<rid>': rid_0}, scanDefName: collection, indexDefName: index1, interval: "
- "{[Const [10], Const [10]]}]\n"
- " BindBlock:\n"
- " [rid_0]\n"
- " Source []\n",
- optimized);
-}
-
-TEST(ABTTranslate, RangeIndex) {
- PrefixId prefixId;
- std::string scanDefName = "collection";
- Metadata metadata = {
- {{scanDefName,
- ScanDefinition{{}, {{"index1", makeIndexDefinition("a", CollationOp::Ascending)}}}}}};
- ABT translated =
- translatePipeline(metadata, "[{$match: {'a': {$gt: 70, $lt: 90}}}]", scanDefName, prefixId);
-
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | scan_0\n"
- "| RefBlock: \n"
- "| Variable [scan_0]\n"
- "Filter []\n"
- "| EvalFilter []\n"
- "| | Variable [scan_0]\n"
- "| PathGet [a]\n"
- "| PathTraverse [1]\n"
- "| PathComposeM []\n"
- "| | PathCompare [Lt]\n"
- "| | Const [\"\"]\n"
- "| PathCompare [Gt]\n"
- "| Const [70]\n"
- "Filter []\n"
- "| EvalFilter []\n"
- "| | Variable [scan_0]\n"
- "| PathGet [a]\n"
- "| PathTraverse [1]\n"
- "| PathComposeM []\n"
- "| | PathCompare [Gte]\n"
- "| | Const [nan]\n"
- "| PathCompare [Lt]\n"
- "| Const [90]\n"
- "Scan [collection]\n"
- " BindBlock:\n"
- " [scan_0]\n"
- " Source []\n",
- translated);
-
- OptPhaseManager phaseManager({OptPhase::MemoSubstitutionPhase,
- OptPhase::MemoExplorationPhase,
- OptPhase::MemoImplementationPhase},
- prefixId,
- metadata,
- DebugInfo::kDefaultForTests);
-
- ABT optimized = std::move(translated);
- phaseManager.getHints()._disableScan = true;
- phaseManager.optimize(optimized);
-
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | scan_0\n"
- "| RefBlock: \n"
- "| Variable [scan_0]\n"
- "BinaryJoin [joinType: Inner, {rid_0}]\n"
- "| | Const [true]\n"
- "| LimitSkip []\n"
- "| | limitSkip:\n"
- "| | limit: 1\n"
- "| | skip: 0\n"
- "| Seek [ridProjection: rid_0, {'<root>': scan_0}, collection]\n"
- "| | BindBlock:\n"
- "| | [scan_0]\n"
- "| | Source []\n"
- "| RefBlock: \n"
- "| Variable [rid_0]\n"
- "Filter []\n"
- "| EvalFilter []\n"
- "| | FunctionCall [getArraySize]\n"
- "| | Variable [sides_0]\n"
- "| PathCompare [Eq]\n"
- "| Const [2]\n"
- "GroupBy []\n"
- "| | groupings: \n"
- "| | RefBlock: \n"
- "| | Variable [rid_0]\n"
- "| aggregations: \n"
- "| [sides_0]\n"
- "| FunctionCall [$addToSet]\n"
- "| Variable [sideId_0]\n"
- "Union []\n"
- "| | BindBlock:\n"
- "| | [rid_0]\n"
- "| | Source []\n"
- "| | [sideId_0]\n"
- "| | Source []\n"
- "| Evaluation []\n"
- "| | BindBlock:\n"
- "| | [sideId_0]\n"
- "| | Const [1]\n"
- "| IndexScan [{'<rid>': rid_0}, scanDefName: collection, indexDefName: index1, interval: "
- "{(Const [70], Const [\"\"])}]\n"
- "| BindBlock:\n"
- "| [rid_0]\n"
- "| Source []\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [sideId_0]\n"
- "| Const [0]\n"
- "IndexScan [{'<rid>': rid_0}, scanDefName: collection, indexDefName: index1, interval: "
- "{[Const [nan], Const [90])}]\n"
- " BindBlock:\n"
- " [rid_0]\n"
- " Source []\n",
- optimized);
-}
-
-TEST(ABTTranslate, Index1) {
- {
- PrefixId prefixId;
- std::string scanDefName = "collection";
- Metadata metadata = {
- {{scanDefName,
- ScanDefinition{{},
- {{"index1",
- IndexDefinition{{{makeIndexPath("a"), CollationOp::Ascending},
- {makeIndexPath("b"), CollationOp::Ascending}},
- true /*multiKey*/}}}}}}};
-
- ABT translated =
- translatePipeline(metadata, "[{$match: {'a': 2, 'b': 2}}]", scanDefName, prefixId);
-
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | scan_0\n"
- "| RefBlock: \n"
- "| Variable [scan_0]\n"
- "Filter []\n"
- "| EvalFilter []\n"
- "| | Variable [scan_0]\n"
- "| PathGet [a]\n"
- "| PathTraverse [1]\n"
- "| PathCompare [Eq]\n"
- "| Const [2]\n"
- "Filter []\n"
- "| EvalFilter []\n"
- "| | Variable [scan_0]\n"
- "| PathGet [b]\n"
- "| PathTraverse [1]\n"
- "| PathCompare [Eq]\n"
- "| Const [2]\n"
- "Scan [collection]\n"
- " BindBlock:\n"
- " [scan_0]\n"
- " Source []\n",
- translated);
-
- OptPhaseManager phaseManager({OptPhase::MemoSubstitutionPhase,
- OptPhase::MemoExplorationPhase,
- OptPhase::MemoImplementationPhase},
- prefixId,
- metadata,
- DebugInfo::kDefaultForTests);
-
- ABT optimized = translated;
- phaseManager.optimize(optimized);
-
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | scan_0\n"
- "| RefBlock: \n"
- "| Variable [scan_0]\n"
- "BinaryJoin [joinType: Inner, {rid_0}]\n"
- "| | Const [true]\n"
- "| LimitSkip []\n"
- "| | limitSkip:\n"
- "| | limit: 1\n"
- "| | skip: 0\n"
- "| Seek [ridProjection: rid_0, {'<root>': scan_0}, collection]\n"
- "| | BindBlock:\n"
- "| | [scan_0]\n"
- "| | Source []\n"
- "| RefBlock: \n"
- "| Variable [rid_0]\n"
- "IndexScan [{'<rid>': rid_0}, scanDefName: collection, indexDefName: index1, interval: "
- "{[Const [2], Const [2]], [Const [2], Const [2]]}]\n"
- " BindBlock:\n"
- " [rid_0]\n"
- " Source []\n",
- optimized);
- }
-
- {
- PrefixId prefixId;
- std::string scanDefName = "collection";
- Metadata metadata = {
- {{scanDefName,
- ScanDefinition{{}, {{"index1", makeIndexDefinition("a", CollationOp::Ascending)}}}}}};
-
- ABT translated =
- translatePipeline(metadata, "[{$match: {'a': 2, 'b': 2}}]", scanDefName, prefixId);
-
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | scan_0\n"
- "| RefBlock: \n"
- "| Variable [scan_0]\n"
- "Filter []\n"
- "| EvalFilter []\n"
- "| | Variable [scan_0]\n"
- "| PathGet [a]\n"
- "| PathTraverse [1]\n"
- "| PathCompare [Eq]\n"
- "| Const [2]\n"
- "Filter []\n"
- "| EvalFilter []\n"
- "| | Variable [scan_0]\n"
- "| PathGet [b]\n"
- "| PathTraverse [1]\n"
- "| PathCompare [Eq]\n"
- "| Const [2]\n"
- "Scan [collection]\n"
- " BindBlock:\n"
- " [scan_0]\n"
- " Source []\n",
- translated);
-
- // Demonstrate we can use an index over only one field.
- OptPhaseManager phaseManager({OptPhase::MemoSubstitutionPhase,
- OptPhase::MemoExplorationPhase,
- OptPhase::MemoImplementationPhase,
- OptPhase::ConstEvalPost},
- prefixId,
- metadata,
- DebugInfo::kDefaultForTests);
-
- ABT optimized = translated;
- phaseManager.optimize(optimized);
-
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | scan_0\n"
- "| RefBlock: \n"
- "| Variable [scan_0]\n"
- "BinaryJoin [joinType: Inner, {rid_0}]\n"
- "| | Const [true]\n"
- "| Filter []\n"
- "| | EvalFilter []\n"
- "| | | Variable [evalTemp_4]\n"
- "| | PathTraverse [1]\n"
- "| | PathCompare [Eq]\n"
- "| | Const [2]\n"
- "| LimitSkip []\n"
- "| | limitSkip:\n"
- "| | limit: 1\n"
- "| | skip: 0\n"
- "| Seek [ridProjection: rid_0, {'<root>': scan_0, 'b': evalTemp_4}, collection]\n"
- "| | BindBlock:\n"
- "| | [evalTemp_4]\n"
- "| | Source []\n"
- "| | [scan_0]\n"
- "| | Source []\n"
- "| RefBlock: \n"
- "| Variable [rid_0]\n"
- "IndexScan [{'<rid>': rid_0}, scanDefName: collection, indexDefName: index1, interval: "
- "{[Const [2], Const [2]]}]\n"
- " BindBlock:\n"
- " [rid_0]\n"
- " Source []\n",
- optimized);
- }
-}
-
-TEST(ABTTranslate, GroupMultiKey) {
- ABT translated = translatePipeline(
- "[{$group: {_id: {'isin': '$isin', 'year': '$year'}, 'count': {$sum: 1}, 'open': {$first: "
- "'$$ROOT'}}}]");
-
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | agg_project_0\n"
- "| RefBlock: \n"
- "| Variable [agg_project_0]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [agg_project_0]\n"
- "| EvalPath []\n"
- "| | Const [{}]\n"
- "| PathComposeM []\n"
- "| | PathField [open]\n"
- "| | PathConstant []\n"
- "| | Variable [open_agg_0]\n"
- "| PathComposeM []\n"
- "| | PathField [count]\n"
- "| | PathConstant []\n"
- "| | Variable [count_agg_0]\n"
- "| PathField [_id]\n"
- "| PathComposeM []\n"
- "| | PathField [year]\n"
- "| | PathConstant []\n"
- "| | Variable [groupByProj_1]\n"
- "| PathField [isin]\n"
- "| PathConstant []\n"
- "| Variable [groupByProj_0]\n"
- "GroupBy []\n"
- "| | groupings: \n"
- "| | RefBlock: \n"
- "| | Variable [groupByProj_0]\n"
- "| | Variable [groupByProj_1]\n"
- "| aggregations: \n"
- "| [count_agg_0]\n"
- "| FunctionCall [$sum]\n"
- "| Const [1]\n"
- "| [open_agg_0]\n"
- "| FunctionCall [$first]\n"
- "| Variable [scan_0]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [groupByProj_1]\n"
- "| EvalPath []\n"
- "| | Variable [scan_0]\n"
- "| PathGet [year]\n"
- "| PathIdentity []\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [groupByProj_0]\n"
- "| EvalPath []\n"
- "| | Variable [scan_0]\n"
- "| PathGet [isin]\n"
- "| PathIdentity []\n"
- "Scan [collection]\n"
- " BindBlock:\n"
- " [scan_0]\n"
- " Source []\n",
- translated);
-}
-
-TEST(ABTTranslate, GroupEvalNoInline) {
- ABT translated = translatePipeline("[{$group: {_id: null, a: {$first: '$b'}}}]");
-
- PrefixId prefixId;
- std::string scanDefName = "collection";
- OptPhaseManager phaseManager(OptPhaseManager::getAllRewritesSet(),
- prefixId,
- {{{scanDefName, ScanDefinition{{}, {}}}}},
- DebugInfo::kDefaultForTests);
-
- ABT optimized = translated;
- phaseManager.optimize(optimized);
-
- // Verify that "b" is not inlined in the group expression, but is coming from the physical scan.
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | agg_project_0\n"
- "| RefBlock: \n"
- "| Variable [agg_project_0]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [agg_project_0]\n"
- "| Let [inputField_1]\n"
- "| | If []\n"
- "| | | | Variable [inputField_1]\n"
- "| | | FunctionCall [setField]\n"
- "| | | | | Variable [a_agg_0]\n"
- "| | | | Const [\"a\"]\n"
- "| | | Variable [inputField_1]\n"
- "| | BinaryOp [Or]\n"
- "| | | FunctionCall [isObject]\n"
- "| | | Variable [inputField_1]\n"
- "| | FunctionCall [exists]\n"
- "| | Variable [a_agg_0]\n"
- "| If []\n"
- "| | | Const [{}]\n"
- "| | FunctionCall [setField]\n"
- "| | | | Variable [groupByProj_0]\n"
- "| | | Const [\"_id\"]\n"
- "| | Const [{}]\n"
- "| BinaryOp [Or]\n"
- "| | FunctionCall [isObject]\n"
- "| | Const [{}]\n"
- "| FunctionCall [exists]\n"
- "| Variable [groupByProj_0]\n"
- "GroupBy []\n"
- "| | groupings: \n"
- "| | RefBlock: \n"
- "| | Variable [groupByProj_0]\n"
- "| aggregations: \n"
- "| [a_agg_0]\n"
- "| FunctionCall [$first]\n"
- "| Variable [groupByInputProj_0]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [groupByProj_0]\n"
- "| Const [null]\n"
- "PhysicalScan [{'b': groupByInputProj_0}, collection]\n"
- " BindBlock:\n"
- " [groupByInputProj_0]\n"
- " Source []\n",
- optimized);
-}
-
-TEST(ABTTranslate, ArrayExpr) {
- ABT translated = translatePipeline("[{$project: {a: ['$b', '$c']}}]");
-
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | combinedProjection_0\n"
- "| RefBlock: \n"
- "| Variable [combinedProjection_0]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [combinedProjection_0]\n"
- "| EvalPath []\n"
- "| | Variable [scan_0]\n"
- "| PathComposeM []\n"
- "| | PathDefault []\n"
- "| | Const [{}]\n"
- "| PathComposeM []\n"
- "| | PathField [a]\n"
- "| | PathConstant []\n"
- "| | Variable [projGetPath_0]\n"
- "| PathKeep [_id, a]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [projGetPath_0]\n"
- "| FunctionCall [newArray]\n"
- "| | EvalPath []\n"
- "| | | Variable [scan_0]\n"
- "| | PathGet [c]\n"
- "| | PathIdentity []\n"
- "| EvalPath []\n"
- "| | Variable [scan_0]\n"
- "| PathGet [b]\n"
- "| PathIdentity []\n"
- "Scan [collection]\n"
- " BindBlock:\n"
- " [scan_0]\n"
- " Source []\n",
- translated);
-}
-
-TEST(ABTTranslate, Union) {
- PrefixId prefixId;
- std::string scanDefA = "collA";
- std::string scanDefB = "collB";
-
- Metadata metadata{{{scanDefA, {}}, {scanDefB, {}}}};
- ABT translated = translatePipeline(metadata,
- "[{$unionWith: 'collB'}, {$match: {_id: 1}}]",
- prefixId.getNextId("scan"),
- scanDefA,
- prefixId,
- {{NamespaceString("a." + scanDefB), {}}});
-
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | scan_0\n"
- "| RefBlock: \n"
- "| Variable [scan_0]\n"
- "Filter []\n"
- "| EvalFilter []\n"
- "| | Variable [scan_0]\n"
- "| PathGet [_id]\n"
- "| PathTraverse [1]\n"
- "| PathCompare [Eq]\n"
- "| Const [1]\n"
- "Union []\n"
- "| | BindBlock:\n"
- "| | [scan_0]\n"
- "| | Source []\n"
- "| Evaluation []\n"
- "| | BindBlock:\n"
- "| | [scan_0]\n"
- "| | EvalPath []\n"
- "| | | Variable [scan_1]\n"
- "| | PathIdentity []\n"
- "| Scan [collB]\n"
- "| BindBlock:\n"
- "| [scan_1]\n"
- "| Source []\n"
- "Scan [collA]\n"
- " BindBlock:\n"
- " [scan_0]\n"
- " Source []\n",
- translated);
-
- OptPhaseManager phaseManager(
- {OptPhase::MemoSubstitutionPhase,
- OptPhase::MemoExplorationPhase,
- OptPhase::MemoImplementationPhase},
- prefixId,
- {{{scanDefA, ScanDefinition{{}, {}}}, {scanDefB, ScanDefinition{{}, {}}}}},
- DebugInfo::kDefaultForTests);
-
- ABT optimized = translated;
- phaseManager.optimize(optimized);
-
- // Note that the optimized ABT will show the filter push-down.
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | scan_0\n"
- "| RefBlock: \n"
- "| Variable [scan_0]\n"
- "Union []\n"
- "| | BindBlock:\n"
- "| | [scan_0]\n"
- "| | Source []\n"
- "| Filter []\n"
- "| | EvalFilter []\n"
- "| | | Variable [scan_0]\n"
- "| | PathGet [_id]\n"
- "| | PathTraverse [1]\n"
- "| | PathCompare [Eq]\n"
- "| | Const [1]\n"
- "| Evaluation []\n"
- "| | BindBlock:\n"
- "| | [scan_0]\n"
- "| | EvalPath []\n"
- "| | | Variable [scan_1]\n"
- "| | PathIdentity []\n"
- "| PhysicalScan [{'<root>': scan_1}, collB]\n"
- "| BindBlock:\n"
- "| [scan_1]\n"
- "| Source []\n"
- "Filter []\n"
- "| EvalFilter []\n"
- "| | Variable [evalTemp_0]\n"
- "| PathTraverse [1]\n"
- "| PathCompare [Eq]\n"
- "| Const [1]\n"
- "PhysicalScan [{'<root>': scan_0, '_id': evalTemp_0}, collA]\n"
- " BindBlock:\n"
- " [evalTemp_0]\n"
- " Source []\n"
- " [scan_0]\n"
- " Source []\n",
- optimized);
-}
-
-TEST(ABTTranslate, PartialIndex) {
- PrefixId prefixId;
- std::string scanDefName = "collection";
- ProjectionName scanProjName = prefixId.getNextId("scan");
-
- // The expression matches the pipeline.
- // By default the constant is translated as "int32".
- auto conversionResult = convertExprToPartialSchemaReq(
- make<EvalFilter>(
- make<PathGet>("b",
- make<PathTraverse>(make<PathCompare>(Operations::Eq, Constant::int32(2)),
- PathTraverse::kSingleLevel)),
- make<Variable>(scanProjName)),
- true /*isFilterContext*/,
- {} /*pathToInterval*/);
- ASSERT_TRUE(conversionResult.has_value());
- ASSERT_FALSE(conversionResult->_retainPredicate);
-
- Metadata metadata = {
- {{scanDefName,
- ScanDefinition{{},
- {{"index1",
- IndexDefinition{{{makeIndexPath("a"), CollationOp::Ascending}},
- true /*multiKey*/,
- {DistributionType::Centralized},
- std::move(conversionResult->_reqMap)}}}}}}};
-
- ABT translated = translatePipeline(
- metadata, "[{$match: {'a': 3, 'b': 2}}]", scanProjName, scanDefName, prefixId);
-
- OptPhaseManager phaseManager(
- OptPhaseManager::getAllRewritesSet(), prefixId, metadata, DebugInfo::kDefaultForTests);
-
- ABT optimized = translated;
- phaseManager.optimize(optimized);
-
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | scan_0\n"
- "| RefBlock: \n"
- "| Variable [scan_0]\n"
- "BinaryJoin [joinType: Inner, {rid_0}]\n"
- "| | Const [true]\n"
- "| Filter []\n"
- "| | FunctionCall [traverseF]\n"
- "| | | | Const [false]\n"
- "| | | LambdaAbstraction [valCmp_0]\n"
- "| | | BinaryOp [Eq]\n"
- "| | | | Const [2]\n"
- "| | | Variable [valCmp_0]\n"
- "| | Variable [evalTemp_4]\n"
- "| LimitSkip []\n"
- "| | limitSkip:\n"
- "| | limit: 1\n"
- "| | skip: 0\n"
- "| Seek [ridProjection: rid_0, {'<root>': scan_0, 'b': evalTemp_4}, collection]\n"
- "| | BindBlock:\n"
- "| | [evalTemp_4]\n"
- "| | Source []\n"
- "| | [scan_0]\n"
- "| | Source []\n"
- "| RefBlock: \n"
- "| Variable [rid_0]\n"
- "IndexScan [{'<rid>': rid_0}, scanDefName: collection, indexDefName: index1, interval: "
- "{[Const [3], Const [3]]}]\n"
- " BindBlock:\n"
- " [rid_0]\n"
- " Source []\n",
- optimized);
-}
-
-TEST(ABTTranslate, PartialIndexNegative) {
- PrefixId prefixId;
- std::string scanDefName = "collection";
- ProjectionName scanProjName = prefixId.getNextId("scan");
-
- // The expression does not match the pipeline.
- auto conversionResult = convertExprToPartialSchemaReq(
- make<EvalFilter>(
- make<PathGet>("b",
- make<PathTraverse>(make<PathCompare>(Operations::Eq, Constant::int32(2)),
- PathTraverse::kSingleLevel)),
- make<Variable>(scanProjName)),
- true /*isFilterContext*/,
- {} /*pathToInterval*/);
- ASSERT_TRUE(conversionResult.has_value());
- ASSERT_FALSE(conversionResult->_retainPredicate);
-
- Metadata metadata = {
- {{scanDefName,
- ScanDefinition{{},
- {{"index1",
- IndexDefinition{{{makeIndexPath("a"), CollationOp::Ascending}},
- true /*multiKey*/,
- {DistributionType::Centralized},
- std::move(conversionResult->_reqMap)}}}}}}};
-
- ABT translated = translatePipeline(
- metadata, "[{$match: {'a': 3, 'b': 3}}]", scanProjName, scanDefName, prefixId);
-
- OptPhaseManager phaseManager(
- OptPhaseManager::getAllRewritesSet(), prefixId, metadata, DebugInfo::kDefaultForTests);
-
- ABT optimized = translated;
- phaseManager.optimize(optimized);
-
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | scan_0\n"
- "| RefBlock: \n"
- "| Variable [scan_0]\n"
- "Filter []\n"
- "| FunctionCall [traverseF]\n"
- "| | | Const [false]\n"
- "| | LambdaAbstraction [valCmp_1]\n"
- "| | BinaryOp [Eq]\n"
- "| | | Const [3]\n"
- "| | Variable [valCmp_1]\n"
- "| Variable [evalTemp_3]\n"
- "Filter []\n"
- "| FunctionCall [traverseF]\n"
- "| | | Const [false]\n"
- "| | LambdaAbstraction [valCmp_0]\n"
- "| | BinaryOp [Eq]\n"
- "| | | Const [3]\n"
- "| | Variable [valCmp_0]\n"
- "| Variable [evalTemp_2]\n"
- "PhysicalScan [{'<root>': scan_0, 'a': evalTemp_2, 'b': evalTemp_3}, collection]\n"
- " BindBlock:\n"
- " [evalTemp_2]\n"
- " Source []\n"
- " [evalTemp_3]\n"
- " Source []\n"
- " [scan_0]\n"
- " Source []\n",
- optimized);
-}
-
-TEST(ABTTranslate, CommonExpressionElimination) {
- PrefixId prefixId;
- Metadata metadata = {{{"test", {{}, {}}}}};
-
- auto rootNode =
- translatePipeline(metadata,
- "[{$project: {foo: {$add: ['$b', 1]}, bar: {$add: ['$b', 1]}}}]",
- "test",
- prefixId);
-
- OptPhaseManager phaseManager(
- {OptPhase::ConstEvalPre}, prefixId, metadata, DebugInfo::kDefaultForTests);
-
- phaseManager.optimize(rootNode);
-
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | combinedProjection_0\n"
- "| RefBlock: \n"
- "| Variable [combinedProjection_0]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [combinedProjection_0]\n"
- "| EvalPath []\n"
- "| | Variable [scan_0]\n"
- "| PathComposeM []\n"
- "| | PathDefault []\n"
- "| | Const [{}]\n"
- "| PathComposeM []\n"
- "| | PathField [foo]\n"
- "| | PathConstant []\n"
- "| | Variable [projGetPath_0]\n"
- "| PathComposeM []\n"
- "| | PathField [bar]\n"
- "| | PathConstant []\n"
- "| | Variable [projGetPath_0]\n"
- "| PathKeep [_id, bar, foo]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [projGetPath_0]\n"
- "| BinaryOp [Add]\n"
- "| | Const [1]\n"
- "| EvalPath []\n"
- "| | Variable [scan_0]\n"
- "| PathGet [b]\n"
- "| PathIdentity []\n"
- "Scan [test]\n"
- " BindBlock:\n"
- " [scan_0]\n"
- " Source []\n",
- rootNode);
-}
-
-TEST(ABTTranslate, GroupByDependency) {
- PrefixId prefixId;
- Metadata metadata = {{{"test", {{}, {}}}}};
-
- ABT translated =
- translatePipeline(metadata,
- "[{$group: {_id: {}, b: {$addToSet: '$a'}}}, {$project: "
- "{_id: 0, b: {$size: '$b'}}}, {$project: {_id: 0, c: '$b'}}]",
- "test",
- prefixId);
-
- OptPhaseManager phaseManager({OptPhase::ConstEvalPre,
- OptPhase::PathFuse,
- OptPhase::MemoSubstitutionPhase,
- OptPhase::MemoExplorationPhase,
- OptPhase::MemoImplementationPhase},
- prefixId,
- metadata,
- DebugInfo::kDefaultForTests);
-
- ABT optimized = translated;
- phaseManager.optimize(optimized);
-
- // Demonstrate that "c" is set to the array size (not the array itself coming from the group).
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | combinedProjection_1\n"
- "| RefBlock: \n"
- "| Variable [combinedProjection_1]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [combinedProjection_1]\n"
- "| EvalPath []\n"
- "| | Const [{}]\n"
- "| PathComposeM []\n"
- "| | PathField [c]\n"
- "| | PathConstant []\n"
- "| | FunctionCall [getArraySize]\n"
- "| | Variable [b_agg_0]\n"
- "| PathKeep []\n"
- "GroupBy []\n"
- "| | groupings: \n"
- "| | RefBlock: \n"
- "| | Variable [groupByProj_0]\n"
- "| aggregations: \n"
- "| [b_agg_0]\n"
- "| FunctionCall [$addToSet]\n"
- "| Variable [groupByInputProj_0]\n"
- "Evaluation []\n"
- "| BindBlock:\n"
- "| [groupByProj_0]\n"
- "| Const [{}]\n"
- "PhysicalScan [{'a': groupByInputProj_0}, test]\n"
- " BindBlock:\n"
- " [groupByInputProj_0]\n"
- " Source []\n",
- optimized);
-}
-
-TEST(ABTTranslate, NotEquals) {
- PrefixId prefixId;
- Metadata metadata = {{{"test", {{}, {}}}}};
-
- ABT translated = translatePipeline(metadata, "[{$match: {'a': {$ne: 2}}}]", "test", prefixId);
-
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | scan_0\n"
- "| RefBlock: \n"
- "| Variable [scan_0]\n"
- "Filter []\n"
- "| EvalFilter []\n"
- "| | Variable [scan_0]\n"
- "| PathConstant []\n"
- "| UnaryOp [Not]\n"
- "| EvalFilter []\n"
- "| | Variable [scan_0]\n"
- "| PathGet [a]\n"
- "| PathTraverse [1]\n"
- "| PathCompare [Eq]\n"
- "| Const [2]\n"
- "Scan [test]\n"
- " BindBlock:\n"
- " [scan_0]\n"
- " Source []\n",
- translated);
-}
-
-TEST(ABTTranslate, DoubleElemMatch) {
- PrefixId prefixId;
- Metadata metadata = {{{"test", {{}, {}}}}};
-
- ABT translated = translatePipeline(
- metadata,
- "[{$match: {a: {$elemMatch: {$gte: 5, $lte: 6}}, b: {$elemMatch: {$gte: 1, $lte: 3}}}}]",
- "test",
- prefixId);
-
- OptPhaseManager phaseManager({OptPhase::MemoSubstitutionPhase},
- prefixId,
- false /*requireRID*/,
- metadata,
- std::make_unique<HeuristicCE>(),
- std::make_unique<DefaultCosting>(),
- defaultConvertPathToInterval,
- DebugInfo::kDefaultForTests);
-
- ABT optimized = translated;
- phaseManager.optimize(optimized);
-
- // Demonstrate we get a single Sargable node which encodes both predicates.
- ASSERT_EXPLAIN_V2(
- "Root []\n"
- "| | projections: \n"
- "| | scan_0\n"
- "| RefBlock: \n"
- "| Variable [scan_0]\n"
- "Sargable [Complete]\n"
- "| | | | | requirementsMap: \n"
- "| | | | | refProjection: scan_0, path: 'PathGet [a] PathIdentity []', "
- "intervals: {{{[Const [[]], Const [BinData(0, )])}}}\n"
- "| | | | | refProjection: scan_0, path: 'PathGet [a] PathTraverse [1] "
- "PathIdentity []', intervals: {{{[Const [5], Const [6]]}}}\n"
- "| | | | | refProjection: scan_0, path: 'PathGet [b] PathIdentity []', "
- "intervals: {{{[Const [[]], Const [BinData(0, )])}}}\n"
- "| | | | | refProjection: scan_0, path: 'PathGet [b] PathTraverse [1] "
- "PathIdentity []', intervals: {{{[Const [1], Const [3]]}}}\n"
- "| | | | candidateIndexes: \n"
- "| | | scanParams: \n"
- "| | | {'a': evalTemp_7, 'b': evalTemp_8}\n"
- "| | | residualReqs: \n"
- "| | | refProjection: evalTemp_7, path: 'PathIdentity []', intervals: "
- "{{{[Const [[]], Const [BinData(0, )])}}}, entryIndex: 0\n"
- "| | | refProjection: evalTemp_7, path: 'PathTraverse [1] PathIdentity "
- "[]', intervals: {{{[Const [5], Const [6]]}}}, entryIndex: 1\n"
- "| | | refProjection: evalTemp_8, path: 'PathIdentity []', intervals: "
- "{{{[Const [[]], Const [BinData(0, )])}}}, entryIndex: 2\n"
- "| | | refProjection: evalTemp_8, path: 'PathTraverse [1] PathIdentity "
- "[]', intervals: {{{[Const [1], Const [3]]}}}, entryIndex: 3\n"
- "| | BindBlock:\n"
- "| RefBlock: \n"
- "| Variable [scan_0]\n"
- "Scan [test]\n"
- " BindBlock:\n"
- " [scan_0]\n"
- " Source []\n",
- optimized);
-}
-
-} // namespace
-} // namespace mongo
diff --git a/src/mongo/db/pipeline/abt/abt_translation_test.cpp b/src/mongo/db/pipeline/abt/abt_translation_test.cpp
new file mode 100644
index 00000000000..c64479e1354
--- /dev/null
+++ b/src/mongo/db/pipeline/abt/abt_translation_test.cpp
@@ -0,0 +1,174 @@
+/**
+ * Copyright (C) 2022-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the Server Side Public License in all respects for
+ * all of the code used other than as permitted herein. If you modify file(s)
+ * with this exception, you may extend this exception to your version of the
+ * file(s), but you are not obligated to do so. If you do not wish to do so,
+ * delete this exception statement from your version. If you delete this
+ * exception statement from all source files in the program, then also delete
+ * it in the license file.
+ */
+
+#include "mongo/db/pipeline/abt/utils.h"
+#include "mongo/db/query/optimizer/explain.h"
+#include "mongo/db/query/optimizer/utils/unit_test_utils.h"
+#include "mongo/unittest/golden_test.h"
+
+namespace mongo::optimizer {
+
+unittest::GoldenTestConfig goldenTestConfigABTTranslation{"src/mongo/db/test_output/pipeline/abt"};
+
+TEST(ABTTranslate, BasicTests) {
+ unittest::GoldenTestContext gctx(&goldenTestConfigABTTranslation);
+
+ testABTTranslationAndOptimization(
+ gctx, "$match with $in, empty list", "[{$match: {a: {$in: []}}}]");
+
+ testABTTranslationAndOptimization(
+ gctx, "match with $in, singleton list", "[{$match: {a: {$in: [1]}}}]");
+
+ testABTTranslationAndOptimization(
+ gctx,
+ "$match with $in and a list of equalities becomes a comparison to an EqMember list.",
+ "[{$match: {a: {$in: [1, 2, 3]}}}]");
+
+ testABTTranslationAndOptimization(gctx,
+ "match with $in over an array, duplicated equalities removed",
+ "[{$match: {a: {$in: ['abc', 'def', 'ghi', 'def']}}}]");
+
+
+ testABTTranslationAndOptimization(
+ gctx, "$match with empty $elemMatch", "[{$match: {'a': {$elemMatch: {}}}}]");
+
+ testABTTranslationAndOptimization(
+ gctx,
+ "ensure the PathGet and PathTraverse operators interact correctly when $in is "
+ "under $elemMatch",
+ "[{$match: {'a.b': {$elemMatch: {$in: [1, 2, 3]}}}}]");
+
+ testABTTranslationAndOptimization(
+ gctx, "sort limit skip", "[{$limit: 5}, {$skip: 3}, {$sort: {a: 1, b: -1}}]");
+
+ testABTTranslationAndOptimization(
+ gctx, "inclusion project", "[{$project: {a1: 1, a2: 1, a3: 1, a4: 1, a5: 1, a6: 1}}]");
+
+ testABTTranslationAndOptimization(
+ gctx,
+ "project rename through addFields: since '$z' is a single element, it will be "
+ "considered a renamed path",
+ "[{$addFields: {a: '$z'}}]");
+
+ testABTTranslationAndOptimization(
+ gctx,
+ "project rename: since '$c' is a single element, it will be considered a renamed path",
+ "[{$project: {'a.b': '$c'}}]");
+
+ testABTTranslationAndOptimization(
+ gctx, "project rename dotted paths", "[{$project: {'a.b.c': '$x.y.z'}}]");
+
+ testABTTranslationAndOptimization(
+ gctx, "inclusion project dotted paths", "[{$project: {'a.b':1, 'a.c':1, 'b':1}}]");
+
+ testABTTranslationAndOptimization(gctx,
+ "inclusion project with computed field",
+ "[{$project: {a: {$add: ['$c.d', 2]}, b: 1}}]");
+
+ testABTTranslationAndOptimization(gctx, "exclusion project", "[{$project: {a: 0, b: 0}}]");
+
+ testABTTranslationAndOptimization(gctx, "replaceRoot", "[{$replaceRoot: {newRoot: '$a'}}]");
+
+ testABTTranslationAndOptimization(gctx, "$match basic", "[{$match: {a: 1, b: 2}}]");
+
+ testABTTranslationAndOptimization(
+ gctx, "$match with $expr $eq", "[{$match: {$expr: {$eq: ['$a', 1]}}}]");
+
+ testABTTranslationAndOptimization(
+ gctx, "$match with $expr $eq with dotted path", "[{$match: {$expr: {$eq: ['$a.b', 1]}}}]");
+
+ testABTTranslationAndOptimization(
+ gctx,
+ "$match with value $elemMatch: observe type bracketing in the filter.",
+ "[{$project: {a: {$literal: [1, 2, 3, 4]}}}, {$match: {a: {$elemMatch: {$gte: 2, $lte: "
+ "3}}}}]");
+
+ testABTTranslationAndOptimization(
+ gctx,
+ "$project then $match",
+ "[{$project: {s: {$add: ['$a', '$b']}, c: 1}}, {$match: {$or: [{c: 2}, {s: {$gte: "
+ "10}}]}}]");
+
+ testABTTranslationAndOptimization(
+ gctx, "$project with deeply nested path", "[{$project: {'a1.b.c':1, 'a.b.c.d.e':'str'}}]");
+
+ testABTTranslationAndOptimization(
+ gctx, "basic $group", "[{$group: {_id: '$a.b', s: {$sum: {$multiply: ['$b', '$c']}}}}]");
+
+ testABTTranslationAndOptimization(
+ gctx, "$group local global", "[{$group: {_id: '$a', c: {$sum: '$b'}}}]");
+
+ testABTTranslationAndOptimization(gctx, "basic $unwind", "[{$unwind: {path: '$a.b.c'}}]");
+
+ testABTTranslationAndOptimization(
+ gctx,
+ "complex $unwind",
+ "[{$unwind: {path: '$a.b.c', includeArrayIndex: 'p1.pid', preserveNullAndEmptyArrays: "
+ "true}}]");
+
+ testABTTranslationAndOptimization(
+ gctx,
+ "$unwind with $group",
+ "[{$unwind:{path: '$a.b', preserveNullAndEmptyArrays: true}}, {$group:{_id: '$a.b'}}]");
+
+ testABTTranslationAndOptimization(gctx, "$match with index", "[{$match: {'a': 10}}]");
+
+ testABTTranslationAndOptimization(
+ gctx, "$match sort index", "[{$match: {'a': 10}}, {$sort: {'a': 1}}]");
+
+ testABTTranslationAndOptimization(
+ gctx, "$match with range", "[{$match: {'a': {$gt: 70, $lt: 90}}}]");
+
+ testABTTranslationAndOptimization(gctx, "index on two keys", "[{$match: {'a': 2, 'b': 2}}]");
+
+ testABTTranslationAndOptimization(
+ gctx,
+ "$group with complex _id",
+ "[{$group: {_id: {'isin': '$isin', 'year': '$year'}, 'count': {$sum: 1}, 'open': {$first: "
+ "'$$ROOT'}}}]");
+
+ testABTTranslationAndOptimization(
+ gctx, "$project with computed array", "[{$project: {a: ['$b', '$c']}}]");
+
+ std::string scanDefA = "collA";
+ std::string scanDefB = "collB";
+ Metadata metadataUnion{{{scanDefA, {}}, {scanDefB, {}}}};
+ testABTTranslationAndOptimization(gctx,
+ "union",
+ "[{$unionWith: 'collB'}, {$match: {_id: 1}}]",
+ scanDefA,
+ {},
+ metadataUnion,
+ {},
+ false,
+ {{NamespaceString("a." + scanDefB), {}}});
+
+ testABTTranslationAndOptimization(gctx, "$match with $ne", "[{$match: {'a': {$ne: 2}}}]");
+}
+} // namespace mongo::optimizer
diff --git a/src/mongo/db/query/optimizer/utils/unit_test_utils.cpp b/src/mongo/db/query/optimizer/utils/unit_test_utils.cpp
index 1bd82ce80f5..c060535acf6 100644
--- a/src/mongo/db/query/optimizer/utils/unit_test_utils.cpp
+++ b/src/mongo/db/query/optimizer/utils/unit_test_utils.cpp
@@ -156,4 +156,177 @@ ABT translatePipeline(const std::string& pipelineStr, std::string scanDefName) {
return translatePipeline(metadata, pipelineStr, std::move(scanDefName), prefixId);
}
+void serializeOptPhases(std::ostream& stream, opt::unordered_set<OptPhase> phaseSet) {
+ // The order of phases in the golden file must be the same every time the test is run.
+ std::set<std::string> orderedPhases;
+ for (const auto& phase : phaseSet) {
+ orderedPhases.insert(OptPhaseEnum::toString[static_cast<int>(phase)]);
+ }
+
+ stream << "optimization phases: " << std::endl;
+ for (const auto& phase : orderedPhases) {
+ stream << "\t" << phase << std::endl;
+ }
+}
+
+void explainPreserveIndentation(std::ostream& stream, std::string baseTabs, std::string explain) {
+ std::string currLine = "";
+ for (char ch : explain) {
+ if (ch == '\n') {
+ stream << baseTabs << currLine << std::endl;
+ currLine = "";
+ } else {
+ currLine += ch;
+ }
+ }
+ stream << std::endl;
+}
+
+void serializeDistributionAndPaths(std::ostream& stream,
+ DistributionAndPaths distributionAndPaths,
+ std::string baseTabs) {
+ stream << baseTabs << "distribution and paths: " << std::endl;
+ stream << baseTabs << "\tdistribution type: "
+ << DistributionTypeEnum::toString[static_cast<int>(distributionAndPaths._type)]
+ << std::endl;
+ stream << baseTabs << "\tdistribution paths: " << std::endl;
+ for (const ABT& abt : distributionAndPaths._paths) {
+ explainPreserveIndentation(stream, baseTabs + "\t\t", ExplainGenerator::explainV2(abt));
+ }
+}
+
+void serializeMetadata(std::ostream& stream, Metadata metadata) {
+ stream << "metadata: " << std::endl;
+
+ stream << "\tnumber of partitions: " << metadata._numberOfPartitions << std::endl;
+
+ // The ScanDefinitions are stored in an unordered map, and the order of the ScanDefinitions in
+ // the golden file must be the same every time the test is run.
+ std::map<std::string, ScanDefinition> orderedScanDefs;
+ for (auto element : metadata._scanDefs) {
+ orderedScanDefs.insert(element);
+ }
+
+ stream << "\tscan definitions: " << std::endl;
+ for (const auto& element : orderedScanDefs) {
+ stream << "\t\t" << element.first << ": " << std::endl;
+
+ ScanDefinition scanDef = element.second;
+
+ stream << "\t\t\toptions: " << std::endl;
+ for (const auto& optionElem : scanDef.getOptionsMap()) {
+ stream << "\t\t\t\t" << optionElem.first << ": " << optionElem.second << std::endl;
+ }
+
+ serializeDistributionAndPaths(stream, scanDef.getDistributionAndPaths(), "\t\t\t");
+
+ stream << "\t\t\tindexes: " << std::endl;
+ for (const auto& indexElem : scanDef.getIndexDefs()) {
+ stream << "\t\t\t\t" << indexElem.first << ": " << std::endl;
+
+ IndexDefinition indexDef = indexElem.second;
+
+ stream << "\t\t\t\t\tcollation spec: " << std::endl;
+ for (const auto& indexCollationEntry : indexDef.getCollationSpec()) {
+ stream << "\t\t\t\t\t\tABT path: " << std::endl;
+ explainPreserveIndentation(stream,
+ "\t\t\t\t\t\t\t",
+ ExplainGenerator::explainV2(indexCollationEntry._path));
+
+ stream << "\t\t\t\t\t\tcollation op: "
+ << CollationOpEnum::toString[static_cast<int>(indexCollationEntry._op)]
+ << std::endl;
+ }
+
+ stream << "\t\t\t\t\tversion: " << indexDef.getVersion() << std::endl;
+ stream << "\t\t\t\t\tordering bits: " << indexDef.getOrdering() << std::endl;
+ stream << "\t\t\t\t\tis multi-key: " << indexDef.isMultiKey() << std::endl;
+
+ serializeDistributionAndPaths(stream, indexDef.getDistributionAndPaths(), "\t\t\t\t\t");
+
+ std::string serializedReqMap =
+ ExplainGenerator::explainPartialSchemaReqMap(indexDef.getPartialReqMap());
+ explainPreserveIndentation(stream, "\t\t\t\t\t", serializedReqMap);
+ }
+
+ stream << "\t\t\tnon multi-key index paths: " << std::endl;
+ for (const auto& indexPath : scanDef.getNonMultiKeyPathSet()) {
+ explainPreserveIndentation(stream, "\t\t\t\t", ExplainGenerator::explainV2(indexPath));
+ }
+
+ stream << "\t\t\tcollection exists: " << scanDef.exists() << std::endl;
+ stream << "\t\t\tCE type: " << scanDef.getCE() << std::endl;
+ }
+}
+
+ABT translatetoABT(const std::string& pipelineStr,
+ std::string scanDefName,
+ Metadata metadata,
+ const std::vector<ExpressionContext::ResolvedNamespace>& involvedNss) {
+ PrefixId prefixId;
+ return translatePipeline(
+ metadata, pipelineStr, prefixId.getNextId("scan"), scanDefName, prefixId, involvedNss);
+}
+
+ABT optimizeABT(ABT abt,
+ opt::unordered_set<OptPhase> phaseSet,
+ Metadata metadata,
+ PathToIntervalFn pathToInterval,
+ bool phaseManagerDisableScan) {
+ PrefixId prefixId;
+
+ OptPhaseManager phaseManager(phaseSet,
+ prefixId,
+ false,
+ metadata,
+ std::make_unique<HeuristicCE>(),
+ std::make_unique<DefaultCosting>(),
+ pathToInterval,
+ DebugInfo::kDefaultForTests);
+ if (phaseManagerDisableScan) {
+ phaseManager.getHints()._disableScan = true;
+ }
+
+ ABT optimized = abt;
+ phaseManager.optimize(optimized);
+ return optimized;
+}
+
+void testABTTranslationAndOptimization(
+ unittest::GoldenTestContext& gctx,
+ const std::string& variationName,
+ const std::string& pipelineStr,
+ std::string scanDefName,
+ opt::unordered_set<OptPhase> phaseSet,
+ Metadata metadata,
+ PathToIntervalFn pathToInterval,
+ bool phaseManagerDisableScan,
+ const std::vector<ExpressionContext::ResolvedNamespace>& involvedNss) {
+ auto& stream = gctx.outStream();
+ bool optimizePipeline = !phaseSet.empty();
+
+ stream << "==== VARIATION: " << variationName << " ====" << std::endl;
+ stream << "-- INPUTS:" << std::endl;
+ stream << "pipeline: " << pipelineStr << std::endl;
+
+ serializeMetadata(stream, metadata);
+ if (optimizePipeline) {
+ serializeOptPhases(stream, phaseSet);
+ }
+
+ stream << std::endl << "-- OUTPUT:" << std::endl;
+
+ ABT translated = translatetoABT(pipelineStr, scanDefName, metadata, involvedNss);
+
+ if (optimizePipeline) {
+ ABT optimized =
+ optimizeABT(translated, phaseSet, metadata, pathToInterval, phaseManagerDisableScan);
+ stream << ExplainGenerator::explainV2(optimized) << std::endl;
+ } else {
+ stream << ExplainGenerator::explainV2(translated) << std::endl;
+ }
+
+ stream << std::endl;
+}
+
} // namespace mongo::optimizer
diff --git a/src/mongo/db/query/optimizer/utils/unit_test_utils.h b/src/mongo/db/query/optimizer/utils/unit_test_utils.h
index f25df1a11b0..1183c3e7859 100644
--- a/src/mongo/db/query/optimizer/utils/unit_test_utils.h
+++ b/src/mongo/db/query/optimizer/utils/unit_test_utils.h
@@ -33,9 +33,12 @@
#include "mongo/db/operation_context_noop.h"
#include "mongo/db/pipeline/expression_context_for_test.h"
#include "mongo/db/pipeline/pipeline.h"
+#include "mongo/db/query/optimizer/cascades/cost_derivation.h"
#include "mongo/db/query/optimizer/defs.h"
#include "mongo/db/query/optimizer/metadata.h"
+#include "mongo/db/query/optimizer/opt_phase_manager.h"
#include "mongo/db/query/optimizer/utils/utils.h"
+#include "mongo/unittest/golden_test.h"
namespace mongo::optimizer {
@@ -107,4 +110,20 @@ ABT translatePipeline(Metadata& metadata,
ABT translatePipeline(const std::string& pipelineStr, std::string scanDefName = "collection");
+/**
+ * This function translates the given pipeline string to an ABT and (if optimization phases are
+ * provided) optimizes the ABT using the parameters specified. It then writes the output to a file
+ * that will be compared to the golden testing file for the test file.
+ **/
+void testABTTranslationAndOptimization(
+ unittest::GoldenTestContext& gctx,
+ const std::string& variationName,
+ const std::string& pipelineStr,
+ std::string scanDefName = "collection",
+ opt::unordered_set<OptPhase> phaseSet = {},
+ Metadata metadata = {{{"collection", ScanDefinition{{}, {}}}}},
+ PathToIntervalFn pathToInterval = {},
+ bool phaseManagerDisableScan = false,
+ const std::vector<ExpressionContext::ResolvedNamespace>& involvedNss = {});
+
} // namespace mongo::optimizer
diff --git a/src/mongo/db/test_output/pipeline/abt/a_b_t_translate/basic_tests.txt b/src/mongo/db/test_output/pipeline/abt/a_b_t_translate/basic_tests.txt
new file mode 100644
index 00000000000..b13052e048a
--- /dev/null
+++ b/src/mongo/db/test_output/pipeline/abt/a_b_t_translate/basic_tests.txt
@@ -0,0 +1,1686 @@
+==== VARIATION: $match with $in, empty list ====
+-- INPUTS:
+pipeline: [{$match: {a: {$in: []}}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+
+-- OUTPUT:
+Root []
+| | projections:
+| | scan_0
+| RefBlock:
+| Variable [scan_0]
+Filter []
+| EvalFilter []
+| | Variable [scan_0]
+| PathConstant []
+| Const [false]
+Scan [collection]
+ BindBlock:
+ [scan_0]
+ Source []
+
+
+==== VARIATION: match with $in, singleton list ====
+-- INPUTS:
+pipeline: [{$match: {a: {$in: [1]}}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+
+-- OUTPUT:
+Root []
+| | projections:
+| | scan_0
+| RefBlock:
+| Variable [scan_0]
+Filter []
+| EvalFilter []
+| | Variable [scan_0]
+| PathGet [a]
+| PathTraverse [1]
+| PathCompare [Eq]
+| Const [1]
+Scan [collection]
+ BindBlock:
+ [scan_0]
+ Source []
+
+
+==== VARIATION: $match with $in and a list of equalities becomes a comparison to an EqMember list. ====
+-- INPUTS:
+pipeline: [{$match: {a: {$in: [1, 2, 3]}}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+
+-- OUTPUT:
+Root []
+| | projections:
+| | scan_0
+| RefBlock:
+| Variable [scan_0]
+Filter []
+| EvalFilter []
+| | Variable [scan_0]
+| PathGet [a]
+| PathTraverse [1]
+| PathCompare [EqMember]
+| Const [[1, 2, 3]]
+Scan [collection]
+ BindBlock:
+ [scan_0]
+ Source []
+
+
+==== VARIATION: match with $in over an array, duplicated equalities removed ====
+-- INPUTS:
+pipeline: [{$match: {a: {$in: ['abc', 'def', 'ghi', 'def']}}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+
+-- OUTPUT:
+Root []
+| | projections:
+| | scan_0
+| RefBlock:
+| Variable [scan_0]
+Filter []
+| EvalFilter []
+| | Variable [scan_0]
+| PathGet [a]
+| PathTraverse [1]
+| PathCompare [EqMember]
+| Const [["abc", "def", "ghi"]]
+Scan [collection]
+ BindBlock:
+ [scan_0]
+ Source []
+
+
+==== VARIATION: $match with empty $elemMatch ====
+-- INPUTS:
+pipeline: [{$match: {'a': {$elemMatch: {}}}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+
+-- OUTPUT:
+Root []
+| | projections:
+| | scan_0
+| RefBlock:
+| Variable [scan_0]
+Filter []
+| EvalFilter []
+| | Variable [scan_0]
+| PathGet [a]
+| PathComposeM []
+| | PathArr []
+| PathTraverse [1]
+| PathComposeM []
+| | PathComposeA []
+| | | PathArr []
+| | PathObj []
+| PathConstant []
+| Const [true]
+Scan [collection]
+ BindBlock:
+ [scan_0]
+ Source []
+
+
+==== VARIATION: ensure the PathGet and PathTraverse operators interact correctly when $in is under $elemMatch ====
+-- INPUTS:
+pipeline: [{$match: {'a.b': {$elemMatch: {$in: [1, 2, 3]}}}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+
+-- OUTPUT:
+Root []
+| | projections:
+| | scan_0
+| RefBlock:
+| Variable [scan_0]
+Filter []
+| EvalFilter []
+| | Variable [scan_0]
+| PathGet [a]
+| PathTraverse [1]
+| PathGet [b]
+| PathComposeM []
+| | PathArr []
+| PathTraverse [1]
+| PathCompare [EqMember]
+| Const [[1, 2, 3]]
+Scan [collection]
+ BindBlock:
+ [scan_0]
+ Source []
+
+
+==== VARIATION: sort limit skip ====
+-- INPUTS:
+pipeline: [{$limit: 5}, {$skip: 3}, {$sort: {a: 1, b: -1}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+
+-- OUTPUT:
+Root []
+| | projections:
+| | scan_0
+| RefBlock:
+| Variable [scan_0]
+Collation []
+| | collation:
+| | sort_0: Ascending
+| | sort_1: Descending
+| RefBlock:
+| Variable [sort_0]
+| Variable [sort_1]
+Evaluation []
+| BindBlock:
+| [sort_1]
+| EvalPath []
+| | Variable [scan_0]
+| PathGet [b]
+| PathIdentity []
+Evaluation []
+| BindBlock:
+| [sort_0]
+| EvalPath []
+| | Variable [scan_0]
+| PathGet [a]
+| PathIdentity []
+LimitSkip []
+| limitSkip:
+| limit: (none)
+| skip: 3
+LimitSkip []
+| limitSkip:
+| limit: 5
+| skip: 0
+Scan [collection]
+ BindBlock:
+ [scan_0]
+ Source []
+
+
+==== VARIATION: inclusion project ====
+-- INPUTS:
+pipeline: [{$project: {a1: 1, a2: 1, a3: 1, a4: 1, a5: 1, a6: 1}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+
+-- OUTPUT:
+Root []
+| | projections:
+| | combinedProjection_0
+| RefBlock:
+| Variable [combinedProjection_0]
+Evaluation []
+| BindBlock:
+| [combinedProjection_0]
+| EvalPath []
+| | Variable [scan_0]
+| PathKeep [_id, a1, a2, a3, a4, a5, a6]
+Scan [collection]
+ BindBlock:
+ [scan_0]
+ Source []
+
+
+==== VARIATION: project rename through addFields: since '$z' is a single element, it will be considered a renamed path ====
+-- INPUTS:
+pipeline: [{$addFields: {a: '$z'}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+
+-- OUTPUT:
+Root []
+| | projections:
+| | combinedProjection_0
+| RefBlock:
+| Variable [combinedProjection_0]
+Evaluation []
+| BindBlock:
+| [combinedProjection_0]
+| EvalPath []
+| | Variable [scan_0]
+| PathComposeM []
+| | PathDefault []
+| | Const [{}]
+| PathField [a]
+| PathConstant []
+| Variable [projRenamedPath_0]
+Evaluation []
+| BindBlock:
+| [projRenamedPath_0]
+| EvalPath []
+| | Variable [scan_0]
+| PathGet [z]
+| PathIdentity []
+Scan [collection]
+ BindBlock:
+ [scan_0]
+ Source []
+
+
+==== VARIATION: project rename: since '$c' is a single element, it will be considered a renamed path ====
+-- INPUTS:
+pipeline: [{$project: {'a.b': '$c'}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+
+-- OUTPUT:
+Root []
+| | projections:
+| | combinedProjection_0
+| RefBlock:
+| Variable [combinedProjection_0]
+Evaluation []
+| BindBlock:
+| [combinedProjection_0]
+| EvalPath []
+| | Variable [scan_0]
+| PathComposeM []
+| | PathField [a]
+| | PathTraverse [inf]
+| | PathComposeM []
+| | | PathDefault []
+| | | Const [{}]
+| | PathComposeM []
+| | | PathField [b]
+| | | PathConstant []
+| | | Variable [projRenamedPath_0]
+| | PathKeep [b]
+| PathKeep [_id, a]
+Evaluation []
+| BindBlock:
+| [projRenamedPath_0]
+| EvalPath []
+| | Variable [scan_0]
+| PathGet [c]
+| PathIdentity []
+Scan [collection]
+ BindBlock:
+ [scan_0]
+ Source []
+
+
+==== VARIATION: project rename dotted paths ====
+-- INPUTS:
+pipeline: [{$project: {'a.b.c': '$x.y.z'}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+
+-- OUTPUT:
+Root []
+| | projections:
+| | combinedProjection_0
+| RefBlock:
+| Variable [combinedProjection_0]
+Evaluation []
+| BindBlock:
+| [combinedProjection_0]
+| EvalPath []
+| | Variable [scan_0]
+| PathComposeM []
+| | PathField [a]
+| | PathTraverse [inf]
+| | PathComposeM []
+| | | PathField [b]
+| | | PathTraverse [inf]
+| | | PathComposeM []
+| | | | PathDefault []
+| | | | Const [{}]
+| | | PathComposeM []
+| | | | PathField [c]
+| | | | PathConstant []
+| | | | Variable [projGetPath_0]
+| | | PathKeep [c]
+| | PathKeep [b]
+| PathKeep [_id, a]
+Evaluation []
+| BindBlock:
+| [projGetPath_0]
+| EvalPath []
+| | Variable [scan_0]
+| PathGet [x]
+| PathTraverse [inf]
+| PathGet [y]
+| PathTraverse [inf]
+| PathGet [z]
+| PathIdentity []
+Scan [collection]
+ BindBlock:
+ [scan_0]
+ Source []
+
+
+==== VARIATION: inclusion project dotted paths ====
+-- INPUTS:
+pipeline: [{$project: {'a.b':1, 'a.c':1, 'b':1}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+
+-- OUTPUT:
+Root []
+| | projections:
+| | combinedProjection_0
+| RefBlock:
+| Variable [combinedProjection_0]
+Evaluation []
+| BindBlock:
+| [combinedProjection_0]
+| EvalPath []
+| | Variable [scan_0]
+| PathComposeM []
+| | PathField [a]
+| | PathTraverse [inf]
+| | PathComposeM []
+| | | PathKeep [b, c]
+| | PathObj []
+| PathKeep [_id, a, b]
+Scan [collection]
+ BindBlock:
+ [scan_0]
+ Source []
+
+
+==== VARIATION: inclusion project with computed field ====
+-- INPUTS:
+pipeline: [{$project: {a: {$add: ['$c.d', 2]}, b: 1}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+
+-- OUTPUT:
+Root []
+| | projections:
+| | combinedProjection_0
+| RefBlock:
+| Variable [combinedProjection_0]
+Evaluation []
+| BindBlock:
+| [combinedProjection_0]
+| EvalPath []
+| | Variable [scan_0]
+| PathComposeM []
+| | PathDefault []
+| | Const [{}]
+| PathComposeM []
+| | PathField [a]
+| | PathConstant []
+| | Variable [projGetPath_0]
+| PathKeep [_id, a, b]
+Evaluation []
+| BindBlock:
+| [projGetPath_0]
+| BinaryOp [Add]
+| | Const [2]
+| EvalPath []
+| | Variable [scan_0]
+| PathGet [c]
+| PathTraverse [inf]
+| PathGet [d]
+| PathIdentity []
+Scan [collection]
+ BindBlock:
+ [scan_0]
+ Source []
+
+
+==== VARIATION: exclusion project ====
+-- INPUTS:
+pipeline: [{$project: {a: 0, b: 0}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+
+-- OUTPUT:
+Root []
+| | projections:
+| | combinedProjection_0
+| RefBlock:
+| Variable [combinedProjection_0]
+Evaluation []
+| BindBlock:
+| [combinedProjection_0]
+| EvalPath []
+| | Variable [scan_0]
+| PathDrop [a, b]
+Scan [collection]
+ BindBlock:
+ [scan_0]
+ Source []
+
+
+==== VARIATION: replaceRoot ====
+-- INPUTS:
+pipeline: [{$replaceRoot: {newRoot: '$a'}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+
+-- OUTPUT:
+Root []
+| | projections:
+| | newRoot_0
+| RefBlock:
+| Variable [newRoot_0]
+Evaluation []
+| BindBlock:
+| [newRoot_0]
+| EvalPath []
+| | Variable [scan_0]
+| PathGet [a]
+| PathIdentity []
+Scan [collection]
+ BindBlock:
+ [scan_0]
+ Source []
+
+
+==== VARIATION: $match basic ====
+-- INPUTS:
+pipeline: [{$match: {a: 1, b: 2}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+
+-- OUTPUT:
+Root []
+| | projections:
+| | scan_0
+| RefBlock:
+| Variable [scan_0]
+Filter []
+| EvalFilter []
+| | Variable [scan_0]
+| PathGet [a]
+| PathTraverse [1]
+| PathCompare [Eq]
+| Const [1]
+Filter []
+| EvalFilter []
+| | Variable [scan_0]
+| PathGet [b]
+| PathTraverse [1]
+| PathCompare [Eq]
+| Const [2]
+Scan [collection]
+ BindBlock:
+ [scan_0]
+ Source []
+
+
+==== VARIATION: $match with $expr $eq ====
+-- INPUTS:
+pipeline: [{$match: {$expr: {$eq: ['$a', 1]}}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+
+-- OUTPUT:
+Root []
+| | projections:
+| | scan_0
+| RefBlock:
+| Variable [scan_0]
+Filter []
+| EvalFilter []
+| | Variable [scan_0]
+| PathGet [a]
+| PathCompare [Eq]
+| Const [1]
+Scan [collection]
+ BindBlock:
+ [scan_0]
+ Source []
+
+
+==== VARIATION: $match with $expr $eq with dotted path ====
+-- INPUTS:
+pipeline: [{$match: {$expr: {$eq: ['$a.b', 1]}}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+
+-- OUTPUT:
+Root []
+| | projections:
+| | scan_0
+| RefBlock:
+| Variable [scan_0]
+Filter []
+| EvalFilter []
+| | Variable [scan_0]
+| PathConstant []
+| BinaryOp [Eq]
+| | Const [1]
+| EvalPath []
+| | Variable [scan_0]
+| PathGet [a]
+| PathTraverse [inf]
+| PathGet [b]
+| PathIdentity []
+Scan [collection]
+ BindBlock:
+ [scan_0]
+ Source []
+
+
+==== VARIATION: $match with value $elemMatch: observe type bracketing in the filter. ====
+-- INPUTS:
+pipeline: [{$project: {a: {$literal: [1, 2, 3, 4]}}}, {$match: {a: {$elemMatch: {$gte: 2, $lte: 3}}}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+
+-- OUTPUT:
+Root []
+| | projections:
+| | combinedProjection_0
+| RefBlock:
+| Variable [combinedProjection_0]
+Filter []
+| EvalFilter []
+| | Variable [combinedProjection_0]
+| PathGet [a]
+| PathComposeM []
+| | PathArr []
+| PathTraverse [1]
+| PathComposeM []
+| | PathComposeM []
+| | | PathCompare [Lt]
+| | | Const [""]
+| | PathCompare [Gte]
+| | Const [2]
+| PathComposeM []
+| | PathCompare [Gte]
+| | Const [nan]
+| PathCompare [Lte]
+| Const [3]
+Evaluation []
+| BindBlock:
+| [combinedProjection_0]
+| EvalPath []
+| | Variable [scan_0]
+| PathComposeM []
+| | PathDefault []
+| | Const [{}]
+| PathComposeM []
+| | PathField [a]
+| | PathConstant []
+| | Variable [projGetPath_0]
+| PathKeep [_id, a]
+Evaluation []
+| BindBlock:
+| [projGetPath_0]
+| Const [[1, 2, 3, 4]]
+Scan [collection]
+ BindBlock:
+ [scan_0]
+ Source []
+
+
+==== VARIATION: $project then $match ====
+-- INPUTS:
+pipeline: [{$project: {s: {$add: ['$a', '$b']}, c: 1}}, {$match: {$or: [{c: 2}, {s: {$gte: 10}}]}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+
+-- OUTPUT:
+Root []
+| | projections:
+| | combinedProjection_0
+| RefBlock:
+| Variable [combinedProjection_0]
+Filter []
+| EvalFilter []
+| | Variable [combinedProjection_0]
+| PathComposeA []
+| | PathGet [c]
+| | PathTraverse [1]
+| | PathCompare [Eq]
+| | Const [2]
+| PathGet [s]
+| PathTraverse [1]
+| PathComposeM []
+| | PathCompare [Lt]
+| | Const [""]
+| PathCompare [Gte]
+| Const [10]
+Evaluation []
+| BindBlock:
+| [combinedProjection_0]
+| EvalPath []
+| | Variable [scan_0]
+| PathComposeM []
+| | PathDefault []
+| | Const [{}]
+| PathComposeM []
+| | PathField [s]
+| | PathConstant []
+| | Variable [projGetPath_0]
+| PathKeep [_id, c, s]
+Evaluation []
+| BindBlock:
+| [projGetPath_0]
+| BinaryOp [Add]
+| | EvalPath []
+| | | Variable [scan_0]
+| | PathGet [b]
+| | PathIdentity []
+| EvalPath []
+| | Variable [scan_0]
+| PathGet [a]
+| PathIdentity []
+Scan [collection]
+ BindBlock:
+ [scan_0]
+ Source []
+
+
+==== VARIATION: $project with deeply nested path ====
+-- INPUTS:
+pipeline: [{$project: {'a1.b.c':1, 'a.b.c.d.e':'str'}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+
+-- OUTPUT:
+Root []
+| | projections:
+| | combinedProjection_0
+| RefBlock:
+| Variable [combinedProjection_0]
+Evaluation []
+| BindBlock:
+| [combinedProjection_0]
+| EvalPath []
+| | Variable [scan_0]
+| PathComposeM []
+| | PathField [a1]
+| | PathTraverse [inf]
+| | PathComposeM []
+| | | PathField [b]
+| | | PathTraverse [inf]
+| | | PathComposeM []
+| | | | PathKeep [c]
+| | | PathObj []
+| | PathComposeM []
+| | | PathKeep [b]
+| | PathObj []
+| PathComposeM []
+| | PathField [a]
+| | PathTraverse [inf]
+| | PathComposeM []
+| | | PathField [b]
+| | | PathTraverse [inf]
+| | | PathComposeM []
+| | | | PathField [c]
+| | | | PathTraverse [inf]
+| | | | PathComposeM []
+| | | | | PathField [d]
+| | | | | PathTraverse [inf]
+| | | | | PathComposeM []
+| | | | | | PathDefault []
+| | | | | | Const [{}]
+| | | | | PathComposeM []
+| | | | | | PathField [e]
+| | | | | | PathConstant []
+| | | | | | Variable [projGetPath_0]
+| | | | | PathKeep [e]
+| | | | PathKeep [d]
+| | | PathKeep [c]
+| | PathKeep [b]
+| PathKeep [_id, a, a1]
+Evaluation []
+| BindBlock:
+| [projGetPath_0]
+| Const ["str"]
+Scan [collection]
+ BindBlock:
+ [scan_0]
+ Source []
+
+
+==== VARIATION: basic $group ====
+-- INPUTS:
+pipeline: [{$group: {_id: '$a.b', s: {$sum: {$multiply: ['$b', '$c']}}}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+
+-- OUTPUT:
+Root []
+| | projections:
+| | agg_project_0
+| RefBlock:
+| Variable [agg_project_0]
+Evaluation []
+| BindBlock:
+| [agg_project_0]
+| EvalPath []
+| | Const [{}]
+| PathComposeM []
+| | PathField [s]
+| | PathConstant []
+| | Variable [s_agg_0]
+| PathField [_id]
+| PathConstant []
+| Variable [groupByProj_0]
+GroupBy []
+| | groupings:
+| | RefBlock:
+| | Variable [groupByProj_0]
+| aggregations:
+| [s_agg_0]
+| FunctionCall [$sum]
+| Variable [groupByInputProj_0]
+Evaluation []
+| BindBlock:
+| [groupByInputProj_0]
+| BinaryOp [Mult]
+| | EvalPath []
+| | | Variable [scan_0]
+| | PathGet [c]
+| | PathIdentity []
+| EvalPath []
+| | Variable [scan_0]
+| PathGet [b]
+| PathIdentity []
+Evaluation []
+| BindBlock:
+| [groupByProj_0]
+| EvalPath []
+| | Variable [scan_0]
+| PathGet [a]
+| PathTraverse [inf]
+| PathGet [b]
+| PathIdentity []
+Scan [collection]
+ BindBlock:
+ [scan_0]
+ Source []
+
+
+==== VARIATION: $group local global ====
+-- INPUTS:
+pipeline: [{$group: {_id: '$a', c: {$sum: '$b'}}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+
+-- OUTPUT:
+Root []
+| | projections:
+| | agg_project_0
+| RefBlock:
+| Variable [agg_project_0]
+Evaluation []
+| BindBlock:
+| [agg_project_0]
+| EvalPath []
+| | Const [{}]
+| PathComposeM []
+| | PathField [c]
+| | PathConstant []
+| | Variable [c_agg_0]
+| PathField [_id]
+| PathConstant []
+| Variable [groupByProj_0]
+GroupBy []
+| | groupings:
+| | RefBlock:
+| | Variable [groupByProj_0]
+| aggregations:
+| [c_agg_0]
+| FunctionCall [$sum]
+| Variable [groupByInputProj_0]
+Evaluation []
+| BindBlock:
+| [groupByInputProj_0]
+| EvalPath []
+| | Variable [scan_0]
+| PathGet [b]
+| PathIdentity []
+Evaluation []
+| BindBlock:
+| [groupByProj_0]
+| EvalPath []
+| | Variable [scan_0]
+| PathGet [a]
+| PathIdentity []
+Scan [collection]
+ BindBlock:
+ [scan_0]
+ Source []
+
+
+==== VARIATION: basic $unwind ====
+-- INPUTS:
+pipeline: [{$unwind: {path: '$a.b.c'}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+
+-- OUTPUT:
+Root []
+| | projections:
+| | embedProj_0
+| RefBlock:
+| Variable [embedProj_0]
+Evaluation []
+| BindBlock:
+| [embedProj_0]
+| EvalPath []
+| | Variable [scan_0]
+| PathField [a]
+| PathTraverse [inf]
+| PathField [b]
+| PathTraverse [inf]
+| PathField [c]
+| PathConstant []
+| Variable [unwoundProj_0]
+Unwind []
+| BindBlock:
+| [unwoundPid_0]
+| Source []
+| [unwoundProj_0]
+| Source []
+Evaluation []
+| BindBlock:
+| [unwoundProj_0]
+| EvalPath []
+| | Variable [scan_0]
+| PathGet [a]
+| PathGet [b]
+| PathGet [c]
+| PathIdentity []
+Scan [collection]
+ BindBlock:
+ [scan_0]
+ Source []
+
+
+==== VARIATION: complex $unwind ====
+-- INPUTS:
+pipeline: [{$unwind: {path: '$a.b.c', includeArrayIndex: 'p1.pid', preserveNullAndEmptyArrays: true}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+
+-- OUTPUT:
+Root []
+| | projections:
+| | embedPidProj_0
+| RefBlock:
+| Variable [embedPidProj_0]
+Evaluation []
+| BindBlock:
+| [embedPidProj_0]
+| EvalPath []
+| | Variable [embedProj_0]
+| PathField [p1]
+| PathField [pid]
+| PathConstant []
+| If []
+| | | Const [null]
+| | Variable [unwoundPid_0]
+| BinaryOp [Gte]
+| | Const [0]
+| Variable [unwoundPid_0]
+Evaluation []
+| BindBlock:
+| [embedProj_0]
+| EvalPath []
+| | Variable [scan_0]
+| PathField [a]
+| PathTraverse [inf]
+| PathField [b]
+| PathTraverse [inf]
+| PathField [c]
+| PathLambda []
+| LambdaAbstraction [unwoundLambdaVarName_0]
+| If []
+| | | Variable [unwoundLambdaVarName_0]
+| | Variable [unwoundProj_0]
+| BinaryOp [Gte]
+| | Const [0]
+| Variable [unwoundPid_0]
+Unwind [retainNonArrays]
+| BindBlock:
+| [unwoundPid_0]
+| Source []
+| [unwoundProj_0]
+| Source []
+Evaluation []
+| BindBlock:
+| [unwoundProj_0]
+| EvalPath []
+| | Variable [scan_0]
+| PathGet [a]
+| PathGet [b]
+| PathGet [c]
+| PathIdentity []
+Scan [collection]
+ BindBlock:
+ [scan_0]
+ Source []
+
+
+==== VARIATION: $unwind with $group ====
+-- INPUTS:
+pipeline: [{$unwind:{path: '$a.b', preserveNullAndEmptyArrays: true}}, {$group:{_id: '$a.b'}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+
+-- OUTPUT:
+Root []
+| | projections:
+| | agg_project_0
+| RefBlock:
+| Variable [agg_project_0]
+Evaluation []
+| BindBlock:
+| [agg_project_0]
+| EvalPath []
+| | Const [{}]
+| PathField [_id]
+| PathConstant []
+| Variable [groupByProj_0]
+GroupBy []
+| | groupings:
+| | RefBlock:
+| | Variable [groupByProj_0]
+| aggregations:
+Evaluation []
+| BindBlock:
+| [groupByProj_0]
+| EvalPath []
+| | Variable [embedProj_0]
+| PathGet [a]
+| PathTraverse [inf]
+| PathGet [b]
+| PathIdentity []
+Evaluation []
+| BindBlock:
+| [embedProj_0]
+| EvalPath []
+| | Variable [scan_0]
+| PathField [a]
+| PathTraverse [inf]
+| PathField [b]
+| PathLambda []
+| LambdaAbstraction [unwoundLambdaVarName_0]
+| If []
+| | | Variable [unwoundLambdaVarName_0]
+| | Variable [unwoundProj_0]
+| BinaryOp [Gte]
+| | Const [0]
+| Variable [unwoundPid_0]
+Unwind [retainNonArrays]
+| BindBlock:
+| [unwoundPid_0]
+| Source []
+| [unwoundProj_0]
+| Source []
+Evaluation []
+| BindBlock:
+| [unwoundProj_0]
+| EvalPath []
+| | Variable [scan_0]
+| PathGet [a]
+| PathGet [b]
+| PathIdentity []
+Scan [collection]
+ BindBlock:
+ [scan_0]
+ Source []
+
+
+==== VARIATION: $match with index ====
+-- INPUTS:
+pipeline: [{$match: {'a': 10}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+
+-- OUTPUT:
+Root []
+| | projections:
+| | scan_0
+| RefBlock:
+| Variable [scan_0]
+Filter []
+| EvalFilter []
+| | Variable [scan_0]
+| PathGet [a]
+| PathTraverse [1]
+| PathCompare [Eq]
+| Const [10]
+Scan [collection]
+ BindBlock:
+ [scan_0]
+ Source []
+
+
+==== VARIATION: $match sort index ====
+-- INPUTS:
+pipeline: [{$match: {'a': 10}}, {$sort: {'a': 1}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+
+-- OUTPUT:
+Root []
+| | projections:
+| | scan_0
+| RefBlock:
+| Variable [scan_0]
+Collation []
+| | collation:
+| | sort_0: Ascending
+| RefBlock:
+| Variable [sort_0]
+Evaluation []
+| BindBlock:
+| [sort_0]
+| EvalPath []
+| | Variable [scan_0]
+| PathGet [a]
+| PathIdentity []
+Filter []
+| EvalFilter []
+| | Variable [scan_0]
+| PathGet [a]
+| PathTraverse [1]
+| PathCompare [Eq]
+| Const [10]
+Scan [collection]
+ BindBlock:
+ [scan_0]
+ Source []
+
+
+==== VARIATION: $match with range ====
+-- INPUTS:
+pipeline: [{$match: {'a': {$gt: 70, $lt: 90}}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+
+-- OUTPUT:
+Root []
+| | projections:
+| | scan_0
+| RefBlock:
+| Variable [scan_0]
+Filter []
+| EvalFilter []
+| | Variable [scan_0]
+| PathGet [a]
+| PathTraverse [1]
+| PathComposeM []
+| | PathCompare [Lt]
+| | Const [""]
+| PathCompare [Gt]
+| Const [70]
+Filter []
+| EvalFilter []
+| | Variable [scan_0]
+| PathGet [a]
+| PathTraverse [1]
+| PathComposeM []
+| | PathCompare [Gte]
+| | Const [nan]
+| PathCompare [Lt]
+| Const [90]
+Scan [collection]
+ BindBlock:
+ [scan_0]
+ Source []
+
+
+==== VARIATION: index on two keys ====
+-- INPUTS:
+pipeline: [{$match: {'a': 2, 'b': 2}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+
+-- OUTPUT:
+Root []
+| | projections:
+| | scan_0
+| RefBlock:
+| Variable [scan_0]
+Filter []
+| EvalFilter []
+| | Variable [scan_0]
+| PathGet [a]
+| PathTraverse [1]
+| PathCompare [Eq]
+| Const [2]
+Filter []
+| EvalFilter []
+| | Variable [scan_0]
+| PathGet [b]
+| PathTraverse [1]
+| PathCompare [Eq]
+| Const [2]
+Scan [collection]
+ BindBlock:
+ [scan_0]
+ Source []
+
+
+==== VARIATION: $group with complex _id ====
+-- INPUTS:
+pipeline: [{$group: {_id: {'isin': '$isin', 'year': '$year'}, 'count': {$sum: 1}, 'open': {$first: '$$ROOT'}}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+
+-- OUTPUT:
+Root []
+| | projections:
+| | agg_project_0
+| RefBlock:
+| Variable [agg_project_0]
+Evaluation []
+| BindBlock:
+| [agg_project_0]
+| EvalPath []
+| | Const [{}]
+| PathComposeM []
+| | PathField [open]
+| | PathConstant []
+| | Variable [open_agg_0]
+| PathComposeM []
+| | PathField [count]
+| | PathConstant []
+| | Variable [count_agg_0]
+| PathField [_id]
+| PathComposeM []
+| | PathField [year]
+| | PathConstant []
+| | Variable [groupByProj_1]
+| PathField [isin]
+| PathConstant []
+| Variable [groupByProj_0]
+GroupBy []
+| | groupings:
+| | RefBlock:
+| | Variable [groupByProj_0]
+| | Variable [groupByProj_1]
+| aggregations:
+| [count_agg_0]
+| FunctionCall [$sum]
+| Const [1]
+| [open_agg_0]
+| FunctionCall [$first]
+| Variable [scan_0]
+Evaluation []
+| BindBlock:
+| [groupByProj_1]
+| EvalPath []
+| | Variable [scan_0]
+| PathGet [year]
+| PathIdentity []
+Evaluation []
+| BindBlock:
+| [groupByProj_0]
+| EvalPath []
+| | Variable [scan_0]
+| PathGet [isin]
+| PathIdentity []
+Scan [collection]
+ BindBlock:
+ [scan_0]
+ Source []
+
+
+==== VARIATION: $project with computed array ====
+-- INPUTS:
+pipeline: [{$project: {a: ['$b', '$c']}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+
+-- OUTPUT:
+Root []
+| | projections:
+| | combinedProjection_0
+| RefBlock:
+| Variable [combinedProjection_0]
+Evaluation []
+| BindBlock:
+| [combinedProjection_0]
+| EvalPath []
+| | Variable [scan_0]
+| PathComposeM []
+| | PathDefault []
+| | Const [{}]
+| PathComposeM []
+| | PathField [a]
+| | PathConstant []
+| | Variable [projGetPath_0]
+| PathKeep [_id, a]
+Evaluation []
+| BindBlock:
+| [projGetPath_0]
+| FunctionCall [newArray]
+| | EvalPath []
+| | | Variable [scan_0]
+| | PathGet [c]
+| | PathIdentity []
+| EvalPath []
+| | Variable [scan_0]
+| PathGet [b]
+| PathIdentity []
+Scan [collection]
+ BindBlock:
+ [scan_0]
+ Source []
+
+
+==== VARIATION: union ====
+-- INPUTS:
+pipeline: [{$unionWith: 'collB'}, {$match: {_id: 1}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collA:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+ collB:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+
+-- OUTPUT:
+Root []
+| | projections:
+| | scan_0
+| RefBlock:
+| Variable [scan_0]
+Filter []
+| EvalFilter []
+| | Variable [scan_0]
+| PathGet [_id]
+| PathTraverse [1]
+| PathCompare [Eq]
+| Const [1]
+Union []
+| | BindBlock:
+| | [scan_0]
+| | Source []
+| Evaluation []
+| | BindBlock:
+| | [scan_0]
+| | EvalPath []
+| | | Variable [scan_1]
+| | PathIdentity []
+| Scan [collB]
+| BindBlock:
+| [scan_1]
+| Source []
+Scan [collA]
+ BindBlock:
+ [scan_0]
+ Source []
+
+
+==== VARIATION: $match with $ne ====
+-- INPUTS:
+pipeline: [{$match: {'a': {$ne: 2}}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+
+-- OUTPUT:
+Root []
+| | projections:
+| | scan_0
+| RefBlock:
+| Variable [scan_0]
+Filter []
+| EvalFilter []
+| | Variable [scan_0]
+| PathConstant []
+| UnaryOp [Not]
+| EvalFilter []
+| | Variable [scan_0]
+| PathGet [a]
+| PathTraverse [1]
+| PathCompare [Eq]
+| Const [2]
+Scan [collection]
+ BindBlock:
+ [scan_0]
+ Source []
+
+
diff --git a/src/mongo/db/test_output/pipeline/abt/a_b_t_translate/optimize_pipeline_tests.txt b/src/mongo/db/test_output/pipeline/abt/a_b_t_translate/optimize_pipeline_tests.txt
new file mode 100644
index 00000000000..9f03e9a01af
--- /dev/null
+++ b/src/mongo/db/test_output/pipeline/abt/a_b_t_translate/optimize_pipeline_tests.txt
@@ -0,0 +1,1438 @@
+==== VARIATION: optimized $match with $or: pipeline is able to use a SargableNode with a disjunction of point intervals. ====
+-- INPUTS:
+pipeline: [{$match: {$or: [{a: 1}, {a: 2}, {a: 3}]}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ index1:
+ collation spec:
+ ABT path:
+ PathGet [a]
+ PathTraverse [1]
+ PathIdentity []
+
+ collation op: Ascending
+ version: 2
+ ordering bits: 0
+ is multi-key: 1
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ requirementsMap:
+
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+optimization phases:
+ MemoSubstitutionPhase
+
+-- OUTPUT:
+Root []
+| | projections:
+| | scan_0
+| RefBlock:
+| Variable [scan_0]
+Sargable [Complete]
+| | | | | requirementsMap:
+| | | | | refProjection: scan_0, path: 'PathGet [a] PathTraverse [1] PathIdentity []', intervals: {{{[Const [3], Const [3]]}} U {{[Const [1], Const [1]]}} U {{[Const [2], Const [2]]}}}
+| | | | candidateIndexes:
+| | | | candidateId: 1, index1, {}, {0}, {{{[Const [3], Const [3]]}} U {{[Const [1], Const [1]]}} U {{[Const [2], Const [2]]}}}
+| | | scanParams:
+| | | {'a': evalTemp_0}
+| | | residualReqs:
+| | | refProjection: evalTemp_0, path: 'PathTraverse [1] PathIdentity []', intervals: {{{[Const [3], Const [3]]}} U {{[Const [1], Const [1]]}} U {{[Const [2], Const [2]]}}}, entryIndex: 0
+| | BindBlock:
+| RefBlock:
+| Variable [scan_0]
+Scan [collection]
+ BindBlock:
+ [scan_0]
+ Source []
+
+
+==== VARIATION: optimized $match with $in and a list of equalities becomes a comparison to an EqMember list. ====
+-- INPUTS:
+pipeline: [{$match: {a: {$in: [1, 2, 3]}}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ index1:
+ collation spec:
+ ABT path:
+ PathGet [a]
+ PathTraverse [1]
+ PathIdentity []
+
+ collation op: Ascending
+ version: 2
+ ordering bits: 0
+ is multi-key: 1
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ requirementsMap:
+
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+optimization phases:
+ MemoSubstitutionPhase
+
+-- OUTPUT:
+Root []
+| | projections:
+| | scan_0
+| RefBlock:
+| Variable [scan_0]
+Filter []
+| EvalFilter []
+| | Variable [scan_0]
+| PathGet [a]
+| PathTraverse [1]
+| PathCompare [EqMember]
+| Const [[1, 2, 3]]
+Scan [collection]
+ BindBlock:
+ [scan_0]
+ Source []
+
+
+==== VARIATION: optimized $project inclusion then $match: observe the Filter can be reordered against the Eval node ====
+-- INPUTS:
+pipeline: [{$project: {a: 1, b: 1}}, {$match: {a: 2}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+optimization phases:
+ ConstEvalPre
+ MemoExplorationPhase
+ MemoImplementationPhase
+ MemoSubstitutionPhase
+ PathFuse
+
+-- OUTPUT:
+Root []
+| | projections:
+| | combinedProjection_0
+| RefBlock:
+| Variable [combinedProjection_0]
+Evaluation []
+| BindBlock:
+| [combinedProjection_0]
+| EvalPath []
+| | Const [{}]
+| PathComposeM []
+| | PathField [b]
+| | PathConstant []
+| | Variable [fieldProj_2]
+| PathComposeM []
+| | PathField [a]
+| | PathConstant []
+| | Variable [fieldProj_1]
+| PathField [_id]
+| PathConstant []
+| Variable [fieldProj_0]
+Filter []
+| EvalFilter []
+| | Variable [fieldProj_1]
+| PathTraverse [1]
+| PathCompare [Eq]
+| Const [2]
+PhysicalScan [{'_id': fieldProj_0, 'a': fieldProj_1, 'b': fieldProj_2}, collection]
+ BindBlock:
+ [fieldProj_0]
+ Source []
+ [fieldProj_1]
+ Source []
+ [fieldProj_2]
+ Source []
+
+
+==== VARIATION: optimized $match basic ====
+-- INPUTS:
+pipeline: [{$match: {a: 1, b: 2}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+optimization phases:
+ MemoExplorationPhase
+ MemoImplementationPhase
+ MemoSubstitutionPhase
+
+-- OUTPUT:
+Root []
+| | projections:
+| | scan_0
+| RefBlock:
+| Variable [scan_0]
+Filter []
+| EvalFilter []
+| | Variable [evalTemp_3]
+| PathTraverse [1]
+| PathCompare [Eq]
+| Const [2]
+Filter []
+| EvalFilter []
+| | Variable [evalTemp_2]
+| PathTraverse [1]
+| PathCompare [Eq]
+| Const [1]
+PhysicalScan [{'<root>': scan_0, 'a': evalTemp_2, 'b': evalTemp_3}, collection]
+ BindBlock:
+ [evalTemp_2]
+ Source []
+ [evalTemp_3]
+ Source []
+ [scan_0]
+ Source []
+
+
+==== VARIATION: optimized $expr filter: make sure we have a single array constant for (1, 2, 'str', ...) ====
+-- INPUTS:
+pipeline: [{$project: {a: {$filter: {input: [1, 2, 'str', {a: 2.0, b:'s'}, 3, 4], as: 'num', cond: {$and: [{$gte: ['$$num', 2]}, {$lte: ['$$num', 3]}]}}}}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+optimization phases:
+ ConstEvalPre
+
+-- OUTPUT:
+Root []
+| | projections:
+| | combinedProjection_0
+| RefBlock:
+| Variable [combinedProjection_0]
+Evaluation []
+| BindBlock:
+| [combinedProjection_0]
+| EvalPath []
+| | Variable [scan_0]
+| PathComposeM []
+| | PathDefault []
+| | Const [{}]
+| PathComposeM []
+| | PathField [a]
+| | PathConstant []
+| | EvalPath []
+| | | Const [[1, 2, "str", {"a" : 2, "b" : "s"}, 3, 4]]
+| | PathTraverse [inf]
+| | PathLambda []
+| | LambdaAbstraction [projGetPath_0_var_1]
+| | If []
+| | | | Const [Nothing]
+| | | Variable [projGetPath_0_var_1]
+| | BinaryOp [And]
+| | | BinaryOp [Gte]
+| | | | Const [2]
+| | | Variable [projGetPath_0_var_1]
+| | BinaryOp [Lte]
+| | | Const [3]
+| | Variable [projGetPath_0_var_1]
+| PathKeep [_id, a]
+Scan [collection]
+ BindBlock:
+ [scan_0]
+ Source []
+
+
+==== VARIATION: optimized $group local global ====
+-- INPUTS:
+pipeline: [{$group: {_id: '$a', c: {$sum: '$b'}}}]
+metadata:
+ number of partitions: 5
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: UnknownPartitioning
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+optimization phases:
+ MemoExplorationPhase
+ MemoImplementationPhase
+ MemoSubstitutionPhase
+
+-- OUTPUT:
+Root []
+| | projections:
+| | agg_project_0
+| RefBlock:
+| Variable [agg_project_0]
+Exchange []
+| | distribution:
+| | type: Centralized
+| RefBlock:
+Evaluation []
+| BindBlock:
+| [agg_project_0]
+| EvalPath []
+| | Const [{}]
+| PathComposeM []
+| | PathField [c]
+| | PathConstant []
+| | Variable [c_agg_0]
+| PathField [_id]
+| PathConstant []
+| Variable [groupByProj_0]
+GroupBy [Global]
+| | groupings:
+| | RefBlock:
+| | Variable [groupByProj_0]
+| aggregations:
+| [c_agg_0]
+| FunctionCall [$sum]
+| Variable [preagg_0]
+Exchange []
+| | distribution:
+| | type: HashPartitioning
+| | projections:
+| | groupByProj_0
+| RefBlock:
+| Variable [groupByProj_0]
+GroupBy [Local]
+| | groupings:
+| | RefBlock:
+| | Variable [groupByProj_0]
+| aggregations:
+| [preagg_0]
+| FunctionCall [$sum]
+| Variable [groupByInputProj_0]
+PhysicalScan [{'a': groupByProj_0, 'b': groupByInputProj_0}, collection, parallel]
+ BindBlock:
+ [groupByInputProj_0]
+ Source []
+ [groupByProj_0]
+ Source []
+
+
+==== VARIATION: optimized $unwind then $sort ====
+-- INPUTS:
+pipeline: [{$unwind: '$x'}, {$sort: {'x': 1}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+optimization phases:
+ ConstEvalPost
+ ConstEvalPre
+ MemoExplorationPhase
+ MemoImplementationPhase
+ MemoSubstitutionPhase
+ PathFuse
+ PathLower
+
+-- OUTPUT:
+Root []
+| | projections:
+| | embedProj_0
+| RefBlock:
+| Variable [embedProj_0]
+Collation []
+| | collation:
+| | sort_0: Ascending
+| RefBlock:
+| Variable [sort_0]
+Evaluation []
+| BindBlock:
+| [sort_0]
+| Variable [unwoundProj_0]
+Evaluation []
+| BindBlock:
+| [embedProj_0]
+| If []
+| | | Variable [scan_0]
+| | FunctionCall [setField]
+| | | | Variable [unwoundProj_0]
+| | | Const ["x"]
+| | Variable [scan_0]
+| BinaryOp [Or]
+| | FunctionCall [isObject]
+| | Variable [scan_0]
+| FunctionCall [exists]
+| Variable [unwoundProj_0]
+Unwind []
+| BindBlock:
+| [unwoundPid_0]
+| Source []
+| [unwoundProj_0]
+| Source []
+PhysicalScan [{'<root>': scan_0, 'x': unwoundProj_0}, collection]
+ BindBlock:
+ [scan_0]
+ Source []
+ [unwoundProj_0]
+ Source []
+
+
+==== VARIATION: optimized $match with index ====
+-- INPUTS:
+pipeline: [{$match: {'a': 10}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ index1:
+ collation spec:
+ ABT path:
+ PathGet [a]
+ PathTraverse [1]
+ PathIdentity []
+
+ collation op: Ascending
+ version: 2
+ ordering bits: 0
+ is multi-key: 1
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ requirementsMap:
+
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+optimization phases:
+ MemoExplorationPhase
+ MemoImplementationPhase
+ MemoSubstitutionPhase
+
+-- OUTPUT:
+Root []
+| | projections:
+| | scan_0
+| RefBlock:
+| Variable [scan_0]
+BinaryJoin [joinType: Inner, {rid_0}]
+| | Const [true]
+| LimitSkip []
+| | limitSkip:
+| | limit: 1
+| | skip: 0
+| Seek [ridProjection: rid_0, {'<root>': scan_0}, collection]
+| | BindBlock:
+| | [scan_0]
+| | Source []
+| RefBlock:
+| Variable [rid_0]
+IndexScan [{'<rid>': rid_0}, scanDefName: collection, indexDefName: index1, interval: {[Const [10], Const [10]]}]
+ BindBlock:
+ [rid_0]
+ Source []
+
+
+==== VARIATION: optimized $match index covered ====
+-- INPUTS:
+pipeline: [{$project: {_id: 0, a: 1}}, {$match: {'a': 10}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ index1:
+ collation spec:
+ ABT path:
+ PathGet [a]
+ PathIdentity []
+
+ collation op: Ascending
+ version: 2
+ ordering bits: 0
+ is multi-key: 0
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ requirementsMap:
+
+ non multi-key index paths:
+ PathGet [a]
+ PathIdentity []
+
+ collection exists: 1
+ CE type: -1
+optimization phases:
+ ConstEvalPre
+ MemoExplorationPhase
+ MemoImplementationPhase
+ MemoSubstitutionPhase
+ PathFuse
+
+-- OUTPUT:
+Root []
+| | projections:
+| | combinedProjection_0
+| RefBlock:
+| Variable [combinedProjection_0]
+Evaluation []
+| BindBlock:
+| [combinedProjection_0]
+| EvalPath []
+| | Const [{}]
+| PathField [a]
+| PathConstant []
+| Variable [fieldProj_0]
+IndexScan [{'<indexKey> 0': fieldProj_0}, scanDefName: collection, indexDefName: index1, interval: {[Const [10], Const [10]]}]
+ BindBlock:
+ [fieldProj_0]
+ Source []
+
+
+==== VARIATION: optimized $match index covered, match then project ====
+-- INPUTS:
+pipeline: [{$match: {'a': 10}}, {$project: {_id: 0, a: 1}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ index1:
+ collation spec:
+ ABT path:
+ PathGet [a]
+ PathIdentity []
+
+ collation op: Ascending
+ version: 2
+ ordering bits: 0
+ is multi-key: 0
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ requirementsMap:
+
+ non multi-key index paths:
+ PathGet [a]
+ PathIdentity []
+
+ collection exists: 1
+ CE type: -1
+optimization phases:
+ ConstEvalPre
+ MemoExplorationPhase
+ MemoImplementationPhase
+ MemoSubstitutionPhase
+ PathFuse
+
+-- OUTPUT:
+Root []
+| | projections:
+| | combinedProjection_0
+| RefBlock:
+| Variable [combinedProjection_0]
+Evaluation []
+| BindBlock:
+| [combinedProjection_0]
+| EvalPath []
+| | Const [{}]
+| PathField [a]
+| PathConstant []
+| Variable [fieldProj_0]
+IndexScan [{'<indexKey> 0': fieldProj_0}, scanDefName: collection, indexDefName: index1, interval: {[Const [10], Const [10]]}]
+ BindBlock:
+ [fieldProj_0]
+ Source []
+
+
+==== VARIATION: optimized $match index covered, match on two indexed keys then project ====
+-- INPUTS:
+pipeline: [{$match: {'a': 10, 'b': 20}}, {$project: {_id: 0, a: 1}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ index1:
+ collation spec:
+ ABT path:
+ PathGet [a]
+ PathIdentity []
+
+ collation op: Ascending
+ ABT path:
+ PathGet [b]
+ PathIdentity []
+
+ collation op: Ascending
+ version: 2
+ ordering bits: 0
+ is multi-key: 0
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ requirementsMap:
+
+ non multi-key index paths:
+ PathGet [a]
+ PathIdentity []
+
+ PathGet [b]
+ PathIdentity []
+
+ collection exists: 1
+ CE type: -1
+optimization phases:
+ ConstEvalPre
+ MemoExplorationPhase
+ MemoImplementationPhase
+ MemoSubstitutionPhase
+ PathFuse
+
+-- OUTPUT:
+Root []
+| | projections:
+| | combinedProjection_0
+| RefBlock:
+| Variable [combinedProjection_0]
+Evaluation []
+| BindBlock:
+| [combinedProjection_0]
+| EvalPath []
+| | Const [{}]
+| PathField [a]
+| PathConstant []
+| Variable [fieldProj_0]
+IndexScan [{'<indexKey> 0': fieldProj_0}, scanDefName: collection, indexDefName: index1, interval: {[Const [10], Const [10]], [Const [20], Const [20]]}]
+ BindBlock:
+ [fieldProj_0]
+ Source []
+
+
+==== VARIATION: optimized $match index covered, match on three indexed keys then project ====
+-- INPUTS:
+pipeline: [{$match: {'a': 10, 'b': 20, 'c': 30}}, {$project: {_id: 0, a: 1, b: 1, c: 1}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ index1:
+ collation spec:
+ ABT path:
+ PathGet [a]
+ PathIdentity []
+
+ collation op: Ascending
+ ABT path:
+ PathGet [b]
+ PathIdentity []
+
+ collation op: Ascending
+ ABT path:
+ PathGet [c]
+ PathIdentity []
+
+ collation op: Ascending
+ version: 2
+ ordering bits: 0
+ is multi-key: 0
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ requirementsMap:
+
+ non multi-key index paths:
+ PathGet [a]
+ PathIdentity []
+
+ PathGet [b]
+ PathIdentity []
+
+ PathGet [c]
+ PathIdentity []
+
+ collection exists: 1
+ CE type: -1
+optimization phases:
+ ConstEvalPre
+ MemoExplorationPhase
+ MemoImplementationPhase
+ MemoSubstitutionPhase
+ PathFuse
+
+-- OUTPUT:
+Root []
+| | projections:
+| | combinedProjection_0
+| RefBlock:
+| Variable [combinedProjection_0]
+Evaluation []
+| BindBlock:
+| [combinedProjection_0]
+| EvalPath []
+| | Const [{}]
+| PathComposeM []
+| | PathField [c]
+| | PathConstant []
+| | Variable [fieldProj_2]
+| PathComposeM []
+| | PathField [b]
+| | PathConstant []
+| | Variable [fieldProj_1]
+| PathField [a]
+| PathConstant []
+| Variable [fieldProj_0]
+IndexScan [{'<indexKey> 0': fieldProj_0, '<indexKey> 1': fieldProj_1, '<indexKey> 2': fieldProj_2}, scanDefName: collection, indexDefName: index1, interval: {[Const [10], Const [10]], [Const [20], Const [20]], [Const [30], Const [30]]}]
+ BindBlock:
+ [fieldProj_0]
+ Source []
+ [fieldProj_1]
+ Source []
+ [fieldProj_2]
+ Source []
+
+
+==== VARIATION: optimized $match index covered, inclusion project then match on three indexed keys ====
+-- INPUTS:
+pipeline: [{$project: {_id: 0, a: 1, b: 1, c: 1}}, {$match: {'a': 10, 'b': 20, 'c': 30}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ index1:
+ collation spec:
+ ABT path:
+ PathGet [a]
+ PathIdentity []
+
+ collation op: Ascending
+ ABT path:
+ PathGet [b]
+ PathIdentity []
+
+ collation op: Ascending
+ ABT path:
+ PathGet [c]
+ PathIdentity []
+
+ collation op: Ascending
+ version: 2
+ ordering bits: 0
+ is multi-key: 0
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ requirementsMap:
+
+ non multi-key index paths:
+ PathGet [a]
+ PathIdentity []
+
+ PathGet [b]
+ PathIdentity []
+
+ PathGet [c]
+ PathIdentity []
+
+ collection exists: 1
+ CE type: -1
+optimization phases:
+ ConstEvalPre
+ MemoExplorationPhase
+ MemoImplementationPhase
+ MemoSubstitutionPhase
+ PathFuse
+
+-- OUTPUT:
+Root []
+| | projections:
+| | combinedProjection_0
+| RefBlock:
+| Variable [combinedProjection_0]
+Evaluation []
+| BindBlock:
+| [combinedProjection_0]
+| EvalPath []
+| | Const [{}]
+| PathComposeM []
+| | PathField [c]
+| | PathConstant []
+| | Variable [fieldProj_2]
+| PathComposeM []
+| | PathField [b]
+| | PathConstant []
+| | Variable [fieldProj_1]
+| PathField [a]
+| PathConstant []
+| Variable [fieldProj_0]
+IndexScan [{'<indexKey> 0': fieldProj_0, '<indexKey> 1': fieldProj_1, '<indexKey> 2': fieldProj_2}, scanDefName: collection, indexDefName: index1, interval: {[Const [10], Const [10]], [Const [20], Const [20]], [Const [30], Const [30]]}]
+ BindBlock:
+ [fieldProj_0]
+ Source []
+ [fieldProj_1]
+ Source []
+ [fieldProj_2]
+ Source []
+
+
+==== VARIATION: optimized $match sort index ====
+-- INPUTS:
+pipeline: [{$match: {'a': 10}}, {$sort: {'a': 1}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ index1:
+ collation spec:
+ ABT path:
+ PathGet [a]
+ PathTraverse [1]
+ PathIdentity []
+
+ collation op: Ascending
+ version: 2
+ ordering bits: 0
+ is multi-key: 1
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ requirementsMap:
+
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+optimization phases:
+ MemoExplorationPhase
+ MemoImplementationPhase
+ MemoSubstitutionPhase
+
+-- OUTPUT:
+Root []
+| | projections:
+| | scan_0
+| RefBlock:
+| Variable [scan_0]
+Collation []
+| | collation:
+| | sort_0: Ascending
+| RefBlock:
+| Variable [sort_0]
+BinaryJoin [joinType: Inner, {rid_0}]
+| | Const [true]
+| LimitSkip []
+| | limitSkip:
+| | limit: 1
+| | skip: 0
+| Seek [ridProjection: rid_0, {'<root>': scan_0, 'a': sort_0}, collection]
+| | BindBlock:
+| | [scan_0]
+| | Source []
+| | [sort_0]
+| | Source []
+| RefBlock:
+| Variable [rid_0]
+IndexScan [{'<rid>': rid_0}, scanDefName: collection, indexDefName: index1, interval: {[Const [10], Const [10]]}]
+ BindBlock:
+ [rid_0]
+ Source []
+
+
+==== VARIATION: optimized range index ====
+-- INPUTS:
+pipeline: [{$match: {'a': {$gt: 70, $lt: 90}}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ index1:
+ collation spec:
+ ABT path:
+ PathGet [a]
+ PathTraverse [1]
+ PathIdentity []
+
+ collation op: Ascending
+ version: 2
+ ordering bits: 0
+ is multi-key: 1
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ requirementsMap:
+
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+optimization phases:
+ MemoExplorationPhase
+ MemoImplementationPhase
+ MemoSubstitutionPhase
+
+-- OUTPUT:
+Root []
+| | projections:
+| | scan_0
+| RefBlock:
+| Variable [scan_0]
+BinaryJoin [joinType: Inner, {rid_0}]
+| | Const [true]
+| LimitSkip []
+| | limitSkip:
+| | limit: 1
+| | skip: 0
+| Seek [ridProjection: rid_0, {'<root>': scan_0}, collection]
+| | BindBlock:
+| | [scan_0]
+| | Source []
+| RefBlock:
+| Variable [rid_0]
+Filter []
+| EvalFilter []
+| | FunctionCall [getArraySize]
+| | Variable [sides_0]
+| PathCompare [Eq]
+| Const [2]
+GroupBy []
+| | groupings:
+| | RefBlock:
+| | Variable [rid_0]
+| aggregations:
+| [sides_0]
+| FunctionCall [$addToSet]
+| Variable [sideId_0]
+Union []
+| | BindBlock:
+| | [rid_0]
+| | Source []
+| | [sideId_0]
+| | Source []
+| Evaluation []
+| | BindBlock:
+| | [sideId_0]
+| | Const [1]
+| IndexScan [{'<rid>': rid_0}, scanDefName: collection, indexDefName: index1, interval: {(Const [70], Const [""])}]
+| BindBlock:
+| [rid_0]
+| Source []
+Evaluation []
+| BindBlock:
+| [sideId_0]
+| Const [0]
+IndexScan [{'<rid>': rid_0}, scanDefName: collection, indexDefName: index1, interval: {[Const [nan], Const [90])}]
+ BindBlock:
+ [rid_0]
+ Source []
+
+
+==== VARIATION: optimized index on two keys ====
+-- INPUTS:
+pipeline: [{$match: {'a': 2, 'b': 2}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ index1:
+ collation spec:
+ ABT path:
+ PathGet [a]
+ PathTraverse [1]
+ PathIdentity []
+
+ collation op: Ascending
+ ABT path:
+ PathGet [b]
+ PathTraverse [1]
+ PathIdentity []
+
+ collation op: Ascending
+ version: 2
+ ordering bits: 0
+ is multi-key: 1
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ requirementsMap:
+
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+optimization phases:
+ MemoExplorationPhase
+ MemoImplementationPhase
+ MemoSubstitutionPhase
+
+-- OUTPUT:
+Root []
+| | projections:
+| | scan_0
+| RefBlock:
+| Variable [scan_0]
+BinaryJoin [joinType: Inner, {rid_0}]
+| | Const [true]
+| LimitSkip []
+| | limitSkip:
+| | limit: 1
+| | skip: 0
+| Seek [ridProjection: rid_0, {'<root>': scan_0}, collection]
+| | BindBlock:
+| | [scan_0]
+| | Source []
+| RefBlock:
+| Variable [rid_0]
+IndexScan [{'<rid>': rid_0}, scanDefName: collection, indexDefName: index1, interval: {[Const [2], Const [2]], [Const [2], Const [2]]}]
+ BindBlock:
+ [rid_0]
+ Source []
+
+
+==== VARIATION: optimized index on one key ====
+-- INPUTS:
+pipeline: [{$match: {'a': 2, 'b': 2}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ index1:
+ collation spec:
+ ABT path:
+ PathGet [a]
+ PathTraverse [1]
+ PathIdentity []
+
+ collation op: Ascending
+ version: 2
+ ordering bits: 0
+ is multi-key: 1
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ requirementsMap:
+
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+optimization phases:
+ ConstEvalPost
+ MemoExplorationPhase
+ MemoImplementationPhase
+ MemoSubstitutionPhase
+
+-- OUTPUT:
+Root []
+| | projections:
+| | scan_0
+| RefBlock:
+| Variable [scan_0]
+BinaryJoin [joinType: Inner, {rid_0}]
+| | Const [true]
+| Filter []
+| | EvalFilter []
+| | | Variable [evalTemp_4]
+| | PathTraverse [1]
+| | PathCompare [Eq]
+| | Const [2]
+| LimitSkip []
+| | limitSkip:
+| | limit: 1
+| | skip: 0
+| Seek [ridProjection: rid_0, {'<root>': scan_0, 'b': evalTemp_4}, collection]
+| | BindBlock:
+| | [evalTemp_4]
+| | Source []
+| | [scan_0]
+| | Source []
+| RefBlock:
+| Variable [rid_0]
+IndexScan [{'<rid>': rid_0}, scanDefName: collection, indexDefName: index1, interval: {[Const [2], Const [2]]}]
+ BindBlock:
+ [rid_0]
+ Source []
+
+
+==== VARIATION: optimized $group eval no inline: verify that "b" is not inlined in the group expression, but is coming from the physical scan ====
+-- INPUTS:
+pipeline: [{$group: {_id: null, a: {$first: '$b'}}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+optimization phases:
+ ConstEvalPost
+ ConstEvalPre
+ MemoExplorationPhase
+ MemoImplementationPhase
+ MemoSubstitutionPhase
+ PathFuse
+ PathLower
+
+-- OUTPUT:
+Root []
+| | projections:
+| | agg_project_0
+| RefBlock:
+| Variable [agg_project_0]
+Evaluation []
+| BindBlock:
+| [agg_project_0]
+| Let [inputField_1]
+| | If []
+| | | | Variable [inputField_1]
+| | | FunctionCall [setField]
+| | | | | Variable [a_agg_0]
+| | | | Const ["a"]
+| | | Variable [inputField_1]
+| | BinaryOp [Or]
+| | | FunctionCall [isObject]
+| | | Variable [inputField_1]
+| | FunctionCall [exists]
+| | Variable [a_agg_0]
+| If []
+| | | Const [{}]
+| | FunctionCall [setField]
+| | | | Variable [groupByProj_0]
+| | | Const ["_id"]
+| | Const [{}]
+| BinaryOp [Or]
+| | FunctionCall [isObject]
+| | Const [{}]
+| FunctionCall [exists]
+| Variable [groupByProj_0]
+GroupBy []
+| | groupings:
+| | RefBlock:
+| | Variable [groupByProj_0]
+| aggregations:
+| [a_agg_0]
+| FunctionCall [$first]
+| Variable [groupByInputProj_0]
+Evaluation []
+| BindBlock:
+| [groupByProj_0]
+| Const [null]
+PhysicalScan [{'b': groupByInputProj_0}, collection]
+ BindBlock:
+ [groupByInputProj_0]
+ Source []
+
+
+==== VARIATION: optimized union ====
+-- INPUTS:
+pipeline: [{$unionWith: 'collB'}, {$match: {_id: 1}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collA:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+ collB:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+optimization phases:
+ MemoExplorationPhase
+ MemoImplementationPhase
+ MemoSubstitutionPhase
+
+-- OUTPUT:
+Root []
+| | projections:
+| | scan_0
+| RefBlock:
+| Variable [scan_0]
+Union []
+| | BindBlock:
+| | [scan_0]
+| | Source []
+| Filter []
+| | EvalFilter []
+| | | Variable [scan_0]
+| | PathGet [_id]
+| | PathTraverse [1]
+| | PathCompare [Eq]
+| | Const [1]
+| Evaluation []
+| | BindBlock:
+| | [scan_0]
+| | EvalPath []
+| | | Variable [scan_1]
+| | PathIdentity []
+| PhysicalScan [{'<root>': scan_1}, collB]
+| BindBlock:
+| [scan_1]
+| Source []
+Filter []
+| EvalFilter []
+| | Variable [evalTemp_0]
+| PathTraverse [1]
+| PathCompare [Eq]
+| Const [1]
+PhysicalScan [{'<root>': scan_0, '_id': evalTemp_0}, collA]
+ BindBlock:
+ [evalTemp_0]
+ Source []
+ [scan_0]
+ Source []
+
+
+==== VARIATION: optimized common expression elimination ====
+-- INPUTS:
+pipeline: [{$project: {foo: {$add: ['$b', 1]}, bar: {$add: ['$b', 1]}}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ test:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+optimization phases:
+ ConstEvalPre
+
+-- OUTPUT:
+Root []
+| | projections:
+| | combinedProjection_0
+| RefBlock:
+| Variable [combinedProjection_0]
+Evaluation []
+| BindBlock:
+| [combinedProjection_0]
+| EvalPath []
+| | Variable [scan_0]
+| PathComposeM []
+| | PathDefault []
+| | Const [{}]
+| PathComposeM []
+| | PathField [foo]
+| | PathConstant []
+| | Variable [projGetPath_0]
+| PathComposeM []
+| | PathField [bar]
+| | PathConstant []
+| | Variable [projGetPath_0]
+| PathKeep [_id, bar, foo]
+Evaluation []
+| BindBlock:
+| [projGetPath_0]
+| BinaryOp [Add]
+| | Const [1]
+| EvalPath []
+| | Variable [scan_0]
+| PathGet [b]
+| PathIdentity []
+Scan [test]
+ BindBlock:
+ [scan_0]
+ Source []
+
+
+==== VARIATION: optimized group by dependency: demonstrate that "c" is set to the array size (not the array itself coming from the group) ====
+-- INPUTS:
+pipeline: [{$group: {_id: {}, b: {$addToSet: '$a'}}}, {$project: {_id: 0, b: {$size: '$b'}}}, {$project: {_id: 0, c: '$b'}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ test:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+optimization phases:
+ ConstEvalPre
+ MemoExplorationPhase
+ MemoImplementationPhase
+ MemoSubstitutionPhase
+ PathFuse
+
+-- OUTPUT:
+Root []
+| | projections:
+| | combinedProjection_1
+| RefBlock:
+| Variable [combinedProjection_1]
+Evaluation []
+| BindBlock:
+| [combinedProjection_1]
+| EvalPath []
+| | Const [{}]
+| PathComposeM []
+| | PathField [c]
+| | PathConstant []
+| | FunctionCall [getArraySize]
+| | Variable [b_agg_0]
+| PathKeep []
+GroupBy []
+| | groupings:
+| | RefBlock:
+| | Variable [groupByProj_0]
+| aggregations:
+| [b_agg_0]
+| FunctionCall [$addToSet]
+| Variable [groupByInputProj_0]
+Evaluation []
+| BindBlock:
+| [groupByProj_0]
+| Const [{}]
+PhysicalScan [{'a': groupByInputProj_0}, test]
+ BindBlock:
+ [groupByInputProj_0]
+ Source []
+
+
+==== VARIATION: optimized double $elemMatch ====
+-- INPUTS:
+pipeline: [{$match: {a: {$elemMatch: {$gte: 5, $lte: 6}}, b: {$elemMatch: {$gte: 1, $lte: 3}}}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ test:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+optimization phases:
+ MemoSubstitutionPhase
+
+-- OUTPUT:
+Root []
+| | projections:
+| | scan_0
+| RefBlock:
+| Variable [scan_0]
+Sargable [Complete]
+| | | | | requirementsMap:
+| | | | | refProjection: scan_0, path: 'PathGet [a] PathIdentity []', intervals: {{{[Const [[]], Const [BinData(0, )])}}}
+| | | | | refProjection: scan_0, path: 'PathGet [a] PathTraverse [1] PathIdentity []', intervals: {{{[Const [5], Const [6]]}}}
+| | | | | refProjection: scan_0, path: 'PathGet [b] PathIdentity []', intervals: {{{[Const [[]], Const [BinData(0, )])}}}
+| | | | | refProjection: scan_0, path: 'PathGet [b] PathTraverse [1] PathIdentity []', intervals: {{{[Const [1], Const [3]]}}}
+| | | | candidateIndexes:
+| | | scanParams:
+| | | {'a': evalTemp_7, 'b': evalTemp_8}
+| | | residualReqs:
+| | | refProjection: evalTemp_7, path: 'PathIdentity []', intervals: {{{[Const [[]], Const [BinData(0, )])}}}, entryIndex: 0
+| | | refProjection: evalTemp_7, path: 'PathTraverse [1] PathIdentity []', intervals: {{{[Const [5], Const [6]]}}}, entryIndex: 1
+| | | refProjection: evalTemp_8, path: 'PathIdentity []', intervals: {{{[Const [[]], Const [BinData(0, )])}}}, entryIndex: 2
+| | | refProjection: evalTemp_8, path: 'PathTraverse [1] PathIdentity []', intervals: {{{[Const [1], Const [3]]}}}, entryIndex: 3
+| | BindBlock:
+| RefBlock:
+| Variable [scan_0]
+Scan [test]
+ BindBlock:
+ [scan_0]
+ Source []
+
+
diff --git a/src/mongo/db/test_output/pipeline/abt/a_b_t_translate/partial_index.txt b/src/mongo/db/test_output/pipeline/abt/a_b_t_translate/partial_index.txt
new file mode 100644
index 00000000000..398b1092a35
--- /dev/null
+++ b/src/mongo/db/test_output/pipeline/abt/a_b_t_translate/partial_index.txt
@@ -0,0 +1,148 @@
+==== VARIATION: optimized partial index: the expression matches the pipeline ====
+-- INPUTS:
+pipeline: [{$match: {'a': 3, 'b': 2}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ index1:
+ collation spec:
+ ABT path:
+ PathGet [a]
+ PathTraverse [1]
+ PathIdentity []
+
+ collation op: Ascending
+ version: 2
+ ordering bits: 0
+ is multi-key: 1
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ requirementsMap:
+ refProjection: scan_0, path: 'PathGet [b] PathTraverse [1] PathIdentity []', intervals: {{{[Const [2], Const [2]]}}}
+
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+optimization phases:
+ ConstEvalPost
+ ConstEvalPre
+ MemoExplorationPhase
+ MemoImplementationPhase
+ MemoSubstitutionPhase
+ PathFuse
+ PathLower
+
+-- OUTPUT:
+Root []
+| | projections:
+| | scan_0
+| RefBlock:
+| Variable [scan_0]
+BinaryJoin [joinType: Inner, {rid_0}]
+| | Const [true]
+| Filter []
+| | FunctionCall [traverseF]
+| | | | Const [false]
+| | | LambdaAbstraction [valCmp_0]
+| | | BinaryOp [Eq]
+| | | | Const [2]
+| | | Variable [valCmp_0]
+| | Variable [evalTemp_4]
+| LimitSkip []
+| | limitSkip:
+| | limit: 1
+| | skip: 0
+| Seek [ridProjection: rid_0, {'<root>': scan_0, 'b': evalTemp_4}, collection]
+| | BindBlock:
+| | [evalTemp_4]
+| | Source []
+| | [scan_0]
+| | Source []
+| RefBlock:
+| Variable [rid_0]
+IndexScan [{'<rid>': rid_0}, scanDefName: collection, indexDefName: index1, interval: {[Const [3], Const [3]]}]
+ BindBlock:
+ [rid_0]
+ Source []
+
+
+==== VARIATION: optimized partial index negative: the expression does not match the pipeline ====
+-- INPUTS:
+pipeline: [{$match: {'a': 3, 'b': 3}}]
+metadata:
+ number of partitions: 1
+ scan definitions:
+ collection:
+ options:
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ indexes:
+ index1:
+ collation spec:
+ ABT path:
+ PathGet [a]
+ PathTraverse [1]
+ PathIdentity []
+
+ collation op: Ascending
+ version: 2
+ ordering bits: 0
+ is multi-key: 1
+ distribution and paths:
+ distribution type: Centralized
+ distribution paths:
+ requirementsMap:
+ refProjection: scan_0, path: 'PathGet [b] PathTraverse [1] PathIdentity []', intervals: {{{[Const [2], Const [2]]}}}
+
+ non multi-key index paths:
+ collection exists: 1
+ CE type: -1
+optimization phases:
+ ConstEvalPost
+ ConstEvalPre
+ MemoExplorationPhase
+ MemoImplementationPhase
+ MemoSubstitutionPhase
+ PathFuse
+ PathLower
+
+-- OUTPUT:
+Root []
+| | projections:
+| | scan_0
+| RefBlock:
+| Variable [scan_0]
+Filter []
+| FunctionCall [traverseF]
+| | | Const [false]
+| | LambdaAbstraction [valCmp_1]
+| | BinaryOp [Eq]
+| | | Const [3]
+| | Variable [valCmp_1]
+| Variable [evalTemp_3]
+Filter []
+| FunctionCall [traverseF]
+| | | Const [false]
+| | LambdaAbstraction [valCmp_0]
+| | BinaryOp [Eq]
+| | | Const [3]
+| | Variable [valCmp_0]
+| Variable [evalTemp_2]
+PhysicalScan [{'<root>': scan_0, 'a': evalTemp_2, 'b': evalTemp_3}, collection]
+ BindBlock:
+ [evalTemp_2]
+ Source []
+ [evalTemp_3]
+ Source []
+ [scan_0]
+ Source []
+
+