void BSONElementIterator::ArrayIterationState::reset(const FieldRef& ref, int start) { restOfPath = ref.dottedField(start).toString(); hasMore = restOfPath.size() > 0; if (hasMore) { nextPieceOfPath = ref.getPart(start); nextPieceOfPathIsNumber = isAllDigits(nextPieceOfPath); } else { nextPieceOfPathIsNumber = false; } }
/** * Helper function to check if path conflicts are all prefixes. */ static Status checkPathIsPrefixOf(const FieldRef& path, const FieldRefSet& conflictPaths) { for (FieldRefSet::const_iterator it = conflictPaths.begin(); it != conflictPaths.end(); ++it) { const FieldRef* conflictingPath = *it; // Conflicts are always prefixes (or equal to) the path, or vice versa if (path.numParts() > conflictingPath->numParts()) { string errMsg = stream() << "field at '" << conflictingPath->dottedField() << "' must be exactly specified, field at sub-path '" << path.dottedField() << "'found"; return Status(ErrorCodes::NotExactValueField, errMsg); } } return Status::OK(); }
Status isUpdatable(const FieldRef& field) { const size_t numParts = field.numParts(); if (numParts == 0) { return Status(ErrorCodes::EmptyFieldName, "An empty update path is not valid."); } for (size_t i = 0; i != numParts; ++i) { const StringData part = field.getPart(i); if (part.empty()) { return Status(ErrorCodes::EmptyFieldName, mongolutils::str::stream() << "The update path '" << field.dottedField() << "' contains an empty field name, which is not allowed."); } } return Status::OK(); }
bool TypeMatchExpression::_matches( const StringData& path, const MatchableDocument* doc, MatchDetails* details ) const { FieldRef fieldRef; fieldRef.parse( path ); bool traversedArray = false; size_t idxPath = 0; BSONElement e = doc->getFieldDottedOrArray( fieldRef, &idxPath, &traversedArray ); string rest = fieldRef.dottedField( 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() ) { BSONMatchableDocument doc( x.Obj() ); found = _matches( rest, &doc, 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; }
/** * Helper function to check if the current equality match paths conflict with a new path. */ static Status checkEqualityConflicts(const EqualityMatches& equalities, const FieldRef& path) { int parentPathPart = -1; const BSONElement& parentEl = findParentEqualityElement(equalities, path, &parentPathPart); if (parentEl.eoo()) return Status::OK(); string errMsg = "cannot infer query fields to set, "; StringData pathStr = path.dottedField(); StringData prefixStr = path.dottedSubstring(0, parentPathPart); StringData suffixStr = path.dottedSubstring(parentPathPart, path.numParts()); if (suffixStr.size() != 0) errMsg += stream() << "both paths '" << pathStr << "' and '" << prefixStr << "' are matched"; else errMsg += stream() << "path '" << pathStr << "' is matched twice"; return Status(ErrorCodes::NotSingleValueField, errMsg); }
Status findLongestPrefix(const FieldRef& prefix, mutablebson::Element root, size_t* idxFound, mutablebson::Element* elemFound) { // If root is empty or the prefix is so, there's no point in looking for a prefix. const size_t prefixSize = prefix.numParts(); if (!root.hasChildren() || prefixSize == 0) { return Status(ErrorCodes::NonExistentPath, "either the document or the path are empty"); } // Loop through prefix's parts. At each iteration, check that the part ('curr') exists // in 'root' and that the type of the previous part ('prev') allows for children. mutablebson::Element curr = root; mutablebson::Element prev = root; size_t i = 0; size_t numericPart = 0; bool viable = true; for (; i < prefixSize; i++) { // If prefix wants to reach 'curr' by applying a non-numeric index to an array // 'prev', or if 'curr' wants to traverse a leaf 'prev', then we'd be in a // non-viable path (see definition on the header file). StringData prefixPart = prefix.getPart(i); prev = curr; switch (curr.getType()) { case Object: curr = prev[prefixPart]; break; case Array: if (!isNumeric(prefixPart, &numericPart)) { viable = false; } else { curr = prev[numericPart]; } break; default: viable = false; } // If we couldn't find the next field part of the prefix in the document or if the // field part we're in constitutes a non-viable path, we can stop looking. if (!curr.ok() || !viable) { break; } } // We broke out of the loop because one of four things happened. (a) 'prefix' and // 'root' have nothing in common, (b) 'prefix' is not viable in 'root', (c) not all the // parts in 'prefix' exist in 'root', or (d) all parts do. In each case, we need to // figure out what index and Element pointer to return. if (i == 0) { return Status(ErrorCodes::NonExistentPath, "cannot find path in the document"); } else if (!viable) { *idxFound = i - 1; *elemFound = prev; return Status(ErrorCodes::PathNotViable, mongolutils::str::stream() << "cannot use the part (" << prefix.getPart(i - 1) << " of " << prefix.dottedField() << ") to traverse the element ({" << curr.toString() << "})"); } else if (curr.ok()) { *idxFound = i - 1; *elemFound = curr; return Status::OK(); } else { *idxFound = i - 1; *elemFound = prev; return Status::OK(); } }
bool LeafMatchExpression::_matches( const FieldRef& fieldRef, const MatchableDocument* doc, MatchDetails* details ) const { bool traversedArray = false; size_t idxPath = 0; BSONElement e = doc->getFieldDottedOrArray( fieldRef, &idxPath, &traversedArray ); if ( e.type() != Array || traversedArray ) { return matchesSingleElement( e ); } string rest = fieldRef.dottedField( idxPath + 1 ); StringData next; bool nextIsNumber = false; if ( rest.size() > 0 ) { next = fieldRef.getPart( idxPath + 1 ); nextIsNumber = isAllDigits( next ); } BSONObjIterator i( e.Obj() ); while ( i.more() ) { BSONElement x = i.next(); bool found = false; if ( rest.size() == 0 ) { found = matchesSingleElement( x ); } else if ( x.type() == Object ) { FieldRef myFieldRef; myFieldRef.parse( rest ); BSONMatchableDocument myDoc( x.Obj() ); found = _matches( myFieldRef, &myDoc, NULL ); } if ( !found && nextIsNumber && next == x.fieldName() ) { string reallyNext = fieldRef.dottedField( idxPath + 2 ); if ( reallyNext.size() == 0 ) { found = matchesSingleElement( x ); } else if ( x.isABSONObj() ) { // TODO: this is slow FieldRef myFieldRef; myFieldRef.parse( "x." + reallyNext ); BSONObjBuilder b; b.appendAs( x, "x" ); BSONObj temp = b.obj(); BSONMatchableDocument myDoc( temp ); found = _matches( myFieldRef, &myDoc, NULL ); } } 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; } } if ( rest.size() > 0 ) { // we're supposed to have gone further down return false; } return matchesSingleElement( e ); }