diff options
author | Justin Seyster <justin.seyster@mongodb.com> | 2018-06-22 18:36:03 -0400 |
---|---|---|
committer | Justin Seyster <justin.seyster@mongodb.com> | 2018-10-15 17:33:40 -0400 |
commit | 8e540c0b6db93ce994cc548f000900bdc740f80a (patch) | |
tree | e4a63314f67158d2d7a784a029f04e0d28865b88 /src | |
parent | f143314e5d5b1021ed11ab2a2c57d0e6ada24926 (diff) | |
download | mongo-8e540c0b6db93ce994cc548f000900bdc740f80a.tar.gz |
SERVER-13946 Put skip stages before fetch stages.
(cherry picked from commit 69f3e89f6921fc4ff2b5413952eeb517af69bb83)
Diffstat (limited to 'src')
-rw-r--r-- | src/mongo/db/query/planner_analysis.cpp | 14 | ||||
-rw-r--r-- | src/mongo/db/query/query_planner_test.cpp | 59 |
2 files changed, 59 insertions, 14 deletions
diff --git a/src/mongo/db/query/planner_analysis.cpp b/src/mongo/db/query/planner_analysis.cpp index cd11da91262..fe174018d47 100644 --- a/src/mongo/db/query/planner_analysis.cpp +++ b/src/mongo/db/query/planner_analysis.cpp @@ -723,6 +723,13 @@ QuerySolution* QueryPlannerAnalysis::analyzeDataAccess( } } + if (qr.getSkip()) { + auto skip = std::make_unique<SkipNode>(); + skip->skip = *qr.getSkip(); + skip->children.push_back(solnRoot.release()); + solnRoot = std::move(skip); + } + // Project the results. if (NULL != query.getProj()) { LOG(5) << "PROJECTION: Current plan is:\n" << redact(solnRoot->toString()); @@ -829,13 +836,6 @@ QuerySolution* QueryPlannerAnalysis::analyzeDataAccess( } } - if (qr.getSkip()) { - SkipNode* skip = new SkipNode(); - skip->skip = *qr.getSkip(); - skip->children.push_back(solnRoot.release()); - solnRoot.reset(skip); - } - // When there is both a blocking sort and a limit, the limit will // be enforced by the blocking sort. // Otherwise, we need to limit the results in the case of a hard limit diff --git a/src/mongo/db/query/query_planner_test.cpp b/src/mongo/db/query/query_planner_test.cpp index 4293806ae38..8f6b0293a84 100644 --- a/src/mongo/db/query/query_planner_test.cpp +++ b/src/mongo/db/query/query_planner_test.cpp @@ -425,10 +425,57 @@ TEST_F(QueryPlannerTest, BasicSkipWithIndex) { ASSERT_EQUALS(getNumSolutions(), 2U); assertSolutionExists("{skip: {n: 8, node: {cscan: {dir: 1, filter: {a: 5}}}}}"); assertSolutionExists( - "{skip: {n: 8, node: {fetch: {filter: null, node: " + "{fetch: {filter: null, node: {skip: {n: 8, node: " "{ixscan: {filter: null, pattern: {a: 1, b: 1}}}}}}}"); } +TEST_F(QueryPlannerTest, CoveredSkipWithIndex) { + addIndex(fromjson("{a: 1, b: 1}")); + + runQuerySortProjSkipNToReturn( + fromjson("{a: 5}"), BSONObj(), fromjson("{_id: 0, a: 1, b: 1}"), 8, 0); + + ASSERT_EQUALS(getNumSolutions(), 2U); + assertSolutionExists( + "{proj: {spec: {_id: 0, a: 1, b: 1}, node: " + "{skip: {n: 8, node: {cscan: {dir: 1, filter: {a: 5}}}}}}}"); + assertSolutionExists( + "{proj: {spec: {_id: 0, a: 1, b: 1}, " + "node: {skip: {n: 8, node: {ixscan: {filter: null, pattern: {a: 1, b: 1}}}}}}}"); +} + +TEST_F(QueryPlannerTest, SkipEvaluatesAfterFetchWithPredicate) { + addIndex(fromjson("{a: 1}")); + + runQuerySkipNToReturn(fromjson("{a: 5, b: 7}"), 8, 0); + + ASSERT_EQUALS(getNumSolutions(), 2U); + assertSolutionExists("{skip: {n: 8, node: {cscan: {dir: 1, filter: {a: 5, b: 7}}}}}"); + + // When a plan includes a fetch with no predicate, the skip should execute first, so we avoid + // fetching a document that we will always discard. When the fetch does have a predicate (as in + // this case), however, that optimization would be invalid; we need to fetch the document and + // evaluate the filter to determine if the document should count towards the number of skipped + // documents. + assertSolutionExists( + "{skip: {n: 8, node: {fetch: {filter: {b: 7}, node: " + "{ixscan: {filter: null, pattern: {a: 1}}}}}}}"); +} + +TEST_F(QueryPlannerTest, SkipEvaluatesBeforeFetchForIndexedOr) { + addIndex(fromjson("{a: 1}")); + + runQuerySkipNToReturn(fromjson("{$or: [{a: 5}, {a: 7}]}"), 8, 0); + + ASSERT_EQUALS(getNumSolutions(), 2U); + assertSolutionExists( + "{skip: {n: 8, node: " + "{cscan: {dir: 1, filter: {$or: [{a: 5}, {a: 7}]}}}}}"); + assertSolutionExists( + "{fetch: {filter: null, node: {skip: {n: 8, node: " + "{ixscan: {filter: null, pattern: {a: 1}}}}}}}"); +} + TEST_F(QueryPlannerTest, BasicLimitNoIndex) { addIndex(BSON("a" << 1)); @@ -481,9 +528,8 @@ TEST_F(QueryPlannerTest, SkipAndLimit) { "{limit: {n: 2, node: {skip: {n: 7, node: " "{cscan: {dir: 1, filter: {x: {$lte: 4}}}}}}}}"); assertSolutionExists( - "{limit: {n: 2, node: {skip: {n: 7, node: {fetch: " - "{filter: null, node: {ixscan: " - "{filter: null, pattern: {x: 1}}}}}}}}}"); + "{limit: {n: 2, node: {fetch: {filter: null, node: " + "{skip: {n: 7, node: {ixscan: {filter: null, pattern: {x: 1}}}}}}}}}"); } TEST_F(QueryPlannerTest, SkipAndSoftLimit) { @@ -496,9 +542,8 @@ TEST_F(QueryPlannerTest, SkipAndSoftLimit) { "{skip: {n: 7, node: " "{cscan: {dir: 1, filter: {x: {$lte: 4}}}}}}"); assertSolutionExists( - "{skip: {n: 7, node: {fetch: " - "{filter: null, node: {ixscan: " - "{filter: null, pattern: {x: 1}}}}}}}"); + "{fetch: {filter: null, node: {skip: {n: 7, node: " + "{ixscan: {filter: null, pattern: {x: 1}}}}}}}"); } // |