Esempio n. 1
0
    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 );
    }
Esempio n. 2
0
    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();
    }
Esempio n. 3
0
    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 );
    }
Esempio n. 4
0
    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;
    }
Esempio n. 5
0
    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;
    }
Esempio n. 6
0
    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;
        }
    }
Esempio n. 7
0
    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;
    }
Esempio n. 8
0
    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();
    }
Esempio n. 9
0
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 );
}