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 ); }
Status UpdateDriver::createFromQuery(const BSONObj& query, mutablebson::Document& doc) { BSONObjIteratorSorted i(query); while (i.more()) { BSONElement e = i.next(); // TODO: get this logic/exclude-list from the query system? if (e.fieldName()[0] == '$' || e.fieldNameStringData() == "_id") continue; if (e.type() == Object && e.embeddedObject().firstElementFieldName()[0] == '$') { // we have something like { x : { $gt : 5 } } // this can be a query piece // or can be a dbref or something int op = e.embeddedObject().firstElement().getGtLtOp(); if (op > 0) { // This means this is a $gt type filter, so don't make it part of the new // object. continue; } if (mongoutils::str::equals(e.embeddedObject().firstElement().fieldName(), "$not")) { // A $not filter operator is not detected in getGtLtOp() and should not // become part of the new object. continue; } } // Add to the field to doc after expanding and checking for conflicts. FieldRef elemName; const StringData& elemNameSD(e.fieldNameStringData()); elemName.parse(elemNameSD); size_t pos; mutablebson::Element* elemFound = NULL; Status status = pathsupport::findLongestPrefix(elemName, doc.root(), &pos, elemFound); // Not NonExistentPath, of OK, return if (!(status.code() == ErrorCodes::NonExistentPath || status.isOK())) return status; status = pathsupport::createPathAt(elemName, 0, doc.root(), doc.makeElementWithNewFieldName( elemName.getPart(elemName.numParts()-1), e)); if (!status.isOK()) return status; } return Status::OK(); }
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 ); }
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 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; }
void FieldRefSet::getConflicts(const FieldRef* toCheck, FieldRefSet* conflicts) const { // If the set is empty, there is no work to do. if (_fieldSet.empty()) return; StringData prefixStr = safeFirstPart(toCheck); FieldRef prefixField; prefixField.parse(prefixStr); FieldSet::iterator it = _fieldSet.lower_bound(&prefixField); // Now, iterate over all the present fields in the set that have the same prefix. while (it != _fieldSet.end() && safeFirstPart(*it) == prefixStr) { size_t common = (*it)->commonPrefixSize(*toCheck); if ((*it)->numParts() == common || toCheck->numParts() == common) { conflicts->_fieldSet.insert(*it); } ++it; } }
bool FieldRefSet::insert(const FieldRef* toInsert, const FieldRef** conflict) { // We can determine if two fields conflict by checking their common prefix. // // If each field is exactly of the size of the common prefix, this means the fields are // the same. If one of the fields is greater than the common prefix and the other // isn't, the latter is a prefix of the former. And vice-versa. // // Example: // // inserted > | a a.c // exiting v | (0) (+1) // ----------------|------------------------ // a (0) | equal prefix < // a.b (+1) | prefix ^ * // // * Disjoint sub-trees // At each insertion, we only need to bother checking the fields in the set that have // at least some common prefix with the 'toInsert' field. StringData prefixStr = safeFirstPart(toInsert); FieldRef prefixField; prefixField.parse(prefixStr); FieldSet::iterator it = _fieldSet.lower_bound(&prefixField); // Now, iterate over all the present fields in the set that have the same prefix. while (it != _fieldSet.end() && safeFirstPart(*it) == prefixStr) { size_t common = (*it)->commonPrefixSize(*toInsert); if ((*it)->numParts() == common || toInsert->numParts() == common) { *conflict = *it; return false; } ++it; } _fieldSet.insert(it, toInsert); *conflict = NULL; return true; }
Status ModifierPush::init(const BSONElement& modExpr) { // // field name analysis // // Break down the field name into its 'dotted' components (aka parts) and check that // the field is fit for updates. _fieldRef.parse(modExpr.fieldName()); Status status = fieldchecker::isUpdatable(_fieldRef); if (! status.isOK()) { return status; } // If a $-positional operator was used, get the index in which it occurred // and ensure only one occurrence. size_t foundCount; bool foundDollar = fieldchecker::isPositional(_fieldRef, &_posDollar, &foundCount); if (foundDollar && foundCount > 1) { return Status(ErrorCodes::BadValue, "too many positional($) elements found."); } // // value analysis // // Are the target push values safe to store? BSONElement sliceElem; BSONElement sortElem; switch (modExpr.type()) { case Array: if (! modExpr.Obj().okForStorage()) { return Status(ErrorCodes::BadValue, "cannot use '$' or '.' as values"); } if (_pushMode == PUSH_ALL) { _eachMode = true; Status status = parseEachMode(PUSH_ALL, modExpr, &_eachElem, &sliceElem, &sortElem); if (!status.isOK()) { return status; } } else { _val = modExpr; } break; case Object: if (_pushMode == PUSH_ALL) { return Status(ErrorCodes::BadValue, "$pushAll requires an array of values"); } // If any known clause ($each, $slice, or $sort) is present, we'd assume // we're using the $each variation of push and would parse accodingly. _eachMode = inEachMode(modExpr); if (_eachMode) { Status status = parseEachMode(PUSH_NORMAL, modExpr, &_eachElem, &sliceElem, &sortElem); if (!status.isOK()) { return status; } } else { if (! modExpr.Obj().okForStorage()) { return Status(ErrorCodes::BadValue, "cannot use '$' as values"); } _val = modExpr; } break; default: if (_pushMode == PUSH_ALL) { return Status(ErrorCodes::BadValue, "$pushAll requires an array of values"); } _val = modExpr; break; } // Is slice present and correct? if (sliceElem.type() != EOO) { if (_pushMode == PUSH_ALL) { return Status(ErrorCodes::BadValue, "cannot use $slice in $pushAll"); } if (!sliceElem.isNumber()) { return Status(ErrorCodes::BadValue, "$slice must be a numeric value"); } // If the value of slice is not fraction, even if it's a double, we allow it. The // reason here is that the shell will use doubles by default unless told otherwise. double fractional = sliceElem.numberDouble(); if (fractional - static_cast<int64_t>(fractional) != 0) { return Status(ErrorCodes::BadValue, "$slice in $push cannot be fractional"); } _slice = sliceElem.numberLong(); if (_slice > 0) { return Status(ErrorCodes::BadValue, "$slice in $push must be zero or negative"); } _slicePresent = true; } // Is sort present and correct? if (sortElem.type() != EOO) { if (_pushMode == PUSH_ALL) { return Status(ErrorCodes::BadValue, "cannot use $sort in $pushAll"); } if (!_slicePresent) { return Status(ErrorCodes::BadValue, "$sort requires $slice to be present"); } else if (sortElem.type() != Object) { return Status(ErrorCodes::BadValue, "invalid $sort clause"); } BSONObj sortObj = sortElem.embeddedObject(); if (sortObj.isEmpty()) { return Status(ErrorCodes::BadValue, "sort parttern is empty"); } // Check if the sort pattern is sound. BSONObjIterator sortIter(sortObj); while (sortIter.more()) { BSONElement sortPatternElem = sortIter.next(); // We require either <field>: 1 or -1 for asc and desc. if (!isPatternElement(sortPatternElem)) { return Status(ErrorCodes::BadValue, "$sort elements' must be either 1 or -1"); } // All fields parts must be valid. FieldRef sortField; sortField.parse(sortPatternElem.fieldName()); if (sortField.numParts() == 0) { return Status(ErrorCodes::BadValue, "$sort field cannot be empty"); } for (size_t i = 0; i < sortField.numParts(); i++) { if (sortField.getPart(i).size() == 0) { return Status(ErrorCodes::BadValue, "empty field in dotted sort pattern"); } } } _sort = PatternElementCmp(sortElem.embeddedObject()); _sortPresent = true; } 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 ); }