diff options
author | David Storch <david.storch@10gen.com> | 2014-06-05 11:47:08 -0400 |
---|---|---|
committer | David Storch <david.storch@10gen.com> | 2014-06-05 18:33:22 -0400 |
commit | 89383b224f061d306240d621745ba082a4ecb23b (patch) | |
tree | f2fd358eb3ef06c2a6e8272372c726b64bdbb519 /src/mongo/db | |
parent | 108ec7640743a97d2e02a081ef16d0c4da748c67 (diff) | |
download | mongo-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.cpp | 15 | ||||
-rw-r--r-- | src/mongo/db/query/query_planner.cpp | 16 | ||||
-rw-r--r-- | src/mongo/db/query/query_planner_test.cpp | 78 |
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}")); |