diff options
Diffstat (limited to 'src/mongo/db/query/query_planner_test.cpp')
-rw-r--r-- | src/mongo/db/query/query_planner_test.cpp | 392 |
1 files changed, 391 insertions, 1 deletions
diff --git a/src/mongo/db/query/query_planner_test.cpp b/src/mongo/db/query/query_planner_test.cpp index e07a5a37b51..6f69c90e520 100644 --- a/src/mongo/db/query/query_planner_test.cpp +++ b/src/mongo/db/query/query_planner_test.cpp @@ -1521,7 +1521,8 @@ TEST_F(QueryPlannerTest, CantUseHashedIndexToProvideSortWithIndexablePred) { TEST_F(QueryPlannerTest, CantUseTextIndexToProvideSort) { addIndex(BSON("x" << 1 << "_fts" << "text" - << "_ftsx" << 1)); + << "_ftsx" + << 1)); runQuerySortProj(BSONObj(), BSON("x" << 1), BSONObj()); ASSERT_EQUALS(getNumSolutions(), 1U); @@ -5686,4 +5687,393 @@ TEST_F(QueryPlannerTest, SolutionSetStableWhenOrEnumerationLimitIsReached) { "1}}}}}]}}"); } +// Test that we enumerate the expected plans with the special parameter set. In this test we have +// two branches of an $or, each with two possible indexed solutions. +TEST_F(QueryPlannerTest, LockstepOrEnumerationSanityCheckTwoChildrenTwoIndexesEach) { + params.options = + QueryPlannerParams::NO_TABLE_SCAN | QueryPlannerParams::ENUMERATE_OR_CHILDREN_LOCKSTEP; + addIndex(BSON("a" << 1 << "b" << 1)); + addIndex(BSON("a" << 1 << "c" << 1)); + + runQueryAsCommand( + fromjson("{find: 'testns', filter: {a: 1, $or: [{b: 1, c: 1}, {b: 2, c: 2}]}}")); + + assertNumSolutions(6U); + + assertSolutionExists( + "{or: {nodes: [{fetch: {filter: {c: {$eq: 1} }, node: {ixscan: {pattern: {a: 1, b: 1}}}}}, " + "{fetch: {filter: {c: {$eq: 2}}, node: {ixscan: {pattern: {a: 1, b: 1}}}}}]}}"); + assertSolutionExists( + "{or: {nodes: [{fetch: {filter: {b: {$eq: 1} }, node: {ixscan: {pattern: {a: 1, c: 1}}}}}, " + "{fetch: {filter: {b: {$eq: 2}}, node: {ixscan: {pattern: {a: 1, c: 1}}}}}]}}"); + assertSolutionExists( + "{or: {nodes: [{fetch: {filter: {c: {$eq: 1} }, node: {ixscan: {pattern: {a: 1, b: 1}}}}}, " + "{fetch: {filter: {b: {$eq: 2}}, node: {ixscan: {pattern: {a: 1, c: 1}}}}}]}}"); + assertSolutionExists( + "{or: {nodes: [{fetch: {filter: {b: {$eq: 1} }, node: {ixscan: {pattern: {a: 1, c: 1}}}}}, " + "{fetch: {filter: {c: {$eq: 2}}, node: {ixscan: {pattern: {a: 1, b: 1}}}}}]}}"); + assertSolutionExists( + "{fetch: {filter: {$or: [{b: {$eq: 1}, c: {$eq: 1}}, {b: {$eq: 2}, c: {$eq: 2}}]}, node: " + "{ixscan: {pattern: {a: 1, b: 1}}}}}}}"); + assertSolutionExists( + "{fetch: {filter: {$or: [{b: {$eq: 1}, c: {$eq: 1}}, {b: {$eq: 2}, c: {$eq: 2}}]}, node: " + "{ixscan: {pattern: {a: 1, c: 1}}}}}}}"); +} + +// Test that we enumerate the expected plans with the special parameter set. In this test we have +// two branches of an $or, each with one possible indexed solution. +TEST_F(QueryPlannerTest, LockstepOrEnumerationSanityCheckTwoChildrenOneIndexEach) { + params.options = + QueryPlannerParams::NO_TABLE_SCAN | QueryPlannerParams::ENUMERATE_OR_CHILDREN_LOCKSTEP; + addIndex(BSON("a" << 1 << "b" << 1)); + addIndex(BSON("a" << 1 << "c" << 1)); + + runQueryAsCommand(fromjson("{find: 'testns', filter: {a: 1, $or: [{b: 1}, {c: 2}]}}")); + + assertNumSolutions(3U); + + assertSolutionExists( + "{fetch: {filter: null, node: {or: {nodes: [{ixscan: {pattern: {a: 1, b: 1}}}, {ixscan: " + "{pattern: {a: 1, c: 1}}}]}}}}"); + assertSolutionExists( + "{fetch: {filter: {$or: [{b: {$eq: 1}}, {c: {$eq: 2}}]}, node: {ixscan: {pattern: {a: 1, " + "b: 1}}}}}}}"); + assertSolutionExists( + "{fetch: {filter: {$or: [{b: {$eq: 1}}, {c: {$eq: 2}}]}, node: {ixscan: {pattern: {a: 1, " + "c: 1}}}}}}}"); +} + +// Test that we enumerate the expected plans with the special parameter set. In this test we have +// two branches of an $or, one with one possible indexed solution, the other with two possible +// indexed solutions. +TEST_F(QueryPlannerTest, LockstepOrEnumerationSanityCheckTwoChildrenDifferentNumSolutions) { + params.options = + QueryPlannerParams::NO_TABLE_SCAN | QueryPlannerParams::ENUMERATE_OR_CHILDREN_LOCKSTEP; + addIndex(BSON("a" << 1 << "b" << 1)); + addIndex(BSON("a" << 1 << "c" << 1)); + + runQueryAsCommand(fromjson("{find: 'testns', filter: {a: 1, $or: [{b: 1}, {b: 2, c: 2}]}}")); + + assertNumSolutions(4U); + + assertSolutionExists( + "{fetch: {filter: null, node: {or: {nodes: [{ixscan: {pattern: {a: 1, b: 1}}}, {fetch: " + "{filter: {c: {$eq: 2}}, node: {ixscan: {pattern: {a: 1, b: 1}}}}}]}}}}"); + assertSolutionExists( + "{fetch: {filter: null, node: {or: {nodes: [{ixscan: {pattern: {a: 1, b: 1}}}, {fetch: " + "{filter: {b: {$eq: 2}}, node: {ixscan: {pattern: {a: 1, c: 1}}}}}]}}}}"); + assertSolutionExists( + "{fetch: {filter: {$or: [{b: {$eq: 1}}, {b: {$eq: 2}, c: {$eq: 2}}]}, node: {ixscan: " + "{pattern: {a: 1, b: 1}}}}}}}"); + assertSolutionExists( + "{fetch: {filter: {$or: [{b: {$eq: 1}}, {b: {$eq: 2}, c: {$eq: 2}}]}, node: {ixscan: " + "{pattern: {a: 1, c: 1}}}}}}}"); +} + +// Test that the special parameter does in fact impact the order of enumeration. Here we rely on the +// cap of number of or enumerations to prove that the plans we're interested in are enumerated +// before we hit the limit. +TEST_F(QueryPlannerTest, NormalOrEnumerationDoesNotPrioritizeLockstepIteration) { + params.options = QueryPlannerParams::NO_TABLE_SCAN; + ASSERT_EQ(internalQueryEnumerationMaxOrSolutions.load(), 10); + addIndex(BSON("a" << 1 << "b" << 1)); + addIndex(BSON("a" << 1 << "c" << 1)); + addIndex(BSON("a" << 1 << "d" << 1)); + + // For this query and the above indexes, each clause of the $or has three options to choose + // from, for a total of 3 * 3 * 3 = 27 possible enumerations for just that $or sub-branch. + runQueryAsCommand( + fromjson("{find: 'testns', filter: {a: 1, $or: [{b: 1, c: 1, d: 1}, {b: 2, c: 2, d: 2}, " + "{b: 3, c: 3, d: 3}]}}")); + + // The $or enumeration is limited to 10, and then we have three plans where just the {a: 1} + // predicate is indexed. + assertNumSolutions(13U); + + assertSolutionExists( + "{or: {nodes: [" + "{fetch: {filter: {c: {$eq: 1}, d: {$eq: 1}}, node: {ixscan: {pattern: {a: 1, b: 1}}}}}, " + "{fetch: {filter: {c: {$eq: 2}, d: {$eq: 2}}, node: {ixscan: {pattern: {a: 1, b: 1}}}}}, " + "{fetch: {filter: {c: {$eq: 3}, d: {$eq: 3}}, node: {ixscan: {pattern: {a: 1, b: 1}}}}} " + "]}}"); + // Because we did not set the 'ENUMERATE_OR_CHILDREN_LOCKSTEP' flag, we don't expect this + // solution to be generated. This is in contrast to the next test case. + ASSERT_THROWS( + assertSolutionExists( + "{or: {nodes: [" + "{fetch: {filter: {b: {$eq: 1}, c: {$eq: 1}}, node: {ixscan: {pattern: {a: 1, d: " + "1}}}}}, " + "{fetch: {filter: {b: {$eq: 2}, c: {$eq: 2}}, node: {ixscan: {pattern: {a: 1, d: " + "1}}}}}, " + "{fetch: {filter: {b: {$eq: 3}, c: {$eq: 3}}, node: {ixscan: {pattern: {a: 1, d: " + "1}}}}} " + "]}}"), + unittest::TestAssertionFailureException); + + // We still expect to generate the solutions which don't index the $or. + assertSolutionExists( + "{fetch: {filter: {$or: [" + "{b: {$eq: 1}, c: {$eq: 1}, d: {$eq: 1}}, " + "{b: {$eq: 2}, c: {$eq: 2}, d: {$eq: 2}}, " + "{b: {$eq: 3}, c: {$eq: 3}, d: {$eq: 3}} " + "]}, node: {ixscan: {pattern: {a: 1, b: 1}}}}}}"); +} + +TEST_F(QueryPlannerTest, LockstepOrEnumerationDoesPrioritizeLockstepIteration) { + params.options = + QueryPlannerParams::NO_TABLE_SCAN | QueryPlannerParams::ENUMERATE_OR_CHILDREN_LOCKSTEP; + ASSERT_EQ(internalQueryEnumerationMaxOrSolutions.load(), 10); + addIndex(BSON("a" << 1 << "b" << 1)); + addIndex(BSON("a" << 1 << "c" << 1)); + addIndex(BSON("a" << 1 << "d" << 1)); + + // For this query and the above indexes, each clause of the $or has three options to choose + // from, for a total of 3 * 3 * 3 = 27 possible enumerations for just that $or sub-branch. + runQueryAsCommand( + fromjson("{find: 'testns', filter: {a: 1, $or: [{b: 1, c: 1, d: 1}, {b: 2, c: 2, d: 2}, " + "{b: 3, c: 3, d: 3}]}}")); + + // The $or enumeration is limited to 10, and then we have three plans where just the {a: 1} + // predicate is indexed. + assertNumSolutions(13U); + + assertSolutionExists( + "{or: {nodes: [" + "{fetch: {filter: {c: {$eq: 1}, d: {$eq: 1}}, node: {ixscan: {pattern: {a: 1, b: 1}}}}}, " + "{fetch: {filter: {c: {$eq: 2}, d: {$eq: 2}}, node: {ixscan: {pattern: {a: 1, b: 1}}}}}, " + "{fetch: {filter: {c: {$eq: 3}, d: {$eq: 3}}, node: {ixscan: {pattern: {a: 1, b: 1}}}}} " + "]}}"); + assertSolutionExists( + "{or: {nodes: [" + "{fetch: {filter: {b: {$eq: 1}, d: {$eq: 1}}, node: {ixscan: {pattern: {a: 1, c: 1}}}}}, " + "{fetch: {filter: {b: {$eq: 2}, d: {$eq: 2}}, node: {ixscan: {pattern: {a: 1, c: 1}}}}}, " + "{fetch: {filter: {b: {$eq: 3}, d: {$eq: 3}}, node: {ixscan: {pattern: {a: 1, c: 1}}}}} " + "]}}"); + assertSolutionExists( + "{or: {nodes: [" + "{fetch: {filter: {b: {$eq: 1}, c: {$eq: 1}}, node: {ixscan: {pattern: {a: 1, d: 1}}}}}, " + "{fetch: {filter: {b: {$eq: 2}, c: {$eq: 2}}, node: {ixscan: {pattern: {a: 1, d: 1}}}}}, " + "{fetch: {filter: {b: {$eq: 3}, c: {$eq: 3}}, node: {ixscan: {pattern: {a: 1, d: 1}}}}} " + "]}}"); + assertSolutionExists( + "{fetch: {filter: {$or: [" + "{b: {$eq: 1}, c: {$eq: 1}, d: {$eq: 1}}, " + "{b: {$eq: 2}, c: {$eq: 2}, d: {$eq: 2}}, " + "{b: {$eq: 3}, c: {$eq: 3}, d: {$eq: 3}} " + "]}, node: {ixscan: {pattern: {a: 1, b: 1}}}}}}"); +} + +TEST_F(QueryPlannerTest, LockstepOrEnumerationDoesPrioritizeLockstepIterationMixedChildren) { + params.options = + QueryPlannerParams::NO_TABLE_SCAN | QueryPlannerParams::ENUMERATE_OR_CHILDREN_LOCKSTEP; + ASSERT_EQ(internalQueryEnumerationMaxOrSolutions.load(), 10); + addIndex(BSON("a" << 1 << "b" << 1)); + addIndex(BSON("a" << 1 << "c" << 1)); + addIndex(BSON("a" << 1 << "d" << 1)); + addIndex(BSON("a" << 1 << "e" << 1)); + + // For this query and the above indexes, each clause of the $or has a varying number options to + // choose from, for a total of 2 * 3 * 4 * 2 = 48 possible enumerations for just that $or + // sub-branch. + runQueryAsCommand( + fromjson("{find: 'testns', filter: {" + " a: 1," + " $or: [" + " {b: 2.1, c: 2.1}," + " {b: 3, c: 3, d: 3}," + " {b: 4, c: 4, d: 4, e: 4}," + " {b: 2.2, c: 2.2}" + "]}}")); + + // The $or enumeration is limited to 10, and then we have four plans where just the {a: 1} + // predicate is indexed. + assertNumSolutions(14U); + + // Lockstep enumerations. Definitely expected. + assertSolutionExists( + "{or: {nodes: [" + "{fetch: {filter: {c: {$eq: 2.1}}, node: {ixscan: {pattern: {a: 1, b: 1}}}}}, " + "{fetch: {filter: {c: {$eq: 3}, d: {$eq: 3}}, node: {ixscan: {pattern: {a: 1, b: 1}}}}}, " + "{fetch: {filter: {c: {$eq: 4}, d: {$eq: 4}, e: {$eq: 4}}," + " node: {ixscan: {pattern: {a: 1, b: 1}}}}}, " + "{fetch: {filter: {c: {$eq: 2.2}}, node: {ixscan: {pattern: {a: 1, b: 1}}}}}" + "]}}"); + assertSolutionExists( + "{or: {nodes: [" + "{fetch: {filter: {b: {$eq: 2.1}}, node: {ixscan: {pattern: {a: 1, c: 1}}}}}, " + "{fetch: {filter: {b: {$eq: 3}, d: {$eq: 3}}, node: {ixscan: {pattern: {a: 1, c: 1}}}}}, " + "{fetch: {filter: {b: {$eq: 4}, d: {$eq: 4}, e: {$eq: 4}}," + " node: {ixscan: {pattern: {a: 1, c: 1}}}}}, " + "{fetch: {filter: {b: {$eq: 2.2}}, node: {ixscan: {pattern: {a: 1, c: 1}}}}}" + "]}}"); + // Everyone advances one more time, no longer lock step. + assertSolutionExists( + "{or: {nodes: [" + "{fetch: {filter: {c: {$eq: 2.1}}, node: {ixscan: {pattern: {a: 1, b: 1}}}}}, " + "{fetch: {filter: {b: {$eq: 3}, c: {$eq: 3}}, node: {ixscan: {pattern: {a: 1, d: 1}}}}}, " + "{fetch: {filter: {b: {$eq: 4}, c: {$eq: 4}, e: {$eq: 4}}," + " node: {ixscan: {pattern: {a: 1, d: 1}}}}}, " + "{fetch: {filter: {c: {$eq: 2.2}}, node: {ixscan: {pattern: {a: 1, b: 1}}}}}" + "]}}"); + // Normal enumeration. Here we observe an interesting phenomena. Before we get into plan + // enumeration, the query is parsed and "normalized". This process involves putting the query in + // a canonical order, in part so that similar queries can be recognized as such for caching. In + // this case, it orders the $or children by their respective number of children. So our original + // query will be enumerated as if it were typed in this order: + // {a: 1, + // $or: [ + // {b: 2.1, c: 2.1}, + // {b: 2.2, c: 2.2}, + // {b: 3, c: 3, d: 3}, + // {b: 4, c: 4, d: 4, e: 4} + // ] + // } + // Here are the exact plans: + assertSolutionExists( + "{or: {nodes: [" + "{fetch: {filter: {b: {$eq: 2.1}}, node: {ixscan: {pattern: {a: 1, c: 1}}}}}, " + "{fetch: {filter: {c: {$eq: 2.2}}, node: {ixscan: {pattern: {a: 1, b: 1}}}}}, " + "{fetch: {filter: {b: {$eq: 3}, c: {$eq: 3}}, node: {ixscan: {pattern: {a: 1, d: 1}}}}}, " + "{fetch: {filter: {b: {$eq: 4}, c: {$eq: 4}, e: {$eq: 4}}," + " node: {ixscan: {pattern: {a: 1, d: 1}}}}}" + "]}}"); + assertSolutionExists( + "{or: {nodes: [" + "{fetch: {filter: {c: {$eq: 2.1}}, node: {ixscan: {pattern: {a: 1, b: 1}}}}}, " + "{fetch: {filter: {b: {$eq: 2.2}}, node: {ixscan: {pattern: {a: 1, c: 1}}}}}, " + "{fetch: {filter: {b: {$eq: 3}, c: {$eq: 3}}, node: {ixscan: {pattern: {a: 1, d: 1}}}}}, " + "{fetch: {filter: {b: {$eq: 4}, c: {$eq: 4}, e: {$eq: 4}}," + " node: {ixscan: {pattern: {a: 1, d: 1}}}}}" + "]}}"); + assertSolutionExists( + "{or: {nodes: [" + "{fetch: {filter: {b: {$eq: 2.1}}, node: {ixscan: {pattern: {a: 1, c: 1}}}}}, " + "{fetch: {filter: {b: {$eq: 2.2}}, node: {ixscan: {pattern: {a: 1, c: 1}}}}}, " + "{fetch: {filter: {b: {$eq: 3}, c: {$eq: 3}}, node: {ixscan: {pattern: {a: 1, d: 1}}}}}, " + "{fetch: {filter: {b: {$eq: 4}, c: {$eq: 4}, e: {$eq: 4}}," + " node: {ixscan: {pattern: {a: 1, d: 1}}}}}" + "]}}"); + assertSolutionExists( + "{or: {nodes: [" + "{fetch: {filter: {c: {$eq: 2.1}}, node: {ixscan: {pattern: {a: 1, b: 1}}}}}, " + "{fetch: {filter: {c: {$eq: 2.2}}, node: {ixscan: {pattern: {a: 1, b: 1}}}}}, " + "{fetch: {filter: {c: {$eq: 3}, d: {$eq: 3}}, node: {ixscan: {pattern: {a: 1, b: 1}}}}}, " + "{fetch: {filter: {b: {$eq: 4}, c: {$eq: 4}, d: {$eq: 4}}," + " node: {ixscan: {pattern: {a: 1, e: 1}}}}}" + "]}}"); + assertSolutionExists( + "{or: {nodes: [" + "{fetch: {filter: {b: {$eq: 2.1}}, node: {ixscan: {pattern: {a: 1, c: 1}}}}}, " + "{fetch: {filter: {c: {$eq: 2.2}}, node: {ixscan: {pattern: {a: 1, b: 1}}}}}, " + "{fetch: {filter: {c: {$eq: 3}, d: {$eq: 3}}, node: {ixscan: {pattern: {a: 1, b: 1}}}}}, " + "{fetch: {filter: {b: {$eq: 4}, c: {$eq: 4}, d: {$eq: 4}}," + " node: {ixscan: {pattern: {a: 1, e: 1}}}}}" + "]}}"); + assertSolutionExists( + "{or: {nodes: [" + "{fetch: {filter: {c: {$eq: 2.1}}, node: {ixscan: {pattern: {a: 1, b: 1}}}}}, " + "{fetch: {filter: {b: {$eq: 2.2}}, node: {ixscan: {pattern: {a: 1, c: 1}}}}}, " + "{fetch: {filter: {c: {$eq: 3}, d: {$eq: 3}}, node: {ixscan: {pattern: {a: 1, b: 1}}}}}, " + "{fetch: {filter: {b: {$eq: 4}, c: {$eq: 4}, d: {$eq: 4}}," + " node: {ixscan: {pattern: {a: 1, e: 1}}}}}" + "]}}"); + assertSolutionExists( + "{or: {nodes: [" + "{fetch: {filter: {b: {$eq: 2.1}}, node: {ixscan: {pattern: {a: 1, c: 1}}}}}, " + "{fetch: {filter: {b: {$eq: 2.2}}, node: {ixscan: {pattern: {a: 1, c: 1}}}}}, " + "{fetch: {filter: {c: {$eq: 3}, d: {$eq: 3}}, node: {ixscan: {pattern: {a: 1, b: 1}}}}}, " + "{fetch: {filter: {b: {$eq: 4}, c: {$eq: 4}, d: {$eq: 4}}," + " node: {ixscan: {pattern: {a: 1, e: 1}}}}}" + "]}}"); + + // Now to the solutions which don't index the $or. + assertSolutionExists( + "{fetch: {filter: {$or: [" + "{b: {$eq: 2.1}, c: {$eq: 2.1}}, " + "{b: {$eq: 2.2}, c: {$eq: 2.2}}, " + "{b: {$eq: 3}, c: {$eq: 3}, d: {$eq: 3}}, " + "{b: {$eq: 4}, c: {$eq: 4}, d: {$eq: 4}, e: {$eq: 4}} " + "]}, node: {ixscan: {pattern: {a: 1, b: 1}}}}}}"); + assertSolutionExists( + "{fetch: {filter: {$or: [" + "{b: {$eq: 2.1}, c: {$eq: 2.1}}, " + "{b: {$eq: 2.2}, c: {$eq: 2.2}}, " + "{b: {$eq: 3}, c: {$eq: 3}, d: {$eq: 3}}, " + "{b: {$eq: 4}, c: {$eq: 4}, d: {$eq: 4}, e: {$eq: 4}} " + "]}, node: {ixscan: {pattern: {a: 1, c: 1}}}}}}"); + assertSolutionExists( + "{fetch: {filter: {$or: [" + "{b: {$eq: 2.1}, c: {$eq: 2.1}}, " + "{b: {$eq: 2.2}, c: {$eq: 2.2}}, " + "{b: {$eq: 3}, c: {$eq: 3}, d: {$eq: 3}}, " + "{b: {$eq: 4}, c: {$eq: 4}, d: {$eq: 4}, e: {$eq: 4}} " + "]}, node: {ixscan: {pattern: {a: 1, d: 1}}}}}}"); + assertSolutionExists( + "{fetch: {filter: {$or: [" + "{b: {$eq: 2.1}, c: {$eq: 2.1}}, " + "{b: {$eq: 2.2}, c: {$eq: 2.2}}, " + "{b: {$eq: 3}, c: {$eq: 3}, d: {$eq: 3}}, " + "{b: {$eq: 4}, c: {$eq: 4}, d: {$eq: 4}, e: {$eq: 4}} " + "]}, node: {ixscan: {pattern: {a: 1, e: 1}}}}}}"); +} + +TEST_F(QueryPlannerTest, LockstepOrEnumerationApplysToEachOrInTree) { + params.options = + QueryPlannerParams::NO_TABLE_SCAN | QueryPlannerParams::ENUMERATE_OR_CHILDREN_LOCKSTEP; + ASSERT_EQ(internalQueryEnumerationMaxOrSolutions.load(), 10); + addIndex(BSON("a" << 1 << "b" << 1)); + addIndex(BSON("a" << 1 << "c" << 1)); + addIndex(BSON("a" << 1 << "x" << 1)); + addIndex(BSON("a" << 1 << "y" << 1)); + + // For this query and the above indexes, each clause of the $or has 2 indexes to choose from, + // for a total of 2 * 2 * 2 * 2 = 16 possible enumerations for just that $or sub-branch. + runQueryAsCommand( + fromjson("{find: 'testns', filter: {" + " a: 1," + " $or: [" + " {b: 2.1, c: 2.1}," + " {b: 2.2, c: 2.2}," + " {$and: [" + " {unindexed: 'thisPredicateToEnsureNestedOrsAreNotCombined'}," + " {$or: [" + " {x: 3.0, y: 3.0}," + " {x: 3.1, y: 3.1}" + " ]}" + " ]}" + "]}}")); + + // The $or enumeration is limited to 10, and then we have 4 plans where just the {a: 1} + // predicate is indexed. + assertNumSolutions(14U); + + // Both lockstep enumerations should be present. + assertSolutionExists( + "{or: {nodes: [" + " {fetch: {filter: {c: {$eq: 2.1}}, node: {ixscan: {pattern: {a: 1, b: 1}}}}}, " + " {fetch: {filter: {c: {$eq: 2.2}}, node: {ixscan: {pattern: {a: 1, b: 1}}}}}, " + " {fetch: {" + " filter: {unindexed: {$eq: 'thisPredicateToEnsureNestedOrsAreNotCombined'}}," + " node: {" + " or: {nodes: [" + " {fetch: {filter: {y: {$eq: 3.0}}, node: {ixscan: {pattern: {a: 1, x: 1}}}}}," + " {fetch: {filter: {y: {$eq: 3.1}}, node: {ixscan: {pattern: {a: 1, x: 1}}}}}" + " ]}}" + " }}" + "]}}"); + assertSolutionExists( + "{or: {nodes: [" + " {fetch: {filter: {b: {$eq: 2.1}}, node: {ixscan: {pattern: {a: 1, c: 1}}}}}, " + " {fetch: {filter: {b: {$eq: 2.2}}, node: {ixscan: {pattern: {a: 1, c: 1}}}}}, " + " {fetch: {" + " filter: {unindexed: {$eq: 'thisPredicateToEnsureNestedOrsAreNotCombined'}}," + " node: {" + " or: {nodes: [" + " {fetch: {filter: {x: {$eq: 3.0}}, node: {ixscan: {pattern: {a: 1, y: 1}}}}}," + " {fetch: {filter: {x: {$eq: 3.1}}, node: {ixscan: {pattern: {a: 1, y: 1}}}}}" + " ]}}" + " }}" + "]}}"); +} + } // namespace |