summaryrefslogtreecommitdiff
path: root/src/mongo/db
diff options
context:
space:
mode:
authorDavid Storch <david.storch@10gen.com>2014-06-05 11:47:08 -0400
committerDavid Storch <david.storch@10gen.com>2014-06-05 18:33:22 -0400
commit89383b224f061d306240d621745ba082a4ecb23b (patch)
treef2fd358eb3ef06c2a6e8272372c726b64bdbb519 /src/mongo/db
parent108ec7640743a97d2e02a081ef16d0c4da748c67 (diff)
downloadmongo-89383b224f061d306240d621745ba082a4ecb23b.tar.gz
SERVER-14176 query planner should obey $natural sort
Diffstat (limited to 'src/mongo/db')
-rw-r--r--src/mongo/db/query/planner_access.cpp15
-rw-r--r--src/mongo/db/query/query_planner.cpp16
-rw-r--r--src/mongo/db/query/query_planner_test.cpp78
3 files changed, 98 insertions, 11 deletions
diff --git a/src/mongo/db/query/planner_access.cpp b/src/mongo/db/query/planner_access.cpp
index ba8ed3644f7..734cb2f9984 100644
--- a/src/mongo/db/query/planner_access.cpp
+++ b/src/mongo/db/query/planner_access.cpp
@@ -69,18 +69,19 @@ namespace mongo {
csn->tailable = tailable;
csn->maxScan = query.getParsed().getMaxScan();
- // If the sort is {$natural: +-1} this changes the direction of the collection scan.
- const BSONObj& sortObj = query.getParsed().getSort();
- if (!sortObj.isEmpty()) {
- BSONElement natural = sortObj.getFieldDotted("$natural");
+ // If the hint is {$natural: +-1} this changes the direction of the collection scan.
+ if (!query.getParsed().getHint().isEmpty()) {
+ BSONElement natural = query.getParsed().getHint().getFieldDotted("$natural");
if (!natural.eoo()) {
csn->direction = natural.numberInt() >= 0 ? 1 : -1;
}
}
- // The hint can specify $natural as well.
- if (!query.getParsed().getHint().isEmpty()) {
- BSONElement natural = query.getParsed().getHint().getFieldDotted("$natural");
+ // The sort can specify $natural as well. The sort direction should override the hint
+ // direction if both are specified.
+ const BSONObj& sortObj = query.getParsed().getSort();
+ if (!sortObj.isEmpty()) {
+ BSONElement natural = sortObj.getFieldDotted("$natural");
if (!natural.eoo()) {
csn->direction = natural.numberInt() >= 0 ? 1 : -1;
}
diff --git a/src/mongo/db/query/query_planner.cpp b/src/mongo/db/query/query_planner.cpp
index 3a333d707f3..1e16eb47815 100644
--- a/src/mongo/db/query/query_planner.cpp
+++ b/src/mongo/db/query/query_planner.cpp
@@ -415,10 +415,18 @@ namespace mongo {
return Status::OK();
}
- // The hint can be $natural: 1. If this happens, output a collscan.
- if (!query.getParsed().getHint().isEmpty()) {
- BSONElement natural = query.getParsed().getHint().getFieldDotted("$natural");
- if (!natural.eoo()) {
+ // The hint or sort can be $natural: 1. If this happens, output a collscan. If both
+ // a $natural hint and a $natural sort are specified, then the direction of the collscan
+ // is determined by the sign of the sort (not the sign of the hint).
+ if (!query.getParsed().getHint().isEmpty() || !query.getParsed().getSort().isEmpty()) {
+ BSONObj hintObj = query.getParsed().getHint();
+ BSONObj sortObj = query.getParsed().getSort();
+ BSONElement naturalHint = hintObj.getFieldDotted("$natural");
+ BSONElement naturalSort = sortObj.getFieldDotted("$natural");
+
+ // A hint overrides a $natural sort. This means that we don't force a table
+ // scan if there is a $natural sort with a non-$natural hint.
+ if (!naturalHint.eoo() || (!naturalSort.eoo() && hintObj.isEmpty())) {
QLOG() << "Forcing a table scan due to hinted $natural\n";
// min/max are incompatible with $natural.
if (canTableScan && query.getParsed().getMin().isEmpty()
diff --git a/src/mongo/db/query/query_planner_test.cpp b/src/mongo/db/query/query_planner_test.cpp
index e2b30e2ecbd..4fdd393c0d9 100644
--- a/src/mongo/db/query/query_planner_test.cpp
+++ b/src/mongo/db/query/query_planner_test.cpp
@@ -2587,6 +2587,84 @@ namespace {
"{cscan: {filter: {a: 1}, dir: 1}}}}");
}
+ // Test $natural sort and its interaction with $natural hint.
+ TEST_F(QueryPlannerTest, NaturalSortAndHint) {
+ addIndex(BSON("x" << 1));
+
+ // Non-empty query, -1 sort, no hint.
+ runQuerySortHint(fromjson("{x: {$exists: true}}"), BSON("$natural" << -1), BSONObj());
+ assertNumSolutions(1U);
+ assertSolutionExists("{cscan: {dir: -1}}");
+
+ // Non-empty query, 1 sort, no hint.
+ runQuerySortHint(fromjson("{x: {$exists: true}}"), BSON("$natural" << 1), BSONObj());
+ assertNumSolutions(1U);
+ assertSolutionExists("{cscan: {dir: 1}}");
+
+ // Non-empty query, -1 sort, -1 hint.
+ runQuerySortHint(fromjson("{x: {$exists: true}}"), BSON("$natural" << -1),
+ BSON("$natural" << -1));
+ assertNumSolutions(1U);
+ assertSolutionExists("{cscan: {dir: -1}}");
+
+ // Non-empty query, 1 sort, -1 hint.
+ runQuerySortHint(fromjson("{x: {$exists: true}}"), BSON("$natural" << 1),
+ BSON("$natural" << -1));
+ assertNumSolutions(1U);
+ assertSolutionExists("{cscan: {dir: 1}}");
+
+ // Non-empty query, -1 sort, 1 hint.
+ runQuerySortHint(fromjson("{x: {$exists: true}}"), BSON("$natural" << -1),
+ BSON("$natural" << 1));
+ assertNumSolutions(1U);
+ assertSolutionExists("{cscan: {dir: -1}}");
+
+ // Non-empty query, 1 sort, 1 hint.
+ runQuerySortHint(fromjson("{x: {$exists: true}}"), BSON("$natural" << 1),
+ BSON("$natural" << 1));
+ assertNumSolutions(1U);
+ assertSolutionExists("{cscan: {dir: 1}}");
+
+ // Empty query, -1 sort, no hint.
+ runQuerySortHint(BSONObj(), BSON("$natural" << -1), BSONObj());
+ assertNumSolutions(1U);
+ assertSolutionExists("{cscan: {dir: -1}}");
+
+ // Empty query, 1 sort, no hint.
+ runQuerySortHint(BSONObj(), BSON("$natural" << 1), BSONObj());
+ assertNumSolutions(1U);
+ assertSolutionExists("{cscan: {dir: 1}}");
+
+ // Empty query, -1 sort, -1 hint.
+ runQuerySortHint(BSONObj(), BSON("$natural" << -1), BSON("$natural" << -1));
+ assertNumSolutions(1U);
+ assertSolutionExists("{cscan: {dir: -1}}");
+
+ // Empty query, 1 sort, -1 hint.
+ runQuerySortHint(BSONObj(), BSON("$natural" << 1), BSON("$natural" << -1));
+ assertNumSolutions(1U);
+ assertSolutionExists("{cscan: {dir: 1}}");
+
+ // Empty query, -1 sort, 1 hint.
+ runQuerySortHint(BSONObj(), BSON("$natural" << -1), BSON("$natural" << 1));
+ assertNumSolutions(1U);
+ assertSolutionExists("{cscan: {dir: -1}}");
+
+ // Empty query, 1 sort, 1 hint.
+ runQuerySortHint(BSONObj(), BSON("$natural" << 1), BSON("$natural" << 1));
+ assertNumSolutions(1U);
+ assertSolutionExists("{cscan: {dir: 1}}");
+ }
+
+ TEST_F(QueryPlannerTest, HintOverridesNaturalSort) {
+ addIndex(BSON("x" << 1));
+ runQuerySortHint(fromjson("{x: {$exists: true}}"), BSON("$natural" << -1), BSON("x" << 1));
+
+ assertNumSolutions(1U);
+ assertSolutionExists("{fetch: {filter: {x:{$exists:true}}, node: "
+ "{ixscan: {filter: null, pattern: {x: 1}}}}}");
+ }
+
TEST_F(QueryPlannerTest, HintValid) {
addIndex(BSON("a" << 1));
runQueryHint(BSONObj(), fromjson("{a: 1}"));