bool AllMatchExpression::matches( const BSONObj& doc, MatchDetails* details ) const { FieldRef path; path.parse(_path); bool traversedArray = false; int32_t idxPath = 0; BSONElement e = getFieldDottedOrArray( doc, path, &idxPath, &traversedArray ); string rest = pathToString( path, idxPath+1 ); if ( e.type() != Array || traversedArray || rest.size() == 0 ) { return matchesSingleElement( e ); } BSONElementSet all; BSONObjIterator i( e.Obj() ); while ( i.more() ) { BSONElement e = i.next(); if ( ! e.isABSONObj() ) continue; e.Obj().getFieldsDotted( rest, all ); } return _match( all ); }
void BSONElementIterator::reset(const ElementPath* path, const BSONObj& objectToIterate) { _path = path; _traversalStartIndex = 0; _traversalStart = getFieldDottedOrArray(objectToIterate, _path->fieldRef(), &_traversalStartIndex); _state = BEGIN; _next.reset(); _subCursor.reset(); _subCursorPath.reset(); }
void BSONElementIterator::_setTraversalStart(size_t suffixIndex, BSONElement elementToIterate) { invariant(_path->fieldRef().numParts() >= suffixIndex); if (suffixIndex == _path->fieldRef().numParts()) { _traversalStart = elementToIterate; } else { if (elementToIterate.type() == BSONType::Object) { _traversalStart = getFieldDottedOrArray( elementToIterate.Obj(), _path->fieldRef(), &_traversalStartIndex, suffixIndex); } else if (elementToIterate.type() == BSONType::Array) { _traversalStart = elementToIterate; } } }
bool LeafMatchExpression::matches( const BSONObj& doc, MatchDetails* details ) const { //log() << "e doc: " << doc << " path: " << _path << std::endl; FieldRef path; path.parse(_path); bool traversedArray = false; int32_t idxPath = 0; BSONElement e = getFieldDottedOrArray( doc, path, &idxPath, &traversedArray ); string rest = pathToString( path, idxPath+1 ); if ( e.type() != Array || traversedArray ) { return matchesSingleElement( e ); } BSONObjIterator i( e.Obj() ); while ( i.more() ) { BSONElement x = i.next(); bool found = false; if ( rest.size() == 0 ) { found = matchesSingleElement( x ); } else if ( x.isABSONObj() ) { BSONElement y = x.Obj().getField( rest ); found = matchesSingleElement( y ); } if ( found ) { if ( !_allHaveToMatch ) { if ( details && details->needRecord() ) { // this block doesn't have to be inside the _allHaveToMatch handler // but this matches the old semantics details->setElemMatchKey( x.fieldName() ); } return true; } } else if ( _allHaveToMatch ) { return false; } } return matchesSingleElement( e ); }
/* makes a new BSONObj with the fields specified in pattern. fields returned in the order they appear in pattern. if any field missing from the original object, that field in the key will be null. n^2 implementation bad if pattern and object have lots of fields - normally pattern doesn't so should be fine. */ BSONObj BSONObj::extractFieldsDotted(BSONObj pattern, BSONObjBuilder& b, const char *&nameWithinArray) const { nameWithinArray = ""; BSONObjIterator i(pattern); while ( i.more() ) { BSONElement e = i.next(); if ( e.eoo() ) break; const char *name = e.fieldName(); BSONElement x = getFieldDottedOrArray( name ); if ( x.eoo() ) { b.appendNull( "" ); continue; } else if ( x.type() == Array ) { // NOTE: Currently set based on last array discovered. nameWithinArray = name; } b.appendAs(x, ""); } return b.done(); }
bool ArrayMatchingMatchExpression::matches( const BSONObj& doc, MatchDetails* details ) const { FieldRef path; path.parse(_path); bool traversedArray = false; int32_t idxPath = 0; BSONElement e = getFieldDottedOrArray( doc, path, &idxPath, &traversedArray ); string rest = pathToString( path, idxPath+1 ); if ( rest.size() == 0 ) { if ( e.type() == Array ) return matchesArray( e.Obj(), details ); return false; } if ( e.type() != Array ) return false; BSONObjIterator i( e.Obj() ); while ( i.more() ) { BSONElement x = i.next(); if ( ! x.isABSONObj() ) continue; BSONElement sub = x.Obj().getFieldDotted( rest ); if ( sub.type() != Array ) continue; if ( matchesArray( sub.Obj(), NULL ) ) { if ( details && details->needRecord() ) { // trying to match crazy semantics?? details->setElemMatchKey( x.fieldName() ); } return true; } } return false; }
bool TypeMatchExpression::_matches( const StringData& path, const BSONObj& doc, MatchDetails* details ) const { FieldRef pathRef; pathRef.parse(path); bool traversedArray = false; int32_t idxPath = 0; BSONElement e = getFieldDottedOrArray( doc, pathRef, &idxPath, &traversedArray ); string rest = pathToString( pathRef, idxPath+1 ); if ( e.type() != Array ) { return matchesSingleElement( e ); } BSONObjIterator i( e.Obj() ); while ( i.more() ) { BSONElement x = i.next(); bool found = false; if ( rest.size() == 0 ) { found = matchesSingleElement( x ); } else if ( x.isABSONObj() ) { found = _matches( rest, x.Obj(), details ); } if ( found ) { if ( details && details->needRecord() ) { // this block doesn't have to be inside the _allHaveToMatch handler // but this matches the old semantics details->setElemMatchKey( x.fieldName() ); } return true; } } return false; }
BSONElementIterator::BSONElementIterator(const ElementPath* path, const BSONObj& objectToIterate) : _path(path), _state(BEGIN) { _traversalStart = getFieldDottedOrArray(objectToIterate, _path->fieldRef(), &_traversalStartIndex); }
bool BSONElementIterator::more() { if (subCursorHasMore()) { return true; } if (!_next.element().eoo()) { return true; } if (_state == DONE) { return false; } if (_state == BEGIN) { size_t idxPath = 0; BSONElement e = getFieldDottedOrArray(_context, _path->fieldRef(), &idxPath); if (e.type() != Array) { _next.reset(e, BSONElement(), false); _state = DONE; return true; } // It's an array. _arrayIterationState.reset(_path->fieldRef(), idxPath + 1); if (_arrayIterationState.hasMore && !_path->shouldTraverseNonleafArrays()) { // Don't allow traversing the array. _state = DONE; return false; } else if (!_arrayIterationState.hasMore && !_path->shouldTraverseLeafArray()) { // Return the leaf array. _next.reset(e, BSONElement(), true); _state = DONE; return true; } _arrayIterationState.startIterator(e); _state = IN_ARRAY; invariant(_next.element().eoo()); } if (_state == IN_ARRAY) { // 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 // element. _next.reset(eltInArray, eltInArray, false); 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) { // The current array element is a subdocument. See if the subdocument generates // any elements matching the remaining subpath. _subCursorPath.reset(new ElementPath()); _subCursorPath->init(_arrayIterationState.restOfPath); _subCursorPath->setTraverseLeafArray(_path->shouldTraverseLeafArray()); _subCursor.reset(new BSONElementIterator(_subCursorPath.get(), eltInArray.Obj())); if (subCursorHasMore()) { return true; } } else if (_arrayIterationState.isArrayOffsetMatch(eltInArray.fieldName())) { // The path we're traversing has an array offset component, and the current // array element corresponds to the offset we're looking for (for example: our // path has a ".0" component, and we're looking at the first element of the // array, so we should look inside this element). if (_arrayIterationState.nextEntireRest()) { // Our path terminates at the array offset. _next should point at the // current array element. _next.reset(eltInArray, eltInArray, false); 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. _subCursorPath.reset(new ElementPath()); _subCursorPath->init(_arrayIterationState.restOfPath.substr( _arrayIterationState.nextPieceOfPath.size() + 1)); _subCursorPath->setTraverseLeafArray(_path->shouldTraverseLeafArray()); 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; _arrayIterationState._current = BSONElement(); if (subCursorHasMore()) { return true; } } } } if (_arrayIterationState.hasMore) { return false; } _next.reset(_arrayIterationState._theArray, BSONElement(), true); _state = DONE; return true; } return false; }
bool BSONElementIterator::more() { if ( _subCursor ) { if ( _subCursor->more() ) return true; _subCursor.reset(); if ( _arrayIterationState.isArrayOffsetMatch( _arrayIterationState._current.fieldName() ) ) { if ( _arrayIterationState.nextEntireRest() ) { _next.reset( _arrayIterationState._current, _arrayIterationState._current, true ); _arrayIterationState._current = BSONElement(); return true; } _subCursorPath.reset( new ElementPath() ); _subCursorPath->init( _arrayIterationState.restOfPath.substr( _arrayIterationState.nextPieceOfPath.size() + 1 ) ); _subCursorPath->setTraverseLeafArray( _path->shouldTraverseLeafArray() ); _subCursor.reset( new BSONElementIterator( _subCursorPath.get(), _arrayIterationState._current.Obj() ) ); _arrayIterationState._current = BSONElement(); return more(); } } if ( !_next.element().eoo() ) return true; if ( _state == DONE ){ return false; } if ( _state == BEGIN ) { size_t idxPath = 0; BSONElement e = getFieldDottedOrArray( _context, _path->fieldRef(), &idxPath ); if ( e.type() != Array ) { _next.reset( e, BSONElement(), false ); _state = DONE; return true; } // its an array _arrayIterationState.reset( _path->fieldRef(), idxPath + 1 ); if ( !_arrayIterationState.hasMore && !_path->shouldTraverseLeafArray() ) { _next.reset( e, BSONElement(), true ); _state = DONE; return true; } _arrayIterationState.startIterator( e ); _state = IN_ARRAY; return more(); } if ( _state == IN_ARRAY ) { while ( _arrayIterationState.more() ) { BSONElement x = _arrayIterationState.next(); if ( !_arrayIterationState.hasMore ) { _next.reset( x, x, false ); return true; } // i have deeper to go if ( x.type() == Object ) { _subCursorPath.reset( new ElementPath() ); _subCursorPath->init( _arrayIterationState.restOfPath ); _subCursorPath->setTraverseLeafArray( _path->shouldTraverseLeafArray() ); _subCursor.reset( new BSONElementIterator( _subCursorPath.get(), x.Obj() ) ); return more(); } if ( _arrayIterationState.isArrayOffsetMatch( x.fieldName() ) ) { if ( _arrayIterationState.nextEntireRest() ) { _next.reset( x, x, false ); return true; } if ( x.isABSONObj() ) { _subCursorPath.reset( new ElementPath() ); _subCursorPath->init( _arrayIterationState.restOfPath.substr( _arrayIterationState.nextPieceOfPath.size() + 1 ) ); _subCursorPath->setTraverseLeafArray( _path->shouldTraverseLeafArray() ); BSONElementIterator* real = new BSONElementIterator( _subCursorPath.get(), _arrayIterationState._current.Obj() ); _subCursor.reset( real ); real->_arrayIterationState.reset( _subCursorPath->fieldRef(), 0 ); real->_arrayIterationState.startIterator( x ); real->_state = IN_ARRAY; _arrayIterationState._current = BSONElement(); return more(); } } } if ( _arrayIterationState.hasMore ) return false; _next.reset( _arrayIterationState._theArray, BSONElement(), true ); _state = DONE; return true; } return false; }