diff options
-rw-r--r-- | src/mongo/db/query/planner_ixselect.cpp | 27 | ||||
-rw-r--r-- | src/mongo/db/query/planner_ixselect_test.cpp | 307 |
2 files changed, 285 insertions, 49 deletions
diff --git a/src/mongo/db/query/planner_ixselect.cpp b/src/mongo/db/query/planner_ixselect.cpp index f340f39915a..506b1b24836 100644 --- a/src/mongo/db/query/planner_ixselect.cpp +++ b/src/mongo/db/query/planner_ixselect.cpp @@ -83,10 +83,11 @@ static bool collatorsMatch(const CollatorInterface* lhs, const CollatorInterface 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) { +// Checks whether 'node' contains any comparison to an element of type 'type'. Nested objects and +// arrays are not checked recursively. 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. +// TODO SERVER-23172: Check nested objects and arrays. +static bool boundsGeneratingNodeContainsComparisonToType(MatchExpression* node, BSONType type) { invariant(node->matchType() != MatchExpression::AND && node->matchType() != MatchExpression::OR && node->matchType() != MatchExpression::NOR && @@ -94,13 +95,13 @@ static bool boundsGeneratingNodeContainsStringComparison(MatchExpression* node) if (Indexability::isEqualityOrInequality(node)) { const ComparisonMatchExpression* expr = static_cast<const ComparisonMatchExpression*>(node); - return expr->getData().type() == BSONType::String; + return expr->getData().type() == type; } 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) { + if (equality.type() == type) { return true; } } @@ -109,12 +110,12 @@ static bool boundsGeneratingNodeContainsStringComparison(MatchExpression* node) if (node->matchType() == MatchExpression::NOT) { invariant(node->numChildren() == 1U); - return boundsGeneratingNodeContainsStringComparison(node->getChild(0)); + return boundsGeneratingNodeContainsComparisonToType(node->getChild(0), type); } if (node->matchType() == MatchExpression::ELEM_MATCH_VALUE) { for (size_t i = 0; i < node->numChildren(); ++i) { - if (boundsGeneratingNodeContainsStringComparison(node->getChild(i))) { + if (boundsGeneratingNodeContainsComparisonToType(node->getChild(i), type)) { return true; } } @@ -177,8 +178,16 @@ bool QueryPlannerIXSelect::compatible(const BSONElement& elt, const IndexEntry& index, MatchExpression* node, const CollatorInterface* collator) { + // Nested object or array comparisons require the query collator to be null. + // TODO SERVER-23172: remove this check. + if (collator != nullptr && + (boundsGeneratingNodeContainsComparisonToType(node, BSONType::Object) || + boundsGeneratingNodeContainsComparisonToType(node, BSONType::Array))) { + return false; + } + // String comparisons require the collators to match. - if (boundsGeneratingNodeContainsStringComparison(node) && + if (boundsGeneratingNodeContainsComparisonToType(node, BSONType::String) && !collatorsMatch(collator, index.collator)) { return false; } diff --git a/src/mongo/db/query/planner_ixselect_test.cpp b/src/mongo/db/query/planner_ixselect_test.cpp index 3dede18abc1..934079fcf24 100644 --- a/src/mongo/db/query/planner_ixselect_test.cpp +++ b/src/mongo/db/query/planner_ixselect_test.cpp @@ -381,7 +381,7 @@ TEST(QueryPlannerIXSelectTest, NoStringComparison) { /** * $gt string comparison requires matching collator. */ -TEST(QueryPlannerIXSelectTest, StringGTRequiresMatchingCollator) { +TEST(QueryPlannerIXSelectTest, StringGTUnequalCollators) { CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); IndexEntry index(BSON("a" << 1)); CollatorInterfaceMock indexCollator(CollatorInterfaceMock::MockType::kReverseString); @@ -390,15 +390,25 @@ TEST(QueryPlannerIXSelectTest, StringGTRequiresMatchingCollator) { 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); +/** + * $gt string comparison requires matching collator. + */ +TEST(QueryPlannerIXSelectTest, StringGTEqualCollators) { + CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); + IndexEntry index(BSON("a" << 1)); + index.collator = &collator; + std::vector<IndexEntry> indices; + indices.push_back(index); + std::set<size_t> expectedIndices = {0}; + testRateIndices("{a: {$gt: 'string'}}", "", &collator, indices, "a", expectedIndices); } /** * $gte string comparison requires matching collator. */ -TEST(QueryPlannerIXSelectTest, StringGTERequiresMatchingCollator) { +TEST(QueryPlannerIXSelectTest, StringGTEUnequalCollators) { CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); IndexEntry index(BSON("a" << 1)); CollatorInterfaceMock indexCollator(CollatorInterfaceMock::MockType::kReverseString); @@ -407,15 +417,25 @@ TEST(QueryPlannerIXSelectTest, StringGTERequiresMatchingCollator) { 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); +/** + * $gte string comparison requires matching collator. + */ +TEST(QueryPlannerIXSelectTest, StringGTEEqualCollators) { + CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); + IndexEntry index(BSON("a" << 1)); + index.collator = &collator; + std::vector<IndexEntry> indices; + indices.push_back(index); + std::set<size_t> expectedIndices = {0}; + testRateIndices("{a: {$gte: 'string'}}", "", &collator, indices, "a", expectedIndices); } /** * $lt string comparison requires matching collator. */ -TEST(QueryPlannerIXSelectTest, StringLTRequiresMatchingCollator) { +TEST(QueryPlannerIXSelectTest, StringLTUnequalCollators) { CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); IndexEntry index(BSON("a" << 1)); CollatorInterfaceMock indexCollator(CollatorInterfaceMock::MockType::kReverseString); @@ -424,15 +444,25 @@ TEST(QueryPlannerIXSelectTest, StringLTRequiresMatchingCollator) { 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); +/** + * $lt string comparison requires matching collator. + */ +TEST(QueryPlannerIXSelectTest, StringLTEqualCollators) { + CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); + IndexEntry index(BSON("a" << 1)); + index.collator = &collator; + std::vector<IndexEntry> indices; + indices.push_back(index); + std::set<size_t> expectedIndices = {0}; + testRateIndices("{a: {$lt: 'string'}}", "", &collator, indices, "a", expectedIndices); } /** * $lte string comparison requires matching collator. */ -TEST(QueryPlannerIXSelectTest, StringLTERequiresMatchingCollator) { +TEST(QueryPlannerIXSelectTest, StringLTEUnequalCollators) { CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); IndexEntry index(BSON("a" << 1)); CollatorInterfaceMock indexCollator(CollatorInterfaceMock::MockType::kReverseString); @@ -441,9 +471,19 @@ TEST(QueryPlannerIXSelectTest, StringLTERequiresMatchingCollator) { 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); +/** + * $lte string comparison requires matching collator. + */ +TEST(QueryPlannerIXSelectTest, StringLTEEqualCollators) { + CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); + IndexEntry index(BSON("a" << 1)); + index.collator = &collator; + std::vector<IndexEntry> indices; + indices.push_back(index); + std::set<size_t> expectedIndices = {0}; + testRateIndices("{a: {$lte: 'string'}}", "", &collator, indices, "a", expectedIndices); } /** @@ -463,7 +503,7 @@ TEST(QueryPlannerIXSelectTest, NoStringComparisonInExpression) { /** * If string comparison is done in an 'in' expression, matching collators are required. */ -TEST(QueryPlannerIXSelectTest, StringComparisonInExpression) { +TEST(QueryPlannerIXSelectTest, StringComparisonInExpressionUnequalCollators) { CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); IndexEntry index(BSON("a" << 1)); CollatorInterfaceMock indexCollator(CollatorInterfaceMock::MockType::kReverseString); @@ -472,10 +512,19 @@ TEST(QueryPlannerIXSelectTest, StringComparisonInExpression) { 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 string comparison is done in an 'in' expression, matching collators are required. + */ +TEST(QueryPlannerIXSelectTest, StringComparisonInExpressionEqualCollators) { + CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); + IndexEntry index(BSON("a" << 1)); + index.collator = &collator; + std::vector<IndexEntry> indices; + indices.push_back(index); + std::set<size_t> expectedIndices = {0}; + testRateIndices("{a: {$in: [1, 2, 'b', 3]}}", "", &collator, indices, "a", expectedIndices); } /** @@ -495,7 +544,7 @@ TEST(QueryPlannerIXSelectTest, NoStringComparisonNotExpression) { /** * If string comparison is done in a 'not' expression, matching collators are required. */ -TEST(QueryPlannerIXSelectTest, StringComparisonNotExpression) { +TEST(QueryPlannerIXSelectTest, StringComparisonNotExpressionUnequalCollators) { CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); IndexEntry index(BSON("a" << 1)); CollatorInterfaceMock indexCollator(CollatorInterfaceMock::MockType::kReverseString); @@ -504,9 +553,19 @@ TEST(QueryPlannerIXSelectTest, StringComparisonNotExpression) { 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 string comparison is done in a 'not' expression, matching collators are required. + */ +TEST(QueryPlannerIXSelectTest, StringComparisonNotExpressionEqualCollators) { + CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); + IndexEntry index(BSON("a" << 1)); + index.collator = &collator; + std::vector<IndexEntry> indices; + indices.push_back(index); + std::set<size_t> expectedIndices = {0}; + testRateIndices("{a: {$not: {$gt: 'a'}}}", "", &collator, indices, "a", expectedIndices); } /** @@ -526,7 +585,7 @@ TEST(QueryPlannerIXSelectTest, NoStringComparisonElemMatchValueExpression) { /** * If string comparison is done in an elemMatch value, matching collators are required. */ -TEST(QueryPlannerIXSelectTest, StringComparisonElemMatchValueExpression) { +TEST(QueryPlannerIXSelectTest, StringComparisonElemMatchValueExpressionUnequalCollators) { CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); IndexEntry index(BSON("a" << 1)); CollatorInterfaceMock indexCollator(CollatorInterfaceMock::MockType::kReverseString); @@ -536,10 +595,20 @@ TEST(QueryPlannerIXSelectTest, StringComparisonElemMatchValueExpression) { std::set<size_t> expectedIndices; testRateIndices( "{a: {$elemMatch: {$gt: 'string'}}}", "", &collator, indices, "a", expectedIndices); +} - expectedIndices.insert(0); +/** + * If string comparison is done in an elemMatch value, matching collators are required. + */ +TEST(QueryPlannerIXSelectTest, StringComparisonElemMatchValueExpressionEqualCollators) { + CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); + IndexEntry index(BSON("a" << 1)); + index.collator = &collator; + std::vector<IndexEntry> indices; + indices.push_back(index); + std::set<size_t> expectedIndices = {0}; testRateIndices( - "{a: {$elemMatch: {$gt: 'string'}}}", "", &indexCollator, indices, "a", expectedIndices); + "{a: {$elemMatch: {$gt: 'string'}}}", "", &collator, indices, "a", expectedIndices); } /** @@ -559,7 +628,7 @@ TEST(QueryPlannerIXSelectTest, NoStringComparisonNotInExpression) { /** * If string comparison is done in an 'in' in a 'not', matching collators are required. */ -TEST(QueryPlannerIXSelectTest, StringComparisonNotInExpression) { +TEST(QueryPlannerIXSelectTest, StringComparisonNotInExpressionUnequalCollators) { CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); IndexEntry index(BSON("a" << 1)); CollatorInterfaceMock indexCollator(CollatorInterfaceMock::MockType::kReverseString); @@ -568,9 +637,19 @@ TEST(QueryPlannerIXSelectTest, StringComparisonNotInExpression) { 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 string comparison is done in an 'in' in a 'not', matching collators are required. + */ +TEST(QueryPlannerIXSelectTest, StringComparisonNotInExpressionEqualCollators) { + CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); + IndexEntry index(BSON("a" << 1)); + index.collator = &collator; + std::vector<IndexEntry> indices; + indices.push_back(index); + std::set<size_t> expectedIndices = {0}; + testRateIndices("{a: {$not: {$in: ['a']}}}", "", &collator, indices, "a", expectedIndices); } /** @@ -590,7 +669,7 @@ TEST(QueryPlannerIXSelectTest, NoStringComparisonNinExpression) { /** * If string comparison is done in a 'nin', matching collators are required. */ -TEST(QueryPlannerIXSelectTest, StringComparisonNinExpression) { +TEST(QueryPlannerIXSelectTest, StringComparisonNinExpressionUnequalCollators) { CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); IndexEntry index(BSON("a" << 1)); CollatorInterfaceMock indexCollator(CollatorInterfaceMock::MockType::kReverseString); @@ -599,15 +678,25 @@ TEST(QueryPlannerIXSelectTest, StringComparisonNinExpression) { 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 a 'nin', matching collators are required. + */ +TEST(QueryPlannerIXSelectTest, StringComparisonNinExpressionEqualCollators) { + CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); + IndexEntry index(BSON("a" << 1)); + index.collator = &collator; + std::vector<IndexEntry> indices; + indices.push_back(index); + std::set<size_t> expectedIndices = {0}; + testRateIndices("{a: {$nin: ['a']}}", "", &collator, indices, "a,a", expectedIndices); } /** * If string comparison is done in an 'or', matching collators are required. */ -TEST(QueryPlannerIXSelectTest, StringComparisonOrExpression) { +TEST(QueryPlannerIXSelectTest, StringComparisonOrExpressionUnequalCollators) { CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); IndexEntry index(BSON("a" << 1)); CollatorInterfaceMock indexCollator(CollatorInterfaceMock::MockType::kReverseString); @@ -616,15 +705,25 @@ TEST(QueryPlannerIXSelectTest, StringComparisonOrExpression) { 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 'or', matching collators are required. + */ +TEST(QueryPlannerIXSelectTest, StringComparisonOrExpressionEqualCollators) { + CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); + IndexEntry index(BSON("a" << 1)); + index.collator = &collator; + std::vector<IndexEntry> indices; + indices.push_back(index); + std::set<size_t> expectedIndices = {0}; + testRateIndices("{$or: [{a: 'string'}]}", "", &collator, indices, "a", expectedIndices); } /** * If string comparison is done in an 'and', matching collators are required. */ -TEST(QueryPlannerIXSelectTest, StringComparisonAndExpression) { +TEST(QueryPlannerIXSelectTest, StringComparisonAndExpressionUnequalCollators) { CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); IndexEntry index(BSON("a" << 1)); CollatorInterfaceMock indexCollator(CollatorInterfaceMock::MockType::kReverseString); @@ -633,15 +732,25 @@ TEST(QueryPlannerIXSelectTest, StringComparisonAndExpression) { 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 'and', matching collators are required. + */ +TEST(QueryPlannerIXSelectTest, StringComparisonAndExpressionEqualCollators) { + CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); + IndexEntry index(BSON("a" << 1)); + index.collator = &collator; + std::vector<IndexEntry> indices; + indices.push_back(index); + std::set<size_t> expectedIndices = {0}; + testRateIndices("{$and: [{a: 'string'}]}", "", &collator, indices, "a", expectedIndices); } /** * If string comparison is done in an 'all', matching collators are required. */ -TEST(QueryPlannerIXSelectTest, StringComparisonAllExpression) { +TEST(QueryPlannerIXSelectTest, StringComparisonAllExpressionUnequalCollators) { CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); IndexEntry index(BSON("a" << 1)); CollatorInterfaceMock indexCollator(CollatorInterfaceMock::MockType::kReverseString); @@ -650,15 +759,25 @@ TEST(QueryPlannerIXSelectTest, StringComparisonAllExpression) { 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 'all', matching collators are required. + */ +TEST(QueryPlannerIXSelectTest, StringComparisonAllExpressionEqualCollators) { + CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); + IndexEntry index(BSON("a" << 1)); + index.collator = &collator; + std::vector<IndexEntry> indices; + indices.push_back(index); + std::set<size_t> expectedIndices = {0}; + testRateIndices("{a: {$all: ['string']}}", "", &collator, indices, "a", expectedIndices); } /** * If string comparison is done in an elemMatch object, matching collators are required. */ -TEST(QueryPlannerIXSelectTest, StringComparisonElemMatchObjectExpression) { +TEST(QueryPlannerIXSelectTest, StringComparisonElemMatchObjectExpressionUnequalCollators) { CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); IndexEntry index(BSON("a.b" << 1)); CollatorInterfaceMock indexCollator(CollatorInterfaceMock::MockType::kReverseString); @@ -668,10 +787,20 @@ TEST(QueryPlannerIXSelectTest, StringComparisonElemMatchObjectExpression) { std::set<size_t> expectedIndices; testRateIndices( "{a: {$elemMatch: {b: 'string'}}}", "", &collator, indices, "a.b", expectedIndices); +} - expectedIndices.insert(0); +/** + * If string comparison is done in an elemMatch object, matching collators are required. + */ +TEST(QueryPlannerIXSelectTest, StringComparisonElemMatchObjectExpressionEqualCollators) { + CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); + IndexEntry index(BSON("a.b" << 1)); + index.collator = &collator; + std::vector<IndexEntry> indices; + indices.push_back(index); + std::set<size_t> expectedIndices = {0}; testRateIndices( - "{a: {$elemMatch: {b: 'string'}}}", "", &indexCollator, indices, "a.b", expectedIndices); + "{a: {$elemMatch: {b: 'string'}}}", "", &collator, indices, "a.b", expectedIndices); } /** @@ -716,4 +845,102 @@ TEST(QueryPlannerIXSelectTest, NoStringComparisonType) { testRateIndices("{a: {$type: 'string'}}", "", &collator, indices, "a", expectedIndices); } +/** + * If nested object comparison is done, the query collator must be null. + * TODO SERVER-23172: remove this test. + */ +TEST(QueryPlannerIXSelectTest, NestedObjectNonNullCollators) { + CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString); + IndexEntry index(BSON("a" << 1)); + index.collator = &collator; + std::vector<IndexEntry> indices; + indices.push_back(index); + std::set<size_t> expectedIndices; + testRateIndices("{a: {b: 1}}", "", &collator, indices, "a", expectedIndices); +} + +/** + * If nested object comparison is done, the query collator must be null. + * TODO SERVER-23172: remove this test. + */ +TEST(QueryPlannerIXSelectTest, NestedObjectNullQueryCollator) { + CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString); + IndexEntry index(BSON("a" << 1)); + index.collator = &collator; + std::vector<IndexEntry> indices; + indices.push_back(index); + std::set<size_t> expectedIndices = {0}; + testRateIndices("{a: {b: 1}}", "", nullptr, indices, "a", expectedIndices); +} + +/** + * If nested object comparison is done, the query collator must be null. + */ +TEST(QueryPlannerIXSelectTest, NestedObjectNonNullQueryCollator) { + CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString); + std::vector<IndexEntry> indices; + indices.push_back(IndexEntry(BSON("a" << 1))); + std::set<size_t> expectedIndices; + testRateIndices("{a: {b: 1}}", "", &collator, indices, "a", expectedIndices); +} + +/** + * If nested object comparison is done, the query collator must be null. + */ +TEST(QueryPlannerIXSelectTest, NestedObjectNullCollators) { + std::vector<IndexEntry> indices; + indices.push_back(IndexEntry(BSON("a" << 1))); + std::set<size_t> expectedIndices = {0}; + testRateIndices("{a: {b: 1}}", "", nullptr, indices, "a", expectedIndices); +} + +/** + * If nested array comparison is done, the query collator must be null. + * TODO SERVER-23172: remove this test. + */ +TEST(QueryPlannerIXSelectTest, NestedArrayNonNullCollators) { + CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString); + IndexEntry index(BSON("a" << 1)); + index.collator = &collator; + std::vector<IndexEntry> indices; + indices.push_back(index); + std::set<size_t> expectedIndices; + testRateIndices("{a: [1]}", "", &collator, indices, "a", expectedIndices); +} + +/** + * If nested array comparison is done, the query collator must be null. + * TODO SERVER-23172: remove this test. + */ +TEST(QueryPlannerIXSelectTest, NestedArrayNullQueryCollator) { + CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString); + IndexEntry index(BSON("a" << 1)); + index.collator = &collator; + std::vector<IndexEntry> indices; + indices.push_back(index); + std::set<size_t> expectedIndices = {0}; + testRateIndices("{a: [1]}", "", nullptr, indices, "a", expectedIndices); +} + +/** + * If nested array comparison is done, the query collator must be null. + */ +TEST(QueryPlannerIXSelectTest, NestedArrayNonNullQueryCollator) { + CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString); + std::vector<IndexEntry> indices; + indices.push_back(IndexEntry(BSON("a" << 1))); + std::set<size_t> expectedIndices; + testRateIndices("{a: [1]}", "", &collator, indices, "a", expectedIndices); +} + +/** + * If nested array comparison is done, the query collator must be null. + */ +TEST(QueryPlannerIXSelectTest, NestedArrayNullCollators) { + std::vector<IndexEntry> indices; + indices.push_back(IndexEntry(BSON("a" << 1))); + std::set<size_t> expectedIndices = {0}; + testRateIndices("{a: [1]}", "", nullptr, indices, "a", expectedIndices); +} + } // namespace |