diff options
author | Kyle Erf <erf@mongodb.com> | 2016-03-14 18:29:06 -0400 |
---|---|---|
committer | Kyle Erf <erf@mongodb.com> | 2016-03-14 18:29:06 -0400 |
commit | c5de48810757e217b0d68d59a48d11d9de42add6 (patch) | |
tree | 6c28851df5731759e8916a265e9cb3d756ba54e5 | |
parent | 488f48f2f497a4e844aa3057e49f96d2a4be1bcb (diff) | |
download | mongo-c5de48810757e217b0d68d59a48d11d9de42add6.tar.gz |
Revert "SERVER-22785 Update QueryPlannerIXSelect's index selection code to be collation-aware"
This reverts commit 0c25cd64ed475dbc88008af3c076cf2f235ce8d5.
-rw-r--r-- | src/mongo/db/query/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/db/query/indexability.h | 9 | ||||
-rw-r--r-- | src/mongo/db/query/planner_ixselect.cpp | 72 | ||||
-rw-r--r-- | src/mongo/db/query/planner_ixselect.h | 10 | ||||
-rw-r--r-- | src/mongo/db/query/planner_ixselect_test.cpp | 482 | ||||
-rw-r--r-- | src/mongo/db/query/query_planner.cpp | 3 |
6 files changed, 27 insertions, 550 deletions
diff --git a/src/mongo/db/query/SConscript b/src/mongo/db/query/SConscript index 65c93dcff6c..574bc0ede8e 100644 --- a/src/mongo/db/query/SConscript +++ b/src/mongo/db/query/SConscript @@ -305,7 +305,6 @@ env.CppUnitTest( "planner_ixselect_test.cpp" ], LIBDEPS=[ - "collation/collator_interface_mock", "query_planner", ], ) diff --git a/src/mongo/db/query/indexability.h b/src/mongo/db/query/indexability.h index 6c8ce44bbb8..a68bf3f328a 100644 --- a/src/mongo/db/query/indexability.h +++ b/src/mongo/db/query/indexability.h @@ -131,15 +131,6 @@ public: return isBoundsGeneratingNot(me) || nodeCanUseIndexOnOwnField(me); } - /** - * Returns true if 'me' is of type EQ, GT, GTE, LT, or LTE. - */ - static bool isEqualityOrInequality(const MatchExpression* me) { - return (me->matchType() == MatchExpression::EQ || me->matchType() == MatchExpression::GT || - me->matchType() == MatchExpression::GTE || me->matchType() == MatchExpression::LT || - me->matchType() == MatchExpression::LTE); - } - private: /** * Returns true if 'me' is "sargable" but is not a negation and diff --git a/src/mongo/db/query/planner_ixselect.cpp b/src/mongo/db/query/planner_ixselect.cpp index f340f39915a..52a357eb95b 100644 --- a/src/mongo/db/query/planner_ixselect.cpp +++ b/src/mongo/db/query/planner_ixselect.cpp @@ -39,7 +39,6 @@ #include "mongo/db/matcher/expression_array.h" #include "mongo/db/matcher/expression_geo.h" #include "mongo/db/matcher/expression_text.h" -#include "mongo/db/query/collation/collator_interface.h" #include "mongo/db/query/indexability.h" #include "mongo/db/query/index_tag.h" #include "mongo/db/query/query_planner_common.h" @@ -73,57 +72,6 @@ static bool twoDWontWrap(const Circle& circle, const IndexEntry& index) { return ret; } -static bool collatorsMatch(const CollatorInterface* lhs, const CollatorInterface* rhs) { - if (lhs == nullptr && rhs == nullptr) { - return true; - } - if (lhs == nullptr || rhs == nullptr) { - return false; - } - return (*lhs == *rhs); -} - -// Checks whether 'node' contains any string comparison. We assume 'node' is bounds-generating or is -// a recursive child of a bounds-generating node, i.e. it does not contain AND, OR, -// ELEM_MATCH_OBJECT, or NOR. -static bool boundsGeneratingNodeContainsStringComparison(MatchExpression* node) { - invariant(node->matchType() != MatchExpression::AND && - node->matchType() != MatchExpression::OR && - node->matchType() != MatchExpression::NOR && - node->matchType() != MatchExpression::ELEM_MATCH_OBJECT); - - if (Indexability::isEqualityOrInequality(node)) { - const ComparisonMatchExpression* expr = static_cast<const ComparisonMatchExpression*>(node); - return expr->getData().type() == BSONType::String; - } - - if (node->matchType() == MatchExpression::MATCH_IN) { - const InMatchExpression* expr = static_cast<const InMatchExpression*>(node); - for (auto const& equality : expr->getData().equalities()) { - if (equality.type() == BSONType::String) { - return true; - } - } - return false; - } - - if (node->matchType() == MatchExpression::NOT) { - invariant(node->numChildren() == 1U); - return boundsGeneratingNodeContainsStringComparison(node->getChild(0)); - } - - if (node->matchType() == MatchExpression::ELEM_MATCH_VALUE) { - for (size_t i = 0; i < node->numChildren(); ++i) { - if (boundsGeneratingNodeContainsStringComparison(node->getChild(i))) { - return true; - } - } - return false; - } - - return false; -} - // static void QueryPlannerIXSelect::getFields(const MatchExpression* node, string prefix, @@ -175,14 +123,7 @@ void QueryPlannerIXSelect::findRelevantIndices(const unordered_set<string>& fiel // static bool QueryPlannerIXSelect::compatible(const BSONElement& elt, const IndexEntry& index, - MatchExpression* node, - const CollatorInterface* collator) { - // String comparisons require the collators to match. - if (boundsGeneratingNodeContainsStringComparison(node) && - !collatorsMatch(collator, index.collator)) { - return false; - } - + MatchExpression* node) { // Historically one could create indices with any particular value for the index spec, // including values that now indicate a special index. As such we have to make sure the // index type wasn't overridden before we pay attention to the string in the index key @@ -365,8 +306,7 @@ bool QueryPlannerIXSelect::compatible(const BSONElement& elt, // static void QueryPlannerIXSelect::rateIndices(MatchExpression* node, string prefix, - const vector<IndexEntry>& indices, - const CollatorInterface* collator) { + const vector<IndexEntry>& indices) { // Do not traverse tree beyond logical NOR node MatchExpression::MatchType exprtype = node->matchType(); if (exprtype == MatchExpression::NOR) { @@ -392,12 +332,12 @@ void QueryPlannerIXSelect::rateIndices(MatchExpression* node, for (size_t i = 0; i < indices.size(); ++i) { BSONObjIterator it(indices[i].keyPattern); BSONElement elt = it.next(); - if (elt.fieldName() == fullPath && compatible(elt, indices[i], node, collator)) { + if (elt.fieldName() == fullPath && compatible(elt, indices[i], node)) { rt->first.push_back(i); } while (it.more()) { elt = it.next(); - if (elt.fieldName() == fullPath && compatible(elt, indices[i], node, collator)) { + if (elt.fieldName() == fullPath && compatible(elt, indices[i], node)) { rt->notFirst.push_back(i); } } @@ -416,11 +356,11 @@ void QueryPlannerIXSelect::rateIndices(MatchExpression* node, prefix += node->path().toString() + "."; } for (size_t i = 0; i < node->numChildren(); ++i) { - rateIndices(node->getChild(i), prefix, indices, collator); + rateIndices(node->getChild(i), prefix, indices); } } else if (node->isLogical()) { for (size_t i = 0; i < node->numChildren(); ++i) { - rateIndices(node->getChild(i), prefix, indices, collator); + rateIndices(node->getChild(i), prefix, indices); } } } diff --git a/src/mongo/db/query/planner_ixselect.h b/src/mongo/db/query/planner_ixselect.h index 914e9265d9b..2928b708677 100644 --- a/src/mongo/db/query/planner_ixselect.h +++ b/src/mongo/db/query/planner_ixselect.h @@ -35,8 +35,6 @@ namespace mongo { -class CollatorInterface; - /** * Methods for determining what fields and predicates can use indices. */ @@ -69,10 +67,7 @@ public: * {field: "2d"} can only be used with some geo predicates. * {field: "2dsphere"} can only be used with some other geo predicates. */ - static bool compatible(const BSONElement& elt, - const IndexEntry& index, - MatchExpression* node, - const CollatorInterface* collator); + static bool compatible(const BSONElement& elt, const IndexEntry& index, MatchExpression* node); /** * Determine how useful all of our relevant 'indices' are to all predicates in the subtree @@ -91,8 +86,7 @@ public: */ static void rateIndices(MatchExpression* node, std::string prefix, - const std::vector<IndexEntry>& indices, - const CollatorInterface* collator); + const std::vector<IndexEntry>& indices); /** * Amend the RelevantTag lists for all predicates in the subtree rooted at 'node' to remove diff --git a/src/mongo/db/query/planner_ixselect_test.cpp b/src/mongo/db/query/planner_ixselect_test.cpp index 9bc7e9aca97..88fb6e44664 100644 --- a/src/mongo/db/query/planner_ixselect_test.cpp +++ b/src/mongo/db/query/planner_ixselect_test.cpp @@ -36,7 +36,6 @@ #include "mongo/db/json.h" #include "mongo/db/matcher/expression_parser.h" #include "mongo/db/matcher/extensions_callback_disallow_extensions.h" -#include "mongo/db/query/collation/collator_interface_mock.h" #include "mongo/db/query/index_tag.h" #include "mongo/unittest/unittest.h" #include "mongo/util/text.h" @@ -156,11 +155,8 @@ TEST(QueryPlannerIXSelectTest, GetFieldsArrayNegation) { /** * Performs a pre-order traversal of expression tree. Validates * that all tagged nodes contain an instance of RelevantTag. - * Finds all indices included in RelevantTags, and returns them in the 'indices' out-parameter. */ -void findRelevantTaggedNodePathsAndIndices(MatchExpression* root, - vector<string>* paths, - std::set<size_t>* indices) { +void findRelevantTaggedNodePaths(MatchExpression* root, vector<string>* paths) { MatchExpression::TagData* tag = root->getTag(); if (tag) { StringBuilder buf; @@ -173,51 +169,38 @@ void findRelevantTaggedNodePathsAndIndices(MatchExpression* root, FAIL(ss); } paths->push_back(r->path); - for (auto const& index : r->first) { - indices->insert(index); - } - for (auto const& index : r->notFirst) { - indices->insert(index); - } } for (size_t i = 0; i < root->numChildren(); ++i) { - findRelevantTaggedNodePathsAndIndices(root->getChild(i), paths, indices); + findRelevantTaggedNodePaths(root->getChild(i), paths); } } /** - * Parses a MatchExpression from query string and passes that along with prefix, collator, and - * indices to rateIndices. Verifies results against list of expected paths and expected indices. In - * future, we may expand this test function to validate which indices are assigned to which node. + * Parses a MatchExpression from query string and passes that along with + * prefix to rateIndices. + * Verifies results against list of expected paths. + * For now, we're only interested in which nodes are tagged. + * In future, we may expand this test function to include + * validate which indices are assigned to a node. */ -void testRateIndices(const char* query, - const char* prefix, - const CollatorInterface* collator, - const vector<IndexEntry>& indices, - const char* expectedPathsStr, - const std::set<size_t>& expectedIndices) { +void testRateIndicesTaggedNodePaths(const char* query, + const char* prefix, + const char* expectedPathsStr) { // Parse and rate query. Some of the nodes in the rated tree // will be tagged after the rating process. BSONObj obj = fromjson(query); unique_ptr<MatchExpression> expr(parseMatchExpression(obj)); - QueryPlannerIXSelect::rateIndices(expr.get(), prefix, indices, collator); + // Currently, we tag every indexable node even when no compatible + // index is available. Hence, it is fine to pass an empty vector of + // indices to rateIndices(). + vector<IndexEntry> indices; + QueryPlannerIXSelect::rateIndices(expr.get(), prefix, indices); - // Retrieve a list of paths and a set of indices embedded in + // Retrieve a list of paths embedded in // tagged nodes. vector<string> paths; - std::set<size_t> actualIndices; - findRelevantTaggedNodePathsAndIndices(expr.get(), &paths, &actualIndices); - - // Compare the expected indices with the actual indices. - if (actualIndices != expectedIndices) { - mongoutils::str::stream ss; - ss << "rateIndices(query=" << query << ", prefix=" << prefix - << "): expected indices did not match actual indices. expected: " - << toString(expectedIndices.begin(), expectedIndices.end()) - << ". actual: " << toString(actualIndices.begin(), actualIndices.end()); - FAIL(ss); - } + findRelevantTaggedNodePaths(expr.get(), &paths); // Compare with expected list of paths. // First verify number of paths retrieved. @@ -248,21 +231,6 @@ void testRateIndices(const char* query, } /** - * Calls testRateIndices with an empty set of indices and a null collation, so we only test which - * nodes are tagged. - */ -void testRateIndicesTaggedNodePaths(const char* query, - const char* prefix, - const char* expectedPathsStr) { - // Currently, we tag every indexable node even when no compatible - // index is available. Hence, it is fine to pass an empty vector of - // indices to rateIndices(). - vector<IndexEntry> indices; - std::set<size_t> expectedIndices; - testRateIndices(query, prefix, nullptr, indices, expectedPathsStr, expectedIndices); -} - -/** * Basic test cases for rateIndices(). * Includes logical operators. */ @@ -302,418 +270,4 @@ TEST(QueryPlannerIXSelectTest, RateIndicesTaggedNodePathArrayNegation) { testRateIndicesTaggedNodePaths("{a: {$all: [{$elemMatch: {b: {$ne: 1}}}]}}", "", "a.b,a.b"); } -/** - * If the collator is null, we select the relevant index with a null collator. - */ -TEST(QueryPlannerIXSelectTest, NullCollatorsMatch) { - std::vector<IndexEntry> indices; - indices.push_back(IndexEntry(BSON("a" << 1))); - std::set<size_t> expectedIndices = {0}; - testRateIndices("{a: 'string'}", "", nullptr, indices, "a", expectedIndices); -} - -/** - * If the collator is not null, we do not select the relevant index with a null collator. - */ -TEST(QueryPlannerIXSelectTest, NonNullCollatorDoesNotMatchIndexWithNullCollator) { - CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); - std::vector<IndexEntry> indices; - indices.push_back(IndexEntry(BSON("a" << 1))); - std::set<size_t> expectedIndices = {}; - testRateIndices("{a: 'string'}", "", &collator, indices, "a", expectedIndices); -} - -/** - * If the collator is null, we do not select the relevant index with a non-null collator. - */ -TEST(QueryPlannerIXSelectTest, NullCollatorDoesNotMatchIndexWithNonNullCollator) { - IndexEntry index(BSON("a" << 1)); - CollatorInterfaceMock indexCollator(CollatorInterfaceMock::MockType::kAlwaysEqual); - index.collator = &indexCollator; - std::vector<IndexEntry> indices; - indices.push_back(index); - std::set<size_t> expectedIndices = {}; - testRateIndices("{a: 'string'}", "", nullptr, indices, "a", expectedIndices); -} - -/** - * If the collator is non-null, we select the relevant index with an equal collator. - */ -TEST(QueryPlannerIXSelectTest, EqualCollatorsMatch) { - CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); - IndexEntry index(BSON("a" << 1)); - CollatorInterfaceMock indexCollator(CollatorInterfaceMock::MockType::kAlwaysEqual); - index.collator = &indexCollator; - std::vector<IndexEntry> indices; - indices.push_back(index); - std::set<size_t> expectedIndices = {0}; - testRateIndices("{a: 'string'}", "", &collator, indices, "a", expectedIndices); -} - -/** - * If the collator is non-null, we do not select the relevant index with an unequal collator. - */ -TEST(QueryPlannerIXSelectTest, UnequalCollatorsDoNotMatch) { - CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); - IndexEntry index(BSON("a" << 1)); - CollatorInterfaceMock indexCollator(CollatorInterfaceMock::MockType::kReverseString); - index.collator = &indexCollator; - std::vector<IndexEntry> indices; - indices.push_back(index); - std::set<size_t> expectedIndices = {}; - testRateIndices("{a: 'string'}", "", &collator, indices, "a", expectedIndices); -} - -/** - * If no string comparison is done, unequal collators are allowed. - */ -TEST(QueryPlannerIXSelectTest, NoStringComparison) { - CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); - IndexEntry index(BSON("a" << 1)); - CollatorInterfaceMock indexCollator(CollatorInterfaceMock::MockType::kReverseString); - index.collator = &indexCollator; - std::vector<IndexEntry> indices; - indices.push_back(index); - std::set<size_t> expectedIndices = {0}; - testRateIndices("{a: 1}", "", &collator, indices, "a", expectedIndices); -} - -/** - * $gt string comparison requires matching collator. - */ -TEST(QueryPlannerIXSelectTest, StringGTRequiresMatchingCollator) { - CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); - IndexEntry index(BSON("a" << 1)); - CollatorInterfaceMock indexCollator(CollatorInterfaceMock::MockType::kReverseString); - index.collator = &indexCollator; - std::vector<IndexEntry> indices; - indices.push_back(index); - std::set<size_t> expectedIndices = {}; - testRateIndices("{a: {$gt: 'string'}}", "", &collator, indices, "a", expectedIndices); - - expectedIndices.insert(0); - testRateIndices("{a: {$gt: 'string'}}", "", &indexCollator, indices, "a", expectedIndices); -} - -/** - * $gte string comparison requires matching collator. - */ -TEST(QueryPlannerIXSelectTest, StringGTERequiresMatchingCollator) { - CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); - IndexEntry index(BSON("a" << 1)); - CollatorInterfaceMock indexCollator(CollatorInterfaceMock::MockType::kReverseString); - index.collator = &indexCollator; - std::vector<IndexEntry> indices; - indices.push_back(index); - std::set<size_t> expectedIndices = {}; - testRateIndices("{a: {$gte: 'string'}}", "", &collator, indices, "a", expectedIndices); - - expectedIndices.insert(0); - testRateIndices("{a: {$gte: 'string'}}", "", &indexCollator, indices, "a", expectedIndices); -} - -/** - * $lt string comparison requires matching collator. - */ -TEST(QueryPlannerIXSelectTest, StringLTRequiresMatchingCollator) { - CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); - IndexEntry index(BSON("a" << 1)); - CollatorInterfaceMock indexCollator(CollatorInterfaceMock::MockType::kReverseString); - index.collator = &indexCollator; - std::vector<IndexEntry> indices; - indices.push_back(index); - std::set<size_t> expectedIndices = {}; - testRateIndices("{a: {$lt: 'string'}}", "", &collator, indices, "a", expectedIndices); - - expectedIndices.insert(0); - testRateIndices("{a: {$lt: 'string'}}", "", &indexCollator, indices, "a", expectedIndices); -} - -/** - * $lte string comparison requires matching collator. - */ -TEST(QueryPlannerIXSelectTest, StringLTERequiresMatchingCollator) { - CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); - IndexEntry index(BSON("a" << 1)); - CollatorInterfaceMock indexCollator(CollatorInterfaceMock::MockType::kReverseString); - index.collator = &indexCollator; - std::vector<IndexEntry> indices; - indices.push_back(index); - std::set<size_t> expectedIndices = {}; - testRateIndices("{a: {$lte: 'string'}}", "", &collator, indices, "a", expectedIndices); - - expectedIndices.insert(0); - testRateIndices("{a: {$lte: 'string'}}", "", &indexCollator, indices, "a", expectedIndices); -} - -/** - * If no string comparison is done in an 'in' expression, unequal collators are allowed. - */ -TEST(QueryPlannerIXSelectTest, NoStringComparisonInExpression) { - CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); - IndexEntry index(BSON("a" << 1)); - CollatorInterfaceMock indexCollator(CollatorInterfaceMock::MockType::kReverseString); - index.collator = &indexCollator; - std::vector<IndexEntry> indices; - indices.push_back(index); - std::set<size_t> expectedIndices = {0}; - testRateIndices("{a: {$in: [1, 2]}}", "", &collator, indices, "a", expectedIndices); -} - -/** - * If string comparison is done in an 'in' expression, matching collators are required. - */ -TEST(QueryPlannerIXSelectTest, StringComparisonInExpression) { - CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); - IndexEntry index(BSON("a" << 1)); - CollatorInterfaceMock indexCollator(CollatorInterfaceMock::MockType::kReverseString); - index.collator = &indexCollator; - std::vector<IndexEntry> indices; - indices.push_back(index); - std::set<size_t> expectedIndices = {}; - testRateIndices("{a: {$in: [1, 2, 'b', 3]}}", "", &collator, indices, "a", expectedIndices); - - expectedIndices.insert(0); - testRateIndices( - "{a: {$in: [1, 2, 'b', 3]}}", "", &indexCollator, indices, "a", expectedIndices); -} - -/** - * If no string comparison is done in a 'not' expression, unequal collators are allowed. - */ -TEST(QueryPlannerIXSelectTest, NoStringComparisonNotExpression) { - CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); - IndexEntry index(BSON("a" << 1)); - CollatorInterfaceMock indexCollator(CollatorInterfaceMock::MockType::kReverseString); - index.collator = &indexCollator; - std::vector<IndexEntry> indices; - indices.push_back(index); - std::set<size_t> expectedIndices = {0}; - testRateIndices("{a: {$not: {$gt: 1}}}", "", &collator, indices, "a", expectedIndices); -} - -/** - * If string comparison is done in a 'not' expression, matching collators are required. - */ -TEST(QueryPlannerIXSelectTest, StringComparisonNotExpression) { - CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); - IndexEntry index(BSON("a" << 1)); - CollatorInterfaceMock indexCollator(CollatorInterfaceMock::MockType::kReverseString); - index.collator = &indexCollator; - std::vector<IndexEntry> indices; - indices.push_back(index); - std::set<size_t> expectedIndices = {}; - testRateIndices("{a: {$not: {$gt: 'a'}}}", "", &collator, indices, "a", expectedIndices); - - expectedIndices.insert(0); - testRateIndices("{a: {$not: {$gt: 'a'}}}", "", &indexCollator, indices, "a", expectedIndices); -} - -/** - * If no string comparison is done in an elemMatch value, unequal collators are allowed. - */ -TEST(QueryPlannerIXSelectTest, NoStringComparisonElemMatchValueExpression) { - CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); - IndexEntry index(BSON("a" << 1)); - CollatorInterfaceMock indexCollator(CollatorInterfaceMock::MockType::kReverseString); - index.collator = &indexCollator; - std::vector<IndexEntry> indices; - indices.push_back(index); - std::set<size_t> expectedIndices = {0}; - testRateIndices("{a: {$elemMatch: {$gt: 1}}}", "", &collator, indices, "a", expectedIndices); -} - -/** - * If string comparison is done in an elemMatch value, matching collators are required. - */ -TEST(QueryPlannerIXSelectTest, StringComparisonElemMatchValueExpression) { - CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); - IndexEntry index(BSON("a" << 1)); - CollatorInterfaceMock indexCollator(CollatorInterfaceMock::MockType::kReverseString); - index.collator = &indexCollator; - std::vector<IndexEntry> indices; - indices.push_back(index); - std::set<size_t> expectedIndices = {}; - testRateIndices( - "{a: {$elemMatch: {$gt: 'string'}}}", "", &collator, indices, "a", expectedIndices); - - expectedIndices.insert(0); - testRateIndices( - "{a: {$elemMatch: {$gt: 'string'}}}", "", &indexCollator, indices, "a", expectedIndices); -} - -/** - * If no string comparison is done in an 'in' in a 'not', unequal collators are allowed. - */ -TEST(QueryPlannerIXSelectTest, NoStringComparisonNotInExpression) { - CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); - IndexEntry index(BSON("a" << 1)); - CollatorInterfaceMock indexCollator(CollatorInterfaceMock::MockType::kReverseString); - index.collator = &indexCollator; - std::vector<IndexEntry> indices; - indices.push_back(index); - std::set<size_t> expectedIndices = {0}; - testRateIndices("{a: {$not: {$in: [1]}}}", "", &collator, indices, "a", expectedIndices); -} - -/** - * If string comparison is done in an 'in' in a 'not', matching collators are required. - */ -TEST(QueryPlannerIXSelectTest, StringComparisonNotInExpression) { - CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); - IndexEntry index(BSON("a" << 1)); - CollatorInterfaceMock indexCollator(CollatorInterfaceMock::MockType::kReverseString); - index.collator = &indexCollator; - std::vector<IndexEntry> indices; - indices.push_back(index); - std::set<size_t> expectedIndices = {}; - testRateIndices("{a: {$not: {$in: ['a']}}}", "", &collator, indices, "a", expectedIndices); - - expectedIndices.insert(0); - testRateIndices("{a: {$not: {$in: ['a']}}}", "", &indexCollator, indices, "a", expectedIndices); -} - -/** - * If no string comparison is done in a 'nin', unequal collators are allowed. - */ -TEST(QueryPlannerIXSelectTest, NoStringComparisonNinExpression) { - CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); - IndexEntry index(BSON("a" << 1)); - CollatorInterfaceMock indexCollator(CollatorInterfaceMock::MockType::kReverseString); - index.collator = &indexCollator; - std::vector<IndexEntry> indices; - indices.push_back(index); - std::set<size_t> expectedIndices = {0}; - testRateIndices("{a: {$nin: [1]}}", "", &collator, indices, "a,a", expectedIndices); -} - -/** - * If string comparison is done in a 'nin', matching collators are required. - */ -TEST(QueryPlannerIXSelectTest, StringComparisonNinExpression) { - CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); - IndexEntry index(BSON("a" << 1)); - CollatorInterfaceMock indexCollator(CollatorInterfaceMock::MockType::kReverseString); - index.collator = &indexCollator; - std::vector<IndexEntry> indices; - indices.push_back(index); - std::set<size_t> expectedIndices = {}; - testRateIndices("{a: {$nin: ['a']}}", "", &collator, indices, "a,a", expectedIndices); - - expectedIndices.insert(0); - testRateIndices("{a: {$nin: ['a']}}", "", &indexCollator, indices, "a,a", expectedIndices); -} - -/** - * If string comparison is done in an 'or', matching collators are required. - */ -TEST(QueryPlannerIXSelectTest, StringComparisonOrExpression) { - CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); - IndexEntry index(BSON("a" << 1)); - CollatorInterfaceMock indexCollator(CollatorInterfaceMock::MockType::kReverseString); - index.collator = &indexCollator; - std::vector<IndexEntry> indices; - indices.push_back(index); - std::set<size_t> expectedIndices = {}; - testRateIndices("{$or: [{a: 'string'}]}", "", &collator, indices, "a", expectedIndices); - - expectedIndices.insert(0); - testRateIndices("{$or: [{a: 'string'}]}", "", &indexCollator, indices, "a", expectedIndices); -} - -/** - * If string comparison is done in an 'and', matching collators are required. - */ -TEST(QueryPlannerIXSelectTest, StringComparisonAndExpression) { - CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); - IndexEntry index(BSON("a" << 1)); - CollatorInterfaceMock indexCollator(CollatorInterfaceMock::MockType::kReverseString); - index.collator = &indexCollator; - std::vector<IndexEntry> indices; - indices.push_back(index); - std::set<size_t> expectedIndices = {}; - testRateIndices("{$and: [{a: 'string'}]}", "", &collator, indices, "a", expectedIndices); - - expectedIndices.insert(0); - testRateIndices("{$and: [{a: 'string'}]}", "", &indexCollator, indices, "a", expectedIndices); -} - -/** - * If string comparison is done in an 'all', matching collators are required. - */ -TEST(QueryPlannerIXSelectTest, StringComparisonAllExpression) { - CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); - IndexEntry index(BSON("a" << 1)); - CollatorInterfaceMock indexCollator(CollatorInterfaceMock::MockType::kReverseString); - index.collator = &indexCollator; - std::vector<IndexEntry> indices; - indices.push_back(index); - std::set<size_t> expectedIndices = {}; - testRateIndices("{a: {$all: ['string']}}", "", &collator, indices, "a", expectedIndices); - - expectedIndices.insert(0); - testRateIndices("{a: {$all: ['string']}}", "", &indexCollator, indices, "a", expectedIndices); -} - -/** - * If string comparison is done in an elemMatch object, matching collators are required. - */ -TEST(QueryPlannerIXSelectTest, StringComparisonElemMatchObjectExpression) { - CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); - IndexEntry index(BSON("a.b" << 1)); - CollatorInterfaceMock indexCollator(CollatorInterfaceMock::MockType::kReverseString); - index.collator = &indexCollator; - std::vector<IndexEntry> indices; - indices.push_back(index); - std::set<size_t> expectedIndices = {}; - testRateIndices( - "{a: {$elemMatch: {b: 'string'}}}", "", &collator, indices, "a.b", expectedIndices); - - expectedIndices.insert(0); - testRateIndices( - "{a: {$elemMatch: {b: 'string'}}}", "", &indexCollator, indices, "a.b", expectedIndices); -} - -/** - * If no string comparison is done in a query containing $mod, unequal collators are allowed. - */ -TEST(QueryPlannerIXSelectTest, NoStringComparisonMod) { - CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); - IndexEntry index(BSON("a" << 1)); - CollatorInterfaceMock indexCollator(CollatorInterfaceMock::MockType::kReverseString); - index.collator = &indexCollator; - std::vector<IndexEntry> indices; - indices.push_back(index); - std::set<size_t> expectedIndices = {0}; - testRateIndices("{a: {$mod: [2, 0]}}", "", &collator, indices, "a", expectedIndices); -} - -/** - * If no string comparison is done in a query containing $exists, unequal collators are allowed. - */ -TEST(QueryPlannerIXSelectTest, NoStringComparisonExists) { - CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); - IndexEntry index(BSON("a" << 1)); - CollatorInterfaceMock indexCollator(CollatorInterfaceMock::MockType::kReverseString); - index.collator = &indexCollator; - std::vector<IndexEntry> indices; - indices.push_back(index); - std::set<size_t> expectedIndices = {0}; - testRateIndices("{a: {$exists: true}}", "", &collator, indices, "a", expectedIndices); -} - -/** - * If no string comparison is done in a query containing $type, unequal collators are allowed. - */ -TEST(QueryPlannerIXSelectTest, NoStringComparisonType) { - CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); - IndexEntry index(BSON("a" << 1)); - CollatorInterfaceMock indexCollator(CollatorInterfaceMock::MockType::kReverseString); - index.collator = &indexCollator; - std::vector<IndexEntry> indices; - indices.push_back(index); - std::set<size_t> expectedIndices = {0}; - testRateIndices("{a: {$type: 'string'}}", "", &collator, indices, "a", expectedIndices); -} - } // namespace diff --git a/src/mongo/db/query/query_planner.cpp b/src/mongo/db/query/query_planner.cpp index eb39d9ae2db..9540d40912e 100644 --- a/src/mongo/db/query/query_planner.cpp +++ b/src/mongo/db/query/query_planner.cpp @@ -659,8 +659,7 @@ Status QueryPlanner::plan(const CanonicalQuery& query, } // Figure out how useful each index is to each predicate. - // TODO: pass the appropriate collator to rateIndices instead of nullptr. - QueryPlannerIXSelect::rateIndices(query.root(), "", relevantIndices, nullptr); + QueryPlannerIXSelect::rateIndices(query.root(), "", relevantIndices); QueryPlannerIXSelect::stripInvalidAssignments(query.root(), relevantIndices); // Unless we have GEO_NEAR, TEXT, or a projection, we may be able to apply an optimization |