diff options
author | Jason Rassi <rassi@10gen.com> | 2014-09-19 16:12:05 -0400 |
---|---|---|
committer | Jason Rassi <rassi@10gen.com> | 2014-09-22 13:38:51 -0400 |
commit | ad1dafb013342faae7e998586a0c35b70ba75636 (patch) | |
tree | e73b3d1d5ce3941d2d2484c026a6f6dcf7990911 /src/mongo | |
parent | 5376c8ad356acc9e43e0b5e1c6cda995ea209ddf (diff) | |
download | mongo-ad1dafb013342faae7e998586a0c35b70ba75636.tar.gz |
SERVER-15287 Can't use index key pattern plugin fields to provide sort
Diffstat (limited to 'src/mongo')
-rw-r--r-- | src/mongo/db/query/SConscript | 10 | ||||
-rw-r--r-- | src/mongo/db/query/lite_parsed_query.cpp | 31 | ||||
-rw-r--r-- | src/mongo/db/query/lite_parsed_query.h | 9 | ||||
-rw-r--r-- | src/mongo/db/query/lite_parsed_query_test.cpp | 70 | ||||
-rw-r--r-- | src/mongo/db/query/planner_analysis.cpp | 17 | ||||
-rw-r--r-- | src/mongo/db/query/planner_analysis.h | 13 | ||||
-rw-r--r-- | src/mongo/db/query/planner_analysis_test.cpp | 112 | ||||
-rw-r--r-- | src/mongo/db/query/query_planner.cpp | 16 | ||||
-rw-r--r-- | src/mongo/db/query/query_planner_test.cpp | 44 | ||||
-rw-r--r-- | src/mongo/db/query/query_solution.cpp | 26 |
10 files changed, 236 insertions, 112 deletions
diff --git a/src/mongo/db/query/SConscript b/src/mongo/db/query/SConscript index bf0a38ee291..a202ab02557 100644 --- a/src/mongo/db/query/SConscript +++ b/src/mongo/db/query/SConscript @@ -192,6 +192,16 @@ env.CppUnitTest( ) env.CppUnitTest( + target="planner_analysis_test", + source=[ + "planner_analysis_test.cpp" + ], + LIBDEPS=[ + "query_planner", + ], +) + +env.CppUnitTest( target="planner_ixselect_test", source=[ "planner_ixselect_test.cpp" diff --git a/src/mongo/db/query/lite_parsed_query.cpp b/src/mongo/db/query/lite_parsed_query.cpp index 316a43fc8f8..a81d7a280db 100644 --- a/src/mongo/db/query/lite_parsed_query.cpp +++ b/src/mongo/db/query/lite_parsed_query.cpp @@ -94,11 +94,8 @@ namespace mongo { // Sort document normalization. BSONObj sort = el.Obj().getOwned(); - if (!sort.isEmpty()) { - if (!isValidSortOrder(sort)) { - return Status(ErrorCodes::BadValue, "bad sort specification"); - } - sort = normalizeSortOrder(sort); + if (!isValidSortOrder(sort)) { + return Status(ErrorCodes::BadValue, "bad sort specification"); } pq->_sort = sort; @@ -410,23 +407,6 @@ namespace mongo { return false; } - // static - BSONObj LiteParsedQuery::normalizeSortOrder(const BSONObj& sortObj) { - BSONObjBuilder b; - BSONObjIterator i(sortObj); - while (i.more()) { - BSONElement e = i.next(); - if (isTextScoreMeta(e)) { - b.append(e); - continue; - } - long long n = e.safeNumberLong(); - int sortOrder = n >= 0 ? 1 : -1; - b.append(e.fieldName(), sortOrder); - } - return b.obj(); - } - LiteParsedQuery::LiteParsedQuery() : _skip(0), _limit(0), @@ -710,11 +690,8 @@ namespace mongo { _options.hasReadPref = queryObj.hasField("$readPreference"); - if (!_sort.isEmpty()) { - if (!isValidSortOrder(_sort)) { - return Status(ErrorCodes::BadValue, "bad sort specification"); - } - _sort = normalizeSortOrder(_sort); + if (!isValidSortOrder(_sort)) { + return Status(ErrorCodes::BadValue, "bad sort specification"); } return validate(); diff --git a/src/mongo/db/query/lite_parsed_query.h b/src/mongo/db/query/lite_parsed_query.h index 86b0157243b..296a6555983 100644 --- a/src/mongo/db/query/lite_parsed_query.h +++ b/src/mongo/db/query/lite_parsed_query.h @@ -159,15 +159,6 @@ namespace mongo { */ static bool isQueryIsolated(const BSONObj& query); - /** - * Helper function to create a normalized sort object. - * Each element of the object returned satisfies one of: - * 1. a number with value 1 - * 2. a number with value -1 - * 3. isTextScoreMeta - */ - static BSONObj normalizeSortOrder(const BSONObj& sortObj); - // Names of the maxTimeMS command and query option. static const std::string cmdOptionMaxTimeMS; static const std::string queryOptionMaxTimeMS; diff --git a/src/mongo/db/query/lite_parsed_query_test.cpp b/src/mongo/db/query/lite_parsed_query_test.cpp index b0fd0c5591f..c6120ee247f 100644 --- a/src/mongo/db/query/lite_parsed_query_test.cpp +++ b/src/mongo/db/query/lite_parsed_query_test.cpp @@ -216,59 +216,43 @@ namespace { ASSERT_FALSE(isFirstElementTextScoreMeta("{a: {$meta: \"textScore\", b: 1}}")); } - void testSortOrder(bool expectedValid, const char* expectedStr, const char* sortStr) { - BSONObj sortOrder = fromjson(sortStr); - bool valid = LiteParsedQuery::isValidSortOrder(sortOrder); - if (expectedValid != valid) { - mongoutils::str::stream ss; - ss << sortStr << ": unexpected validation result. Expected: " << expectedValid; - FAIL(ss); - } - BSONObj normalizedSortOrder = LiteParsedQuery::normalizeSortOrder(sortOrder); - if (fromjson(expectedStr) != normalizedSortOrder) { - mongoutils::str::stream ss; - ss << sortStr << ": unexpected normalization result. Expected: " << expectedStr - << ". Actual: " << normalizedSortOrder.toString(); - FAIL(ss); - } - } - // - // Sort order validation and normalization + // Sort order validation // In a valid sort order, each element satisfies one of: // 1. a number with value 1 // 2. a number with value -1 // 3. isTextScoreMeta // - TEST(LiteParsedQueryTest, NormalizeAndValidateSortOrder) { + TEST(LiteParsedQueryTest, ValidateSortOrder) { // Valid sorts - testSortOrder(true, "{}", "{}"); - testSortOrder(true, "{a: 1}", "{a: 1}"); - testSortOrder(true, "{a: -1}", "{a: -1}"); - testSortOrder(true, "{a: {$meta: \"textScore\"}}", "{a: {$meta: \"textScore\"}}"); + ASSERT(LiteParsedQuery::isValidSortOrder(fromjson("{}"))); + ASSERT(LiteParsedQuery::isValidSortOrder(fromjson("{a: 1}"))); + ASSERT(LiteParsedQuery::isValidSortOrder(fromjson("{a: -1}"))); + ASSERT(LiteParsedQuery::isValidSortOrder(fromjson("{a: {$meta: \"textScore\"}}"))); // Invalid sorts - testSortOrder(false, "{a: 1}", "{a: 100}"); - testSortOrder(false, "{a: 1}", "{a: 0}"); - testSortOrder(false, "{a: -1}", "{a: -100}"); - testSortOrder(false, "{a: 1}", "{a: Infinity}"); - testSortOrder(false, "{a: -1}", "{a: -Infinity}"); - testSortOrder(false, "{a: 1}", "{a: true}"); - testSortOrder(false, "{a: 1}", "{a: false}"); - testSortOrder(false, "{a: 1}", "{a: null}"); - testSortOrder(false, "{a: 1}", "{a: {}}"); - testSortOrder(false, "{a: 1}", "{a: {b: 1}}"); - testSortOrder(false, "{a: 1}", "{a: []}"); - testSortOrder(false, "{a: 1}", "{a: [1, 2, 3]}"); - testSortOrder(false, "{a: 1}", "{a: \"\"}"); - testSortOrder(false, "{a: 1}", "{a: \"bb\"}"); - testSortOrder(false, "{a: 1}", "{a: {$meta: 1}}"); - testSortOrder(false, "{a: 1}", "{a: {$meta: \"image\"}}"); - testSortOrder(false, "{a: 1}", "{a: {$world: \"textScore\"}}"); - testSortOrder(false, "{a: 1}", "{a: {$meta: \"textScore\", b: 1}}"); - testSortOrder(false, "{'': 1}", "{'': 1}"); - testSortOrder(false, "{'': -1}", "{'': -1}"); + ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: 100}"))); + ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: 0}"))); + ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: -100}"))); + ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: Infinity}"))); + ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: -Infinity}"))); + ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: true}"))); + ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: false}"))); + ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: null}"))); + ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: {}}"))); + ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: {b: 1}}"))); + ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: []}"))); + ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: [1, 2, 3]}"))); + ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: \"\"}"))); + ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: \"bb\"}"))); + ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: {$meta: 1}}"))); + ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: {$meta: \"image\"}}"))); + ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: {$world: \"textScore\"}}"))); + ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{a: {$meta: \"textScore\"," + " b: 1}}"))); + ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{'': 1}"))); + ASSERT_FALSE(LiteParsedQuery::isValidSortOrder(fromjson("{'': -1}"))); } // diff --git a/src/mongo/db/query/planner_analysis.cpp b/src/mongo/db/query/planner_analysis.cpp index 048615e9e5e..52b081f9ad5 100644 --- a/src/mongo/db/query/planner_analysis.cpp +++ b/src/mongo/db/query/planner_analysis.cpp @@ -248,6 +248,23 @@ namespace mongo { } // namespace + // static + BSONObj QueryPlannerAnalysis::getSortPattern(const BSONObj& indexKeyPattern) { + BSONObjBuilder sortBob; + BSONObjIterator kpIt(indexKeyPattern); + while (kpIt.more()) { + BSONElement elt = kpIt.next(); + if (elt.type() == mongo::String) { + break; + } + long long val = elt.safeNumberLong(); + int sortOrder = val >= 0 ? 1 : -1; + sortBob.append(elt.fieldName(), sortOrder); + } + return sortBob.obj(); + } + + // static bool QueryPlannerAnalysis::explodeForSort(const CanonicalQuery& query, const QueryPlannerParams& params, QuerySolutionNode** solnRoot) { diff --git a/src/mongo/db/query/planner_analysis.h b/src/mongo/db/query/planner_analysis.h index 8547a0f15d8..b7591bb31b5 100644 --- a/src/mongo/db/query/planner_analysis.h +++ b/src/mongo/db/query/planner_analysis.h @@ -39,6 +39,19 @@ namespace mongo { class QueryPlannerAnalysis { public: /** + * Takes an index key pattern and returns an object describing the "maximal sort" that this + * index can provide. Returned object is in normalized sort form (all elements have value 1 + * or -1). + * + * Examples: + * - {a: 1, b: -1} => {a: 1, b: -1} + * - {a: true} => {a: 1} + * - {a: "hashed"} => {} + * - {a: 1, b: "text", c: 1} => {a: 1} + */ + static BSONObj getSortPattern(const BSONObj& indexKeyPattern); + + /** * In brief: performs sort and covering analysis. * * The solution rooted at 'solnRoot' provides data for the query, whether through some diff --git a/src/mongo/db/query/planner_analysis_test.cpp b/src/mongo/db/query/planner_analysis_test.cpp new file mode 100644 index 00000000000..e638228db50 --- /dev/null +++ b/src/mongo/db/query/planner_analysis_test.cpp @@ -0,0 +1,112 @@ +/** + * Copyright (C) 2014 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#include "mongo/db/query/planner_analysis.h" + +#include "mongo/db/json.h" +#include "mongo/unittest/unittest.h" + +using namespace mongo; + +namespace { + + TEST(QueryPlannerAnalysis, GetSortPatternBasic) { + ASSERT_EQUALS(fromjson("{a: 1}"), + QueryPlannerAnalysis::getSortPattern(fromjson("{a: 1}"))); + ASSERT_EQUALS(fromjson("{a: -1}"), + QueryPlannerAnalysis::getSortPattern(fromjson("{a: -1}"))); + ASSERT_EQUALS(fromjson("{a: 1, b: 1}"), + QueryPlannerAnalysis::getSortPattern(fromjson("{a: 1, b: 1}"))); + ASSERT_EQUALS(fromjson("{a: 1, b: -1}"), + QueryPlannerAnalysis::getSortPattern(fromjson("{a: 1, b: -1}"))); + ASSERT_EQUALS(fromjson("{a: -1, b: 1}"), + QueryPlannerAnalysis::getSortPattern(fromjson("{a: -1, b: 1}"))); + ASSERT_EQUALS(fromjson("{a: -1, b: -1}"), + QueryPlannerAnalysis::getSortPattern(fromjson("{a: -1, b: -1}"))); + } + + TEST(QueryPlannerAnalysis, GetSortPatternOtherElements) { + ASSERT_EQUALS(fromjson("{a: 1}"), + QueryPlannerAnalysis::getSortPattern(fromjson("{a: 0}"))); + ASSERT_EQUALS(fromjson("{a: 1}"), + QueryPlannerAnalysis::getSortPattern(fromjson("{a: 100}"))); + ASSERT_EQUALS(fromjson("{a: 1}"), + QueryPlannerAnalysis::getSortPattern(fromjson("{a: Infinity}"))); + ASSERT_EQUALS(fromjson("{a: 1}"), + QueryPlannerAnalysis::getSortPattern(fromjson("{a: true}"))); + ASSERT_EQUALS(fromjson("{a: 1}"), + QueryPlannerAnalysis::getSortPattern(fromjson("{a: false}"))); + ASSERT_EQUALS(fromjson("{a: 1}"), + QueryPlannerAnalysis::getSortPattern(fromjson("{a: []}"))); + ASSERT_EQUALS(fromjson("{a: 1}"), + QueryPlannerAnalysis::getSortPattern(fromjson("{a: {}}"))); + + ASSERT_EQUALS(fromjson("{a: -1}"), + QueryPlannerAnalysis::getSortPattern(fromjson("{a: -100}"))); + ASSERT_EQUALS(fromjson("{a: -1}"), + QueryPlannerAnalysis::getSortPattern(fromjson("{a: -Infinity}"))); + + ASSERT_EQUALS(fromjson("{}"), + QueryPlannerAnalysis::getSortPattern(fromjson("{}"))); + } + + TEST(QueryPlannerAnalysis, GetSortPatternSpecialIndexTypes) { + ASSERT_EQUALS(fromjson("{}"), + QueryPlannerAnalysis::getSortPattern(fromjson("{a: 'hashed'}"))); + ASSERT_EQUALS(fromjson("{}"), + QueryPlannerAnalysis::getSortPattern(fromjson("{a: 'text'}"))); + ASSERT_EQUALS(fromjson("{}"), + QueryPlannerAnalysis::getSortPattern(fromjson("{a: '2dsphere'}"))); + ASSERT_EQUALS(fromjson("{}"), + QueryPlannerAnalysis::getSortPattern(fromjson("{a: ''}"))); + ASSERT_EQUALS(fromjson("{}"), + QueryPlannerAnalysis::getSortPattern(fromjson("{a: 'foo'}"))); + + ASSERT_EQUALS(fromjson("{a: -1}"), + QueryPlannerAnalysis::getSortPattern(fromjson("{a: -1, b: 'text'}"))); + ASSERT_EQUALS(fromjson("{a: -1}"), + QueryPlannerAnalysis::getSortPattern(fromjson("{a: -1, b: '2dsphere'}"))); + ASSERT_EQUALS(fromjson("{a: 1}"), + QueryPlannerAnalysis::getSortPattern(fromjson("{a: 1, b: 'text'}"))); + ASSERT_EQUALS(fromjson("{a: 1}"), + QueryPlannerAnalysis::getSortPattern(fromjson("{a: 1, b: '2dsphere'}"))); + + ASSERT_EQUALS(fromjson("{a: 1}"), + QueryPlannerAnalysis::getSortPattern(fromjson("{a: 1, b: 'text', c: 1}"))); + ASSERT_EQUALS(fromjson("{a: 1}"), + QueryPlannerAnalysis::getSortPattern(fromjson("{a: 1, b: '2dsphere'," + " c: 1}"))); + + ASSERT_EQUALS(fromjson("{a: 1, b: 1}"), + QueryPlannerAnalysis::getSortPattern(fromjson("{a: 1, b: 1, c: 'text'}"))); + ASSERT_EQUALS(fromjson("{a: 1, b: 1}"), + QueryPlannerAnalysis::getSortPattern(fromjson("{a: 1, b: 1, c: 'text'," + " d: 1}"))); + } + +} // namespace diff --git a/src/mongo/db/query/query_planner.cpp b/src/mongo/db/query/query_planner.cpp index 4256c5dcb07..e6b06e32460 100644 --- a/src/mongo/db/query/query_planner.cpp +++ b/src/mongo/db/query/query_planner.cpp @@ -232,19 +232,7 @@ namespace mongo { } bool providesSort(const CanonicalQuery& query, const BSONObj& kp) { - BSONObjIterator sortIt(query.getParsed().getSort()); - BSONObjIterator kpIt(kp); - - while (sortIt.more() && kpIt.more()) { - // We want the field name to be the same as well (so we pass true). - // TODO: see if we can pull a reverse sort out... - if (0 != sortIt.next().woCompare(kpIt.next(), true)) { - return false; - } - } - - // every elt in sort matched kp - return !sortIt.more(); + return query.getParsed().getSort().isPrefixOf(kp); } // static @@ -877,7 +865,7 @@ namespace mongo { // 2dsphereIndexVersion=2) should be able to provide a sort for // find({b: GEO}).sort({a:1}). SERVER-10801. - const BSONObj kp = LiteParsedQuery::normalizeSortOrder(index.keyPattern); + const BSONObj kp = QueryPlannerAnalysis::getSortPattern(index.keyPattern); if (providesSort(query, kp)) { QLOG() << "Planner: outputting soln that uses index to provide sort." << endl; diff --git a/src/mongo/db/query/query_planner_test.cpp b/src/mongo/db/query/query_planner_test.cpp index c14ec5bac40..1c6a3d934f4 100644 --- a/src/mongo/db/query/query_planner_test.cpp +++ b/src/mongo/db/query/query_planner_test.cpp @@ -1345,6 +1345,19 @@ namespace { "node: {cscan: {dir: 1, filter: {}}}}}"); } + TEST_F(QueryPlannerTest, CantUseHashedIndexToProvideSortWithIndexablePred) { + addIndex(BSON("x" << "hashed")); + runQuerySortProj(BSON("x" << BSON("$in" << BSON_ARRAY(0 << 1))), BSON("x" << 1), BSONObj()); + + ASSERT_EQUALS(getNumSolutions(), 2U); + assertSolutionExists("{sort: {pattern: {x: 1}, limit: 0, node: " + "{fetch: {node: " + "{ixscan: {pattern: {x: 'hashed'}}}}}}}"); + assertSolutionExists("{sort: {pattern: {x: 1}, limit: 0, node: " + "{cscan: {dir: 1, filter: {x: {$in: [0, 1]}}}}}}"); + } + + TEST_F(QueryPlannerTest, CantUseTextIndexToProvideSort) { addIndex(BSON("x" << 1 << "_fts" << "text" << "_ftsx" << 1)); runQuerySortProj(BSONObj(), BSON("x" << 1), BSONObj()); @@ -1363,7 +1376,22 @@ namespace { "node: {cscan: {dir: 1, filter: {}}}}}"); } - TEST_F(QueryPlannerTest, CantUseCompoundGeoIndexToProvideSort) { + TEST_F(QueryPlannerTest, CantUseNonCompoundGeoIndexToProvideSortWithIndexablePred) { + addIndex(BSON("x" << "2dsphere")); + runQuerySortProj(fromjson("{x: {$geoIntersects: {$geometry: {type: 'Point'," + " coordinates: [0, 0]}}}}"), + BSON("x" << 1), + BSONObj()); + + ASSERT_EQUALS(getNumSolutions(), 2U); + assertSolutionExists("{sort: {pattern: {x: 1}, limit: 0, node: " + "{fetch: {node: " + "{ixscan: {pattern: {x: '2dsphere'}}}}}}}"); + assertSolutionExists("{sort: {pattern: {x: 1}, limit: 0, node: " + "{cscan: {dir: 1}}}}"); + } + + TEST_F(QueryPlannerTest, CantUseCompoundGeoIndexToProvideSortIfNoGeoPred) { addIndex(BSON("x" << 1 << "y" << "2dsphere")); runQuerySortProj(BSONObj(), BSON("x" << 1), BSONObj()); @@ -1372,6 +1400,20 @@ namespace { "node: {cscan: {dir: 1, filter: {}}}}}"); } + TEST_F(QueryPlannerTest, CanUseCompoundGeoIndexToProvideSortWithGeoPred) { + addIndex(BSON("x" << 1 << "y" << "2dsphere")); + runQuerySortProj(fromjson("{x: 1, y: {$geoIntersects: {$geometry: {type: 'Point'," + " coordinates: [0, 0]}}}}"), + BSON("x" << 1), + BSONObj()); + + ASSERT_EQUALS(getNumSolutions(), 2U); + assertSolutionExists("{fetch: {node: " + "{ixscan: {pattern: {x: 1, y: '2dsphere'}}}}}"); + assertSolutionExists("{sort: {pattern: {x: 1}, limit: 0, node: " + "{cscan: {dir: 1}}}}"); + } + TEST_F(QueryPlannerTest, BasicSortWithIndexablePred) { addIndex(BSON("a" << 1)); addIndex(BSON("b" << 1)); diff --git a/src/mongo/db/query/query_solution.cpp b/src/mongo/db/query/query_solution.cpp index 3e042cb2b7c..34a98a46d9c 100644 --- a/src/mongo/db/query/query_solution.cpp +++ b/src/mongo/db/query/query_solution.cpp @@ -26,10 +26,12 @@ * it in the license file. */ -#include "mongo/db/index_names.h" #include "mongo/db/query/query_solution.h" -#include "mongo/db/query/lite_parsed_query.h" + +#include "mongo/db/index_names.h" #include "mongo/db/matcher/expression_geo.h" +#include "mongo/db/query/planner_analysis.h" +#include "mongo/db/query/query_planner_common.h" namespace mongo { @@ -454,23 +456,11 @@ namespace mongo { void IndexScanNode::computeProperties() { _sorts.clear(); - BSONObj sortPattern; - { - BSONObjBuilder sortBob; - BSONObj normalizedIndexKeyPattern(LiteParsedQuery::normalizeSortOrder(indexKeyPattern)); - BSONObjIterator it(normalizedIndexKeyPattern); - while (it.more()) { - BSONElement elt = it.next(); - // Zero is returned if elt is not a number. This happens when elt is hashed or - // 2dsphere, our two projection indices. We want to drop those from the sort - // pattern. - int val = elt.numberInt() * direction; - if (0 != val) { - sortBob.append(elt.fieldName(), val); - } - } - sortPattern = sortBob.obj(); + BSONObj sortPattern = QueryPlannerAnalysis::getSortPattern(indexKeyPattern); + if (direction == -1) { + sortPattern = QueryPlannerCommon::reverseSortObj(sortPattern); } + _sorts.insert(sortPattern); const int nFields = sortPattern.nFields(); |