diff --git a/src/mongo/db/matcher/path.cpp b/src/mongo/db/matcher/path.cpp index e06412814d..94bcb96aa4 100644 --- a/src/mongo/db/matcher/path.cpp +++ b/src/mongo/db/matcher/path.cpp @@ -171,6 +171,7 @@ BSONElement BSONElementIterator::ArrayIterationState::next() { bool BSONElementIterator::subCursorHasMore() { + // While we still are still finding arrays along the path, keep traversing deeper. while (_subCursor) { if (_subCursor->more()) { @@ -267,6 +268,7 @@ bool BSONElementIterator::more() { // We're traversing an array. Look at each array element. while (_arrayIterationState.more()) { + BSONElement eltInArray = _arrayIterationState.next(); if (!_arrayIterationState.hasMore) { // Our path terminates at this array. _next should point at the current array @@ -275,10 +277,13 @@ bool BSONElementIterator::more() { return true; } - // Our path does not terminate at this array; there's a subpath left over. Inspect - // the current array element to see if it could match the subpath. - - if (eltInArray.type() == Object) { + // Our path does not terminate at this array; there's a subpath left over. Inspect + // the current array element to see if it could match the subpath. Note that we match + // the subpath without positional path component only when the immediate field is not a + // numeric. For example, in document {a: [0, 0, {1: 0}]}, the inner object {1: 0} can + // only be referenced by specifying the position of the object in the array. To access + // the inner object {1: 0} the path should be specified as "a.2.1". + if (eltInArray.type() == Object && !_arrayIterationState.nextPieceOfPathIsNumber) { // The current array element is a subdocument. See if the subdocument generates // any elements matching the remaining subpath. _subCursorPath.reset(new ElementPath()); @@ -303,10 +308,9 @@ bool BSONElementIterator::more() { return true; } - invariant(eltInArray.type() != Object); // Handled above. - if (eltInArray.type() == Array) { - // The current array element is itself an array. See if the nested array - // has any elements matching the remainihng. + if (eltInArray.type() == Array || eltInArray.type() == Object) { + // The current array element is itself an array/object. See if the nested + // array/object has any elements matching the remaining. _subCursorPath.reset(new ElementPath()); _subCursorPath->init(_arrayIterationState.restOfPath.substr( _arrayIterationState.nextPieceOfPath.size() + 1)); @@ -314,15 +318,17 @@ bool BSONElementIterator::more() { BSONElementIterator* real = new BSONElementIterator( _subCursorPath.get(), _arrayIterationState._current.Obj()); _subCursor.reset(real); - real->_arrayIterationState.reset(_subCursorPath->fieldRef(), 0); - real->_arrayIterationState.startIterator(eltInArray); - real->_state = IN_ARRAY; - // Set _arrayIterationState._current to EOO. This is not an implicit array - // traversal, so we should not override the array offset of the subcursor with - // the current array offset. - _arrayIterationState._current = BSONElement(); + if (eltInArray.type() == Array) { + real->_arrayIterationState.reset(_subCursorPath->fieldRef(), 0); + real->_arrayIterationState.startIterator(eltInArray); + real->_state = IN_ARRAY; + // Set _arrayIterationState._current to EOO. This is not an implicit array + // traversal, so we should not override the array offset of the subcursor + // with the current array offset. + _arrayIterationState._current = BSONElement(); + } if (subCursorHasMore()) { return true; } @@ -338,7 +344,6 @@ bool BSONElementIterator::more() { _state = DONE; return true; } - return false; }