intrusive_ptr<DocumentSource> DocumentSourceProject::createFromBson(
            BSONElement elem,
            const intrusive_ptr<ExpressionContext> &pExpCtx) {

        /* validate */
        uassert(15969, str::stream() << projectName <<
                " specification must be an object",
                elem.type() == Object);

        Expression::ObjectCtx objectCtx(
              Expression::ObjectCtx::DOCUMENT_OK
            | Expression::ObjectCtx::TOP_LEVEL
            | Expression::ObjectCtx::INCLUSION_OK
            );

        VariablesIdGenerator idGenerator;
        VariablesParseState vps(&idGenerator);
        intrusive_ptr<Expression> parsed = Expression::parseObject(elem.Obj(), &objectCtx, vps);
        ExpressionObject* exprObj = dynamic_cast<ExpressionObject*>(parsed.get());
        massert(16402, "parseObject() returned wrong type of Expression", exprObj);
        uassert(16403, "$projection requires at least one output field", exprObj->getFieldCount());

        intrusive_ptr<DocumentSourceProject> pProject(new DocumentSourceProject(pExpCtx, exprObj));
        pProject->_variables.reset(new Variables(idGenerator.getIdCount()));

        BSONObj projectObj = elem.Obj();
        pProject->_raw = projectObj.getOwned();

        return pProject;
    }
Esempio n. 2
0
intrusive_ptr<DocumentSource> DocumentSourceGroup::createFromBson(
    BSONElement elem, const intrusive_ptr<ExpressionContext>& pExpCtx) {
    uassert(15947, "a group's fields must be specified in an object", elem.type() == Object);

    intrusive_ptr<DocumentSourceGroup> pGroup(DocumentSourceGroup::create(pExpCtx));

    BSONObj groupObj(elem.Obj());
    BSONObjIterator groupIterator(groupObj);
    VariablesIdGenerator idGenerator;
    VariablesParseState vps(&idGenerator);
    while (groupIterator.more()) {
        BSONElement groupField(groupIterator.next());
        const char* pFieldName = groupField.fieldName();

        if (str::equals(pFieldName, "_id")) {
            uassert(
                15948, "a group's _id may only be specified once", pGroup->_idExpressions.empty());
            pGroup->parseIdExpression(groupField, vps);
            invariant(!pGroup->_idExpressions.empty());
        } else if (str::equals(pFieldName, "$doingMerge")) {
            massert(17030, "$doingMerge should be true if present", groupField.Bool());

            pGroup->setDoingMerge(true);
        } else {
            /*
              Treat as a projection field with the additional ability to
              add aggregation operators.
            */
            auto parsedAccumulator = Accumulator::parseAccumulator(groupField, vps);
            auto fieldName = parsedAccumulator.first.toString();
            auto accExpression = parsedAccumulator.second;
            auto factory =
                Accumulator::getFactory(groupField.embeddedObject().firstElementFieldName());

            pGroup->addAccumulator(fieldName, factory, accExpression);
        }
    }

    uassert(15955, "a group specification must include an _id", !pGroup->_idExpressions.empty());

    pGroup->_variables.reset(new Variables(idGenerator.getIdCount()));

    return pGroup;
}
intrusive_ptr<DocumentSource> DocumentSourceRedact::createFromBson(
    BSONElement elem, const intrusive_ptr<AggregationExecContext>& expCtx) {
    VariablesIdGenerator idGenerator;
    VariablesParseState vps(&idGenerator);
    Variables::Id currentId = vps.defineVariable("CURRENT");  // will differ from ROOT
    Variables::Id decendId = vps.defineVariable("DESCEND");
    Variables::Id pruneId = vps.defineVariable("PRUNE");
    Variables::Id keepId = vps.defineVariable("KEEP");
    intrusive_ptr<Expression> expression = Expression::parseOperand(elem, vps);
    intrusive_ptr<DocumentSourceRedact> source = new DocumentSourceRedact(expCtx, expression);

    // TODO figure out how much of this belongs in constructor and how much here.
    // Set up variables. Never need to reset DESCEND, PRUNE, or KEEP.
    source->_currentId = currentId;
    source->_variables.reset(new Variables(idGenerator.getIdCount()));
    source->_variables->setValue(decendId, descendVal);
    source->_variables->setValue(pruneId, pruneVal);
    source->_variables->setValue(keepId, keepVal);


    return source;
}
Esempio n. 4
0
    intrusive_ptr<DocumentSource> DocumentSourceProject::createFromBson(
            BSONElement elem,
            const intrusive_ptr<ExpressionContext> &pExpCtx) {

        /* validate */
        uassert(15969, str::stream() << projectName <<
                " specification must be an object",
                elem.type() == Object);

        Expression::ObjectCtx objectCtx(
              Expression::ObjectCtx::DOCUMENT_OK
            | Expression::ObjectCtx::TOP_LEVEL
            | Expression::ObjectCtx::INCLUSION_OK
            );

        VariablesIdGenerator idGenerator;
        VariablesParseState vps(&idGenerator);
        intrusive_ptr<Expression> parsed = Expression::parseObject(elem.Obj(), &objectCtx, vps);
        ExpressionObject* exprObj = dynamic_cast<ExpressionObject*>(parsed.get());
        massert(16402, "parseObject() returned wrong type of Expression", exprObj);
        uassert(16403, "$projection requires at least one output field", exprObj->getFieldCount());

        intrusive_ptr<DocumentSourceProject> pProject(new DocumentSourceProject(pExpCtx, exprObj));
        pProject->_variables.reset(new Variables(idGenerator.getIdCount()));

        BSONObj projectObj = elem.Obj();
        pProject->_raw = projectObj.getOwned(); // probably not necessary, but better to be safe

#if defined(_DEBUG)
        if (exprObj->isSimple()) {
            set<string> deps;
            vector<string> path;
            exprObj->addDependencies(deps, &path);
            pProject->_simpleProjection.init(depsToProjection(deps));
        }
#endif

        return pProject;
    }
    // Check for valid replaceRoot options, and populate internal state variables.
    void parse(const BSONElement& spec) {
        // We need a VariablesParseState in order to parse the 'newRoot' expression.
        VariablesIdGenerator idGenerator;
        VariablesParseState vps(&idGenerator);

        // Get the options from this stage. Currently the only option is newRoot.
        for (auto&& argument : spec.Obj()) {
            const StringData argName = argument.fieldNameStringData();

            if (argName == "newRoot") {
                // Allows for field path, object, and other expressions.
                _newRoot = Expression::parseOperand(argument, vps);
            } else {
                uasserted(40230,
                          str::stream() << "unrecognized option to $replaceRoot stage: " << argName
                          << ", only valid option is 'newRoot'.");
            }
        }

        // Check that there was a new root specified.
        uassert(40231, "no newRoot specified for the $replaceRoot stage", _newRoot);
        _variables = stdx::make_unique<Variables>(idGenerator.getIdCount());
    }
intrusive_ptr<DocumentSource> DocumentSourceBucketAuto::createFromBson(
    BSONElement elem, const intrusive_ptr<ExpressionContext>& pExpCtx) {
    uassert(40240,
            str::stream() << "The argument to $bucketAuto must be an object, but found type: "
                          << typeName(elem.type()),
            elem.type() == BSONType::Object);

    VariablesIdGenerator idGenerator;
    VariablesParseState vps(&idGenerator);
    vector<AccumulationStatement> accumulationStatements;
    boost::intrusive_ptr<Expression> groupByExpression;
    boost::optional<int> numBuckets;
    boost::intrusive_ptr<GranularityRounder> granularityRounder;

    for (auto&& argument : elem.Obj()) {
        const auto argName = argument.fieldNameStringData();
        if ("groupBy" == argName) {
            groupByExpression = parseGroupByExpression(argument, vps);
        } else if ("buckets" == argName) {
            Value bucketsValue = Value(argument);

            uassert(
                40241,
                str::stream()
                    << "The $bucketAuto 'buckets' field must be a numeric value, but found type: "
                    << typeName(argument.type()),
                bucketsValue.numeric());

            uassert(40242,
                    str::stream() << "The $bucketAuto 'buckets' field must be representable as a "
                                     "32-bit integer, but found "
                                  << Value(argument).coerceToDouble(),
                    bucketsValue.integral());

            numBuckets = bucketsValue.coerceToInt();
        } else if ("output" == argName) {
            uassert(40244,
                    str::stream()
                        << "The $bucketAuto 'output' field must be an object, but found type: "
                        << typeName(argument.type()),
                    argument.type() == BSONType::Object);

            for (auto&& outputField : argument.embeddedObject()) {
                accumulationStatements.push_back(
                    AccumulationStatement::parseAccumulationStatement(outputField, vps));
            }
        } else if ("granularity" == argName) {
            uassert(40261,
                    str::stream()
                        << "The $bucketAuto 'granularity' field must be a string, but found type: "
                        << typeName(argument.type()),
                    argument.type() == BSONType::String);
            granularityRounder = GranularityRounder::getGranularityRounder(argument.str());
        } else {
            uasserted(40245, str::stream() << "Unrecognized option to $bucketAuto: " << argName);
        }
    }

    uassert(40246,
            "$bucketAuto requires 'groupBy' and 'buckets' to be specified",
            groupByExpression && numBuckets);

    return DocumentSourceBucketAuto::create(pExpCtx,
                                            groupByExpression,
                                            idGenerator.getIdCount(),
                                            numBuckets.get(),
                                            accumulationStatements,
                                            granularityRounder);
}
    intrusive_ptr<DocumentSource> DocumentSourceGroup::createFromBson(
            BSONElement elem,
            const intrusive_ptr<ExpressionContext> &pExpCtx) {
        uassert(15947, "a group's fields must be specified in an object",
                elem.type() == Object);

        intrusive_ptr<DocumentSourceGroup> pGroup(
            DocumentSourceGroup::create(pExpCtx));

        BSONObj groupObj(elem.Obj());
        BSONObjIterator groupIterator(groupObj);
        VariablesIdGenerator idGenerator;
        VariablesParseState vps(&idGenerator);
        while(groupIterator.more()) {
            BSONElement groupField(groupIterator.next());
            const char *pFieldName = groupField.fieldName();

            if (str::equals(pFieldName, "_id")) {
                uassert(15948, "a group's _id may only be specified once",
                        pGroup->_idExpressions.empty());
                pGroup->parseIdExpression(groupField, vps);
                invariant(!pGroup->_idExpressions.empty());
            }
            else if (str::equals(pFieldName, "$doingMerge")) {
                massert(17030, "$doingMerge should be true if present",
                        groupField.Bool());

                pGroup->setDoingMerge(true);
            }
            else {
                /*
                  Treat as a projection field with the additional ability to
                  add aggregation operators.
                */
                uassert(16414, str::stream() <<
                        "the group aggregate field name '" << pFieldName <<
                        "' cannot be used because $group's field names cannot contain '.'",
                        !str::contains(pFieldName, '.') );

                uassert(15950, str::stream() <<
                        "the group aggregate field name '" <<
                        pFieldName << "' cannot be an operator name",
                        pFieldName[0] != '$');

                uassert(15951, str::stream() <<
                        "the group aggregate field '" << pFieldName <<
                        "' must be defined as an expression inside an object",
                        groupField.type() == Object);

                BSONObj subField(groupField.Obj());
                BSONObjIterator subIterator(subField);
                size_t subCount = 0;
                for(; subIterator.more(); ++subCount) {
                    BSONElement subElement(subIterator.next());

                    /* look for the specified operator */
                    GroupOpDesc key;
                    key.name = subElement.fieldName();
                    const GroupOpDesc *pOp =
                        (const GroupOpDesc *)bsearch(
                              &key, GroupOpTable, NGroupOp, sizeof(GroupOpDesc),
                                      GroupOpDescCmp);

                    uassert(15952, str::stream() << "unknown group operator '" << key.name << "'",
                            pOp);

                    intrusive_ptr<Expression> pGroupExpr;

                    BSONType elementType = subElement.type();
                    if (elementType == Object) {
                        Expression::ObjectCtx oCtx(Expression::ObjectCtx::DOCUMENT_OK);
                        pGroupExpr = Expression::parseObject(subElement.Obj(), &oCtx, vps);
                    }
                    else if (elementType == Array) {
                        uasserted(15953, str::stream()
                                << "aggregating group operators are unary (" << key.name << ")");
                    }
                    else { /* assume its an atomic single operand */
                        pGroupExpr = Expression::parseOperand(subElement, vps);
                    }

                    pGroup->addAccumulator(pFieldName, pOp->factory, pGroupExpr);
                }

                uassert(15954, str::stream() <<
                        "the computed aggregate '" <<
                        pFieldName << "' must specify exactly one operator",
                        subCount == 1);
            }
        }

        uassert(15955, "a group specification must include an _id",
                !pGroup->_idExpressions.empty());

        pGroup->_variables.reset(new Variables(idGenerator.getIdCount()));

        return pGroup;
    }
intrusive_ptr<DocumentSource> DocumentSourceGraphLookUp::createFromBson(
    BSONElement elem, const boost::intrusive_ptr<ExpressionContext>& expCtx) {
    NamespaceString from;
    std::string as;
    boost::intrusive_ptr<Expression> startWith;
    std::string connectFromField;
    std::string connectToField;
    boost::optional<FieldPath> depthField;
    boost::optional<long long> maxDepth;
    boost::optional<BSONObj> additionalFilter;

    VariablesIdGenerator idGenerator;
    VariablesParseState vps(&idGenerator);

    for (auto&& argument : elem.Obj()) {
        const auto argName = argument.fieldNameStringData();

        if (argName == "startWith") {
            startWith = Expression::parseOperand(argument, vps);
            continue;
        } else if (argName == "maxDepth") {
            uassert(40100,
                    str::stream() << "maxDepth must be numeric, found type: "
                                  << typeName(argument.type()),
                    argument.isNumber());
            maxDepth = argument.safeNumberLong();
            uassert(40101,
                    str::stream() << "maxDepth requires a nonnegative argument, found: "
                                  << *maxDepth,
                    *maxDepth >= 0);
            uassert(40102,
                    str::stream() << "maxDepth could not be represented as a long long: "
                                  << *maxDepth,
                    *maxDepth == argument.number());
            continue;
        } else if (argName == "restrictSearchWithMatch") {
            uassert(40185,
                    str::stream() << "restrictSearchWithMatch must be an object, found "
                                  << typeName(argument.type()),
                    argument.type() == Object);

            // We don't need to keep ahold of the MatchExpression, but we do need to ensure that
            // the specified object is parseable.
            auto parsedMatchExpression = MatchExpressionParser::parse(
                argument.embeddedObject(), ExtensionsCallbackDisallowExtensions(), nullptr);

            uassert(40186,
                    str::stream()
                        << "Failed to parse 'restrictSearchWithMatch' option to $graphLookup: "
                        << parsedMatchExpression.getStatus().reason(),
                    parsedMatchExpression.isOK());

            uassert(40187,
                    str::stream()
                        << "Failed to parse 'restrictSearchWithMatch' option to $graphLookup: "
                        << "$near not supported.",
                    !QueryPlannerCommon::hasNode(parsedMatchExpression.getValue().get(),
                                                 MatchExpression::GEO_NEAR));

            additionalFilter = argument.embeddedObject().getOwned();
            continue;
        }

        if (argName == "from" || argName == "as" || argName == "connectFromField" ||
            argName == "depthField" || argName == "connectToField") {
            // All remaining arguments to $graphLookup are expected to be strings.
            uassert(40103,
                    str::stream() << "expected string as argument for " << argName << ", found: "
                                  << argument.toString(false, false),
                    argument.type() == String);
        }

        if (argName == "from") {
            from = NamespaceString(expCtx->ns.db().toString() + '.' + argument.String());
        } else if (argName == "as") {
            as = argument.String();
        } else if (argName == "connectFromField") {
            connectFromField = argument.String();
        } else if (argName == "connectToField") {
            connectToField = argument.String();
        } else if (argName == "depthField") {
            depthField = boost::optional<FieldPath>(FieldPath(argument.String()));
        } else {
            uasserted(40104,
                      str::stream() << "Unknown argument to $graphLookup: "
                                    << argument.fieldName());
        }
    }

    const bool isMissingRequiredField = from.ns().empty() || as.empty() || !startWith ||
        connectFromField.empty() || connectToField.empty();

    uassert(40105,
            str::stream() << "$graphLookup requires 'from', 'as', 'startWith', 'connectFromField', "
                          << "and 'connectToField' to be specified.",
            !isMissingRequiredField);

    intrusive_ptr<DocumentSourceGraphLookUp> newSource(
        new DocumentSourceGraphLookUp(std::move(from),
                                      std::move(as),
                                      std::move(connectFromField),
                                      std::move(connectToField),
                                      std::move(startWith),
                                      additionalFilter,
                                      depthField,
                                      maxDepth,
                                      expCtx));

    newSource->_variables.reset(new Variables(idGenerator.getIdCount()));

    return std::move(newSource);
}
intrusive_ptr<DocumentSource> DocumentSourceGraphLookUp::createFromBson(
    BSONElement elem, const boost::intrusive_ptr<ExpressionContext>& expCtx) {
    NamespaceString from;
    std::string as;
    boost::intrusive_ptr<Expression> startWith;
    std::string connectFromField;
    std::string connectToField;
    boost::optional<FieldPath> depthField;
    boost::optional<long long> maxDepth;

    VariablesIdGenerator idGenerator;
    VariablesParseState vps(&idGenerator);

    for (auto&& argument : elem.Obj()) {
        const auto argName = argument.fieldNameStringData();

        if (argName == "startWith") {
            startWith = Expression::parseOperand(argument, vps);
            continue;
        } else if (argName == "maxDepth") {
            uassert(40100,
                    str::stream() << "maxDepth must be numeric, found type: "
                                  << typeName(argument.type()),
                    argument.isNumber());
            maxDepth = argument.safeNumberLong();
            uassert(40101,
                    str::stream() << "maxDepth requires a nonnegative argument, found: "
                                  << *maxDepth,
                    *maxDepth >= 0);
            uassert(40102,
                    str::stream() << "maxDepth could not be represented as a long long: "
                                  << *maxDepth,
                    *maxDepth == argument.number());
            continue;
        }

        if (argName == "from" || argName == "as" || argName == "connectFromField" ||
            argName == "depthField" || argName == "connectToField") {
            // All remaining arguments to $graphLookup are expected to be strings.
            uassert(40103,
                    str::stream() << "expected string as argument for " << argName << ", found: "
                                  << argument.toString(false, false),
                    argument.type() == String);
        }

        if (argName == "from") {
            from = NamespaceString(expCtx->ns.db().toString() + '.' + argument.String());
        } else if (argName == "as") {
            as = argument.String();
        } else if (argName == "connectFromField") {
            connectFromField = argument.String();
        } else if (argName == "connectToField") {
            connectToField = argument.String();
        } else if (argName == "depthField") {
            depthField = boost::optional<FieldPath>(FieldPath(argument.String()));
        } else {
            uasserted(40104,
                      str::stream() << "Unknown argument to $graphLookup: "
                                    << argument.fieldName());
        }
    }

    const bool isMissingRequiredField = from.ns().empty() || as.empty() || !startWith ||
        connectFromField.empty() || connectToField.empty();

    uassert(40105,
            str::stream() << "$graphLookup requires 'from', 'as', 'startWith', 'connectFromField', "
                          << "and 'connectToField' to be specified.",
            !isMissingRequiredField);

    intrusive_ptr<DocumentSourceGraphLookUp> newSource(
        new DocumentSourceGraphLookUp(std::move(from),
                                      std::move(as),
                                      std::move(connectFromField),
                                      std::move(connectToField),
                                      std::move(startWith),
                                      depthField,
                                      maxDepth,
                                      expCtx));

    newSource->_variables.reset(new Variables(idGenerator.getIdCount()));

    return std::move(newSource);
}