SERVER-108945 Handle $elemMatch with empty path under a contained $or correctly (#39824) (#40915)

GitOrigin-RevId: 2c7e210d9047bcd86a6f46e0768584b8ca70faa9
This commit is contained in:
Henri Nikku 2025-10-07 21:13:44 +01:00 committed by MongoDB Bot
parent 5b2863df43
commit e70e212e13
4 changed files with 18 additions and 3 deletions

View File

@ -21,6 +21,8 @@
#
last-continuous:
all:
- test_file: jstests/core/index/elemmatch_index.js
ticket: SERVER-108945
- test_file: jstests/replsets/malformed_heartbeat_request.js
ticket: SERVER-104563
- test_file: jstests/core/query/query_settings/query_settings_strict_api.js
@ -473,6 +475,8 @@ last-continuous:
suites: null
last-lts:
all:
- test_file: jstests/core/index/elemmatch_index.js
ticket: SERVER-108945
- test_file: jstests/replsets/malformed_heartbeat_request.js
ticket: SERVER-104563
- test_file: jstests/core/query/query_settings/query_settings_strict_api.js

View File

@ -80,8 +80,19 @@ assertIndexResults(coll, {a: {$all: [{$elemMatch: {"b.c": "x"}}]}}, true, 1);
// Tests that $elemMatch with path components that are empty strings are correctly handled in the
// plan enumerator in case if an index is used on another predicate of the query.
assertIndexResults(coll, {$and: [{"a.b.c": "x"}, {"": {$elemMatch: {"a.b.c": "x"}}}]}, true, 0);
assertIndexResults(coll, {$and: [{"a.b.c": "x"}, {"a.b.c": {$elemMatch: {"": "x"}}}]}, true, 0);
// Similar to the above but with the $elemMatch placed under a contained $or.
assertIndexResults(
coll,
{$and: [{"a.b.c": "x"}, {$or: [{"a.b.c": "y"}, {"": {$elemMatch: {"a.b.c": "x"}}}]}]},
true,
0);
assertIndexResults(
coll,
{$and: [{"a.b.c": "x"}, {$or: [{"a.b.c": "y"}, {"a.b.c": {$elemMatch: {"": "x"}}}]}]},
true,
0);
})();
(function() {

View File

@ -440,7 +440,7 @@ bool PlanEnumerator::prepMemo(MatchExpression* node, const PrepMemoContext& cont
assign->assignment = std::move(orAssignment);
}
return true;
} else if (Indexability::arrayUsesIndexOnChildren(node)) {
} else if (Indexability::isBoundsGeneratingElemMatchObject(node)) {
// Add each of our children as a subnode. We enumerate through each subnode one at a
// time until it's exhausted then we move on.
ArrayAssignment aa;

View File

@ -810,7 +810,7 @@ void QueryPlannerIXSelect::rateIndices(MatchExpression* node,
childRt->path = rt->path;
node->getChild(0)->setTag(childRt);
}
} else if (Indexability::arrayUsesIndexOnChildren(node) && !node->path().empty()) {
} else if (Indexability::isBoundsGeneratingElemMatchObject(node)) {
// Note we skip empty path components since they are not allowed in index key patterns.
const auto newPath = prefix + std::string{node->path()};
ElemMatchContext newEMContext;