diff options
author | Mark Benvenuto <mark.benvenuto@mongodb.com> | 2015-06-20 00:22:50 -0400 |
---|---|---|
committer | Mark Benvenuto <mark.benvenuto@mongodb.com> | 2015-06-20 10:56:02 -0400 |
commit | 9c2ed42daa8fbbef4a919c21ec564e2db55e8d60 (patch) | |
tree | 3814f79c10d7b490948d8cb7b112ac1dd41ceff1 /src/mongo/db/pipeline/expression.cpp | |
parent | 01965cf52bce6976637ecb8f4a622aeb05ab256a (diff) | |
download | mongo-9c2ed42daa8fbbef4a919c21ec564e2db55e8d60.tar.gz |
SERVER-18579: Clang-Format - reformat code, no comment reflow
Diffstat (limited to 'src/mongo/db/pipeline/expression.cpp')
-rw-r--r-- | src/mongo/db/pipeline/expression.cpp | 4221 |
1 files changed, 2067 insertions, 2154 deletions
diff --git a/src/mongo/db/pipeline/expression.cpp b/src/mongo/db/pipeline/expression.cpp index 34cbda702a3..8db17f3152a 100644 --- a/src/mongo/db/pipeline/expression.cpp +++ b/src/mongo/db/pipeline/expression.cpp @@ -31,7 +31,7 @@ #include "mongo/db/pipeline/expression.h" #include <boost/algorithm/string.hpp> -#include <boost/preprocessor/cat.hpp> // like the ## operator but works with __LINE__ +#include <boost/preprocessor/cat.hpp> // like the ## operator but works with __LINE__ #include <cstdio> #include "mongo/base/init.h" @@ -44,268 +44,266 @@ #include "mongo/util/mongoutils/str.h" namespace mongo { - using namespace mongoutils; +using namespace mongoutils; - using boost::intrusive_ptr; - using std::set; - using std::string; - using std::vector; +using boost::intrusive_ptr; +using std::set; +using std::string; +using std::vector; - /// Helper function to easily wrap constants with $const. - static Value serializeConstant(Value val) { - return Value(DOC("$const" << val)); - } - - void Variables::uassertValidNameForUserWrite(StringData varName) { - // System variables users allowed to write to (currently just one) - if (varName == "CURRENT") { - return; - } - - uassert(16866, "empty variable names are not allowed", - !varName.empty()); - - const bool firstCharIsValid = (varName[0] >= 'a' && varName[0] <= 'z') - || (varName[0] & '\x80') // non-ascii - ; - - uassert(16867, str::stream() << - "'" << varName << "' starts with an invalid character for a user variable name", - firstCharIsValid); - - for (size_t i = 1; i < varName.size(); i++) { - const bool charIsValid = (varName[i] >= 'a' && varName[i] <= 'z') - || (varName[i] >= 'A' && varName[i] <= 'Z') - || (varName[i] >= '0' && varName[i] <= '9') - || (varName[i] == '_') - || (varName[i] & '\x80') // non-ascii - ; +/// Helper function to easily wrap constants with $const. +static Value serializeConstant(Value val) { + return Value(DOC("$const" << val)); +} - uassert(16868, str::stream() << "'" << varName << "' contains an invalid character " - << "for a variable name: '" << varName[i] << "'", - charIsValid); - } +void Variables::uassertValidNameForUserWrite(StringData varName) { + // System variables users allowed to write to (currently just one) + if (varName == "CURRENT") { + return; } - void Variables::uassertValidNameForUserRead(StringData varName) { - uassert(16869, "empty variable names are not allowed", - !varName.empty()); + uassert(16866, "empty variable names are not allowed", !varName.empty()); - const bool firstCharIsValid = (varName[0] >= 'a' && varName[0] <= 'z') - || (varName[0] >= 'A' && varName[0] <= 'Z') - || (varName[0] & '\x80') // non-ascii - ; + const bool firstCharIsValid = + (varName[0] >= 'a' && varName[0] <= 'z') || (varName[0] & '\x80') // non-ascii + ; - uassert(16870, str::stream() << - "'" << varName << "' starts with an invalid character for a variable name", - firstCharIsValid); + uassert(16867, + str::stream() << "'" << varName + << "' starts with an invalid character for a user variable name", + firstCharIsValid); - for (size_t i = 1; i < varName.size(); i++) { - const bool charIsValid = (varName[i] >= 'a' && varName[i] <= 'z') - || (varName[i] >= 'A' && varName[i] <= 'Z') - || (varName[i] >= '0' && varName[i] <= '9') - || (varName[i] == '_') - || (varName[i] & '\x80') // non-ascii - ; + for (size_t i = 1; i < varName.size(); i++) { + const bool charIsValid = (varName[i] >= 'a' && varName[i] <= 'z') || + (varName[i] >= 'A' && varName[i] <= 'Z') || (varName[i] >= '0' && varName[i] <= '9') || + (varName[i] == '_') || (varName[i] & '\x80') // non-ascii + ; - uassert(16871, str::stream() << "'" << varName << "' contains an invalid character " - << "for a variable name: '" << varName[i] << "'", - charIsValid); - } - } - - void Variables::setValue(Id id, const Value& value) { - massert(17199, "can't use Variables::setValue to set ROOT", - id != ROOT_ID); - - verify(id < _numVars); - _rest[id] = value; + uassert(16868, + str::stream() << "'" << varName << "' contains an invalid character " + << "for a variable name: '" << varName[i] << "'", + charIsValid); } +} - Value Variables::getValue(Id id) const { - if (id == ROOT_ID) - return Value(_root); +void Variables::uassertValidNameForUserRead(StringData varName) { + uassert(16869, "empty variable names are not allowed", !varName.empty()); - verify(id < _numVars); - return _rest[id]; - } + const bool firstCharIsValid = (varName[0] >= 'a' && varName[0] <= 'z') || + (varName[0] >= 'A' && varName[0] <= 'Z') || (varName[0] & '\x80') // non-ascii + ; - Document Variables::getDocument(Id id) const { - if (id == ROOT_ID) - return _root; + uassert(16870, + str::stream() << "'" << varName + << "' starts with an invalid character for a variable name", + firstCharIsValid); - verify(id < _numVars); - const Value var = _rest[id]; - if (var.getType() == Object) - return var.getDocument(); + for (size_t i = 1; i < varName.size(); i++) { + const bool charIsValid = (varName[i] >= 'a' && varName[i] <= 'z') || + (varName[i] >= 'A' && varName[i] <= 'Z') || (varName[i] >= '0' && varName[i] <= '9') || + (varName[i] == '_') || (varName[i] & '\x80') // non-ascii + ; - return Document(); + uassert(16871, + str::stream() << "'" << varName << "' contains an invalid character " + << "for a variable name: '" << varName[i] << "'", + charIsValid); } +} - Variables::Id VariablesParseState::defineVariable(StringData name) { - // caller should have validated before hand by using Variables::uassertValidNameForUserWrite - massert(17275, "Can't redefine ROOT", - name != "ROOT"); +void Variables::setValue(Id id, const Value& value) { + massert(17199, "can't use Variables::setValue to set ROOT", id != ROOT_ID); - Variables::Id id = _idGenerator->generateId(); - _variables[name] = id; - return id; - } - - Variables::Id VariablesParseState::getVariable(StringData name) const { - StringMap<Variables::Id>::const_iterator it = _variables.find(name); - if (it != _variables.end()) - return it->second; + verify(id < _numVars); + _rest[id] = value; +} - uassert(17276, str::stream() << "Use of undefined variable: " << name, - name == "ROOT" || name == "CURRENT"); +Value Variables::getValue(Id id) const { + if (id == ROOT_ID) + return Value(_root); - return Variables::ROOT_ID; - } + verify(id < _numVars); + return _rest[id]; +} - /* --------------------------- Expression ------------------------------ */ +Document Variables::getDocument(Id id) const { + if (id == ROOT_ID) + return _root; - Expression::ObjectCtx::ObjectCtx(int theOptions) - : options(theOptions) - {} + verify(id < _numVars); + const Value var = _rest[id]; + if (var.getType() == Object) + return var.getDocument(); - bool Expression::ObjectCtx::documentOk() const { - return ((options & DOCUMENT_OK) != 0); - } + return Document(); +} - bool Expression::ObjectCtx::topLevel() const { - return ((options & TOP_LEVEL) != 0); - } +Variables::Id VariablesParseState::defineVariable(StringData name) { + // caller should have validated before hand by using Variables::uassertValidNameForUserWrite + massert(17275, "Can't redefine ROOT", name != "ROOT"); - bool Expression::ObjectCtx::inclusionOk() const { - return ((options & INCLUSION_OK) != 0); - } + Variables::Id id = _idGenerator->generateId(); + _variables[name] = id; + return id; +} - string Expression::removeFieldPrefix(const string &prefixedField) { - uassert(16419, str::stream()<<"field path must not contain embedded null characters" << prefixedField.find("\0") << "," , - prefixedField.find('\0') == string::npos); +Variables::Id VariablesParseState::getVariable(StringData name) const { + StringMap<Variables::Id>::const_iterator it = _variables.find(name); + if (it != _variables.end()) + return it->second; - const char* pPrefixedField = prefixedField.c_str(); - uassert(15982, str::stream() << - "field path references must be prefixed with a '$' ('" << - prefixedField << "'", pPrefixedField[0] == '$'); + uassert(17276, + str::stream() << "Use of undefined variable: " << name, + name == "ROOT" || name == "CURRENT"); - return string(pPrefixedField + 1); - } + return Variables::ROOT_ID; +} - intrusive_ptr<Expression> Expression::parseObject( - BSONObj obj, - ObjectCtx* pCtx, - const VariablesParseState& vps) { - /* - An object expression can take any of the following forms: +/* --------------------------- Expression ------------------------------ */ - f0: {f1: ..., f2: ..., f3: ...} - f0: {$operator:[operand1, operand2, ...]} - */ +Expression::ObjectCtx::ObjectCtx(int theOptions) : options(theOptions) {} - intrusive_ptr<Expression> pExpression; // the result - intrusive_ptr<ExpressionObject> pExpressionObject; // alt result - enum { UNKNOWN, NOTOPERATOR, OPERATOR } kind = UNKNOWN; +bool Expression::ObjectCtx::documentOk() const { + return ((options & DOCUMENT_OK) != 0); +} - if (obj.isEmpty()) - return ExpressionObject::create(); - BSONObjIterator iter(obj); +bool Expression::ObjectCtx::topLevel() const { + return ((options & TOP_LEVEL) != 0); +} - for(size_t fieldCount = 0; iter.more(); ++fieldCount) { - BSONElement fieldElement(iter.next()); - const char* pFieldName = fieldElement.fieldName(); +bool Expression::ObjectCtx::inclusionOk() const { + return ((options & INCLUSION_OK) != 0); +} - if (pFieldName[0] == '$') { - uassert(15983, str::stream() << - "the operator must be the only field in a pipeline object (at '" - << pFieldName << "'", - fieldCount == 0); +string Expression::removeFieldPrefix(const string& prefixedField) { + uassert(16419, + str::stream() << "field path must not contain embedded null characters" + << prefixedField.find("\0") << ",", + prefixedField.find('\0') == string::npos); - uassert(16404, "$expressions are not allowed at the top-level of $project", - !pCtx->topLevel()); + const char* pPrefixedField = prefixedField.c_str(); + uassert(15982, + str::stream() << "field path references must be prefixed with a '$' ('" << prefixedField + << "'", + pPrefixedField[0] == '$'); - /* we've determined this "object" is an operator expression */ - kind = OPERATOR; + return string(pPrefixedField + 1); +} - pExpression = parseExpression(fieldElement, vps); +intrusive_ptr<Expression> Expression::parseObject(BSONObj obj, + ObjectCtx* pCtx, + const VariablesParseState& vps) { + /* + An object expression can take any of the following forms: + + f0: {f1: ..., f2: ..., f3: ...} + f0: {$operator:[operand1, operand2, ...]} + */ + + intrusive_ptr<Expression> pExpression; // the result + intrusive_ptr<ExpressionObject> pExpressionObject; // alt result + enum { UNKNOWN, NOTOPERATOR, OPERATOR } kind = UNKNOWN; + + if (obj.isEmpty()) + return ExpressionObject::create(); + BSONObjIterator iter(obj); + + for (size_t fieldCount = 0; iter.more(); ++fieldCount) { + BSONElement fieldElement(iter.next()); + const char* pFieldName = fieldElement.fieldName(); + + if (pFieldName[0] == '$') { + uassert( + 15983, + str::stream() << "the operator must be the only field in a pipeline object (at '" + << pFieldName << "'", + fieldCount == 0); + + uassert(16404, + "$expressions are not allowed at the top-level of $project", + !pCtx->topLevel()); + + /* we've determined this "object" is an operator expression */ + kind = OPERATOR; + + pExpression = parseExpression(fieldElement, vps); + } else { + uassert(15990, + str::stream() << "this object is already an operator expression, and can't be " + "used as a document expression (at '" << pFieldName << "')", + kind != OPERATOR); + + uassert(16405, + "dotted field names are only allowed at the top level", + pCtx->topLevel() || !str::contains(pFieldName, '.')); + + /* if it's our first time, create the document expression */ + if (!pExpression.get()) { + verify(pCtx->documentOk()); + // CW TODO error: document not allowed in this context + + pExpressionObject = + pCtx->topLevel() ? ExpressionObject::createRoot() : ExpressionObject::create(); + pExpression = pExpressionObject; + + /* this "object" is not an operator expression */ + kind = NOTOPERATOR; } - else { - uassert(15990, str::stream() << "this object is already an operator expression, and can't be used as a document expression (at '" << - pFieldName << "')", - kind != OPERATOR); - uassert(16405, "dotted field names are only allowed at the top level", - pCtx->topLevel() || !str::contains(pFieldName, '.')); + BSONType fieldType = fieldElement.type(); + string fieldName(pFieldName); + switch (fieldType) { + case Object: { + /* it's a nested document */ + ObjectCtx oCtx((pCtx->documentOk() ? ObjectCtx::DOCUMENT_OK : 0) | + (pCtx->inclusionOk() ? ObjectCtx::INCLUSION_OK : 0)); - /* if it's our first time, create the document expression */ - if (!pExpression.get()) { - verify(pCtx->documentOk()); - // CW TODO error: document not allowed in this context - - pExpressionObject = pCtx->topLevel() ? ExpressionObject::createRoot() - : ExpressionObject::create(); - pExpression = pExpressionObject; - - /* this "object" is not an operator expression */ - kind = NOTOPERATOR; + pExpressionObject->addField(fieldName, + parseObject(fieldElement.Obj(), &oCtx, vps)); + break; } - - BSONType fieldType = fieldElement.type(); - string fieldName(pFieldName); - switch (fieldType){ - case Object: { - /* it's a nested document */ - ObjectCtx oCtx( - (pCtx->documentOk() ? ObjectCtx::DOCUMENT_OK : 0) - | (pCtx->inclusionOk() ? ObjectCtx::INCLUSION_OK : 0)); - - pExpressionObject->addField(fieldName, - parseObject(fieldElement.Obj(), &oCtx, vps)); - break; - } - case String: { - /* it's a renamed field */ - // CW TODO could also be a constant - pExpressionObject->addField(fieldName, - ExpressionFieldPath::parse(fieldElement.str(), - vps)); - break; - } - case Bool: - case NumberDouble: - case NumberLong: - case NumberInt: { - /* it's an inclusion specification */ - if (fieldElement.trueValue()) { - uassert(16420, "field inclusion is not allowed inside of $expressions", - pCtx->inclusionOk()); - pExpressionObject->includePath(fieldName); - } - else { - uassert(16406, - "The top-level _id field is the only field currently supported for exclusion", - pCtx->topLevel() && fieldName == "_id"); - pExpressionObject->excludeId(true); - } - break; + case String: { + /* it's a renamed field */ + // CW TODO could also be a constant + pExpressionObject->addField( + fieldName, ExpressionFieldPath::parse(fieldElement.str(), vps)); + break; + } + case Bool: + case NumberDouble: + case NumberLong: + case NumberInt: { + /* it's an inclusion specification */ + if (fieldElement.trueValue()) { + uassert(16420, + "field inclusion is not allowed inside of $expressions", + pCtx->inclusionOk()); + pExpressionObject->includePath(fieldName); + } else { + uassert(16406, + "The top-level _id field is the only field currently supported for " + "exclusion", + pCtx->topLevel() && fieldName == "_id"); + pExpressionObject->excludeId(true); } - default: - uassert(15992, str::stream() << - "disallowed field type " << typeName(fieldType) << - " in object expression (at '" << - fieldName << "')", false); + break; } + default: + uassert(15992, + str::stream() << "disallowed field type " << typeName(fieldType) + << " in object expression (at '" << fieldName << "')", + false); } } - - return pExpression; } + return pExpression; +} + namespace { - typedef stdx::function<intrusive_ptr<Expression>(BSONElement, const VariablesParseState&)> - ExpressionParser; - StringMap<ExpressionParser> expressionParserMap; +typedef stdx::function<intrusive_ptr<Expression>(BSONElement, const VariablesParseState&)> + ExpressionParser; +StringMap<ExpressionParser> expressionParserMap; } /** Registers an ExpressionParser so it can be called from parseExpression and friends. @@ -313,2440 +311,2355 @@ namespace { * As an example, if your expression looks like {"$foo": [1,2,3]} you would add this line: * REGISTER_EXPRESSION("$foo", ExpressionFoo::parse); */ -#define REGISTER_EXPRESSION(key, parserFunc) \ - MONGO_INITIALIZER(BOOST_PP_CAT(addToExpressionParserMap, __LINE__))(InitializerContext*) { \ - /* prevent duplicate expressions */ \ - StringMap<ExpressionParser>::const_iterator op = expressionParserMap.find(key); \ - massert(17064, str::stream() << "Duplicate expression (" << key << ") detected at " \ - << __FILE__ << ":" << __LINE__, \ - op == expressionParserMap.end()); \ - /* register expression */ \ - expressionParserMap[key] = (parserFunc); \ - return Status::OK(); \ - } - - intrusive_ptr<Expression> Expression::parseExpression( - BSONElement exprElement, - const VariablesParseState& vps) { - - /* look for the specified operator */ - const char* opName = exprElement.fieldName(); - StringMap<ExpressionParser>::const_iterator op = expressionParserMap.find(opName); - uassert(15999, str::stream() << "invalid operator '" << opName << "'", - op != expressionParserMap.end()); - - /* make the expression node */ - return op->second(exprElement, vps); - } - - Expression::ExpressionVector ExpressionNary::parseArguments( - BSONElement exprElement, - const VariablesParseState& vps) { - - ExpressionVector out; - if (exprElement.type() == Array) { - BSONForEach(elem, exprElement.Obj()) { - out.push_back(Expression::parseOperand(elem, vps)); - } - } - else { // assume it's an atomic operand - out.push_back(Expression::parseOperand(exprElement, vps)); - } +#define REGISTER_EXPRESSION(key, parserFunc) \ + MONGO_INITIALIZER(BOOST_PP_CAT(addToExpressionParserMap, __LINE__))(InitializerContext*) { \ + /* prevent duplicate expressions */ \ + StringMap<ExpressionParser>::const_iterator op = expressionParserMap.find(key); \ + massert(17064, \ + str::stream() << "Duplicate expression (" << key << ") detected at " << __FILE__ \ + << ":" << __LINE__, \ + op == expressionParserMap.end()); \ + /* register expression */ \ + expressionParserMap[key] = (parserFunc); \ + return Status::OK(); \ + } + +intrusive_ptr<Expression> Expression::parseExpression(BSONElement exprElement, + const VariablesParseState& vps) { + /* look for the specified operator */ + const char* opName = exprElement.fieldName(); + StringMap<ExpressionParser>::const_iterator op = expressionParserMap.find(opName); + uassert(15999, + str::stream() << "invalid operator '" << opName << "'", + op != expressionParserMap.end()); + + /* make the expression node */ + return op->second(exprElement, vps); +} - return out; +Expression::ExpressionVector ExpressionNary::parseArguments(BSONElement exprElement, + const VariablesParseState& vps) { + ExpressionVector out; + if (exprElement.type() == Array) { + BSONForEach(elem, exprElement.Obj()) { + out.push_back(Expression::parseOperand(elem, vps)); + } + } else { // assume it's an atomic operand + out.push_back(Expression::parseOperand(exprElement, vps)); } - intrusive_ptr<Expression> Expression::parseOperand( - BSONElement exprElement, - const VariablesParseState& vps) { + return out; +} - BSONType type = exprElement.type(); +intrusive_ptr<Expression> Expression::parseOperand(BSONElement exprElement, + const VariablesParseState& vps) { + BSONType type = exprElement.type(); - if (type == String && exprElement.valuestr()[0] == '$') { - /* if we got here, this is a field path expression */ - return ExpressionFieldPath::parse(exprElement.str(), vps); - } - else if (type == Object) { - ObjectCtx oCtx(ObjectCtx::DOCUMENT_OK); - return Expression::parseObject(exprElement.Obj(), &oCtx, vps); - } - else { - return ExpressionConstant::parse(exprElement, vps); - } + if (type == String && exprElement.valuestr()[0] == '$') { + /* if we got here, this is a field path expression */ + return ExpressionFieldPath::parse(exprElement.str(), vps); + } else if (type == Object) { + ObjectCtx oCtx(ObjectCtx::DOCUMENT_OK); + return Expression::parseObject(exprElement.Obj(), &oCtx, vps); + } else { + return ExpressionConstant::parse(exprElement, vps); } +} - /* ----------------------- ExpressionAbs ---------------------------- */ +/* ----------------------- ExpressionAbs ---------------------------- */ - Value ExpressionAbs::evaluateInternal(Variables* vars) const { - Value val = vpOperand[0]->evaluateInternal(vars); +Value ExpressionAbs::evaluateInternal(Variables* vars) const { + Value val = vpOperand[0]->evaluateInternal(vars); - if (val.numeric()) { - BSONType type = val.getType(); - if (type == NumberDouble) { - return Value(std::abs(val.getDouble())); - } - else { - long long num = val.getLong(); - uassert(28680, "can't take $abs of long long min", - num != std::numeric_limits<long long>::min()); - long long absVal = std::abs(num); - return type == NumberLong ? Value(absVal) : Value::createIntOrLong(absVal); - } - } - else if (val.nullish()) { - return Value(BSONNULL); - } - else { - uasserted(28681, str::stream() << "$abs only supports numeric types, not " - << typeName(val.getType())); + if (val.numeric()) { + BSONType type = val.getType(); + if (type == NumberDouble) { + return Value(std::abs(val.getDouble())); + } else { + long long num = val.getLong(); + uassert(28680, + "can't take $abs of long long min", + num != std::numeric_limits<long long>::min()); + long long absVal = std::abs(num); + return type == NumberLong ? Value(absVal) : Value::createIntOrLong(absVal); } + } else if (val.nullish()) { + return Value(BSONNULL); + } else { + uasserted(28681, + str::stream() << "$abs only supports numeric types, not " + << typeName(val.getType())); } +} - REGISTER_EXPRESSION("$abs", ExpressionAbs::parse); - const char* ExpressionAbs::getOpName() const { - return "$abs"; - } +REGISTER_EXPRESSION("$abs", ExpressionAbs::parse); +const char* ExpressionAbs::getOpName() const { + return "$abs"; +} - /* ------------------------- ExpressionAdd ----------------------------- */ +/* ------------------------- ExpressionAdd ----------------------------- */ - Value ExpressionAdd::evaluateInternal(Variables* vars) const { +Value ExpressionAdd::evaluateInternal(Variables* vars) const { + /* + We'll try to return the narrowest possible result value. To do that + without creating intermediate Values, do the arithmetic for double + and integral types in parallel, tracking the current narrowest + type. + */ + double doubleTotal = 0; + long long longTotal = 0; + BSONType totalType = NumberInt; + bool haveDate = false; - /* - We'll try to return the narrowest possible result value. To do that - without creating intermediate Values, do the arithmetic for double - and integral types in parallel, tracking the current narrowest - type. - */ - double doubleTotal = 0; - long long longTotal = 0; - BSONType totalType = NumberInt; - bool haveDate = false; - - const size_t n = vpOperand.size(); - for (size_t i = 0; i < n; ++i) { - Value val = vpOperand[i]->evaluateInternal(vars); - - if (val.numeric()) { - totalType = Value::getWidestNumeric(totalType, val.getType()); - - doubleTotal += val.coerceToDouble(); - longTotal += val.coerceToLong(); - } - else if (val.getType() == Date) { - uassert(16612, "only one Date allowed in an $add expression", - !haveDate); - haveDate = true; + const size_t n = vpOperand.size(); + for (size_t i = 0; i < n; ++i) { + Value val = vpOperand[i]->evaluateInternal(vars); - // We don't manipulate totalType here. + if (val.numeric()) { + totalType = Value::getWidestNumeric(totalType, val.getType()); - longTotal += val.getDate(); - doubleTotal += val.getDate(); - } - else if (val.nullish()) { - return Value(BSONNULL); - } - else { - uasserted(16554, str::stream() << "$add only supports numeric or date types, not " - << typeName(val.getType())); - } - } + doubleTotal += val.coerceToDouble(); + longTotal += val.coerceToLong(); + } else if (val.getType() == Date) { + uassert(16612, "only one Date allowed in an $add expression", !haveDate); + haveDate = true; - if (haveDate) { - if (totalType == NumberDouble) - longTotal = static_cast<long long>(doubleTotal); - return Value(Date_t::fromMillisSinceEpoch(longTotal)); - } - else if (totalType == NumberLong) { - return Value(longTotal); - } - else if (totalType == NumberDouble) { - return Value(doubleTotal); - } - else if (totalType == NumberInt) { - return Value::createIntOrLong(longTotal); - } - else { - massert(16417, "$add resulted in a non-numeric type", false); - } - } + // We don't manipulate totalType here. - REGISTER_EXPRESSION("$add", ExpressionAdd::parse); - const char* ExpressionAdd::getOpName() const { - return "$add"; + longTotal += val.getDate(); + doubleTotal += val.getDate(); + } else if (val.nullish()) { + return Value(BSONNULL); + } else { + uasserted(16554, + str::stream() << "$add only supports numeric or date types, not " + << typeName(val.getType())); + } + } + + if (haveDate) { + if (totalType == NumberDouble) + longTotal = static_cast<long long>(doubleTotal); + return Value(Date_t::fromMillisSinceEpoch(longTotal)); + } else if (totalType == NumberLong) { + return Value(longTotal); + } else if (totalType == NumberDouble) { + return Value(doubleTotal); + } else if (totalType == NumberInt) { + return Value::createIntOrLong(longTotal); + } else { + massert(16417, "$add resulted in a non-numeric type", false); } +} - /* ------------------------- ExpressionAllElementsTrue -------------------------- */ +REGISTER_EXPRESSION("$add", ExpressionAdd::parse); +const char* ExpressionAdd::getOpName() const { + return "$add"; +} - Value ExpressionAllElementsTrue::evaluateInternal(Variables* vars) const { - const Value arr = vpOperand[0]->evaluateInternal(vars); - uassert(17040, str::stream() << getOpName() << "'s argument must be an array, but is " - << typeName(arr.getType()), - arr.getType() == Array); - const vector<Value>& array = arr.getArray(); - for (vector<Value>::const_iterator it = array.begin(); it != array.end(); ++it) { - if (!it->coerceToBool()) { - return Value(false); - } - } - return Value(true); - } +/* ------------------------- ExpressionAllElementsTrue -------------------------- */ - REGISTER_EXPRESSION("$allElementsTrue", ExpressionAllElementsTrue::parse); - const char* ExpressionAllElementsTrue::getOpName() const { - return "$allElementsTrue"; +Value ExpressionAllElementsTrue::evaluateInternal(Variables* vars) const { + const Value arr = vpOperand[0]->evaluateInternal(vars); + uassert(17040, + str::stream() << getOpName() << "'s argument must be an array, but is " + << typeName(arr.getType()), + arr.getType() == Array); + const vector<Value>& array = arr.getArray(); + for (vector<Value>::const_iterator it = array.begin(); it != array.end(); ++it) { + if (!it->coerceToBool()) { + return Value(false); + } } + return Value(true); +} - /* ------------------------- ExpressionAnd ----------------------------- */ - - intrusive_ptr<Expression> ExpressionAnd::optimize() { - /* optimize the conjunction as much as possible */ - intrusive_ptr<Expression> pE(ExpressionNary::optimize()); +REGISTER_EXPRESSION("$allElementsTrue", ExpressionAllElementsTrue::parse); +const char* ExpressionAllElementsTrue::getOpName() const { + return "$allElementsTrue"; +} - /* if the result isn't a conjunction, we can't do anything */ - ExpressionAnd *pAnd = dynamic_cast<ExpressionAnd *>(pE.get()); - if (!pAnd) - return pE; +/* ------------------------- ExpressionAnd ----------------------------- */ - /* - Check the last argument on the result; if it's not constant (as - promised by ExpressionNary::optimize(),) then there's nothing - we can do. - */ - const size_t n = pAnd->vpOperand.size(); - // ExpressionNary::optimize() generates an ExpressionConstant for {$and:[]}. - verify(n > 0); - intrusive_ptr<Expression> pLast(pAnd->vpOperand[n - 1]); - const ExpressionConstant *pConst = - dynamic_cast<ExpressionConstant *>(pLast.get()); - if (!pConst) - return pE; +intrusive_ptr<Expression> ExpressionAnd::optimize() { + /* optimize the conjunction as much as possible */ + intrusive_ptr<Expression> pE(ExpressionNary::optimize()); - /* - Evaluate and coerce the last argument to a boolean. If it's false, - then we can replace this entire expression. - */ - bool last = pConst->getValue().coerceToBool(); - if (!last) { - intrusive_ptr<ExpressionConstant> pFinal( - ExpressionConstant::create(Value(false))); - return pFinal; - } + /* if the result isn't a conjunction, we can't do anything */ + ExpressionAnd* pAnd = dynamic_cast<ExpressionAnd*>(pE.get()); + if (!pAnd) + return pE; - /* - If we got here, the final operand was true, so we don't need it - anymore. If there was only one other operand, we don't need the - conjunction either. Note we still need to keep the promise that - the result will be a boolean. - */ - if (n == 2) { - intrusive_ptr<Expression> pFinal( - ExpressionCoerceToBool::create(pAnd->vpOperand[0])); - return pFinal; - } + /* + Check the last argument on the result; if it's not constant (as + promised by ExpressionNary::optimize(),) then there's nothing + we can do. + */ + const size_t n = pAnd->vpOperand.size(); + // ExpressionNary::optimize() generates an ExpressionConstant for {$and:[]}. + verify(n > 0); + intrusive_ptr<Expression> pLast(pAnd->vpOperand[n - 1]); + const ExpressionConstant* pConst = dynamic_cast<ExpressionConstant*>(pLast.get()); + if (!pConst) + return pE; - /* - Remove the final "true" value, and return the new expression. + /* + Evaluate and coerce the last argument to a boolean. If it's false, + then we can replace this entire expression. + */ + bool last = pConst->getValue().coerceToBool(); + if (!last) { + intrusive_ptr<ExpressionConstant> pFinal(ExpressionConstant::create(Value(false))); + return pFinal; + } - CW TODO: - Note that because of any implicit conversions, we may need to - apply an implicit boolean conversion. - */ - pAnd->vpOperand.resize(n - 1); - return pE; + /* + If we got here, the final operand was true, so we don't need it + anymore. If there was only one other operand, we don't need the + conjunction either. Note we still need to keep the promise that + the result will be a boolean. + */ + if (n == 2) { + intrusive_ptr<Expression> pFinal(ExpressionCoerceToBool::create(pAnd->vpOperand[0])); + return pFinal; } - Value ExpressionAnd::evaluateInternal(Variables* vars) const { - const size_t n = vpOperand.size(); - for(size_t i = 0; i < n; ++i) { - Value pValue(vpOperand[i]->evaluateInternal(vars)); - if (!pValue.coerceToBool()) - return Value(false); - } + /* + Remove the final "true" value, and return the new expression. - return Value(true); - } + CW TODO: + Note that because of any implicit conversions, we may need to + apply an implicit boolean conversion. + */ + pAnd->vpOperand.resize(n - 1); + return pE; +} - REGISTER_EXPRESSION("$and", ExpressionAnd::parse); - const char* ExpressionAnd::getOpName() const { - return "$and"; +Value ExpressionAnd::evaluateInternal(Variables* vars) const { + const size_t n = vpOperand.size(); + for (size_t i = 0; i < n; ++i) { + Value pValue(vpOperand[i]->evaluateInternal(vars)); + if (!pValue.coerceToBool()) + return Value(false); } - /* ------------------------- ExpressionAnyElementTrue -------------------------- */ + return Value(true); +} - Value ExpressionAnyElementTrue::evaluateInternal(Variables* vars) const { - const Value arr = vpOperand[0]->evaluateInternal(vars); - uassert(17041, str::stream() << getOpName() << "'s argument must be an array, but is " - << typeName(arr.getType()), - arr.getType() == Array); - const vector<Value>& array = arr.getArray(); - for (vector<Value>::const_iterator it = array.begin(); it != array.end(); ++it) { - if (it->coerceToBool()) { - return Value(true); - } +REGISTER_EXPRESSION("$and", ExpressionAnd::parse); +const char* ExpressionAnd::getOpName() const { + return "$and"; +} + +/* ------------------------- ExpressionAnyElementTrue -------------------------- */ + +Value ExpressionAnyElementTrue::evaluateInternal(Variables* vars) const { + const Value arr = vpOperand[0]->evaluateInternal(vars); + uassert(17041, + str::stream() << getOpName() << "'s argument must be an array, but is " + << typeName(arr.getType()), + arr.getType() == Array); + const vector<Value>& array = arr.getArray(); + for (vector<Value>::const_iterator it = array.begin(); it != array.end(); ++it) { + if (it->coerceToBool()) { + return Value(true); } - return Value(false); } + return Value(false); +} - REGISTER_EXPRESSION("$anyElementTrue", ExpressionAnyElementTrue::parse); - const char* ExpressionAnyElementTrue::getOpName() const { - return "$anyElementTrue"; - } +REGISTER_EXPRESSION("$anyElementTrue", ExpressionAnyElementTrue::parse); +const char* ExpressionAnyElementTrue::getOpName() const { + return "$anyElementTrue"; +} - /* ------------------------- ExpressionArrayElemAt -------------------------- */ +/* ------------------------- ExpressionArrayElemAt -------------------------- */ + +Value ExpressionArrayElemAt::evaluateInternal(Variables* vars) const { + const Value array = vpOperand[0]->evaluateInternal(vars); + const Value indexArg = vpOperand[1]->evaluateInternal(vars); + + if (array.nullish() || indexArg.nullish()) { + return Value(BSONNULL); + } + + uassert(28689, + str::stream() << getOpName() << "'s first argument must be an array, but is " + << typeName(array.getType()), + array.getType() == Array); + uassert(28690, + str::stream() << getOpName() << "'s second argument must be a numeric value," + << " but is " << typeName(indexArg.getType()), + indexArg.numeric()); + uassert(28691, + str::stream() << getOpName() << "'s second argument must be representable as" + << " a 32-bit integer: " << indexArg.coerceToDouble(), + indexArg.integral()); + + long long i = indexArg.coerceToLong(); + if (i < 0 && static_cast<size_t>(std::abs(i)) > array.getArrayLength()) { + // Positive indices that are too large are handled automatically by Value. + return Value(); + } else if (i < 0) { + // Index from the back of the array. + i = array.getArrayLength() + i; + } + const size_t index = static_cast<size_t>(i); + return array[index]; +} - Value ExpressionArrayElemAt::evaluateInternal(Variables* vars) const { - const Value array = vpOperand[0]->evaluateInternal(vars); - const Value indexArg = vpOperand[1]->evaluateInternal(vars); +REGISTER_EXPRESSION("$arrayElemAt", ExpressionArrayElemAt::parse); +const char* ExpressionArrayElemAt::getOpName() const { + return "$arrayElemAt"; +} - if (array.nullish() || indexArg.nullish()) { - return Value(BSONNULL); - } +/* -------------------- ExpressionCoerceToBool ------------------------- */ - uassert(28689, str::stream() << getOpName() << "'s first argument must be an array, but is " - << typeName(array.getType()), - array.getType() == Array); - uassert(28690, str::stream() << getOpName() << "'s second argument must be a numeric value," - << " but is " << typeName(indexArg.getType()), - indexArg.numeric()); - uassert(28691, str::stream() << getOpName() << "'s second argument must be representable as" - << " a 32-bit integer: " << indexArg.coerceToDouble(), - indexArg.integral()); - - long long i = indexArg.coerceToLong(); - if (i < 0 && static_cast<size_t>(std::abs(i)) > array.getArrayLength()) { - // Positive indices that are too large are handled automatically by Value. - return Value(); - } - else if (i < 0) { - // Index from the back of the array. - i = array.getArrayLength() + i; - } - const size_t index = static_cast<size_t>(i); - return array[index]; - } +intrusive_ptr<ExpressionCoerceToBool> ExpressionCoerceToBool::create( + const intrusive_ptr<Expression>& pExpression) { + intrusive_ptr<ExpressionCoerceToBool> pNew(new ExpressionCoerceToBool(pExpression)); + return pNew; +} - REGISTER_EXPRESSION("$arrayElemAt", ExpressionArrayElemAt::parse); - const char* ExpressionArrayElemAt::getOpName() const { - return "$arrayElemAt"; - } +ExpressionCoerceToBool::ExpressionCoerceToBool(const intrusive_ptr<Expression>& pTheExpression) + : Expression(), pExpression(pTheExpression) {} - /* -------------------- ExpressionCoerceToBool ------------------------- */ +intrusive_ptr<Expression> ExpressionCoerceToBool::optimize() { + /* optimize the operand */ + pExpression = pExpression->optimize(); - intrusive_ptr<ExpressionCoerceToBool> ExpressionCoerceToBool::create( - const intrusive_ptr<Expression> &pExpression) { - intrusive_ptr<ExpressionCoerceToBool> pNew( - new ExpressionCoerceToBool(pExpression)); - return pNew; - } + /* if the operand already produces a boolean, then we don't need this */ + /* LATER - Expression to support a "typeof" query? */ + Expression* pE = pExpression.get(); + if (dynamic_cast<ExpressionAnd*>(pE) || dynamic_cast<ExpressionOr*>(pE) || + dynamic_cast<ExpressionNot*>(pE) || dynamic_cast<ExpressionCoerceToBool*>(pE)) + return pExpression; - ExpressionCoerceToBool::ExpressionCoerceToBool( - const intrusive_ptr<Expression> &pTheExpression): - Expression(), - pExpression(pTheExpression) { - } + return intrusive_ptr<Expression>(this); +} - intrusive_ptr<Expression> ExpressionCoerceToBool::optimize() { - /* optimize the operand */ - pExpression = pExpression->optimize(); +void ExpressionCoerceToBool::addDependencies(DepsTracker* deps, vector<string>* path) const { + pExpression->addDependencies(deps); +} - /* if the operand already produces a boolean, then we don't need this */ - /* LATER - Expression to support a "typeof" query? */ - Expression *pE = pExpression.get(); - if (dynamic_cast<ExpressionAnd *>(pE) || - dynamic_cast<ExpressionOr *>(pE) || - dynamic_cast<ExpressionNot *>(pE) || - dynamic_cast<ExpressionCoerceToBool *>(pE)) - return pExpression; +Value ExpressionCoerceToBool::evaluateInternal(Variables* vars) const { + Value pResult(pExpression->evaluateInternal(vars)); + bool b = pResult.coerceToBool(); + if (b) + return Value(true); + return Value(false); +} - return intrusive_ptr<Expression>(this); - } +Value ExpressionCoerceToBool::serialize(bool explain) const { + // When not explaining, serialize to an $and expression. When parsed, the $and expression + // will be optimized back into a ExpressionCoerceToBool. + const char* name = explain ? "$coerceToBool" : "$and"; + return Value(DOC(name << DOC_ARRAY(pExpression->serialize(explain)))); +} - void ExpressionCoerceToBool::addDependencies(DepsTracker* deps, vector<string>* path) const { - pExpression->addDependencies(deps); - } +/* ----------------------- ExpressionCompare --------------------------- */ + +REGISTER_EXPRESSION("$cmp", + stdx::bind(ExpressionCompare::parse, + stdx::placeholders::_1, + stdx::placeholders::_2, + ExpressionCompare::CMP)); +REGISTER_EXPRESSION("$eq", + stdx::bind(ExpressionCompare::parse, + stdx::placeholders::_1, + stdx::placeholders::_2, + ExpressionCompare::EQ)); +REGISTER_EXPRESSION("$gt", + stdx::bind(ExpressionCompare::parse, + stdx::placeholders::_1, + stdx::placeholders::_2, + ExpressionCompare::GT)); +REGISTER_EXPRESSION("$gte", + stdx::bind(ExpressionCompare::parse, + stdx::placeholders::_1, + stdx::placeholders::_2, + ExpressionCompare::GTE)); +REGISTER_EXPRESSION("$lt", + stdx::bind(ExpressionCompare::parse, + stdx::placeholders::_1, + stdx::placeholders::_2, + ExpressionCompare::LT)); +REGISTER_EXPRESSION("$lte", + stdx::bind(ExpressionCompare::parse, + stdx::placeholders::_1, + stdx::placeholders::_2, + ExpressionCompare::LTE)); +REGISTER_EXPRESSION("$ne", + stdx::bind(ExpressionCompare::parse, + stdx::placeholders::_1, + stdx::placeholders::_2, + ExpressionCompare::NE)); +intrusive_ptr<Expression> ExpressionCompare::parse(BSONElement bsonExpr, + const VariablesParseState& vps, + CmpOp op) { + intrusive_ptr<ExpressionCompare> expr = new ExpressionCompare(op); + ExpressionVector args = parseArguments(bsonExpr, vps); + expr->validateArguments(args); + expr->vpOperand = args; + return expr; +} - Value ExpressionCoerceToBool::evaluateInternal(Variables* vars) const { - Value pResult(pExpression->evaluateInternal(vars)); - bool b = pResult.coerceToBool(); - if (b) - return Value(true); - return Value(false); - } - - Value ExpressionCoerceToBool::serialize(bool explain) const { - // When not explaining, serialize to an $and expression. When parsed, the $and expression - // will be optimized back into a ExpressionCoerceToBool. - const char* name = explain ? "$coerceToBool" : "$and"; - return Value(DOC(name << DOC_ARRAY(pExpression->serialize(explain)))); - } - - /* ----------------------- ExpressionCompare --------------------------- */ - - REGISTER_EXPRESSION("$cmp", - stdx::bind(ExpressionCompare::parse, stdx::placeholders::_1, stdx::placeholders::_2, ExpressionCompare::CMP)); - REGISTER_EXPRESSION("$eq", - stdx::bind(ExpressionCompare::parse, stdx::placeholders::_1, stdx::placeholders::_2, ExpressionCompare::EQ)); - REGISTER_EXPRESSION("$gt", - stdx::bind(ExpressionCompare::parse, stdx::placeholders::_1, stdx::placeholders::_2, ExpressionCompare::GT)); - REGISTER_EXPRESSION("$gte", - stdx::bind(ExpressionCompare::parse, stdx::placeholders::_1, stdx::placeholders::_2, ExpressionCompare::GTE)); - REGISTER_EXPRESSION("$lt", - stdx::bind(ExpressionCompare::parse, stdx::placeholders::_1, stdx::placeholders::_2, ExpressionCompare::LT)); - REGISTER_EXPRESSION("$lte", - stdx::bind(ExpressionCompare::parse, stdx::placeholders::_1, stdx::placeholders::_2, ExpressionCompare::LTE)); - REGISTER_EXPRESSION("$ne", - stdx::bind(ExpressionCompare::parse, stdx::placeholders::_1, stdx::placeholders::_2, ExpressionCompare::NE)); - intrusive_ptr<Expression> ExpressionCompare::parse( - BSONElement bsonExpr, - const VariablesParseState& vps, - CmpOp op) { - - intrusive_ptr<ExpressionCompare> expr = new ExpressionCompare(op); - ExpressionVector args = parseArguments(bsonExpr, vps); - expr->validateArguments(args); - expr->vpOperand = args; - return expr; - } - - ExpressionCompare::ExpressionCompare(CmpOp theCmpOp) - : cmpOp(theCmpOp) - {} +ExpressionCompare::ExpressionCompare(CmpOp theCmpOp) : cmpOp(theCmpOp) {} namespace { - // Lookup table for truth value returns - struct CmpLookup { - const bool truthValue[3]; // truth value for -1, 0, 1 - const ExpressionCompare::CmpOp reverse; // reverse(b,a) returns the same as op(a,b) - const char name[5]; // string name with trailing '\0' - }; - static const CmpLookup cmpLookup[7] = { - /* -1 0 1 reverse name */ - /* EQ */ { { false, true, false }, ExpressionCompare::EQ, "$eq" }, - /* NE */ { { true, false, true }, ExpressionCompare::NE, "$ne" }, - /* GT */ { { false, false, true }, ExpressionCompare::LT, "$gt" }, - /* GTE */ { { false, true, true }, ExpressionCompare::LTE, "$gte" }, - /* LT */ { { true, false, false }, ExpressionCompare::GT, "$lt" }, - /* LTE */ { { true, true, false }, ExpressionCompare::GTE, "$lte" }, - - // CMP is special. Only name is used. - /* CMP */ { { false, false, false }, ExpressionCompare::CMP, "$cmp" }, - }; -} - - Value ExpressionCompare::evaluateInternal(Variables* vars) const { - Value pLeft(vpOperand[0]->evaluateInternal(vars)); - Value pRight(vpOperand[1]->evaluateInternal(vars)); - - int cmp = Value::compare(pLeft, pRight); - - // Make cmp one of 1, 0, or -1. - if (cmp == 0) { - // leave as 0 - } else if (cmp < 0) { - cmp = -1; - } else if (cmp > 0) { - cmp = 1; - } +// Lookup table for truth value returns +struct CmpLookup { + const bool truthValue[3]; // truth value for -1, 0, 1 + const ExpressionCompare::CmpOp reverse; // reverse(b,a) returns the same as op(a,b) + const char name[5]; // string name with trailing '\0' +}; +static const CmpLookup cmpLookup[7] = { + /* -1 0 1 reverse name */ + /* EQ */ {{false, true, false}, ExpressionCompare::EQ, "$eq"}, + /* NE */ {{true, false, true}, ExpressionCompare::NE, "$ne"}, + /* GT */ {{false, false, true}, ExpressionCompare::LT, "$gt"}, + /* GTE */ {{false, true, true}, ExpressionCompare::LTE, "$gte"}, + /* LT */ {{true, false, false}, ExpressionCompare::GT, "$lt"}, + /* LTE */ {{true, true, false}, ExpressionCompare::GTE, "$lte"}, + + // CMP is special. Only name is used. + /* CMP */ {{false, false, false}, ExpressionCompare::CMP, "$cmp"}, +}; +} - if (cmpOp == CMP) - return Value(cmp); +Value ExpressionCompare::evaluateInternal(Variables* vars) const { + Value pLeft(vpOperand[0]->evaluateInternal(vars)); + Value pRight(vpOperand[1]->evaluateInternal(vars)); - bool returnValue = cmpLookup[cmpOp].truthValue[cmp + 1]; - return Value(returnValue); - } + int cmp = Value::compare(pLeft, pRight); - const char* ExpressionCompare::getOpName() const { - return cmpLookup[cmpOp].name; + // Make cmp one of 1, 0, or -1. + if (cmp == 0) { + // leave as 0 + } else if (cmp < 0) { + cmp = -1; + } else if (cmp > 0) { + cmp = 1; } - /* ------------------------- ExpressionConcat ----------------------------- */ + if (cmpOp == CMP) + return Value(cmp); - Value ExpressionConcat::evaluateInternal(Variables* vars) const { - const size_t n = vpOperand.size(); + bool returnValue = cmpLookup[cmpOp].truthValue[cmp + 1]; + return Value(returnValue); +} - StringBuilder result; - for (size_t i = 0; i < n; ++i) { - Value val = vpOperand[i]->evaluateInternal(vars); - if (val.nullish()) - return Value(BSONNULL); +const char* ExpressionCompare::getOpName() const { + return cmpLookup[cmpOp].name; +} - uassert(16702, str::stream() << "$concat only supports strings, not " - << typeName(val.getType()), - val.getType() == String); +/* ------------------------- ExpressionConcat ----------------------------- */ - result << val.coerceToString(); - } +Value ExpressionConcat::evaluateInternal(Variables* vars) const { + const size_t n = vpOperand.size(); - return Value(result.str()); - } + StringBuilder result; + for (size_t i = 0; i < n; ++i) { + Value val = vpOperand[i]->evaluateInternal(vars); + if (val.nullish()) + return Value(BSONNULL); + + uassert(16702, + str::stream() << "$concat only supports strings, not " << typeName(val.getType()), + val.getType() == String); - REGISTER_EXPRESSION("$concat", ExpressionConcat::parse); - const char* ExpressionConcat::getOpName() const { - return "$concat"; + result << val.coerceToString(); } - /* ------------------------- ExpressionConcatArrays ----------------------------- */ + return Value(result.str()); +} - Value ExpressionConcatArrays::evaluateInternal(Variables* vars) const { - const size_t n = vpOperand.size(); - vector<Value> values; +REGISTER_EXPRESSION("$concat", ExpressionConcat::parse); +const char* ExpressionConcat::getOpName() const { + return "$concat"; +} - for (size_t i = 0; i < n; ++i) { - Value val = vpOperand[i]->evaluateInternal(vars); - if (val.nullish()) { - return Value(BSONNULL); - } +/* ------------------------- ExpressionConcatArrays ----------------------------- */ - uassert(28664, str::stream() << "$concatArrays only supports arrays, not " - << typeName(val.getType()), - val.getType() == Array); +Value ExpressionConcatArrays::evaluateInternal(Variables* vars) const { + const size_t n = vpOperand.size(); + vector<Value> values; - const auto& subValues = val.getArray(); - values.insert(values.end(), subValues.begin(), subValues.end()); + for (size_t i = 0; i < n; ++i) { + Value val = vpOperand[i]->evaluateInternal(vars); + if (val.nullish()) { + return Value(BSONNULL); } - return Value(std::move(values)); - } - - REGISTER_EXPRESSION("$concatArrays", ExpressionConcatArrays::parse); - const char* ExpressionConcatArrays::getOpName() const { - return "$concatArrays"; - } - /* ----------------------- ExpressionCond ------------------------------ */ + uassert(28664, + str::stream() << "$concatArrays only supports arrays, not " + << typeName(val.getType()), + val.getType() == Array); - Value ExpressionCond::evaluateInternal(Variables* vars) const { - Value pCond(vpOperand[0]->evaluateInternal(vars)); - int idx = pCond.coerceToBool() ? 1 : 2; - return vpOperand[idx]->evaluateInternal(vars); + const auto& subValues = val.getArray(); + values.insert(values.end(), subValues.begin(), subValues.end()); } + return Value(std::move(values)); +} - intrusive_ptr<Expression> ExpressionCond::parse( - BSONElement expr, - const VariablesParseState& vps) { +REGISTER_EXPRESSION("$concatArrays", ExpressionConcatArrays::parse); +const char* ExpressionConcatArrays::getOpName() const { + return "$concatArrays"; +} - if (expr.type() != Object) { - return Base::parse(expr, vps); - } - verify(str::equals(expr.fieldName(), "$cond")); - - intrusive_ptr<ExpressionCond> ret = new ExpressionCond(); - ret->vpOperand.resize(3); - - const BSONObj args = expr.embeddedObject(); - BSONForEach(arg, args) { - if (str::equals(arg.fieldName(), "if")) { - ret->vpOperand[0] = parseOperand(arg, vps); - } else if (str::equals(arg.fieldName(), "then")) { - ret->vpOperand[1] = parseOperand(arg, vps); - } else if (str::equals(arg.fieldName(), "else")) { - ret->vpOperand[2] = parseOperand(arg, vps); - } else { - uasserted(17083, str::stream() - << "Unrecognized parameter to $cond: " << arg.fieldName()); - } - } +/* ----------------------- ExpressionCond ------------------------------ */ - uassert(17080, "Missing 'if' parameter to $cond", - ret->vpOperand[0]); - uassert(17081, "Missing 'then' parameter to $cond", - ret->vpOperand[1]); - uassert(17082, "Missing 'else' parameter to $cond", - ret->vpOperand[2]); +Value ExpressionCond::evaluateInternal(Variables* vars) const { + Value pCond(vpOperand[0]->evaluateInternal(vars)); + int idx = pCond.coerceToBool() ? 1 : 2; + return vpOperand[idx]->evaluateInternal(vars); +} - return ret; +intrusive_ptr<Expression> ExpressionCond::parse(BSONElement expr, const VariablesParseState& vps) { + if (expr.type() != Object) { + return Base::parse(expr, vps); } + verify(str::equals(expr.fieldName(), "$cond")); + + intrusive_ptr<ExpressionCond> ret = new ExpressionCond(); + ret->vpOperand.resize(3); - REGISTER_EXPRESSION("$cond", ExpressionCond::parse); - const char* ExpressionCond::getOpName() const { - return "$cond"; + const BSONObj args = expr.embeddedObject(); + BSONForEach(arg, args) { + if (str::equals(arg.fieldName(), "if")) { + ret->vpOperand[0] = parseOperand(arg, vps); + } else if (str::equals(arg.fieldName(), "then")) { + ret->vpOperand[1] = parseOperand(arg, vps); + } else if (str::equals(arg.fieldName(), "else")) { + ret->vpOperand[2] = parseOperand(arg, vps); + } else { + uasserted(17083, + str::stream() << "Unrecognized parameter to $cond: " << arg.fieldName()); + } } - /* ---------------------- ExpressionConstant --------------------------- */ + uassert(17080, "Missing 'if' parameter to $cond", ret->vpOperand[0]); + uassert(17081, "Missing 'then' parameter to $cond", ret->vpOperand[1]); + uassert(17082, "Missing 'else' parameter to $cond", ret->vpOperand[2]); - intrusive_ptr<Expression> ExpressionConstant::parse( - BSONElement exprElement, - const VariablesParseState& vps) { - return new ExpressionConstant(Value(exprElement)); - } + return ret; +} +REGISTER_EXPRESSION("$cond", ExpressionCond::parse); +const char* ExpressionCond::getOpName() const { + return "$cond"; +} - intrusive_ptr<ExpressionConstant> ExpressionConstant::create(const Value& pValue) { - intrusive_ptr<ExpressionConstant> pEC(new ExpressionConstant(pValue)); - return pEC; - } +/* ---------------------- ExpressionConstant --------------------------- */ - ExpressionConstant::ExpressionConstant(const Value& pTheValue): pValue(pTheValue) {} +intrusive_ptr<Expression> ExpressionConstant::parse(BSONElement exprElement, + const VariablesParseState& vps) { + return new ExpressionConstant(Value(exprElement)); +} - intrusive_ptr<Expression> ExpressionConstant::optimize() { - /* nothing to do */ - return intrusive_ptr<Expression>(this); - } +intrusive_ptr<ExpressionConstant> ExpressionConstant::create(const Value& pValue) { + intrusive_ptr<ExpressionConstant> pEC(new ExpressionConstant(pValue)); + return pEC; +} - void ExpressionConstant::addDependencies(DepsTracker* deps, vector<string>* path) const { - /* nothing to do */ - } +ExpressionConstant::ExpressionConstant(const Value& pTheValue) : pValue(pTheValue) {} - Value ExpressionConstant::evaluateInternal(Variables* vars) const { - return pValue; - } - Value ExpressionConstant::serialize(bool explain) const { - return serializeConstant(pValue); - } +intrusive_ptr<Expression> ExpressionConstant::optimize() { + /* nothing to do */ + return intrusive_ptr<Expression>(this); +} - REGISTER_EXPRESSION("$const", ExpressionConstant::parse); - REGISTER_EXPRESSION("$literal", ExpressionConstant::parse); // alias - const char* ExpressionConstant::getOpName() const { - return "$const"; - } +void ExpressionConstant::addDependencies(DepsTracker* deps, vector<string>* path) const { + /* nothing to do */ +} + +Value ExpressionConstant::evaluateInternal(Variables* vars) const { + return pValue; +} + +Value ExpressionConstant::serialize(bool explain) const { + return serializeConstant(pValue); +} - /* ---------------------- ExpressionDateToString ----------------------- */ +REGISTER_EXPRESSION("$const", ExpressionConstant::parse); +REGISTER_EXPRESSION("$literal", ExpressionConstant::parse); // alias +const char* ExpressionConstant::getOpName() const { + return "$const"; +} - REGISTER_EXPRESSION("$dateToString", ExpressionDateToString::parse); - intrusive_ptr<Expression> ExpressionDateToString::parse( - BSONElement expr, - const VariablesParseState& vps) { +/* ---------------------- ExpressionDateToString ----------------------- */ - verify(str::equals(expr.fieldName(), "$dateToString")); +REGISTER_EXPRESSION("$dateToString", ExpressionDateToString::parse); +intrusive_ptr<Expression> ExpressionDateToString::parse(BSONElement expr, + const VariablesParseState& vps) { + verify(str::equals(expr.fieldName(), "$dateToString")); - uassert(18629, "$dateToString only supports an object as its argument", - expr.type() == Object); + uassert(18629, "$dateToString only supports an object as its argument", expr.type() == Object); - BSONElement formatElem; - BSONElement dateElem; - const BSONObj args = expr.embeddedObject(); - BSONForEach(arg, args) { - if (str::equals(arg.fieldName(), "format")) { - formatElem = arg; - } else if (str::equals(arg.fieldName(), "date")) { - dateElem = arg; - } else { - uasserted(18534, str::stream() << "Unrecognized argument to $dateToString: " - << arg.fieldName()); - } + BSONElement formatElem; + BSONElement dateElem; + const BSONObj args = expr.embeddedObject(); + BSONForEach(arg, args) { + if (str::equals(arg.fieldName(), "format")) { + formatElem = arg; + } else if (str::equals(arg.fieldName(), "date")) { + dateElem = arg; + } else { + uasserted(18534, + str::stream() + << "Unrecognized argument to $dateToString: " << arg.fieldName()); } + } - uassert(18627, "Missing 'format' parameter to $dateToString", - !formatElem.eoo()); - uassert(18628, "Missing 'date' parameter to $dateToString", - !dateElem.eoo()); - - uassert(18533, "The 'format' parameter to $dateToString must be a string literal", - formatElem.type() == String); + uassert(18627, "Missing 'format' parameter to $dateToString", !formatElem.eoo()); + uassert(18628, "Missing 'date' parameter to $dateToString", !dateElem.eoo()); - const string format = formatElem.str(); + uassert(18533, + "The 'format' parameter to $dateToString must be a string literal", + formatElem.type() == String); - validateFormat(format); + const string format = formatElem.str(); - return new ExpressionDateToString(format, parseOperand(dateElem, vps)); - } + validateFormat(format); - ExpressionDateToString::ExpressionDateToString(const string& format, - intrusive_ptr<Expression> date) - : _format(format) - , _date(date) - {} + return new ExpressionDateToString(format, parseOperand(dateElem, vps)); +} - intrusive_ptr<Expression> ExpressionDateToString::optimize() { - _date = _date->optimize(); - return this; - } +ExpressionDateToString::ExpressionDateToString(const string& format, intrusive_ptr<Expression> date) + : _format(format), _date(date) {} - Value ExpressionDateToString::serialize(bool explain) const { - return Value(DOC("$dateToString" << DOC("format" << _format - << "date" << _date->serialize(explain) - ))); - } +intrusive_ptr<Expression> ExpressionDateToString::optimize() { + _date = _date->optimize(); + return this; +} - Value ExpressionDateToString::evaluateInternal(Variables* vars) const { - const Value date = _date->evaluateInternal(vars); +Value ExpressionDateToString::serialize(bool explain) const { + return Value( + DOC("$dateToString" << DOC("format" << _format << "date" << _date->serialize(explain)))); +} - if (date.nullish()) { - return Value(BSONNULL); - } +Value ExpressionDateToString::evaluateInternal(Variables* vars) const { + const Value date = _date->evaluateInternal(vars); - return Value(formatDate(_format, date.coerceToTm(), date.coerceToDate())); + if (date.nullish()) { + return Value(BSONNULL); } - // verifies that any '%' is followed by a valid format character, and that - // the format string ends with an even number of '%' symbols - void ExpressionDateToString::validateFormat(const std::string& format) { - for (string::const_iterator it = format.begin(); it != format.end(); ++it) { - if (*it != '%') { - continue; - } + return Value(formatDate(_format, date.coerceToTm(), date.coerceToDate())); +} + +// verifies that any '%' is followed by a valid format character, and that +// the format string ends with an even number of '%' symbols +void ExpressionDateToString::validateFormat(const std::string& format) { + for (string::const_iterator it = format.begin(); it != format.end(); ++it) { + if (*it != '%') { + continue; + } - ++it; // next character must be format modifier - uassert(18535, "Unmatched '%' at end of $dateToString format string", - it != format.end()); + ++it; // next character must be format modifier + uassert(18535, "Unmatched '%' at end of $dateToString format string", it != format.end()); - switch (*it) { + switch (*it) { // all of these fall through intentionally - case '%': case 'Y': case 'm': - case 'd': case 'H': case 'M': - case 'S': case 'L': case 'j': - case 'w': case 'U': + case '%': + case 'Y': + case 'm': + case 'd': + case 'H': + case 'M': + case 'S': + case 'L': + case 'j': + case 'w': + case 'U': break; default: - uasserted(18536, str::stream() << "Invalid format character '%" - << *it - << "' in $dateToString format string"); - } + uasserted(18536, + str::stream() << "Invalid format character '%" << *it + << "' in $dateToString format string"); } } +} - string ExpressionDateToString::formatDate(const string& format, - const tm& tm, - const long long date) { - StringBuilder formatted; - for (string::const_iterator it = format.begin(); it != format.end(); ++it) { - if (*it != '%') { - formatted << *it; - continue; - } +string ExpressionDateToString::formatDate(const string& format, + const tm& tm, + const long long date) { + StringBuilder formatted; + for (string::const_iterator it = format.begin(); it != format.end(); ++it) { + if (*it != '%') { + formatted << *it; + continue; + } - ++it; // next character is format modifier - invariant(it != format.end()); // checked in validateFormat + ++it; // next character is format modifier + invariant(it != format.end()); // checked in validateFormat - switch (*it) { - case '%': // Escaped literal % + switch (*it) { + case '%': // Escaped literal % formatted << '%'; break; - case 'Y': // Year - { - const int year = ExpressionYear::extract(tm); - uassert(18537, str::stream() << "$dateToString is only defined on year 0-9999," - << " tried to use year " - << year, - (year >= 0) && (year <= 9999)); - insertPadded(formatted, year, 4); - break; - } - case 'm': // Month + case 'Y': // Year + { + const int year = ExpressionYear::extract(tm); + uassert(18537, + str::stream() << "$dateToString is only defined on year 0-9999," + << " tried to use year " << year, + (year >= 0) && (year <= 9999)); + insertPadded(formatted, year, 4); + break; + } + case 'm': // Month insertPadded(formatted, ExpressionMonth::extract(tm), 2); break; - case 'd': // Day of month + case 'd': // Day of month insertPadded(formatted, ExpressionDayOfMonth::extract(tm), 2); break; - case 'H': // Hour + case 'H': // Hour insertPadded(formatted, ExpressionHour::extract(tm), 2); break; - case 'M': // Minute + case 'M': // Minute insertPadded(formatted, ExpressionMinute::extract(tm), 2); break; - case 'S': // Second + case 'S': // Second insertPadded(formatted, ExpressionSecond::extract(tm), 2); break; - case 'L': // Millisecond + case 'L': // Millisecond insertPadded(formatted, ExpressionMillisecond::extract(date), 3); break; - case 'j': // Day of year + case 'j': // Day of year insertPadded(formatted, ExpressionDayOfYear::extract(tm), 3); break; - case 'w': // Day of week + case 'w': // Day of week insertPadded(formatted, ExpressionDayOfWeek::extract(tm), 1); break; - case 'U': // Week + case 'U': // Week insertPadded(formatted, ExpressionWeek::extract(tm), 2); break; default: // Should never happen as format is pre-validated invariant(false); - } } - return formatted.str(); - } - - // Only works with 1 <= spaces <= 4 and 0 <= number <= 9999. - // If spaces is less than the digit count of number we simply insert the number - // without padding. - void ExpressionDateToString::insertPadded(StringBuilder& sb, int number, int width) { - invariant(width >= 1); - invariant(width <= 4); - invariant(number >= 0); - invariant(number <= 9999); - - int digits = 1; - - if (number >= 1000) { - digits = 4; - } else if (number >= 100) { - digits = 3; - } else if (number >= 10) { - digits = 2; - } - - if (width > digits) { - sb.write("0000", width - digits); - } - sb << number; } + return formatted.str(); +} - void ExpressionDateToString::addDependencies(DepsTracker* deps, vector<string> *path) const { - _date->addDependencies(deps); - } +// Only works with 1 <= spaces <= 4 and 0 <= number <= 9999. +// If spaces is less than the digit count of number we simply insert the number +// without padding. +void ExpressionDateToString::insertPadded(StringBuilder& sb, int number, int width) { + invariant(width >= 1); + invariant(width <= 4); + invariant(number >= 0); + invariant(number <= 9999); - /* ---------------------- ExpressionDayOfMonth ------------------------- */ + int digits = 1; - Value ExpressionDayOfMonth::evaluateInternal(Variables* vars) const { - Value pDate(vpOperand[0]->evaluateInternal(vars)); - return Value(extract(pDate.coerceToTm())); + if (number >= 1000) { + digits = 4; + } else if (number >= 100) { + digits = 3; + } else if (number >= 10) { + digits = 2; } - REGISTER_EXPRESSION("$dayOfMonth", ExpressionDayOfMonth::parse); - const char* ExpressionDayOfMonth::getOpName() const { - return "$dayOfMonth"; + if (width > digits) { + sb.write("0000", width - digits); } + sb << number; +} - /* ------------------------- ExpressionDayOfWeek ----------------------------- */ +void ExpressionDateToString::addDependencies(DepsTracker* deps, vector<string>* path) const { + _date->addDependencies(deps); +} - Value ExpressionDayOfWeek::evaluateInternal(Variables* vars) const { - Value pDate(vpOperand[0]->evaluateInternal(vars)); - return Value(extract(pDate.coerceToTm())); - } +/* ---------------------- ExpressionDayOfMonth ------------------------- */ - REGISTER_EXPRESSION("$dayOfWeek", ExpressionDayOfWeek::parse); - const char* ExpressionDayOfWeek::getOpName() const { - return "$dayOfWeek"; - } +Value ExpressionDayOfMonth::evaluateInternal(Variables* vars) const { + Value pDate(vpOperand[0]->evaluateInternal(vars)); + return Value(extract(pDate.coerceToTm())); +} - /* ------------------------- ExpressionDayOfYear ----------------------------- */ +REGISTER_EXPRESSION("$dayOfMonth", ExpressionDayOfMonth::parse); +const char* ExpressionDayOfMonth::getOpName() const { + return "$dayOfMonth"; +} - Value ExpressionDayOfYear::evaluateInternal(Variables* vars) const { - Value pDate(vpOperand[0]->evaluateInternal(vars)); - return Value(extract(pDate.coerceToTm())); - } +/* ------------------------- ExpressionDayOfWeek ----------------------------- */ - REGISTER_EXPRESSION("$dayOfYear", ExpressionDayOfYear::parse); - const char* ExpressionDayOfYear::getOpName() const { - return "$dayOfYear"; - } +Value ExpressionDayOfWeek::evaluateInternal(Variables* vars) const { + Value pDate(vpOperand[0]->evaluateInternal(vars)); + return Value(extract(pDate.coerceToTm())); +} - /* ----------------------- ExpressionDivide ---------------------------- */ +REGISTER_EXPRESSION("$dayOfWeek", ExpressionDayOfWeek::parse); +const char* ExpressionDayOfWeek::getOpName() const { + return "$dayOfWeek"; +} - Value ExpressionDivide::evaluateInternal(Variables* vars) const { - Value lhs = vpOperand[0]->evaluateInternal(vars); - Value rhs = vpOperand[1]->evaluateInternal(vars); +/* ------------------------- ExpressionDayOfYear ----------------------------- */ - if (lhs.numeric() && rhs.numeric()) { - double numer = lhs.coerceToDouble(); - double denom = rhs.coerceToDouble(); - uassert(16608, "can't $divide by zero", - denom != 0); +Value ExpressionDayOfYear::evaluateInternal(Variables* vars) const { + Value pDate(vpOperand[0]->evaluateInternal(vars)); + return Value(extract(pDate.coerceToTm())); +} - return Value(numer / denom); - } - else if (lhs.nullish() || rhs.nullish()) { - return Value(BSONNULL); - } - else { - uasserted(16609, str::stream() << "$divide only supports numeric types, not " - << typeName(lhs.getType()) - << " and " - << typeName(rhs.getType())); - } - } +REGISTER_EXPRESSION("$dayOfYear", ExpressionDayOfYear::parse); +const char* ExpressionDayOfYear::getOpName() const { + return "$dayOfYear"; +} - REGISTER_EXPRESSION("$divide", ExpressionDivide::parse); - const char* ExpressionDivide::getOpName() const { - return "$divide"; - } +/* ----------------------- ExpressionDivide ---------------------------- */ - /* ---------------------- ExpressionObject --------------------------- */ +Value ExpressionDivide::evaluateInternal(Variables* vars) const { + Value lhs = vpOperand[0]->evaluateInternal(vars); + Value rhs = vpOperand[1]->evaluateInternal(vars); - intrusive_ptr<ExpressionObject> ExpressionObject::create() { - return new ExpressionObject(false); - } + if (lhs.numeric() && rhs.numeric()) { + double numer = lhs.coerceToDouble(); + double denom = rhs.coerceToDouble(); + uassert(16608, "can't $divide by zero", denom != 0); - intrusive_ptr<ExpressionObject> ExpressionObject::createRoot() { - return new ExpressionObject(true); + return Value(numer / denom); + } else if (lhs.nullish() || rhs.nullish()) { + return Value(BSONNULL); + } else { + uasserted(16609, + str::stream() << "$divide only supports numeric types, not " + << typeName(lhs.getType()) << " and " << typeName(rhs.getType())); } +} - ExpressionObject::ExpressionObject(bool atRoot) - : _excludeId(false) - , _atRoot(atRoot) - {} +REGISTER_EXPRESSION("$divide", ExpressionDivide::parse); +const char* ExpressionDivide::getOpName() const { + return "$divide"; +} - intrusive_ptr<Expression> ExpressionObject::optimize() { - for (FieldMap::iterator it(_expressions.begin()); it!=_expressions.end(); ++it) { - if (it->second) - it->second = it->second->optimize(); - } +/* ---------------------- ExpressionObject --------------------------- */ + +intrusive_ptr<ExpressionObject> ExpressionObject::create() { + return new ExpressionObject(false); +} - return intrusive_ptr<Expression>(this); +intrusive_ptr<ExpressionObject> ExpressionObject::createRoot() { + return new ExpressionObject(true); +} + +ExpressionObject::ExpressionObject(bool atRoot) : _excludeId(false), _atRoot(atRoot) {} + +intrusive_ptr<Expression> ExpressionObject::optimize() { + for (FieldMap::iterator it(_expressions.begin()); it != _expressions.end(); ++it) { + if (it->second) + it->second = it->second->optimize(); } - bool ExpressionObject::isSimple() { - for (FieldMap::iterator it(_expressions.begin()); it!=_expressions.end(); ++it) { - if (it->second && !it->second->isSimple()) - return false; - } - return true; + return intrusive_ptr<Expression>(this); +} + +bool ExpressionObject::isSimple() { + for (FieldMap::iterator it(_expressions.begin()); it != _expressions.end(); ++it) { + if (it->second && !it->second->isSimple()) + return false; } + return true; +} - void ExpressionObject::addDependencies(DepsTracker* deps, vector<string>* path) const { - string pathStr; - if (path) { - if (path->empty()) { - // we are in the top level of a projection so _id is implicit - if (!_excludeId) - deps->fields.insert("_id"); - } - else { - FieldPath f (*path); - pathStr = f.getPath(false); - pathStr += '.'; - } - } - else { - verify(!_excludeId); +void ExpressionObject::addDependencies(DepsTracker* deps, vector<string>* path) const { + string pathStr; + if (path) { + if (path->empty()) { + // we are in the top level of a projection so _id is implicit + if (!_excludeId) + deps->fields.insert("_id"); + } else { + FieldPath f(*path); + pathStr = f.getPath(false); + pathStr += '.'; } + } else { + verify(!_excludeId); + } - for (FieldMap::const_iterator it(_expressions.begin()); it!=_expressions.end(); ++it) { - if (it->second) { - if (path) path->push_back(it->first); - it->second->addDependencies(deps, path); - if (path) path->pop_back(); - } - else { // inclusion - uassert(16407, "inclusion not supported in objects nested in $expressions", - path); + for (FieldMap::const_iterator it(_expressions.begin()); it != _expressions.end(); ++it) { + if (it->second) { + if (path) + path->push_back(it->first); + it->second->addDependencies(deps, path); + if (path) + path->pop_back(); + } else { // inclusion + uassert(16407, "inclusion not supported in objects nested in $expressions", path); - deps->fields.insert(pathStr + it->first); - } + deps->fields.insert(pathStr + it->first); } } +} - void ExpressionObject::addToDocument( - MutableDocument& out, - const Document& currentDoc, - Variables* vars - ) const - { - FieldMap::const_iterator end = _expressions.end(); +void ExpressionObject::addToDocument(MutableDocument& out, + const Document& currentDoc, + Variables* vars) const { + FieldMap::const_iterator end = _expressions.end(); - // This is used to mark fields we've done so that we can add the ones we haven't - set<string> doneFields; + // This is used to mark fields we've done so that we can add the ones we haven't + set<string> doneFields; - FieldIterator fields(currentDoc); - while(fields.more()) { - Document::FieldPair field (fields.next()); + FieldIterator fields(currentDoc); + while (fields.more()) { + Document::FieldPair field(fields.next()); - // TODO don't make a new string here - const string fieldName = field.first.toString(); - FieldMap::const_iterator exprIter = _expressions.find(fieldName); + // TODO don't make a new string here + const string fieldName = field.first.toString(); + FieldMap::const_iterator exprIter = _expressions.find(fieldName); - // This field is not supposed to be in the output (unless it is _id) - if (exprIter == end) { - if (!_excludeId && _atRoot && field.first == "_id") { - // _id from the root doc is always included (until exclusion is supported) - // not updating doneFields since "_id" isn't in _expressions - out.addField(field.first, field.second); - } - continue; + // This field is not supposed to be in the output (unless it is _id) + if (exprIter == end) { + if (!_excludeId && _atRoot && field.first == "_id") { + // _id from the root doc is always included (until exclusion is supported) + // not updating doneFields since "_id" isn't in _expressions + out.addField(field.first, field.second); } + continue; + } - // make sure we don't add this field again - doneFields.insert(exprIter->first); + // make sure we don't add this field again + doneFields.insert(exprIter->first); - Expression* expr = exprIter->second.get(); + Expression* expr = exprIter->second.get(); - if (!expr) { - // This means pull the matching field from the input document - out.addField(field.first, field.second); - continue; - } + if (!expr) { + // This means pull the matching field from the input document + out.addField(field.first, field.second); + continue; + } - ExpressionObject* exprObj = dynamic_cast<ExpressionObject*>(expr); - BSONType valueType = field.second.getType(); - if ((valueType != Object && valueType != Array) || !exprObj ) { - // This expression replace the whole field + ExpressionObject* exprObj = dynamic_cast<ExpressionObject*>(expr); + BSONType valueType = field.second.getType(); + if ((valueType != Object && valueType != Array) || !exprObj) { + // This expression replace the whole field - Value pValue(expr->evaluateInternal(vars)); + Value pValue(expr->evaluateInternal(vars)); - // don't add field if nothing was found in the subobject - if (exprObj && pValue.getDocument().empty()) - continue; + // don't add field if nothing was found in the subobject + if (exprObj && pValue.getDocument().empty()) + continue; - /* - Don't add non-existent values (note: different from NULL or Undefined); - this is consistent with existing selection syntax which doesn't - force the appearance of non-existent fields. - */ - if (!pValue.missing()) - out.addField(field.first, pValue); + /* + Don't add non-existent values (note: different from NULL or Undefined); + this is consistent with existing selection syntax which doesn't + force the appearance of non-existent fields. + */ + if (!pValue.missing()) + out.addField(field.first, pValue); - continue; - } + continue; + } + /* + Check on the type of the input value. If it's an + object, just walk down into that recursively, and + add it to the result. + */ + if (valueType == Object) { + MutableDocument sub(exprObj->getSizeHint()); + exprObj->addToDocument(sub, field.second.getDocument(), vars); + out.addField(field.first, sub.freezeToValue()); + } else if (valueType == Array) { /* - Check on the type of the input value. If it's an - object, just walk down into that recursively, and - add it to the result. + If it's an array, we have to do the same thing, + but to each array element. Then, add the array + of results to the current document. */ - if (valueType == Object) { - MutableDocument sub (exprObj->getSizeHint()); - exprObj->addToDocument(sub, field.second.getDocument(), vars); - out.addField(field.first, sub.freezeToValue()); - } - else if (valueType == Array) { - /* - If it's an array, we have to do the same thing, - but to each array element. Then, add the array - of results to the current document. - */ - vector<Value> result; - const vector<Value>& input = field.second.getArray(); - for (size_t i=0; i < input.size(); i++) { - // can't look for a subfield in a non-object value. - if (input[i].getType() != Object) - continue; - - MutableDocument doc (exprObj->getSizeHint()); - exprObj->addToDocument(doc, input[i].getDocument(), vars); - result.push_back(doc.freezeToValue()); - } + vector<Value> result; + const vector<Value>& input = field.second.getArray(); + for (size_t i = 0; i < input.size(); i++) { + // can't look for a subfield in a non-object value. + if (input[i].getType() != Object) + continue; - out.addField(field.first, Value(std::move(result))); + MutableDocument doc(exprObj->getSizeHint()); + exprObj->addToDocument(doc, input[i].getDocument(), vars); + result.push_back(doc.freezeToValue()); } - else { - verify( false ); - } - } - if (doneFields.size() == _expressions.size()) - return; + out.addField(field.first, Value(std::move(result))); + } else { + verify(false); + } + } - /* add any remaining fields we haven't already taken care of */ - for (vector<string>::const_iterator i(_order.begin()); i!=_order.end(); ++i) { - FieldMap::const_iterator it = _expressions.find(*i); - string fieldName(it->first); + if (doneFields.size() == _expressions.size()) + return; - /* if we've already dealt with this field, above, do nothing */ - if (doneFields.count(fieldName)) - continue; + /* add any remaining fields we haven't already taken care of */ + for (vector<string>::const_iterator i(_order.begin()); i != _order.end(); ++i) { + FieldMap::const_iterator it = _expressions.find(*i); + string fieldName(it->first); - // this is a missing inclusion field - if (!it->second) - continue; + /* if we've already dealt with this field, above, do nothing */ + if (doneFields.count(fieldName)) + continue; - Value pValue(it->second->evaluateInternal(vars)); + // this is a missing inclusion field + if (!it->second) + continue; - /* - Don't add non-existent values (note: different from NULL or Undefined); - this is consistent with existing selection syntax which doesn't - force the appearnance of non-existent fields. - */ - if (pValue.missing()) - continue; + Value pValue(it->second->evaluateInternal(vars)); - // don't add field if nothing was found in the subobject - if (dynamic_cast<ExpressionObject*>(it->second.get()) - && pValue.getDocument().empty()) - continue; + /* + Don't add non-existent values (note: different from NULL or Undefined); + this is consistent with existing selection syntax which doesn't + force the appearnance of non-existent fields. + */ + if (pValue.missing()) + continue; + // don't add field if nothing was found in the subobject + if (dynamic_cast<ExpressionObject*>(it->second.get()) && pValue.getDocument().empty()) + continue; - out.addField(fieldName, pValue); - } - } - size_t ExpressionObject::getSizeHint() const { - // Note: this can overestimate, but that is better than underestimating - return _expressions.size() + (_excludeId ? 0 : 1); + out.addField(fieldName, pValue); } +} - Document ExpressionObject::evaluateDocument(Variables* vars) const { - /* create and populate the result */ - MutableDocument out (getSizeHint()); +size_t ExpressionObject::getSizeHint() const { + // Note: this can overestimate, but that is better than underestimating + return _expressions.size() + (_excludeId ? 0 : 1); +} - addToDocument(out, - Document(), // No inclusion field matching. - vars); - return out.freeze(); - } +Document ExpressionObject::evaluateDocument(Variables* vars) const { + /* create and populate the result */ + MutableDocument out(getSizeHint()); - Value ExpressionObject::evaluateInternal(Variables* vars) const { - return Value(evaluateDocument(vars)); - } + addToDocument(out, + Document(), // No inclusion field matching. + vars); + return out.freeze(); +} - void ExpressionObject::addField(const FieldPath &fieldPath, - const intrusive_ptr<Expression> &pExpression) { - const string fieldPart = fieldPath.getFieldName(0); - const bool haveExpr = _expressions.count(fieldPart); +Value ExpressionObject::evaluateInternal(Variables* vars) const { + return Value(evaluateDocument(vars)); +} - intrusive_ptr<Expression>& expr = _expressions[fieldPart]; // inserts if !haveExpr - intrusive_ptr<ExpressionObject> subObj = dynamic_cast<ExpressionObject*>(expr.get()); +void ExpressionObject::addField(const FieldPath& fieldPath, + const intrusive_ptr<Expression>& pExpression) { + const string fieldPart = fieldPath.getFieldName(0); + const bool haveExpr = _expressions.count(fieldPart); - if (!haveExpr) { - _order.push_back(fieldPart); - } - else { // we already have an expression or inclusion for this field - if (fieldPath.getPathLength() == 1) { - // This expression is for right here - - ExpressionObject* newSubObj = dynamic_cast<ExpressionObject*>(pExpression.get()); - uassert(16400, str::stream() - << "can't add an expression for field " << fieldPart - << " because there is already an expression for that field" - << " or one of its sub-fields.", - subObj && newSubObj); // we can merge them - - // Copy everything from the newSubObj to the existing subObj - // This is for cases like { $project:{ 'b.c':1, b:{ a:1 } } } - for (vector<string>::const_iterator it (newSubObj->_order.begin()); - it != newSubObj->_order.end(); - ++it) { - // asserts if any fields are dupes - subObj->addField(*it, newSubObj->_expressions[*it]); - } - return; - } - else { - // This expression is for a subfield - uassert(16401, str::stream() - << "can't add an expression for a subfield of " << fieldPart - << " because there is already an expression that applies to" - << " the whole field", - subObj); - } - } + intrusive_ptr<Expression>& expr = _expressions[fieldPart]; // inserts if !haveExpr + intrusive_ptr<ExpressionObject> subObj = dynamic_cast<ExpressionObject*>(expr.get()); + if (!haveExpr) { + _order.push_back(fieldPart); + } else { // we already have an expression or inclusion for this field if (fieldPath.getPathLength() == 1) { - verify(!haveExpr); // haveExpr case handled above. - expr = pExpression; + // This expression is for right here + + ExpressionObject* newSubObj = dynamic_cast<ExpressionObject*>(pExpression.get()); + uassert(16400, + str::stream() << "can't add an expression for field " << fieldPart + << " because there is already an expression for that field" + << " or one of its sub-fields.", + subObj && newSubObj); // we can merge them + + // Copy everything from the newSubObj to the existing subObj + // This is for cases like { $project:{ 'b.c':1, b:{ a:1 } } } + for (vector<string>::const_iterator it(newSubObj->_order.begin()); + it != newSubObj->_order.end(); + ++it) { + // asserts if any fields are dupes + subObj->addField(*it, newSubObj->_expressions[*it]); + } return; + } else { + // This expression is for a subfield + uassert(16401, + str::stream() << "can't add an expression for a subfield of " << fieldPart + << " because there is already an expression that applies to" + << " the whole field", + subObj); } - - if (!haveExpr) - expr = subObj = ExpressionObject::create(); - - subObj->addField(fieldPath.tail(), pExpression); } - void ExpressionObject::includePath(const string &theFieldPath) { - addField(theFieldPath, NULL); + if (fieldPath.getPathLength() == 1) { + verify(!haveExpr); // haveExpr case handled above. + expr = pExpression; + return; } - Value ExpressionObject::serialize(bool explain) const { - MutableDocument valBuilder; - if (_excludeId) - valBuilder["_id"] = Value(false); + if (!haveExpr) + expr = subObj = ExpressionObject::create(); - for (vector<string>::const_iterator it(_order.begin()); it!=_order.end(); ++it) { - string fieldName = *it; - verify(_expressions.find(fieldName) != _expressions.end()); - intrusive_ptr<Expression> expr = _expressions.find(fieldName)->second; + subObj->addField(fieldPath.tail(), pExpression); +} - if (!expr) { - // this is inclusion, not an expression - valBuilder[fieldName] = Value(true); - } - else { - valBuilder[fieldName] = expr->serialize(explain); - } - } - return valBuilder.freezeToValue(); - } +void ExpressionObject::includePath(const string& theFieldPath) { + addField(theFieldPath, NULL); +} - /* --------------------- ExpressionFieldPath --------------------------- */ +Value ExpressionObject::serialize(bool explain) const { + MutableDocument valBuilder; + if (_excludeId) + valBuilder["_id"] = Value(false); - // this is the old deprecated version only used by tests not using variables - intrusive_ptr<ExpressionFieldPath> ExpressionFieldPath::create(const string& fieldPath) { - return new ExpressionFieldPath("CURRENT." + fieldPath, Variables::ROOT_ID); - } + for (vector<string>::const_iterator it(_order.begin()); it != _order.end(); ++it) { + string fieldName = *it; + verify(_expressions.find(fieldName) != _expressions.end()); + intrusive_ptr<Expression> expr = _expressions.find(fieldName)->second; - // this is the new version that supports every syntax - intrusive_ptr<ExpressionFieldPath> ExpressionFieldPath::parse( - const string& raw, - const VariablesParseState& vps) { + if (!expr) { + // this is inclusion, not an expression + valBuilder[fieldName] = Value(true); + } else { + valBuilder[fieldName] = expr->serialize(explain); + } + } + return valBuilder.freezeToValue(); +} - uassert(16873, str::stream() << "FieldPath '" << raw << "' doesn't start with $", - raw.c_str()[0] == '$'); // c_str()[0] is always a valid reference. +/* --------------------- ExpressionFieldPath --------------------------- */ - uassert(16872, str::stream() << "'$' by itself is not a valid FieldPath", - raw.size() >= 2); // need at least "$" and either "$" or a field name +// this is the old deprecated version only used by tests not using variables +intrusive_ptr<ExpressionFieldPath> ExpressionFieldPath::create(const string& fieldPath) { + return new ExpressionFieldPath("CURRENT." + fieldPath, Variables::ROOT_ID); +} - if (raw[1] == '$') { - const StringData rawSD = raw; - const StringData fieldPath = rawSD.substr(2); // strip off $$ - const StringData varName = fieldPath.substr(0, fieldPath.find('.')); - Variables::uassertValidNameForUserRead(varName); - return new ExpressionFieldPath(fieldPath.toString(), vps.getVariable(varName)); - } - else { - return new ExpressionFieldPath("CURRENT." + raw.substr(1), // strip the "$" prefix - vps.getVariable("CURRENT")); - } +// this is the new version that supports every syntax +intrusive_ptr<ExpressionFieldPath> ExpressionFieldPath::parse(const string& raw, + const VariablesParseState& vps) { + uassert(16873, + str::stream() << "FieldPath '" << raw << "' doesn't start with $", + raw.c_str()[0] == '$'); // c_str()[0] is always a valid reference. + + uassert(16872, + str::stream() << "'$' by itself is not a valid FieldPath", + raw.size() >= 2); // need at least "$" and either "$" or a field name + + if (raw[1] == '$') { + const StringData rawSD = raw; + const StringData fieldPath = rawSD.substr(2); // strip off $$ + const StringData varName = fieldPath.substr(0, fieldPath.find('.')); + Variables::uassertValidNameForUserRead(varName); + return new ExpressionFieldPath(fieldPath.toString(), vps.getVariable(varName)); + } else { + return new ExpressionFieldPath("CURRENT." + raw.substr(1), // strip the "$" prefix + vps.getVariable("CURRENT")); } +} - ExpressionFieldPath::ExpressionFieldPath(const string& theFieldPath, Variables::Id variable) - : _fieldPath(theFieldPath) - , _variable(variable) - {} +ExpressionFieldPath::ExpressionFieldPath(const string& theFieldPath, Variables::Id variable) + : _fieldPath(theFieldPath), _variable(variable) {} - intrusive_ptr<Expression> ExpressionFieldPath::optimize() { - /* nothing can be done for these */ - return intrusive_ptr<Expression>(this); - } +intrusive_ptr<Expression> ExpressionFieldPath::optimize() { + /* nothing can be done for these */ + return intrusive_ptr<Expression>(this); +} - void ExpressionFieldPath::addDependencies(DepsTracker* deps, vector<string>* path) const { - if (_variable == Variables::ROOT_ID) { // includes CURRENT when it is equivalent to ROOT. - if (_fieldPath.getPathLength() == 1) { - deps->needWholeDocument = true; // need full doc if just "$$ROOT" - } else { - deps->fields.insert(_fieldPath.tail().getPath(false)); - } +void ExpressionFieldPath::addDependencies(DepsTracker* deps, vector<string>* path) const { + if (_variable == Variables::ROOT_ID) { // includes CURRENT when it is equivalent to ROOT. + if (_fieldPath.getPathLength() == 1) { + deps->needWholeDocument = true; // need full doc if just "$$ROOT" + } else { + deps->fields.insert(_fieldPath.tail().getPath(false)); } } +} - Value ExpressionFieldPath::evaluatePathArray(size_t index, const Value& input) const { - dassert(input.getType() == Array); - - // Check for remaining path in each element of array - vector<Value> result; - const vector<Value>& array = input.getArray(); - for (size_t i=0; i < array.size(); i++) { - if (array[i].getType() != Object) - continue; +Value ExpressionFieldPath::evaluatePathArray(size_t index, const Value& input) const { + dassert(input.getType() == Array); - const Value nested = evaluatePath(index, array[i].getDocument()); - if (!nested.missing()) - result.push_back(nested); - } + // Check for remaining path in each element of array + vector<Value> result; + const vector<Value>& array = input.getArray(); + for (size_t i = 0; i < array.size(); i++) { + if (array[i].getType() != Object) + continue; - return Value(std::move(result)); + const Value nested = evaluatePath(index, array[i].getDocument()); + if (!nested.missing()) + result.push_back(nested); } - Value ExpressionFieldPath::evaluatePath(size_t index, const Document& input) const { - // Note this function is very hot so it is important that is is well optimized. - // In particular, all return paths should support RVO. - /* if we've hit the end of the path, stop */ - if (index == _fieldPath.getPathLength() - 1) - return input[_fieldPath.getFieldName(index)]; + return Value(std::move(result)); +} +Value ExpressionFieldPath::evaluatePath(size_t index, const Document& input) const { + // Note this function is very hot so it is important that is is well optimized. + // In particular, all return paths should support RVO. + + /* if we've hit the end of the path, stop */ + if (index == _fieldPath.getPathLength() - 1) + return input[_fieldPath.getFieldName(index)]; - // Try to dive deeper - const Value val = input[_fieldPath.getFieldName(index)]; - switch (val.getType()) { + // Try to dive deeper + const Value val = input[_fieldPath.getFieldName(index)]; + switch (val.getType()) { case Object: - return evaluatePath(index+1, val.getDocument()); + return evaluatePath(index + 1, val.getDocument()); case Array: - return evaluatePathArray(index+1, val); + return evaluatePathArray(index + 1, val); default: return Value(); - } } +} - Value ExpressionFieldPath::evaluateInternal(Variables* vars) const { - if (_fieldPath.getPathLength() == 1) // get the whole variable - return vars->getValue(_variable); +Value ExpressionFieldPath::evaluateInternal(Variables* vars) const { + if (_fieldPath.getPathLength() == 1) // get the whole variable + return vars->getValue(_variable); - if (_variable == Variables::ROOT_ID) { - // ROOT is always a document so use optimized code path - return evaluatePath(1, vars->getRoot()); - } - - Value var = vars->getValue(_variable); - switch (var.getType()) { - case Object: return evaluatePath(1, var.getDocument()); - case Array: return evaluatePathArray(1, var); - default: return Value(); - } + if (_variable == Variables::ROOT_ID) { + // ROOT is always a document so use optimized code path + return evaluatePath(1, vars->getRoot()); } - Value ExpressionFieldPath::serialize(bool explain) const { - if (_fieldPath.getFieldName(0) == "CURRENT" && _fieldPath.getPathLength() > 1) { - // use short form for "$$CURRENT.foo" but not just "$$CURRENT" - return Value("$" + _fieldPath.tail().getPath(false)); - } - else { - return Value("$$" + _fieldPath.getPath(false)); - } + Value var = vars->getValue(_variable); + switch (var.getType()) { + case Object: + return evaluatePath(1, var.getDocument()); + case Array: + return evaluatePathArray(1, var); + default: + return Value(); } +} - /* ------------------------- ExpressionFilter ----------------------------- */ +Value ExpressionFieldPath::serialize(bool explain) const { + if (_fieldPath.getFieldName(0) == "CURRENT" && _fieldPath.getPathLength() > 1) { + // use short form for "$$CURRENT.foo" but not just "$$CURRENT" + return Value("$" + _fieldPath.tail().getPath(false)); + } else { + return Value("$$" + _fieldPath.getPath(false)); + } +} - REGISTER_EXPRESSION("$filter", ExpressionFilter::parse); - intrusive_ptr<Expression> ExpressionFilter::parse(BSONElement expr, - const VariablesParseState& vpsIn) { +/* ------------------------- ExpressionFilter ----------------------------- */ - verify(str::equals(expr.fieldName(), "$filter")); +REGISTER_EXPRESSION("$filter", ExpressionFilter::parse); +intrusive_ptr<Expression> ExpressionFilter::parse(BSONElement expr, + const VariablesParseState& vpsIn) { + verify(str::equals(expr.fieldName(), "$filter")); - uassert(28646, "$filter only supports an object as its argument", - expr.type() == Object); + uassert(28646, "$filter only supports an object as its argument", expr.type() == Object); - // "cond" must be parsed after "as" regardless of BSON order. - BSONElement inputElem; - BSONElement asElem; - BSONElement condElem; - for (auto elem : expr.Obj()) { - if (str::equals(elem.fieldName(), "input")) { - inputElem = elem; - } else if (str::equals(elem.fieldName(), "as")) { - asElem = elem; - } else if (str::equals(elem.fieldName(), "cond")) { - condElem = elem; - } else { - uasserted(28647, str::stream() - << "Unrecognized parameter to $filter: " << elem.fieldName()); - } + // "cond" must be parsed after "as" regardless of BSON order. + BSONElement inputElem; + BSONElement asElem; + BSONElement condElem; + for (auto elem : expr.Obj()) { + if (str::equals(elem.fieldName(), "input")) { + inputElem = elem; + } else if (str::equals(elem.fieldName(), "as")) { + asElem = elem; + } else if (str::equals(elem.fieldName(), "cond")) { + condElem = elem; + } else { + uasserted(28647, + str::stream() << "Unrecognized parameter to $filter: " << elem.fieldName()); } + } - uassert(28648, "Missing 'input' parameter to $filter", - !inputElem.eoo()); - uassert(28649, "Missing 'as' parameter to $filter", - !asElem.eoo()); - uassert(28650, "Missing 'cond' parameter to $filter", - !condElem.eoo()); + uassert(28648, "Missing 'input' parameter to $filter", !inputElem.eoo()); + uassert(28649, "Missing 'as' parameter to $filter", !asElem.eoo()); + uassert(28650, "Missing 'cond' parameter to $filter", !condElem.eoo()); - // Parse "input", only has outer variables. - intrusive_ptr<Expression> input = parseOperand(inputElem, vpsIn); + // Parse "input", only has outer variables. + intrusive_ptr<Expression> input = parseOperand(inputElem, vpsIn); - // Parse "as". - VariablesParseState vpsSub(vpsIn); // vpsSub gets our variable, vpsIn doesn't. - string varName = asElem.str(); - Variables::uassertValidNameForUserWrite(varName); - Variables::Id varId = vpsSub.defineVariable(varName); + // Parse "as". + VariablesParseState vpsSub(vpsIn); // vpsSub gets our variable, vpsIn doesn't. + string varName = asElem.str(); + Variables::uassertValidNameForUserWrite(varName); + Variables::Id varId = vpsSub.defineVariable(varName); - // Parse "cond", has access to "as" variable. - intrusive_ptr<Expression> cond = parseOperand(condElem, vpsSub); - - return new ExpressionFilter(std::move(varName), varId, std::move(input), std::move(cond)); - } + // Parse "cond", has access to "as" variable. + intrusive_ptr<Expression> cond = parseOperand(condElem, vpsSub); - ExpressionFilter::ExpressionFilter(string varName, - Variables::Id varId, - intrusive_ptr<Expression> input, - intrusive_ptr<Expression> filter) - : _varName(std::move(varName)) - , _varId(varId) - , _input(std::move(input)) - , _filter(std::move(filter)) - {} + return new ExpressionFilter(std::move(varName), varId, std::move(input), std::move(cond)); +} - intrusive_ptr<Expression> ExpressionFilter::optimize() { - // TODO handle when _input is constant. - _input = _input->optimize(); - _filter = _filter->optimize(); - return this; - } +ExpressionFilter::ExpressionFilter(string varName, + Variables::Id varId, + intrusive_ptr<Expression> input, + intrusive_ptr<Expression> filter) + : _varName(std::move(varName)), + _varId(varId), + _input(std::move(input)), + _filter(std::move(filter)) {} + +intrusive_ptr<Expression> ExpressionFilter::optimize() { + // TODO handle when _input is constant. + _input = _input->optimize(); + _filter = _filter->optimize(); + return this; +} - Value ExpressionFilter::serialize(bool explain) const { - return Value(DOC("$filter" << DOC("input" << _input->serialize(explain) - << "as" << _varName - << "cond" << _filter->serialize(explain) - ))); - } +Value ExpressionFilter::serialize(bool explain) const { + return Value(DOC("$filter" << DOC("input" << _input->serialize(explain) << "as" << _varName + << "cond" << _filter->serialize(explain)))); +} - Value ExpressionFilter::evaluateInternal(Variables* vars) const { - // We are guaranteed at parse time that this isn't using our _varId. - const Value inputVal = _input->evaluateInternal(vars); - if (inputVal.nullish()) - return Value(BSONNULL); +Value ExpressionFilter::evaluateInternal(Variables* vars) const { + // We are guaranteed at parse time that this isn't using our _varId. + const Value inputVal = _input->evaluateInternal(vars); + if (inputVal.nullish()) + return Value(BSONNULL); - uassert(28651, str::stream() << "input to $filter must be an Array not " - << typeName(inputVal.getType()), - inputVal.getType() == Array); + uassert(28651, + str::stream() << "input to $filter must be an Array not " + << typeName(inputVal.getType()), + inputVal.getType() == Array); - const vector<Value>& input = inputVal.getArray(); + const vector<Value>& input = inputVal.getArray(); - if (input.empty()) - return inputVal; + if (input.empty()) + return inputVal; - vector<Value> output; - for (const auto& elem : input) { - vars->setValue(_varId, elem); + vector<Value> output; + for (const auto& elem : input) { + vars->setValue(_varId, elem); - if (_filter->evaluateInternal(vars).coerceToBool()) { - output.push_back(std::move(elem)); - } + if (_filter->evaluateInternal(vars).coerceToBool()) { + output.push_back(std::move(elem)); } - - return Value(std::move(output)); } - void ExpressionFilter::addDependencies(DepsTracker* deps, vector<string>* path) const { - _input->addDependencies(deps); - _filter->addDependencies(deps); - } + return Value(std::move(output)); +} - /* ------------------------- ExpressionLet ----------------------------- */ +void ExpressionFilter::addDependencies(DepsTracker* deps, vector<string>* path) const { + _input->addDependencies(deps); + _filter->addDependencies(deps); +} - REGISTER_EXPRESSION("$let", ExpressionLet::parse); - intrusive_ptr<Expression> ExpressionLet::parse( - BSONElement expr, - const VariablesParseState& vpsIn) { +/* ------------------------- ExpressionLet ----------------------------- */ - verify(str::equals(expr.fieldName(), "$let")); +REGISTER_EXPRESSION("$let", ExpressionLet::parse); +intrusive_ptr<Expression> ExpressionLet::parse(BSONElement expr, const VariablesParseState& vpsIn) { + verify(str::equals(expr.fieldName(), "$let")); - uassert(16874, "$let only supports an object as its argument", - expr.type() == Object); - const BSONObj args = expr.embeddedObject(); + uassert(16874, "$let only supports an object as its argument", expr.type() == Object); + const BSONObj args = expr.embeddedObject(); - // varsElem must be parsed before inElem regardless of BSON order. - BSONElement varsElem; - BSONElement inElem; - BSONForEach(arg, args) { - if (str::equals(arg.fieldName(), "vars")) { - varsElem = arg; - } else if (str::equals(arg.fieldName(), "in")) { - inElem = arg; - } else { - uasserted(16875, str::stream() - << "Unrecognized parameter to $let: " << arg.fieldName()); - } + // varsElem must be parsed before inElem regardless of BSON order. + BSONElement varsElem; + BSONElement inElem; + BSONForEach(arg, args) { + if (str::equals(arg.fieldName(), "vars")) { + varsElem = arg; + } else if (str::equals(arg.fieldName(), "in")) { + inElem = arg; + } else { + uasserted(16875, + str::stream() << "Unrecognized parameter to $let: " << arg.fieldName()); } + } - uassert(16876, "Missing 'vars' parameter to $let", - !varsElem.eoo()); - uassert(16877, "Missing 'in' parameter to $let", - !inElem.eoo()); - - // parse "vars" - VariablesParseState vpsSub(vpsIn); // vpsSub gets our vars, vpsIn doesn't. - VariableMap vars; - BSONForEach(varElem, varsElem.embeddedObjectUserCheck()) { - const string varName = varElem.fieldName(); - Variables::uassertValidNameForUserWrite(varName); - Variables::Id id = vpsSub.defineVariable(varName); - - vars[id] = NameAndExpression(varName, - parseOperand(varElem, vpsIn)); // only has outer vars - } + uassert(16876, "Missing 'vars' parameter to $let", !varsElem.eoo()); + uassert(16877, "Missing 'in' parameter to $let", !inElem.eoo()); - // parse "in" - intrusive_ptr<Expression> subExpression = parseOperand(inElem, vpsSub); // has our vars + // parse "vars" + VariablesParseState vpsSub(vpsIn); // vpsSub gets our vars, vpsIn doesn't. + VariableMap vars; + BSONForEach(varElem, varsElem.embeddedObjectUserCheck()) { + const string varName = varElem.fieldName(); + Variables::uassertValidNameForUserWrite(varName); + Variables::Id id = vpsSub.defineVariable(varName); - return new ExpressionLet(vars, subExpression); + vars[id] = NameAndExpression(varName, parseOperand(varElem, vpsIn)); // only has outer vars } - ExpressionLet::ExpressionLet(const VariableMap& vars, intrusive_ptr<Expression> subExpression) - : _variables(vars) - , _subExpression(subExpression) - {} + // parse "in" + intrusive_ptr<Expression> subExpression = parseOperand(inElem, vpsSub); // has our vars - intrusive_ptr<Expression> ExpressionLet::optimize() { - if (_variables.empty()) { - // we aren't binding any variables so just return the subexpression - return _subExpression->optimize(); - } + return new ExpressionLet(vars, subExpression); +} - for (VariableMap::iterator it=_variables.begin(), end=_variables.end(); it != end; ++it) { - it->second.expression = it->second.expression->optimize(); - } +ExpressionLet::ExpressionLet(const VariableMap& vars, intrusive_ptr<Expression> subExpression) + : _variables(vars), _subExpression(subExpression) {} - // TODO be smarter with constant "variables" - _subExpression = _subExpression->optimize(); +intrusive_ptr<Expression> ExpressionLet::optimize() { + if (_variables.empty()) { + // we aren't binding any variables so just return the subexpression + return _subExpression->optimize(); + } - return this; + for (VariableMap::iterator it = _variables.begin(), end = _variables.end(); it != end; ++it) { + it->second.expression = it->second.expression->optimize(); } - Value ExpressionLet::serialize(bool explain) const { - MutableDocument vars; - for (VariableMap::const_iterator it=_variables.begin(), end=_variables.end(); - it != end; ++it) { - vars[it->second.name] = it->second.expression->serialize(explain); - } + // TODO be smarter with constant "variables" + _subExpression = _subExpression->optimize(); + + return this; +} - return Value(DOC("$let" << DOC("vars" << vars.freeze() - << "in" << _subExpression->serialize(explain)) - )); +Value ExpressionLet::serialize(bool explain) const { + MutableDocument vars; + for (VariableMap::const_iterator it = _variables.begin(), end = _variables.end(); it != end; + ++it) { + vars[it->second.name] = it->second.expression->serialize(explain); } - Value ExpressionLet::evaluateInternal(Variables* vars) const { - for (VariableMap::const_iterator it=_variables.begin(), end=_variables.end(); - it != end; ++it) { - // It is guaranteed at parse-time that these expressions don't use the variable ids we - // are setting - vars->setValue(it->first, - it->second.expression->evaluateInternal(vars)); - } + return Value( + DOC("$let" << DOC("vars" << vars.freeze() << "in" << _subExpression->serialize(explain)))); +} - return _subExpression->evaluateInternal(vars); +Value ExpressionLet::evaluateInternal(Variables* vars) const { + for (VariableMap::const_iterator it = _variables.begin(), end = _variables.end(); it != end; + ++it) { + // It is guaranteed at parse-time that these expressions don't use the variable ids we + // are setting + vars->setValue(it->first, it->second.expression->evaluateInternal(vars)); } - void ExpressionLet::addDependencies(DepsTracker* deps, vector<string>* path) const { - for (VariableMap::const_iterator it=_variables.begin(), end=_variables.end(); - it != end; ++it) { - it->second.expression->addDependencies(deps); - } + return _subExpression->evaluateInternal(vars); +} - // TODO be smarter when CURRENT is a bound variable - _subExpression->addDependencies(deps); +void ExpressionLet::addDependencies(DepsTracker* deps, vector<string>* path) const { + for (VariableMap::const_iterator it = _variables.begin(), end = _variables.end(); it != end; + ++it) { + it->second.expression->addDependencies(deps); } + // TODO be smarter when CURRENT is a bound variable + _subExpression->addDependencies(deps); +} - /* ------------------------- ExpressionMap ----------------------------- */ - REGISTER_EXPRESSION("$map", ExpressionMap::parse); - intrusive_ptr<Expression> ExpressionMap::parse( - BSONElement expr, - const VariablesParseState& vpsIn) { +/* ------------------------- ExpressionMap ----------------------------- */ - verify(str::equals(expr.fieldName(), "$map")); +REGISTER_EXPRESSION("$map", ExpressionMap::parse); +intrusive_ptr<Expression> ExpressionMap::parse(BSONElement expr, const VariablesParseState& vpsIn) { + verify(str::equals(expr.fieldName(), "$map")); - uassert(16878, "$map only supports an object as its argument", - expr.type() == Object); + uassert(16878, "$map only supports an object as its argument", expr.type() == Object); - // "in" must be parsed after "as" regardless of BSON order - BSONElement inputElem; - BSONElement asElem; - BSONElement inElem; - const BSONObj args = expr.embeddedObject(); - BSONForEach(arg, args) { - if (str::equals(arg.fieldName(), "input")) { - inputElem = arg; - } else if (str::equals(arg.fieldName(), "as")) { - asElem = arg; - } else if (str::equals(arg.fieldName(), "in")) { - inElem = arg; - } else { - uasserted(16879, str::stream() - << "Unrecognized parameter to $map: " << arg.fieldName()); - } + // "in" must be parsed after "as" regardless of BSON order + BSONElement inputElem; + BSONElement asElem; + BSONElement inElem; + const BSONObj args = expr.embeddedObject(); + BSONForEach(arg, args) { + if (str::equals(arg.fieldName(), "input")) { + inputElem = arg; + } else if (str::equals(arg.fieldName(), "as")) { + asElem = arg; + } else if (str::equals(arg.fieldName(), "in")) { + inElem = arg; + } else { + uasserted(16879, + str::stream() << "Unrecognized parameter to $map: " << arg.fieldName()); } + } - uassert(16880, "Missing 'input' parameter to $map", - !inputElem.eoo()); - uassert(16881, "Missing 'as' parameter to $map", - !asElem.eoo()); - uassert(16882, "Missing 'in' parameter to $map", - !inElem.eoo()); - - // parse "input" - intrusive_ptr<Expression> input = parseOperand(inputElem, vpsIn); // only has outer vars - - // parse "as" - VariablesParseState vpsSub(vpsIn); // vpsSub gets our vars, vpsIn doesn't. - string varName = asElem.str(); - Variables::uassertValidNameForUserWrite(varName); - Variables::Id varId = vpsSub.defineVariable(varName); - - // parse "in" - intrusive_ptr<Expression> in = parseOperand(inElem, vpsSub); // has access to map variable + uassert(16880, "Missing 'input' parameter to $map", !inputElem.eoo()); + uassert(16881, "Missing 'as' parameter to $map", !asElem.eoo()); + uassert(16882, "Missing 'in' parameter to $map", !inElem.eoo()); - return new ExpressionMap(varName, varId, input, in); - } + // parse "input" + intrusive_ptr<Expression> input = parseOperand(inputElem, vpsIn); // only has outer vars - ExpressionMap::ExpressionMap(const string& varName, - Variables::Id varId, - intrusive_ptr<Expression> input, - intrusive_ptr<Expression> each) - : _varName(varName) - , _varId(varId) - , _input(input) - , _each(each) - {} + // parse "as" + VariablesParseState vpsSub(vpsIn); // vpsSub gets our vars, vpsIn doesn't. + string varName = asElem.str(); + Variables::uassertValidNameForUserWrite(varName); + Variables::Id varId = vpsSub.defineVariable(varName); - intrusive_ptr<Expression> ExpressionMap::optimize() { - // TODO handle when _input is constant - _input = _input->optimize(); - _each = _each->optimize(); - return this; - } + // parse "in" + intrusive_ptr<Expression> in = parseOperand(inElem, vpsSub); // has access to map variable - Value ExpressionMap::serialize(bool explain) const { - return Value(DOC("$map" << DOC("input" << _input->serialize(explain) - << "as" << _varName - << "in" << _each->serialize(explain) - ))); - } + return new ExpressionMap(varName, varId, input, in); +} - Value ExpressionMap::evaluateInternal(Variables* vars) const { - // guaranteed at parse time that this isn't using our _varId - const Value inputVal = _input->evaluateInternal(vars); - if (inputVal.nullish()) - return Value(BSONNULL); +ExpressionMap::ExpressionMap(const string& varName, + Variables::Id varId, + intrusive_ptr<Expression> input, + intrusive_ptr<Expression> each) + : _varName(varName), _varId(varId), _input(input), _each(each) {} + +intrusive_ptr<Expression> ExpressionMap::optimize() { + // TODO handle when _input is constant + _input = _input->optimize(); + _each = _each->optimize(); + return this; +} - uassert(16883, str::stream() << "input to $map must be an Array not " - << typeName(inputVal.getType()), - inputVal.getType() == Array); +Value ExpressionMap::serialize(bool explain) const { + return Value(DOC("$map" << DOC("input" << _input->serialize(explain) << "as" << _varName << "in" + << _each->serialize(explain)))); +} - const vector<Value>& input = inputVal.getArray(); +Value ExpressionMap::evaluateInternal(Variables* vars) const { + // guaranteed at parse time that this isn't using our _varId + const Value inputVal = _input->evaluateInternal(vars); + if (inputVal.nullish()) + return Value(BSONNULL); - if (input.empty()) - return inputVal; + uassert(16883, + str::stream() << "input to $map must be an Array not " << typeName(inputVal.getType()), + inputVal.getType() == Array); - vector<Value> output; - output.reserve(input.size()); - for (size_t i=0; i < input.size(); i++) { - vars->setValue(_varId, input[i]); + const vector<Value>& input = inputVal.getArray(); - Value toInsert = _each->evaluateInternal(vars); - if (toInsert.missing()) - toInsert = Value(BSONNULL); // can't insert missing values into array + if (input.empty()) + return inputVal; - output.push_back(toInsert); - } + vector<Value> output; + output.reserve(input.size()); + for (size_t i = 0; i < input.size(); i++) { + vars->setValue(_varId, input[i]); - return Value(std::move(output)); - } + Value toInsert = _each->evaluateInternal(vars); + if (toInsert.missing()) + toInsert = Value(BSONNULL); // can't insert missing values into array - void ExpressionMap::addDependencies(DepsTracker* deps, vector<string>* path) const { - _input->addDependencies(deps); - _each->addDependencies(deps); + output.push_back(toInsert); } - /* ------------------------- ExpressionMeta ----------------------------- */ + return Value(std::move(output)); +} - REGISTER_EXPRESSION("$meta", ExpressionMeta::parse); - intrusive_ptr<Expression> ExpressionMeta::parse( - BSONElement expr, - const VariablesParseState& vpsIn) { +void ExpressionMap::addDependencies(DepsTracker* deps, vector<string>* path) const { + _input->addDependencies(deps); + _each->addDependencies(deps); +} - uassert(17307, "$meta only supports String arguments", - expr.type() == String); - uassert(17308, "Unsupported argument to $meta: " + expr.String(), - expr.String() == "textScore"); +/* ------------------------- ExpressionMeta ----------------------------- */ - return new ExpressionMeta(); - } +REGISTER_EXPRESSION("$meta", ExpressionMeta::parse); +intrusive_ptr<Expression> ExpressionMeta::parse(BSONElement expr, + const VariablesParseState& vpsIn) { + uassert(17307, "$meta only supports String arguments", expr.type() == String); + uassert(17308, "Unsupported argument to $meta: " + expr.String(), expr.String() == "textScore"); - Value ExpressionMeta::serialize(bool explain) const { - return Value(DOC("$meta" << "textScore")); - } + return new ExpressionMeta(); +} - Value ExpressionMeta::evaluateInternal(Variables* vars) const { - const Document& root = vars->getRoot(); - return root.hasTextScore() - ? Value(root.getTextScore()) - : Value(); - } +Value ExpressionMeta::serialize(bool explain) const { + return Value(DOC("$meta" + << "textScore")); +} - void ExpressionMeta::addDependencies(DepsTracker* deps, vector<string>* path) const { - deps->needTextScore = true; - } +Value ExpressionMeta::evaluateInternal(Variables* vars) const { + const Document& root = vars->getRoot(); + return root.hasTextScore() ? Value(root.getTextScore()) : Value(); +} - /* ------------------------- ExpressionMillisecond ----------------------------- */ +void ExpressionMeta::addDependencies(DepsTracker* deps, vector<string>* path) const { + deps->needTextScore = true; +} - Value ExpressionMillisecond::evaluateInternal(Variables* vars) const { - Value date(vpOperand[0]->evaluateInternal(vars)); - return Value(extract(date.coerceToDate())); - } +/* ------------------------- ExpressionMillisecond ----------------------------- */ - int ExpressionMillisecond::extract(const long long date) { - const int ms = date % 1000LL; - // adding 1000 since dates before 1970 would have negative ms - return ms >= 0 ? ms : 1000 + ms; - } +Value ExpressionMillisecond::evaluateInternal(Variables* vars) const { + Value date(vpOperand[0]->evaluateInternal(vars)); + return Value(extract(date.coerceToDate())); +} - REGISTER_EXPRESSION("$millisecond", ExpressionMillisecond::parse); - const char* ExpressionMillisecond::getOpName() const { - return "$millisecond"; - } +int ExpressionMillisecond::extract(const long long date) { + const int ms = date % 1000LL; + // adding 1000 since dates before 1970 would have negative ms + return ms >= 0 ? ms : 1000 + ms; +} - /* ------------------------- ExpressionMinute -------------------------- */ +REGISTER_EXPRESSION("$millisecond", ExpressionMillisecond::parse); +const char* ExpressionMillisecond::getOpName() const { + return "$millisecond"; +} - Value ExpressionMinute::evaluateInternal(Variables* vars) const { - Value pDate(vpOperand[0]->evaluateInternal(vars)); - return Value(extract(pDate.coerceToTm())); - } +/* ------------------------- ExpressionMinute -------------------------- */ - REGISTER_EXPRESSION("$minute", ExpressionMinute::parse); - const char* ExpressionMinute::getOpName() const { - return "$minute"; - } +Value ExpressionMinute::evaluateInternal(Variables* vars) const { + Value pDate(vpOperand[0]->evaluateInternal(vars)); + return Value(extract(pDate.coerceToTm())); +} - /* ----------------------- ExpressionMod ---------------------------- */ +REGISTER_EXPRESSION("$minute", ExpressionMinute::parse); +const char* ExpressionMinute::getOpName() const { + return "$minute"; +} - Value ExpressionMod::evaluateInternal(Variables* vars) const { - Value lhs = vpOperand[0]->evaluateInternal(vars); - Value rhs = vpOperand[1]->evaluateInternal(vars); +/* ----------------------- ExpressionMod ---------------------------- */ - BSONType leftType = lhs.getType(); - BSONType rightType = rhs.getType(); +Value ExpressionMod::evaluateInternal(Variables* vars) const { + Value lhs = vpOperand[0]->evaluateInternal(vars); + Value rhs = vpOperand[1]->evaluateInternal(vars); - if (lhs.numeric() && rhs.numeric()) { - // ensure we aren't modding by 0 - double right = rhs.coerceToDouble(); + BSONType leftType = lhs.getType(); + BSONType rightType = rhs.getType(); - uassert(16610, "can't $mod by 0", - right != 0); + if (lhs.numeric() && rhs.numeric()) { + // ensure we aren't modding by 0 + double right = rhs.coerceToDouble(); - if (leftType == NumberDouble || (rightType == NumberDouble && !rhs.integral())) { - // Need to do fmod. Integer-valued double case is handled below. + uassert(16610, "can't $mod by 0", right != 0); - double left = lhs.coerceToDouble(); - return Value(fmod(left, right)); - } - else if (leftType == NumberLong || rightType == NumberLong) { - // if either is long, return long - long long left = lhs.coerceToLong(); - long long rightLong = rhs.coerceToLong(); - return Value(left % rightLong); - } + if (leftType == NumberDouble || (rightType == NumberDouble && !rhs.integral())) { + // Need to do fmod. Integer-valued double case is handled below. - // lastly they must both be ints, return int - int left = lhs.coerceToInt(); - int rightInt = rhs.coerceToInt(); - return Value(left % rightInt); - } - else if (lhs.nullish() || rhs.nullish()) { - return Value(BSONNULL); - } - else { - uasserted(16611, str::stream() << "$mod only supports numeric types, not " - << typeName(lhs.getType()) - << " and " - << typeName(rhs.getType())); + double left = lhs.coerceToDouble(); + return Value(fmod(left, right)); + } else if (leftType == NumberLong || rightType == NumberLong) { + // if either is long, return long + long long left = lhs.coerceToLong(); + long long rightLong = rhs.coerceToLong(); + return Value(left % rightLong); } - } - REGISTER_EXPRESSION("$mod", ExpressionMod::parse); - const char* ExpressionMod::getOpName() const { - return "$mod"; + // lastly they must both be ints, return int + int left = lhs.coerceToInt(); + int rightInt = rhs.coerceToInt(); + return Value(left % rightInt); + } else if (lhs.nullish() || rhs.nullish()) { + return Value(BSONNULL); + } else { + uasserted(16611, + str::stream() << "$mod only supports numeric types, not " + << typeName(lhs.getType()) << " and " << typeName(rhs.getType())); } +} - /* ------------------------ ExpressionMonth ----------------------------- */ +REGISTER_EXPRESSION("$mod", ExpressionMod::parse); +const char* ExpressionMod::getOpName() const { + return "$mod"; +} - Value ExpressionMonth::evaluateInternal(Variables* vars) const { - Value pDate(vpOperand[0]->evaluateInternal(vars)); - return Value(extract(pDate.coerceToTm())); - } +/* ------------------------ ExpressionMonth ----------------------------- */ - REGISTER_EXPRESSION("$month", ExpressionMonth::parse); - const char* ExpressionMonth::getOpName() const { - return "$month"; - } +Value ExpressionMonth::evaluateInternal(Variables* vars) const { + Value pDate(vpOperand[0]->evaluateInternal(vars)); + return Value(extract(pDate.coerceToTm())); +} - /* ------------------------- ExpressionMultiply ----------------------------- */ +REGISTER_EXPRESSION("$month", ExpressionMonth::parse); +const char* ExpressionMonth::getOpName() const { + return "$month"; +} - Value ExpressionMultiply::evaluateInternal(Variables* vars) const { - /* - We'll try to return the narrowest possible result value. To do that - without creating intermediate Values, do the arithmetic for double - and integral types in parallel, tracking the current narrowest - type. - */ - double doubleProduct = 1; - long long longProduct = 1; - BSONType productType = NumberInt; - - const size_t n = vpOperand.size(); - for(size_t i = 0; i < n; ++i) { - Value val = vpOperand[i]->evaluateInternal(vars); - - if (val.numeric()) { - productType = Value::getWidestNumeric(productType, val.getType()); - - doubleProduct *= val.coerceToDouble(); - longProduct *= val.coerceToLong(); - } - else if (val.nullish()) { - return Value(BSONNULL); - } - else { - uasserted(16555, str::stream() << "$multiply only supports numeric types, not " - << typeName(val.getType())); - } - } +/* ------------------------- ExpressionMultiply ----------------------------- */ - if (productType == NumberDouble) - return Value(doubleProduct); - else if (productType == NumberLong) - return Value(longProduct); - else if (productType == NumberInt) - return Value::createIntOrLong(longProduct); - else - massert(16418, "$multiply resulted in a non-numeric type", false); - } +Value ExpressionMultiply::evaluateInternal(Variables* vars) const { + /* + We'll try to return the narrowest possible result value. To do that + without creating intermediate Values, do the arithmetic for double + and integral types in parallel, tracking the current narrowest + type. + */ + double doubleProduct = 1; + long long longProduct = 1; + BSONType productType = NumberInt; - REGISTER_EXPRESSION("$multiply", ExpressionMultiply::parse); - const char* ExpressionMultiply::getOpName() const { - return "$multiply"; - } + const size_t n = vpOperand.size(); + for (size_t i = 0; i < n; ++i) { + Value val = vpOperand[i]->evaluateInternal(vars); - /* ------------------------- ExpressionHour ----------------------------- */ + if (val.numeric()) { + productType = Value::getWidestNumeric(productType, val.getType()); - Value ExpressionHour::evaluateInternal(Variables* vars) const { - Value pDate(vpOperand[0]->evaluateInternal(vars)); - return Value(extract(pDate.coerceToTm())); + doubleProduct *= val.coerceToDouble(); + longProduct *= val.coerceToLong(); + } else if (val.nullish()) { + return Value(BSONNULL); + } else { + uasserted(16555, + str::stream() << "$multiply only supports numeric types, not " + << typeName(val.getType())); + } } - REGISTER_EXPRESSION("$hour", ExpressionHour::parse); - const char* ExpressionHour::getOpName() const { - return "$hour"; - } + if (productType == NumberDouble) + return Value(doubleProduct); + else if (productType == NumberLong) + return Value(longProduct); + else if (productType == NumberInt) + return Value::createIntOrLong(longProduct); + else + massert(16418, "$multiply resulted in a non-numeric type", false); +} - /* ----------------------- ExpressionIfNull ---------------------------- */ +REGISTER_EXPRESSION("$multiply", ExpressionMultiply::parse); +const char* ExpressionMultiply::getOpName() const { + return "$multiply"; +} - Value ExpressionIfNull::evaluateInternal(Variables* vars) const { - Value pLeft(vpOperand[0]->evaluateInternal(vars)); - if (!pLeft.nullish()) - return pLeft; +/* ------------------------- ExpressionHour ----------------------------- */ - Value pRight(vpOperand[1]->evaluateInternal(vars)); - return pRight; - } +Value ExpressionHour::evaluateInternal(Variables* vars) const { + Value pDate(vpOperand[0]->evaluateInternal(vars)); + return Value(extract(pDate.coerceToTm())); +} - REGISTER_EXPRESSION("$ifNull", ExpressionIfNull::parse); - const char* ExpressionIfNull::getOpName() const { - return "$ifNull"; - } +REGISTER_EXPRESSION("$hour", ExpressionHour::parse); +const char* ExpressionHour::getOpName() const { + return "$hour"; +} - /* ------------------------ ExpressionNary ----------------------------- */ +/* ----------------------- ExpressionIfNull ---------------------------- */ - intrusive_ptr<Expression> ExpressionNary::optimize() { - const size_t n = vpOperand.size(); +Value ExpressionIfNull::evaluateInternal(Variables* vars) const { + Value pLeft(vpOperand[0]->evaluateInternal(vars)); + if (!pLeft.nullish()) + return pLeft; - // optimize sub-expressions and count constants - unsigned constCount = 0; - for(size_t i = 0; i < n; ++i) { - intrusive_ptr<Expression> optimized = vpOperand[i]->optimize(); + Value pRight(vpOperand[1]->evaluateInternal(vars)); + return pRight; +} - // substitute the optimized expression - vpOperand[i] = optimized; +REGISTER_EXPRESSION("$ifNull", ExpressionIfNull::parse); +const char* ExpressionIfNull::getOpName() const { + return "$ifNull"; +} - // check to see if the result was a constant - if (dynamic_cast<ExpressionConstant*>(optimized.get())) { - constCount++; - } - } +/* ------------------------ ExpressionNary ----------------------------- */ - // If all the operands are constant, we can replace this expression with a constant. Using - // an empty Variables since it will never be accessed. - if (constCount == n) { - Variables emptyVars; - Value pResult(evaluateInternal(&emptyVars)); - intrusive_ptr<Expression> pReplacement( - ExpressionConstant::create(pResult)); - return pReplacement; - } +intrusive_ptr<Expression> ExpressionNary::optimize() { + const size_t n = vpOperand.size(); - // Remaining optimizations are only for associative and commutative expressions. - if (!isAssociativeAndCommutative()) - return this; - - // Process vpOperand to split it into constant and nonconstant vectors. - // This can leave vpOperand in an invalid state that is cleaned up after the loop. - ExpressionVector constExprs; - ExpressionVector nonConstExprs; - for(size_t i = 0; i < vpOperand.size(); ++i) { // NOTE: vpOperand grows in loop - intrusive_ptr<Expression> expr = vpOperand[i]; - if (dynamic_cast<ExpressionConstant*>(expr.get())) { - constExprs.push_back(expr); - } - else { - // If the child operand is the same type as this, then we can - // extract its operands and inline them here because we know - // this is commutative and associative. We detect sameness of - // the child operator by checking for equality of the opNames - ExpressionNary* nary = dynamic_cast<ExpressionNary*>(expr.get()); - if (!nary || !str::equals(nary->getOpName(), getOpName())) { - nonConstExprs.push_back(expr); - } - else { - // same expression, so flatten by adding to vpOperand which - // will be processed later in this loop. - vpOperand.insert(vpOperand.end(), - nary->vpOperand.begin(), - nary->vpOperand.end()); - } - } - } + // optimize sub-expressions and count constants + unsigned constCount = 0; + for (size_t i = 0; i < n; ++i) { + intrusive_ptr<Expression> optimized = vpOperand[i]->optimize(); - // collapse all constant expressions (if any) - Value constValue; - if (!constExprs.empty()) { - vpOperand = constExprs; - Variables emptyVars; - constValue = evaluateInternal(&emptyVars); - } + // substitute the optimized expression + vpOperand[i] = optimized; - // now set the final expression list with constant (if any) at the end - vpOperand = nonConstExprs; - if (!constExprs.empty()) { - vpOperand.push_back(ExpressionConstant::create(constValue)); + // check to see if the result was a constant + if (dynamic_cast<ExpressionConstant*>(optimized.get())) { + constCount++; } + } - return this; + // If all the operands are constant, we can replace this expression with a constant. Using + // an empty Variables since it will never be accessed. + if (constCount == n) { + Variables emptyVars; + Value pResult(evaluateInternal(&emptyVars)); + intrusive_ptr<Expression> pReplacement(ExpressionConstant::create(pResult)); + return pReplacement; } - void ExpressionNary::addDependencies(DepsTracker* deps, vector<string>* path) const { - for(ExpressionVector::const_iterator i(vpOperand.begin()); - i != vpOperand.end(); ++i) { - (*i)->addDependencies(deps); + // Remaining optimizations are only for associative and commutative expressions. + if (!isAssociativeAndCommutative()) + return this; + + // Process vpOperand to split it into constant and nonconstant vectors. + // This can leave vpOperand in an invalid state that is cleaned up after the loop. + ExpressionVector constExprs; + ExpressionVector nonConstExprs; + for (size_t i = 0; i < vpOperand.size(); ++i) { // NOTE: vpOperand grows in loop + intrusive_ptr<Expression> expr = vpOperand[i]; + if (dynamic_cast<ExpressionConstant*>(expr.get())) { + constExprs.push_back(expr); + } else { + // If the child operand is the same type as this, then we can + // extract its operands and inline them here because we know + // this is commutative and associative. We detect sameness of + // the child operator by checking for equality of the opNames + ExpressionNary* nary = dynamic_cast<ExpressionNary*>(expr.get()); + if (!nary || !str::equals(nary->getOpName(), getOpName())) { + nonConstExprs.push_back(expr); + } else { + // same expression, so flatten by adding to vpOperand which + // will be processed later in this loop. + vpOperand.insert(vpOperand.end(), nary->vpOperand.begin(), nary->vpOperand.end()); + } } } - void ExpressionNary::addOperand(const intrusive_ptr<Expression>& pExpression) { - vpOperand.push_back(pExpression); + // collapse all constant expressions (if any) + Value constValue; + if (!constExprs.empty()) { + vpOperand = constExprs; + Variables emptyVars; + constValue = evaluateInternal(&emptyVars); } - Value ExpressionNary::serialize(bool explain) const { - const size_t nOperand = vpOperand.size(); - vector<Value> array; - /* build up the array */ - for(size_t i = 0; i < nOperand; i++) - array.push_back(vpOperand[i]->serialize(explain)); + // now set the final expression list with constant (if any) at the end + vpOperand = nonConstExprs; + if (!constExprs.empty()) { + vpOperand.push_back(ExpressionConstant::create(constValue)); + } - return Value(DOC(getOpName() << array)); + return this; +} + +void ExpressionNary::addDependencies(DepsTracker* deps, vector<string>* path) const { + for (ExpressionVector::const_iterator i(vpOperand.begin()); i != vpOperand.end(); ++i) { + (*i)->addDependencies(deps); } +} - /* ------------------------- ExpressionNot ----------------------------- */ +void ExpressionNary::addOperand(const intrusive_ptr<Expression>& pExpression) { + vpOperand.push_back(pExpression); +} - Value ExpressionNot::evaluateInternal(Variables* vars) const { - Value pOp(vpOperand[0]->evaluateInternal(vars)); +Value ExpressionNary::serialize(bool explain) const { + const size_t nOperand = vpOperand.size(); + vector<Value> array; + /* build up the array */ + for (size_t i = 0; i < nOperand; i++) + array.push_back(vpOperand[i]->serialize(explain)); - bool b = pOp.coerceToBool(); - return Value(!b); - } + return Value(DOC(getOpName() << array)); +} - REGISTER_EXPRESSION("$not", ExpressionNot::parse); - const char* ExpressionNot::getOpName() const { - return "$not"; - } +/* ------------------------- ExpressionNot ----------------------------- */ - /* -------------------------- ExpressionOr ----------------------------- */ +Value ExpressionNot::evaluateInternal(Variables* vars) const { + Value pOp(vpOperand[0]->evaluateInternal(vars)); - Value ExpressionOr::evaluateInternal(Variables* vars) const { - const size_t n = vpOperand.size(); - for(size_t i = 0; i < n; ++i) { - Value pValue(vpOperand[i]->evaluateInternal(vars)); - if (pValue.coerceToBool()) - return Value(true); - } + bool b = pOp.coerceToBool(); + return Value(!b); +} - return Value(false); - } +REGISTER_EXPRESSION("$not", ExpressionNot::parse); +const char* ExpressionNot::getOpName() const { + return "$not"; +} - intrusive_ptr<Expression> ExpressionOr::optimize() { - /* optimize the disjunction as much as possible */ - intrusive_ptr<Expression> pE(ExpressionNary::optimize()); +/* -------------------------- ExpressionOr ----------------------------- */ - /* if the result isn't a disjunction, we can't do anything */ - ExpressionOr *pOr = dynamic_cast<ExpressionOr *>(pE.get()); - if (!pOr) - return pE; +Value ExpressionOr::evaluateInternal(Variables* vars) const { + const size_t n = vpOperand.size(); + for (size_t i = 0; i < n; ++i) { + Value pValue(vpOperand[i]->evaluateInternal(vars)); + if (pValue.coerceToBool()) + return Value(true); + } - /* - Check the last argument on the result; if it's not constant (as - promised by ExpressionNary::optimize(),) then there's nothing - we can do. - */ - const size_t n = pOr->vpOperand.size(); - // ExpressionNary::optimize() generates an ExpressionConstant for {$or:[]}. - verify(n > 0); - intrusive_ptr<Expression> pLast(pOr->vpOperand[n - 1]); - const ExpressionConstant *pConst = - dynamic_cast<ExpressionConstant *>(pLast.get()); - if (!pConst) - return pE; + return Value(false); +} - /* - Evaluate and coerce the last argument to a boolean. If it's true, - then we can replace this entire expression. - */ - bool last = pConst->getValue().coerceToBool(); - if (last) { - intrusive_ptr<ExpressionConstant> pFinal( - ExpressionConstant::create(Value(true))); - return pFinal; - } +intrusive_ptr<Expression> ExpressionOr::optimize() { + /* optimize the disjunction as much as possible */ + intrusive_ptr<Expression> pE(ExpressionNary::optimize()); - /* - If we got here, the final operand was false, so we don't need it - anymore. If there was only one other operand, we don't need the - conjunction either. Note we still need to keep the promise that - the result will be a boolean. - */ - if (n == 2) { - intrusive_ptr<Expression> pFinal( - ExpressionCoerceToBool::create(pOr->vpOperand[0])); - return pFinal; - } + /* if the result isn't a disjunction, we can't do anything */ + ExpressionOr* pOr = dynamic_cast<ExpressionOr*>(pE.get()); + if (!pOr) + return pE; - /* - Remove the final "false" value, and return the new expression. - */ - pOr->vpOperand.resize(n - 1); + /* + Check the last argument on the result; if it's not constant (as + promised by ExpressionNary::optimize(),) then there's nothing + we can do. + */ + const size_t n = pOr->vpOperand.size(); + // ExpressionNary::optimize() generates an ExpressionConstant for {$or:[]}. + verify(n > 0); + intrusive_ptr<Expression> pLast(pOr->vpOperand[n - 1]); + const ExpressionConstant* pConst = dynamic_cast<ExpressionConstant*>(pLast.get()); + if (!pConst) return pE; - } - REGISTER_EXPRESSION("$or", ExpressionOr::parse); - const char* ExpressionOr::getOpName() const { - return "$or"; + /* + Evaluate and coerce the last argument to a boolean. If it's true, + then we can replace this entire expression. + */ + bool last = pConst->getValue().coerceToBool(); + if (last) { + intrusive_ptr<ExpressionConstant> pFinal(ExpressionConstant::create(Value(true))); + return pFinal; } - /* ------------------------- ExpressionSecond ----------------------------- */ - - Value ExpressionSecond::evaluateInternal(Variables* vars) const { - Value pDate(vpOperand[0]->evaluateInternal(vars)); - return Value(extract(pDate.coerceToTm())); + /* + If we got here, the final operand was false, so we don't need it + anymore. If there was only one other operand, we don't need the + conjunction either. Note we still need to keep the promise that + the result will be a boolean. + */ + if (n == 2) { + intrusive_ptr<Expression> pFinal(ExpressionCoerceToBool::create(pOr->vpOperand[0])); + return pFinal; } - REGISTER_EXPRESSION("$second", ExpressionSecond::parse); - const char* ExpressionSecond::getOpName() const { - return "$second"; - } + /* + Remove the final "false" value, and return the new expression. + */ + pOr->vpOperand.resize(n - 1); + return pE; +} - namespace { - ValueSet arrayToSet(const Value& val) { - const vector<Value>& array = val.getArray(); - return ValueSet(array.begin(), array.end()); - } - } +REGISTER_EXPRESSION("$or", ExpressionOr::parse); +const char* ExpressionOr::getOpName() const { + return "$or"; +} - /* ----------------------- ExpressionSetDifference ---------------------------- */ +/* ------------------------- ExpressionSecond ----------------------------- */ - Value ExpressionSetDifference::evaluateInternal(Variables* vars) const { - const Value lhs = vpOperand[0]->evaluateInternal(vars); - const Value rhs = vpOperand[1]->evaluateInternal(vars); +Value ExpressionSecond::evaluateInternal(Variables* vars) const { + Value pDate(vpOperand[0]->evaluateInternal(vars)); + return Value(extract(pDate.coerceToTm())); +} - if (lhs.nullish() || rhs.nullish()) { - return Value(BSONNULL); - } +REGISTER_EXPRESSION("$second", ExpressionSecond::parse); +const char* ExpressionSecond::getOpName() const { + return "$second"; +} - uassert(17048, str::stream() << "both operands of $setDifference must be arrays. First " - << "argument is of type: " << typeName(lhs.getType()), - lhs.getType() == Array); - uassert(17049, str::stream() << "both operands of $setDifference must be arrays. Second " - << "argument is of type: " << typeName(rhs.getType()), - rhs.getType() == Array); +namespace { +ValueSet arrayToSet(const Value& val) { + const vector<Value>& array = val.getArray(); + return ValueSet(array.begin(), array.end()); +} +} - ValueSet rhsSet = arrayToSet(rhs); - const vector<Value>& lhsArray = lhs.getArray(); - vector<Value> returnVec; +/* ----------------------- ExpressionSetDifference ---------------------------- */ - for (vector<Value>::const_iterator it = lhsArray.begin(); it != lhsArray.end(); ++it) { - // rhsSet serves the dual role of filtering out elements that were originally present - // in RHS and of eleminating duplicates from LHS - if (rhsSet.insert(*it).second) { - returnVec.push_back(*it); - } - } - return Value(std::move(returnVec)); - } +Value ExpressionSetDifference::evaluateInternal(Variables* vars) const { + const Value lhs = vpOperand[0]->evaluateInternal(vars); + const Value rhs = vpOperand[1]->evaluateInternal(vars); - REGISTER_EXPRESSION("$setDifference", ExpressionSetDifference::parse); - const char* ExpressionSetDifference::getOpName() const { - return "$setDifference"; + if (lhs.nullish() || rhs.nullish()) { + return Value(BSONNULL); } - /* ----------------------- ExpressionSetEquals ---------------------------- */ + uassert(17048, + str::stream() << "both operands of $setDifference must be arrays. First " + << "argument is of type: " << typeName(lhs.getType()), + lhs.getType() == Array); + uassert(17049, + str::stream() << "both operands of $setDifference must be arrays. Second " + << "argument is of type: " << typeName(rhs.getType()), + rhs.getType() == Array); - void ExpressionSetEquals::validateArguments(const ExpressionVector& args) const { - uassert(17045, str::stream() << "$setEquals needs at least two arguments had: " - << args.size(), - args.size() >= 2); + ValueSet rhsSet = arrayToSet(rhs); + const vector<Value>& lhsArray = lhs.getArray(); + vector<Value> returnVec; + + for (vector<Value>::const_iterator it = lhsArray.begin(); it != lhsArray.end(); ++it) { + // rhsSet serves the dual role of filtering out elements that were originally present + // in RHS and of eleminating duplicates from LHS + if (rhsSet.insert(*it).second) { + returnVec.push_back(*it); + } } + return Value(std::move(returnVec)); +} - Value ExpressionSetEquals::evaluateInternal(Variables* vars) const { - const size_t n = vpOperand.size(); - std::set<Value> lhs; +REGISTER_EXPRESSION("$setDifference", ExpressionSetDifference::parse); +const char* ExpressionSetDifference::getOpName() const { + return "$setDifference"; +} - for (size_t i = 0; i < n; i++) { - const Value nextEntry = vpOperand[i]->evaluateInternal(vars); - uassert(17044, str::stream() << "All operands of $setEquals must be arrays. One " - << "argument is of type: " - << typeName(nextEntry.getType()), - nextEntry.getType() == Array); +/* ----------------------- ExpressionSetEquals ---------------------------- */ - if (i == 0) { - lhs.insert(nextEntry.getArray().begin(), nextEntry.getArray().end()); - } - else { - const std::set<Value> rhs(nextEntry.getArray().begin(), nextEntry.getArray().end()); - if (lhs != rhs) { - return Value(false); - } +void ExpressionSetEquals::validateArguments(const ExpressionVector& args) const { + uassert(17045, + str::stream() << "$setEquals needs at least two arguments had: " << args.size(), + args.size() >= 2); +} + +Value ExpressionSetEquals::evaluateInternal(Variables* vars) const { + const size_t n = vpOperand.size(); + std::set<Value> lhs; + + for (size_t i = 0; i < n; i++) { + const Value nextEntry = vpOperand[i]->evaluateInternal(vars); + uassert(17044, + str::stream() << "All operands of $setEquals must be arrays. One " + << "argument is of type: " << typeName(nextEntry.getType()), + nextEntry.getType() == Array); + + if (i == 0) { + lhs.insert(nextEntry.getArray().begin(), nextEntry.getArray().end()); + } else { + const std::set<Value> rhs(nextEntry.getArray().begin(), nextEntry.getArray().end()); + if (lhs != rhs) { + return Value(false); } } - return Value(true); } + return Value(true); +} - REGISTER_EXPRESSION("$setEquals", ExpressionSetEquals::parse); - const char* ExpressionSetEquals::getOpName() const { - return "$setEquals"; - } +REGISTER_EXPRESSION("$setEquals", ExpressionSetEquals::parse); +const char* ExpressionSetEquals::getOpName() const { + return "$setEquals"; +} - /* ----------------------- ExpressionSetIntersection ---------------------------- */ +/* ----------------------- ExpressionSetIntersection ---------------------------- */ - Value ExpressionSetIntersection::evaluateInternal(Variables* vars) const { - const size_t n = vpOperand.size(); - ValueSet currentIntersection; - for (size_t i = 0; i < n; i++) { - const Value nextEntry = vpOperand[i]->evaluateInternal(vars); - if (nextEntry.nullish()) { - return Value(BSONNULL); - } - uassert(17047, str::stream() << "All operands of $setIntersection must be arrays. One " - << "argument is of type: " - << typeName(nextEntry.getType()), - nextEntry.getType() == Array); - - if (i == 0) { - currentIntersection.insert(nextEntry.getArray().begin(), - nextEntry.getArray().end()); +Value ExpressionSetIntersection::evaluateInternal(Variables* vars) const { + const size_t n = vpOperand.size(); + ValueSet currentIntersection; + for (size_t i = 0; i < n; i++) { + const Value nextEntry = vpOperand[i]->evaluateInternal(vars); + if (nextEntry.nullish()) { + return Value(BSONNULL); + } + uassert(17047, + str::stream() << "All operands of $setIntersection must be arrays. One " + << "argument is of type: " << typeName(nextEntry.getType()), + nextEntry.getType() == Array); + + if (i == 0) { + currentIntersection.insert(nextEntry.getArray().begin(), nextEntry.getArray().end()); + } else { + ValueSet nextSet = arrayToSet(nextEntry); + if (currentIntersection.size() > nextSet.size()) { + // to iterate over whichever is the smaller set + nextSet.swap(currentIntersection); } - else { - ValueSet nextSet = arrayToSet(nextEntry); - if (currentIntersection.size() > nextSet.size()) { - // to iterate over whichever is the smaller set - nextSet.swap(currentIntersection); - } - ValueSet::iterator it = currentIntersection.begin(); - while (it != currentIntersection.end()) { - if (!nextSet.count(*it)) { - ValueSet::iterator del = it; - ++it; - currentIntersection.erase(del); - } - else { - ++it; - } + ValueSet::iterator it = currentIntersection.begin(); + while (it != currentIntersection.end()) { + if (!nextSet.count(*it)) { + ValueSet::iterator del = it; + ++it; + currentIntersection.erase(del); + } else { + ++it; } } - if (currentIntersection.empty()) { - break; - } } - return Value(vector<Value>(currentIntersection.begin(), - currentIntersection.end())); + if (currentIntersection.empty()) { + break; + } } + return Value(vector<Value>(currentIntersection.begin(), currentIntersection.end())); +} - REGISTER_EXPRESSION("$setIntersection", ExpressionSetIntersection::parse); - const char* ExpressionSetIntersection::getOpName() const { - return "$setIntersection"; - } +REGISTER_EXPRESSION("$setIntersection", ExpressionSetIntersection::parse); +const char* ExpressionSetIntersection::getOpName() const { + return "$setIntersection"; +} - /* ----------------------- ExpressionSetIsSubset ---------------------------- */ +/* ----------------------- ExpressionSetIsSubset ---------------------------- */ namespace { - Value setIsSubsetHelper(const vector<Value>& lhs, const ValueSet& rhs) { - // do not shortcircuit when lhs.size() > rhs.size() - // because lhs can have redundant entries - for (vector<Value>::const_iterator it = lhs.begin(); it != lhs.end(); ++it) { - if (!rhs.count(*it)) { - return Value(false); - } +Value setIsSubsetHelper(const vector<Value>& lhs, const ValueSet& rhs) { + // do not shortcircuit when lhs.size() > rhs.size() + // because lhs can have redundant entries + for (vector<Value>::const_iterator it = lhs.begin(); it != lhs.end(); ++it) { + if (!rhs.count(*it)) { + return Value(false); } - return Value(true); } + return Value(true); +} } - Value ExpressionSetIsSubset::evaluateInternal(Variables* vars) const { - const Value lhs = vpOperand[0]->evaluateInternal(vars); - const Value rhs = vpOperand[1]->evaluateInternal(vars); - - uassert(17046, str::stream() << "both operands of $setIsSubset must be arrays. First " - << "argument is of type: " << typeName(lhs.getType()), - lhs.getType() == Array); - uassert(17042, str::stream() << "both operands of $setIsSubset must be arrays. Second " - << "argument is of type: " << typeName(rhs.getType()), - rhs.getType() == Array); +Value ExpressionSetIsSubset::evaluateInternal(Variables* vars) const { + const Value lhs = vpOperand[0]->evaluateInternal(vars); + const Value rhs = vpOperand[1]->evaluateInternal(vars); - return setIsSubsetHelper(lhs.getArray(), arrayToSet(rhs)); - } + uassert(17046, + str::stream() << "both operands of $setIsSubset must be arrays. First " + << "argument is of type: " << typeName(lhs.getType()), + lhs.getType() == Array); + uassert(17042, + str::stream() << "both operands of $setIsSubset must be arrays. Second " + << "argument is of type: " << typeName(rhs.getType()), + rhs.getType() == Array); - /** - * This class handles the case where the RHS set is constant. - * - * Since it is constant we can construct the hashset once which makes the runtime performance - * effectively constant with respect to the size of RHS. Large, constant RHS is expected to be a - * major use case for $redact and this has been verified to improve performance significantly. - */ - class ExpressionSetIsSubset::Optimized : public ExpressionSetIsSubset { - public: - Optimized(const ValueSet& cachedRhsSet, const ExpressionVector& operands) - : _cachedRhsSet(cachedRhsSet) - { - vpOperand = operands; - } + return setIsSubsetHelper(lhs.getArray(), arrayToSet(rhs)); +} - virtual Value evaluateInternal(Variables* vars) const { - const Value lhs = vpOperand[0]->evaluateInternal(vars); +/** + * This class handles the case where the RHS set is constant. + * + * Since it is constant we can construct the hashset once which makes the runtime performance + * effectively constant with respect to the size of RHS. Large, constant RHS is expected to be a + * major use case for $redact and this has been verified to improve performance significantly. + */ +class ExpressionSetIsSubset::Optimized : public ExpressionSetIsSubset { +public: + Optimized(const ValueSet& cachedRhsSet, const ExpressionVector& operands) + : _cachedRhsSet(cachedRhsSet) { + vpOperand = operands; + } - uassert(17310, str::stream() << "both operands of $setIsSubset must be arrays. First " - << "argument is of type: " << typeName(lhs.getType()), - lhs.getType() == Array); + virtual Value evaluateInternal(Variables* vars) const { + const Value lhs = vpOperand[0]->evaluateInternal(vars); - return setIsSubsetHelper(lhs.getArray(), _cachedRhsSet); - } + uassert(17310, + str::stream() << "both operands of $setIsSubset must be arrays. First " + << "argument is of type: " << typeName(lhs.getType()), + lhs.getType() == Array); - private: - const ValueSet _cachedRhsSet; - }; + return setIsSubsetHelper(lhs.getArray(), _cachedRhsSet); + } - intrusive_ptr<Expression> ExpressionSetIsSubset::optimize() { - // perfore basic optimizations - intrusive_ptr<Expression> optimized = ExpressionNary::optimize(); +private: + const ValueSet _cachedRhsSet; +}; - // if ExpressionNary::optimize() created a new value, return it directly - if (optimized.get() != this) - return optimized; +intrusive_ptr<Expression> ExpressionSetIsSubset::optimize() { + // perfore basic optimizations + intrusive_ptr<Expression> optimized = ExpressionNary::optimize(); - if (ExpressionConstant* ec = dynamic_cast<ExpressionConstant*>(vpOperand[1].get())) { - const Value rhs = ec->getValue(); - uassert(17311, str::stream() << "both operands of $setIsSubset must be arrays. Second " - << "argument is of type: " << typeName(rhs.getType()), - rhs.getType() == Array); + // if ExpressionNary::optimize() created a new value, return it directly + if (optimized.get() != this) + return optimized; - return new Optimized(arrayToSet(rhs), vpOperand); - } + if (ExpressionConstant* ec = dynamic_cast<ExpressionConstant*>(vpOperand[1].get())) { + const Value rhs = ec->getValue(); + uassert(17311, + str::stream() << "both operands of $setIsSubset must be arrays. Second " + << "argument is of type: " << typeName(rhs.getType()), + rhs.getType() == Array); - return optimized; + return new Optimized(arrayToSet(rhs), vpOperand); } - REGISTER_EXPRESSION("$setIsSubset", ExpressionSetIsSubset::parse); - const char* ExpressionSetIsSubset::getOpName() const { - return "$setIsSubset"; - } + return optimized; +} - /* ----------------------- ExpressionSetUnion ---------------------------- */ +REGISTER_EXPRESSION("$setIsSubset", ExpressionSetIsSubset::parse); +const char* ExpressionSetIsSubset::getOpName() const { + return "$setIsSubset"; +} - Value ExpressionSetUnion::evaluateInternal(Variables* vars) const { - ValueSet unionedSet; - const size_t n = vpOperand.size(); - for (size_t i = 0; i < n; i++) { - const Value newEntries = vpOperand[i]->evaluateInternal(vars); - if (newEntries.nullish()) { - return Value(BSONNULL); - } - uassert(17043, str::stream() << "All operands of $setUnion must be arrays. One argument" - << " is of type: " << typeName(newEntries.getType()), - newEntries.getType() == Array); +/* ----------------------- ExpressionSetUnion ---------------------------- */ - unionedSet.insert(newEntries.getArray().begin(), newEntries.getArray().end()); +Value ExpressionSetUnion::evaluateInternal(Variables* vars) const { + ValueSet unionedSet; + const size_t n = vpOperand.size(); + for (size_t i = 0; i < n; i++) { + const Value newEntries = vpOperand[i]->evaluateInternal(vars); + if (newEntries.nullish()) { + return Value(BSONNULL); } - return Value(vector<Value>(unionedSet.begin(), unionedSet.end())); - } + uassert(17043, + str::stream() << "All operands of $setUnion must be arrays. One argument" + << " is of type: " << typeName(newEntries.getType()), + newEntries.getType() == Array); - REGISTER_EXPRESSION("$setUnion", ExpressionSetUnion::parse); - const char* ExpressionSetUnion::getOpName() const { - return "$setUnion"; + unionedSet.insert(newEntries.getArray().begin(), newEntries.getArray().end()); } + return Value(vector<Value>(unionedSet.begin(), unionedSet.end())); +} - /* ----------------------- ExpressionIsArray ---------------------------- */ - - Value ExpressionIsArray::evaluateInternal(Variables* vars) const { - Value argument = vpOperand[0]->evaluateInternal(vars); - return Value(argument.getType() == Array); - } - - REGISTER_EXPRESSION("$isArray", ExpressionIsArray::parse); - const char* ExpressionIsArray::getOpName() const { - return "$isArray"; - } - - /* ----------------------- ExpressionSize ---------------------------- */ - - Value ExpressionSize::evaluateInternal(Variables* vars) const { - Value array = vpOperand[0]->evaluateInternal(vars); - - uassert(17124, str::stream() << "The argument to $size must be an Array, but was of type: " - << typeName(array.getType()), - array.getType() == Array); - return Value::createIntOrLong(array.getArray().size()); - } +REGISTER_EXPRESSION("$setUnion", ExpressionSetUnion::parse); +const char* ExpressionSetUnion::getOpName() const { + return "$setUnion"; +} - REGISTER_EXPRESSION("$size", ExpressionSize::parse); - const char* ExpressionSize::getOpName() const { - return "$size"; - } +/* ----------------------- ExpressionIsArray ---------------------------- */ - /* ----------------------- ExpressionStrcasecmp ---------------------------- */ +Value ExpressionIsArray::evaluateInternal(Variables* vars) const { + Value argument = vpOperand[0]->evaluateInternal(vars); + return Value(argument.getType() == Array); +} - Value ExpressionStrcasecmp::evaluateInternal(Variables* vars) const { - Value pString1(vpOperand[0]->evaluateInternal(vars)); - Value pString2(vpOperand[1]->evaluateInternal(vars)); +REGISTER_EXPRESSION("$isArray", ExpressionIsArray::parse); +const char* ExpressionIsArray::getOpName() const { + return "$isArray"; +} - /* boost::iequals returns a bool not an int so strings must actually be allocated */ - string str1 = boost::to_upper_copy( pString1.coerceToString() ); - string str2 = boost::to_upper_copy( pString2.coerceToString() ); - int result = str1.compare(str2); +/* ----------------------- ExpressionSize ---------------------------- */ - if (result == 0) - return Value(0); - else if (result > 0) - return Value(1); - else - return Value(-1); - } +Value ExpressionSize::evaluateInternal(Variables* vars) const { + Value array = vpOperand[0]->evaluateInternal(vars); - REGISTER_EXPRESSION("$strcasecmp", ExpressionStrcasecmp::parse); - const char* ExpressionStrcasecmp::getOpName() const { - return "$strcasecmp"; - } + uassert(17124, + str::stream() << "The argument to $size must be an Array, but was of type: " + << typeName(array.getType()), + array.getType() == Array); + return Value::createIntOrLong(array.getArray().size()); +} - /* ----------------------- ExpressionSubstr ---------------------------- */ +REGISTER_EXPRESSION("$size", ExpressionSize::parse); +const char* ExpressionSize::getOpName() const { + return "$size"; +} - Value ExpressionSubstr::evaluateInternal(Variables* vars) const { - Value pString(vpOperand[0]->evaluateInternal(vars)); - Value pLower(vpOperand[1]->evaluateInternal(vars)); - Value pLength(vpOperand[2]->evaluateInternal(vars)); +/* ----------------------- ExpressionStrcasecmp ---------------------------- */ - string str = pString.coerceToString(); - uassert(16034, str::stream() << getOpName() << - ": starting index must be a numeric type (is BSON type " << - typeName(pLower.getType()) << ")", - (pLower.getType() == NumberInt - || pLower.getType() == NumberLong - || pLower.getType() == NumberDouble)); - uassert(16035, str::stream() << getOpName() << - ": length must be a numeric type (is BSON type " << - typeName(pLength.getType() )<< ")", - (pLength.getType() == NumberInt - || pLength.getType() == NumberLong - || pLength.getType() == NumberDouble)); +Value ExpressionStrcasecmp::evaluateInternal(Variables* vars) const { + Value pString1(vpOperand[0]->evaluateInternal(vars)); + Value pString2(vpOperand[1]->evaluateInternal(vars)); - string::size_type lower = static_cast< string::size_type >( pLower.coerceToLong() ); - string::size_type length = static_cast< string::size_type >( pLength.coerceToLong() ); + /* boost::iequals returns a bool not an int so strings must actually be allocated */ + string str1 = boost::to_upper_copy(pString1.coerceToString()); + string str2 = boost::to_upper_copy(pString2.coerceToString()); + int result = str1.compare(str2); - auto isContinuationByte = [](char c){ return ((c & 0xc0) == 0x80); }; + if (result == 0) + return Value(0); + else if (result > 0) + return Value(1); + else + return Value(-1); +} - uassert(28656, str::stream() << getOpName() << - ": Invalid range, starting index is a UTF-8 continuation byte.", - (lower >= str.length() || !isContinuationByte(str[lower]))); +REGISTER_EXPRESSION("$strcasecmp", ExpressionStrcasecmp::parse); +const char* ExpressionStrcasecmp::getOpName() const { + return "$strcasecmp"; +} - // Check the byte after the last character we'd return. If it is a continuation byte, that - // means we're in the middle of a UTF-8 character. - uassert(28657, str::stream() << getOpName() << - ": Invalid range, ending index is in the middle of a UTF-8 character.", - (lower + length >= str.length() || !isContinuationByte(str[lower + length]))); +/* ----------------------- ExpressionSubstr ---------------------------- */ + +Value ExpressionSubstr::evaluateInternal(Variables* vars) const { + Value pString(vpOperand[0]->evaluateInternal(vars)); + Value pLower(vpOperand[1]->evaluateInternal(vars)); + Value pLength(vpOperand[2]->evaluateInternal(vars)); + + string str = pString.coerceToString(); + uassert(16034, + str::stream() << getOpName() + << ": starting index must be a numeric type (is BSON type " + << typeName(pLower.getType()) << ")", + (pLower.getType() == NumberInt || pLower.getType() == NumberLong || + pLower.getType() == NumberDouble)); + uassert(16035, + str::stream() << getOpName() << ": length must be a numeric type (is BSON type " + << typeName(pLength.getType()) << ")", + (pLength.getType() == NumberInt || pLength.getType() == NumberLong || + pLength.getType() == NumberDouble)); + + string::size_type lower = static_cast<string::size_type>(pLower.coerceToLong()); + string::size_type length = static_cast<string::size_type>(pLength.coerceToLong()); + + auto isContinuationByte = [](char c) { return ((c & 0xc0) == 0x80); }; + + uassert(28656, + str::stream() << getOpName() + << ": Invalid range, starting index is a UTF-8 continuation byte.", + (lower >= str.length() || !isContinuationByte(str[lower]))); + + // Check the byte after the last character we'd return. If it is a continuation byte, that + // means we're in the middle of a UTF-8 character. + uassert( + 28657, + str::stream() << getOpName() + << ": Invalid range, ending index is in the middle of a UTF-8 character.", + (lower + length >= str.length() || !isContinuationByte(str[lower + length]))); + + if (lower >= str.length()) { + // If lower > str.length() then string::substr() will throw out_of_range, so return an + // empty string if lower is not a valid string index. + return Value(""); + } + return Value(str.substr(lower, length)); +} - if ( lower >= str.length() ) { - // If lower > str.length() then string::substr() will throw out_of_range, so return an - // empty string if lower is not a valid string index. - return Value(""); - } - return Value(str.substr(lower, length)); - } +REGISTER_EXPRESSION("$substr", ExpressionSubstr::parse); +const char* ExpressionSubstr::getOpName() const { + return "$substr"; +} - REGISTER_EXPRESSION("$substr", ExpressionSubstr::parse); - const char* ExpressionSubstr::getOpName() const { - return "$substr"; +/* ----------------------- ExpressionSubtract ---------------------------- */ + +Value ExpressionSubtract::evaluateInternal(Variables* vars) const { + Value lhs = vpOperand[0]->evaluateInternal(vars); + Value rhs = vpOperand[1]->evaluateInternal(vars); + + BSONType diffType = Value::getWidestNumeric(rhs.getType(), lhs.getType()); + + if (diffType == NumberDouble) { + double right = rhs.coerceToDouble(); + double left = lhs.coerceToDouble(); + return Value(left - right); + } else if (diffType == NumberLong) { + long long right = rhs.coerceToLong(); + long long left = lhs.coerceToLong(); + return Value(left - right); + } else if (diffType == NumberInt) { + long long right = rhs.coerceToLong(); + long long left = lhs.coerceToLong(); + return Value::createIntOrLong(left - right); + } else if (lhs.nullish() || rhs.nullish()) { + return Value(BSONNULL); + } else if (lhs.getType() == Date) { + if (rhs.getType() == Date) { + long long timeDelta = lhs.getDate() - rhs.getDate(); + return Value(timeDelta); + } else if (rhs.numeric()) { + long long millisSinceEpoch = lhs.getDate() - rhs.coerceToLong(); + return Value(Date_t::fromMillisSinceEpoch(millisSinceEpoch)); + } else { + uasserted(16613, + str::stream() << "cant $subtract a " << typeName(rhs.getType()) + << " from a Date"); + } + } else { + uasserted(16556, + str::stream() << "cant $subtract a" << typeName(rhs.getType()) << " from a " + << typeName(lhs.getType())); } +} - /* ----------------------- ExpressionSubtract ---------------------------- */ - - Value ExpressionSubtract::evaluateInternal(Variables* vars) const { - Value lhs = vpOperand[0]->evaluateInternal(vars); - Value rhs = vpOperand[1]->evaluateInternal(vars); +REGISTER_EXPRESSION("$subtract", ExpressionSubtract::parse); +const char* ExpressionSubtract::getOpName() const { + return "$subtract"; +} - BSONType diffType = Value::getWidestNumeric(rhs.getType(), lhs.getType()); +/* ------------------------- ExpressionToLower ----------------------------- */ - if (diffType == NumberDouble) { - double right = rhs.coerceToDouble(); - double left = lhs.coerceToDouble(); - return Value(left - right); - } - else if (diffType == NumberLong) { - long long right = rhs.coerceToLong(); - long long left = lhs.coerceToLong(); - return Value(left - right); - } - else if (diffType == NumberInt) { - long long right = rhs.coerceToLong(); - long long left = lhs.coerceToLong(); - return Value::createIntOrLong(left - right); - } - else if (lhs.nullish() || rhs.nullish()) { - return Value(BSONNULL); - } - else if (lhs.getType() == Date) { - if (rhs.getType() == Date) { - long long timeDelta = lhs.getDate() - rhs.getDate(); - return Value(timeDelta); - } - else if (rhs.numeric()) { - long long millisSinceEpoch = lhs.getDate() - rhs.coerceToLong(); - return Value(Date_t::fromMillisSinceEpoch(millisSinceEpoch)); - } - else { - uasserted(16613, str::stream() << "cant $subtract a " - << typeName(rhs.getType()) - << " from a Date"); - } - } - else { - uasserted(16556, str::stream() << "cant $subtract a" - << typeName(rhs.getType()) - << " from a " - << typeName(lhs.getType())); - } - } +Value ExpressionToLower::evaluateInternal(Variables* vars) const { + Value pString(vpOperand[0]->evaluateInternal(vars)); + string str = pString.coerceToString(); + boost::to_lower(str); + return Value(str); +} - REGISTER_EXPRESSION("$subtract", ExpressionSubtract::parse); - const char* ExpressionSubtract::getOpName() const { - return "$subtract"; - } +REGISTER_EXPRESSION("$toLower", ExpressionToLower::parse); +const char* ExpressionToLower::getOpName() const { + return "$toLower"; +} - /* ------------------------- ExpressionToLower ----------------------------- */ +/* ------------------------- ExpressionToUpper -------------------------- */ - Value ExpressionToLower::evaluateInternal(Variables* vars) const { - Value pString(vpOperand[0]->evaluateInternal(vars)); - string str = pString.coerceToString(); - boost::to_lower(str); - return Value(str); - } +Value ExpressionToUpper::evaluateInternal(Variables* vars) const { + Value pString(vpOperand[0]->evaluateInternal(vars)); + string str(pString.coerceToString()); + boost::to_upper(str); + return Value(str); +} - REGISTER_EXPRESSION("$toLower", ExpressionToLower::parse); - const char* ExpressionToLower::getOpName() const { - return "$toLower"; - } +REGISTER_EXPRESSION("$toUpper", ExpressionToUpper::parse); +const char* ExpressionToUpper::getOpName() const { + return "$toUpper"; +} - /* ------------------------- ExpressionToUpper -------------------------- */ +/* ------------------------- ExpressionWeek ----------------------------- */ - Value ExpressionToUpper::evaluateInternal(Variables* vars) const { - Value pString(vpOperand[0]->evaluateInternal(vars)); - string str(pString.coerceToString()); - boost::to_upper(str); - return Value(str); - } +Value ExpressionWeek::evaluateInternal(Variables* vars) const { + Value pDate(vpOperand[0]->evaluateInternal(vars)); + return Value(extract(pDate.coerceToTm())); +} - REGISTER_EXPRESSION("$toUpper", ExpressionToUpper::parse); - const char* ExpressionToUpper::getOpName() const { - return "$toUpper"; - } +int ExpressionWeek::extract(const tm& tm) { + int dayOfWeek = tm.tm_wday; + int dayOfYear = tm.tm_yday; + int prevSundayDayOfYear = dayOfYear - dayOfWeek; // may be negative + int nextSundayDayOfYear = prevSundayDayOfYear + 7; // must be positive - /* ------------------------- ExpressionWeek ----------------------------- */ + // Return the zero based index of the week of the next sunday, equal to the one based index + // of the week of the previous sunday, which is to be returned. + int nextSundayWeek = nextSundayDayOfYear / 7; - Value ExpressionWeek::evaluateInternal(Variables* vars) const { - Value pDate(vpOperand[0]->evaluateInternal(vars)); - return Value(extract(pDate.coerceToTm())); + // Verify that the week calculation is consistent with strftime "%U". + DEV { + char buf[3]; + verify(strftime(buf, 3, "%U", &tm)); + verify(int(str::toUnsigned(buf)) == nextSundayWeek); } - int ExpressionWeek::extract(const tm& tm) { - int dayOfWeek = tm.tm_wday; - int dayOfYear = tm.tm_yday; - int prevSundayDayOfYear = dayOfYear - dayOfWeek; // may be negative - int nextSundayDayOfYear = prevSundayDayOfYear + 7; // must be positive - - // Return the zero based index of the week of the next sunday, equal to the one based index - // of the week of the previous sunday, which is to be returned. - int nextSundayWeek = nextSundayDayOfYear / 7; - - // Verify that the week calculation is consistent with strftime "%U". - DEV{ - char buf[3]; - verify(strftime(buf,3,"%U",&tm)); - verify(int(str::toUnsigned(buf))==nextSundayWeek); - } - - return nextSundayWeek; - } + return nextSundayWeek; +} - REGISTER_EXPRESSION("$week", ExpressionWeek::parse); - const char* ExpressionWeek::getOpName() const { - return "$week"; - } +REGISTER_EXPRESSION("$week", ExpressionWeek::parse); +const char* ExpressionWeek::getOpName() const { + return "$week"; +} - /* ------------------------- ExpressionYear ----------------------------- */ +/* ------------------------- ExpressionYear ----------------------------- */ - Value ExpressionYear::evaluateInternal(Variables* vars) const { - Value pDate(vpOperand[0]->evaluateInternal(vars)); - return Value(extract(pDate.coerceToTm())); - } +Value ExpressionYear::evaluateInternal(Variables* vars) const { + Value pDate(vpOperand[0]->evaluateInternal(vars)); + return Value(extract(pDate.coerceToTm())); +} - REGISTER_EXPRESSION("$year", ExpressionYear::parse); - const char* ExpressionYear::getOpName() const { - return "$year"; - } +REGISTER_EXPRESSION("$year", ExpressionYear::parse); +const char* ExpressionYear::getOpName() const { + return "$year"; +} } |