summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJustin Seyster <justin.seyster@mongodb.com>2018-06-22 18:36:03 -0400
committerJustin Seyster <justin.seyster@mongodb.com>2018-10-15 17:33:40 -0400
commit8e540c0b6db93ce994cc548f000900bdc740f80a (patch)
treee4a63314f67158d2d7a784a029f04e0d28865b88 /src
parentf143314e5d5b1021ed11ab2a2c57d0e6ada24926 (diff)
downloadmongo-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.cpp14
-rw-r--r--src/mongo/db/query/query_planner_test.cpp59
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}}}}}}}");
}
//