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(); }
PreparedState(mutablebson::Document& doc) : doc(doc) , idxFound(0) , elemFound(doc.end()) , newValue() , noOp(false) { }
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) {}
PreparedState(mb::Document& doc) : doc(doc) , idxFound(0) , elemFound(doc.end()) , elementsToRemove() , noOp(false) { }
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.$[]"],
PreparedState(mutablebson::Document& doc) : doc(doc), elemFound(doc.end()), idxFound(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(); }