Example #1
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();
    }
Example #2
0
 PreparedState(mutablebson::Document& doc)
     : doc(doc)
     , idxFound(0)
     , elemFound(doc.end())
     , newValue()
     , noOp(false) {
 }
Example #3
0
 PreparedState(mutablebson::Document& doc)
     : doc(doc)
     , idxFound(0)
     , elemFound(doc.end())
     , boundDollar("")
     , noOp(false) {
 }
 PreparedState(mb::Document& doc)
     : doc(doc),
       idxFound(0),
       elemFound(doc.end()),
       addAll(false),
       elementsToAdd(),
       noOp(false) {}
Example #5
0
 PreparedState(mb::Document& doc)
     : doc(doc)
     , idxFound(0)
     , elemFound(doc.end())
     , elementsToRemove()
     , noOp(false) {
 }
Example #6
0
void storageValid(const mutablebson::Document& doc) {
    auto currElem = doc.root().leftChild();
    while (currElem.ok()) {
        if (currElem.getFieldName() == idFieldName) {
            switch (currElem.getType()) {
                case BSONType::RegEx:
                case BSONType::Array:
                case BSONType::Undefined:
                    uasserted(ErrorCodes::InvalidIdField,
                              str::stream() << "The '_id' value cannot be of type "
                                            << typeName(currElem.getType()));
                default:
                    break;
            }
        }

        // Validate this child element.
        const auto deep = true;
        const uint32_t recursionLevel = 1;
        storageValid(currElem, deep, recursionLevel);

        currElem = currElem.rightSibling();
    }
}
    auto update = fromjson("{$set: {'a.$[i].b': 0}}");
    auto arrayFilter = fromjson("{'i.c': 0}");
    boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
    std::map<StringData, std::unique_ptr<ExpressionWithPlaceholder>> arrayFilters;
    auto parsedFilter = assertGet(MatchExpressionParser::parse(arrayFilter, expCtx));
    arrayFilters["i"] = assertGet(ExpressionWithPlaceholder::make(std::move(parsedFilter)));
    std::set<std::string> foundIdentifiers;
    UpdateObjectNode root;
    ASSERT_OK(UpdateObjectNode::parseAndMerge(&root,
                                              modifiertable::ModifierType::MOD_SET,
                                              update["$set"]["a.$[i].b"],
                                              expCtx,
                                              arrayFilters,
                                              foundIdentifiers));

    mutablebson::Document doc(fromjson("{a: [{c: 0}, {c: 0}, {c: 1}]}"));
    ASSERT_OK(doc.root()["a"][1]["c"].setValueInt(1));
    ASSERT_OK(doc.root()["a"][2]["c"].setValueInt(0));
    addIndexedPath("a");
    root.apply(getApplyParams(doc.root()));
}

TEST_F(UpdateArrayNodeTest, UpdateForEmptyIdentifierIsAppliedToAllArrayElements) {
    auto update = fromjson("{$set: {'a.$[]': 1}}");
    boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
    std::map<StringData, std::unique_ptr<ExpressionWithPlaceholder>> arrayFilters;
    std::set<std::string> foundIdentifiers;
    UpdateObjectNode root;
    ASSERT_OK(UpdateObjectNode::parseAndMerge(&root,
                                              modifiertable::ModifierType::MOD_SET,
                                              update["$set"]["a.$[]"],
Example #8
0
 PreparedState(mutablebson::Document& doc) : doc(doc), elemFound(doc.end()), idxFound(0) {}
Example #9
0
    Status UpdateDriver::populateDocumentWithQueryFields(const CanonicalQuery* query,
                                                         mutablebson::Document& doc) const {

        MatchExpression* root = query->root();

        MatchExpression::MatchType rootType = root->matchType();

        // These copies are needed until we apply the modifiers at the end.
        std::vector<BSONObj> copies;

        // We only care about equality and "and"ed equality fields, everything else is ignored
        if (rootType != MatchExpression::EQ && rootType != MatchExpression::AND)
            return Status::OK();

        if (isDocReplacement()) {
            BSONElement idElem = query->getQueryObj().getField("_id");

            // Replacement mods need the _id field copied explicitly.
            if (idElem.ok()) {
                mb::Element elem = doc.makeElement(idElem);
                return doc.root().pushFront(elem);
            }

            return Status::OK();
        }

        // Create a new UpdateDriver to create the base doc from the query
        Options opts;
        opts.logOp = false;
        opts.multi = false;
        opts.upsert = true;
        opts.modOptions = modOptions();

        UpdateDriver insertDriver(opts);
        insertDriver.setContext(ModifierInterface::ExecInfo::INSERT_CONTEXT);

        // If we are a single equality match query
        if (root->matchType() == MatchExpression::EQ) {
            EqualityMatchExpression* eqMatch =
                    static_cast<EqualityMatchExpression*>(root);

            const BSONElement matchData = eqMatch->getData();
            BSONElement childElem = matchData;

            // Make copy to new path if not the same field name (for cases like $all)
            if (!root->path().empty() && matchData.fieldNameStringData() != root->path()) {
                BSONObjBuilder copyBuilder;
                copyBuilder.appendAs(eqMatch->getData(), root->path());
                const BSONObj copy = copyBuilder.obj();
                copies.push_back(copy);
                childElem = copy[root->path()];
            }

            // Add this element as a $set modifier
            Status s = insertDriver.addAndParse(modifiertable::MOD_SET,
                                                childElem);
            if (!s.isOK())
                return s;

        }
        else {

            // parse query $set mods, including only equality stuff
            for (size_t i = 0; i < root->numChildren(); ++i) {
                MatchExpression* child = root->getChild(i);
                if (child->matchType() == MatchExpression::EQ) {
                    EqualityMatchExpression* eqMatch =
                            static_cast<EqualityMatchExpression*>(child);

                    const BSONElement matchData = eqMatch->getData();
                    BSONElement childElem = matchData;

                    // Make copy to new path if not the same field name (for cases like $all)
                    if (!child->path().empty() &&
                            matchData.fieldNameStringData() != child->path()) {
                        BSONObjBuilder copyBuilder;
                        copyBuilder.appendAs(eqMatch->getData(), child->path());
                        const BSONObj copy = copyBuilder.obj();
                        copies.push_back(copy);
                        childElem = copy[child->path()];
                    }

                    // Add this element as a $set modifier
                    Status s = insertDriver.addAndParse(modifiertable::MOD_SET,
                                                        childElem);
                    if (!s.isOK())
                        return s;
                }
            }
        }

        // update the document with base field
        Status s = insertDriver.update(StringData(), &doc);
        copies.clear();
        if (!s.isOK()) {
            return Status(ErrorCodes::UnsupportedFormat,
                          str::stream() << "Cannot create base during"
                                           " insert of update. Caused by :"
                                        << s.toString());
        }

        return Status::OK();
    }