Status AddToSetNode::init(BSONElement modExpr, const CollatorInterface* collator) { invariant(modExpr.ok()); bool isEach = false; // If the value of 'modExpr' is an object whose first field is '$each', treat it as an $each. if (modExpr.type() == BSONType::Object) { auto firstElement = modExpr.Obj().firstElement(); if (firstElement && firstElement.fieldNameStringData() == "$each") { isEach = true; if (firstElement.type() != BSONType::Array) { return Status( ErrorCodes::TypeMismatch, str::stream() << "The argument to $each in $addToSet must be an array but it was of type " << typeName(firstElement.type())); } if (modExpr.Obj().nFields() > 1) { return Status(ErrorCodes::BadValue, str::stream() << "Found unexpected fields after $each in $addToSet: " << modExpr.Obj()); } _elements = firstElement.Array(); } } // If the value of 'modExpr' was not an $each, we append the entire element. if (!isEach) { _elements.push_back(modExpr); } setCollator(collator); return Status::OK(); }
MatchExpression::ExpressionOptimizerFunc InMatchExpression::getOptimizer() const { return [](std::unique_ptr<MatchExpression> expression) -> std::unique_ptr<MatchExpression> { // NOTE: We do not recursively call optimize() on the RegexMatchExpression children in the // _regexes list. We assume that optimize() on a RegexMatchExpression is a no-op. auto& regexList = static_cast<InMatchExpression&>(*expression)._regexes; auto& equalitySet = static_cast<InMatchExpression&>(*expression)._equalitySet; auto collator = static_cast<InMatchExpression&>(*expression).getCollator(); if (regexList.size() == 1 && equalitySet.empty()) { // Simplify IN of exactly one regex to be a regex match. auto& childRe = regexList.front(); invariant(!childRe->getTag()); auto simplifiedExpression = stdx::make_unique<RegexMatchExpression>( expression->path(), childRe->getString(), childRe->getFlags()); if (expression->getTag()) { simplifiedExpression->setTag(expression->getTag()->clone()); } return std::move(simplifiedExpression); } else if (equalitySet.size() == 1 && regexList.empty()) { // Simplify IN of exactly one equality to be an EqualityMatchExpression. auto simplifiedExpression = stdx::make_unique<EqualityMatchExpression>( expression->path(), *(equalitySet.begin())); simplifiedExpression->setCollator(collator); if (expression->getTag()) { simplifiedExpression->setTag(expression->getTag()->clone()); } return std::move(simplifiedExpression); } return expression; }; }
std::unique_ptr<MatchExpression> InternalExprEqMatchExpression::shallowClone() const { auto clone = stdx::make_unique<InternalExprEqMatchExpression>(path(), _rhs); clone->setCollator(_collator); if (getTag()) { clone->setTag(getTag()->clone()); } return std::move(clone); }
std::unique_ptr<MatchExpression> InMatchExpression::shallowClone() const { auto next = stdx::make_unique<InMatchExpression>(path()); next->setCollator(_collator); if (getTag()) { next->setTag(getTag()->clone()); } next->_hasNull = _hasNull; next->_hasEmptyArray = _hasEmptyArray; next->_equalitySet = _equalitySet; next->_originalEqualityVector = _originalEqualityVector; for (auto&& regex : _regexes) { std::unique_ptr<RegexMatchExpression> clonedRegex( static_cast<RegexMatchExpression*>(regex->shallowClone().release())); next->_regexes.push_back(std::move(clonedRegex)); } return std::move(next); }
// static MatchExpression* CanonicalQuery::normalizeTree(MatchExpression* root) { if (MatchExpression::AND == root->matchType() || MatchExpression::OR == root->matchType()) { // We could have AND of AND of AND. Make sure we clean up our children before merging them. for (size_t i = 0; i < root->getChildVector()->size(); ++i) { (*root->getChildVector())[i] = normalizeTree(root->getChild(i)); } // If any of our children are of the same logical operator that we are, we remove the // child's children and append them to ourselves after we examine all children. std::vector<MatchExpression*> absorbedChildren; for (size_t i = 0; i < root->numChildren();) { MatchExpression* child = root->getChild(i); if (child->matchType() == root->matchType()) { // AND of an AND or OR of an OR. Absorb child's children into ourself. for (size_t j = 0; j < child->numChildren(); ++j) { absorbedChildren.push_back(child->getChild(j)); } // TODO(opt): this is possibly n^2-ish root->getChildVector()->erase(root->getChildVector()->begin() + i); child->getChildVector()->clear(); // Note that this only works because we cleared the child's children delete child; // Don't increment 'i' as the current child 'i' used to be child 'i+1' } else { ++i; } } root->getChildVector()->insert( root->getChildVector()->end(), absorbedChildren.begin(), absorbedChildren.end()); // AND of 1 thing is the thing, OR of 1 thing is the thing. if (1 == root->numChildren()) { MatchExpression* ret = root->getChild(0); root->getChildVector()->clear(); delete root; return ret; } } else if (MatchExpression::NOR == root->matchType()) { // First clean up children. for (size_t i = 0; i < root->getChildVector()->size(); ++i) { (*root->getChildVector())[i] = normalizeTree(root->getChild(i)); } // NOR of one thing is NOT of the thing. if (1 == root->numChildren()) { // Detach the child and assume ownership. std::unique_ptr<MatchExpression> child(root->getChild(0)); root->getChildVector()->clear(); // Delete the root when this goes out of scope. std::unique_ptr<NorMatchExpression> ownedRoot(static_cast<NorMatchExpression*>(root)); // Make a NOT to be the new root and transfer ownership of the child to it. auto newRoot = stdx::make_unique<NotMatchExpression>(); newRoot->init(child.release()).transitional_ignore(); return newRoot.release(); } } else if (MatchExpression::NOT == root->matchType()) { // Normalize the rest of the tree hanging off this NOT node. NotMatchExpression* nme = static_cast<NotMatchExpression*>(root); MatchExpression* child = nme->releaseChild(); // normalizeTree(...) takes ownership of 'child', and then // transfers ownership of its return value to 'nme'. nme->resetChild(normalizeTree(child)); } else if (MatchExpression::ELEM_MATCH_OBJECT == root->matchType()) { // Normalize the rest of the tree hanging off this ELEM_MATCH_OBJECT node. ElemMatchObjectMatchExpression* emome = static_cast<ElemMatchObjectMatchExpression*>(root); auto child = emome->releaseChild(); // normalizeTree(...) takes ownership of 'child', and then // transfers ownership of its return value to 'emome'. emome->resetChild(std::unique_ptr<MatchExpression>(normalizeTree(child.release()))); } else if (MatchExpression::ELEM_MATCH_VALUE == root->matchType()) { // Just normalize our children. for (size_t i = 0; i < root->getChildVector()->size(); ++i) { (*root->getChildVector())[i] = normalizeTree(root->getChild(i)); } } else if (MatchExpression::MATCH_IN == root->matchType()) { std::unique_ptr<InMatchExpression> in(static_cast<InMatchExpression*>(root)); // IN of 1 regex is the regex. if (in->getRegexes().size() == 1 && in->getEqualities().empty()) { RegexMatchExpression* childRe = in->getRegexes().begin()->get(); invariant(!childRe->getTag()); // Create a new RegexMatchExpression, because 'childRe' does not have a path. auto re = stdx::make_unique<RegexMatchExpression>(); re->init(in->path(), childRe->getString(), childRe->getFlags()).transitional_ignore(); if (in->getTag()) { re->setTag(in->getTag()->clone()); } return normalizeTree(re.release()); } // IN of 1 equality is the equality. if (in->getEqualities().size() == 1 && in->getRegexes().empty()) { auto eq = stdx::make_unique<EqualityMatchExpression>(); eq->init(in->path(), *(in->getEqualities().begin())).transitional_ignore(); eq->setCollator(in->getCollator()); if (in->getTag()) { eq->setTag(in->getTag()->clone()); } return eq.release(); } return in.release(); } return root; }
Status ModifierAddToSet::init(const BSONElement& modExpr, const Options& opts, bool* positional) { // Perform standard field name and updateable checks. _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 (positional) *positional = foundDollar; if (foundDollar && foundCount > 1) { return Status(ErrorCodes::BadValue, str::stream() << "Too many positional (i.e. '$') elements found in path '" << _fieldRef.dottedField() << "'"); } // TODO: The driver could potentially do this re-writing. // If the type of the value is 'Object', we might be dealing with a $each. See if that // is the case. if (modExpr.type() == mongo::Object) { BSONElement modExprObjPayload = modExpr.embeddedObject().firstElement(); if (!modExprObjPayload.eoo() && StringData(modExprObjPayload.fieldName()) == "$each") { // It is a $each. Verify that the payload is an array as is required for $each, // set our flag, and store the array as our value. if (modExprObjPayload.type() != mongo::Array) { return Status(ErrorCodes::BadValue, str::stream() << "The argument to $each in $addToSet must " "be an array but it was of type " << typeName(modExprObjPayload.type())); } status = _valDoc.root().appendElement(modExprObjPayload); if (!status.isOK()) return status; _val = _valDoc.root().leftChild(); } } // If this wasn't an 'each', turn it into one. No need to sort or de-dup since we only // have one element. if (_val == _valDoc.end()) { mb::Element each = _valDoc.makeElementArray("$each"); status = each.appendElement(modExpr); if (!status.isOK()) return status; status = _valDoc.root().pushBack(each); if (!status.isOK()) return status; _val = each; } // Check if no invalid data (such as fields with '$'s) are being used in the $each // clause. mb::ConstElement valCursor = _val.leftChild(); while (valCursor.ok()) { const BSONType type = valCursor.getType(); dassert(valCursor.hasValue()); switch (type) { case mongo::Object: { Status s = valCursor.getValueObject().storageValidEmbedded(); if (!s.isOK()) return s; break; } case mongo::Array: { Status s = valCursor.getValueArray().storageValidEmbedded(); if (!s.isOK()) return s; break; } default: break; } valCursor = valCursor.rightSibling(); } setCollator(opts.collator); return Status::OK(); }