summaryrefslogtreecommitdiff
path: root/src/mongo/db/pipeline
diff options
context:
space:
mode:
authorJacob Evans <jacob.evans@10gen.com>2019-04-12 12:21:15 -0400
committerJacob Evans <jacob.evans@10gen.com>2019-05-03 14:36:58 -0400
commitd226d5d236f2542bcece138c3c2337d67348d84d (patch)
tree125a55de39cbff86909a64f0d9e6f2d7e9ba1dca /src/mongo/db/pipeline
parent734266e9ec2f8cbf0b75bd7797f8b573262af471 (diff)
downloadmongo-d226d5d236f2542bcece138c3c2337d67348d84d.tar.gz
SERVER-40526 Make agg Expressions walkable
Diffstat (limited to 'src/mongo/db/pipeline')
-rw-r--r--src/mongo/db/pipeline/document_source_group_test.cpp15
-rw-r--r--src/mongo/db/pipeline/expression.cpp693
-rw-r--r--src/mongo/db/pipeline/expression.h418
-rw-r--r--src/mongo/db/pipeline/expression_test.cpp63
4 files changed, 699 insertions, 490 deletions
diff --git a/src/mongo/db/pipeline/document_source_group_test.cpp b/src/mongo/db/pipeline/document_source_group_test.cpp
index 111a2db01f2..da25b97d397 100644
--- a/src/mongo/db/pipeline/document_source_group_test.cpp
+++ b/src/mongo/db/pipeline/document_source_group_test.cpp
@@ -207,7 +207,20 @@ TEST_F(DocumentSourceGroupTest, ShouldReportMultipleFieldGroupKeysAsARename) {
VariablesParseState vps = expCtx->variablesParseState;
auto x = ExpressionFieldPath::parse(expCtx, "$x", vps);
auto y = ExpressionFieldPath::parse(expCtx, "$y", vps);
- auto groupByExpression = ExpressionObject::create(expCtx, {{"x", x}, {"y", y}});
+ auto groupByExpression = [&]() {
+ std::vector<boost::intrusive_ptr<Expression>> children;
+ std::vector<std::pair<std::string, boost::intrusive_ptr<Expression>&>> expressions;
+ auto doc = std::vector<std::pair<std::string, boost::intrusive_ptr<Expression>>>{{"x", x},
+ {"y", y}};
+ for (auto & [ unused, expression ] : doc)
+ children.push_back(std::move(expression));
+ std::vector<boost::intrusive_ptr<Expression>>::size_type index = 0;
+ for (auto & [ fieldName, unused ] : doc) {
+ expressions.emplace_back(fieldName, children[index]);
+ ++index;
+ }
+ return ExpressionObject::create(expCtx, std::move(children), std::move(expressions));
+ }();
auto group = DocumentSourceGroup::create(expCtx, groupByExpression, {});
auto modifiedPathsRet = group->getModifiedPaths();
ASSERT(modifiedPathsRet.type == DocumentSource::GetModPathsReturn::Type::kAllExcept);
diff --git a/src/mongo/db/pipeline/expression.cpp b/src/mongo/db/pipeline/expression.cpp
index 0084f870d30..8895e4a98ac 100644
--- a/src/mongo/db/pipeline/expression.cpp
+++ b/src/mongo/db/pipeline/expression.cpp
@@ -37,6 +37,7 @@
#include <boost/algorithm/string.hpp>
#include <cstdio>
#include <pcrecpp.h>
+#include <utility>
#include <vector>
#include "mongo/db/commands/feature_compatibility_version_documentation.h"
@@ -93,7 +94,7 @@ intrusive_ptr<Expression> Expression::parseObject(
BSONObj obj,
const VariablesParseState& vps) {
if (obj.isEmpty()) {
- return ExpressionObject::create(expCtx, {});
+ return ExpressionObject::create(expCtx, {}, {});
}
if (obj.firstElementFieldName()[0] == '$') {
@@ -273,9 +274,9 @@ Value ExpressionAdd::evaluate(const Document& root) const {
BSONType totalType = NumberInt;
bool haveDate = false;
- const size_t n = vpOperand.size();
+ const size_t n = _children.size();
for (size_t i = 0; i < n; ++i) {
- Value val = vpOperand[i]->evaluate(root);
+ Value val = _children[i]->evaluate(root);
switch (val.getType()) {
case NumberDecimal:
@@ -346,7 +347,7 @@ const char* ExpressionAdd::getOpName() const {
/* ------------------------- ExpressionAllElementsTrue -------------------------- */
Value ExpressionAllElementsTrue::evaluate(const Document& root) const {
- const Value arr = vpOperand[0]->evaluate(root);
+ const Value arr = _children[0]->evaluate(root);
uassert(17040,
str::stream() << getOpName() << "'s argument must be an array, but is "
<< typeName(arr.getType()),
@@ -381,10 +382,10 @@ intrusive_ptr<Expression> ExpressionAnd::optimize() {
promised by ExpressionNary::optimize(),) then there's nothing
we can do.
*/
- const size_t n = pAnd->vpOperand.size();
+ const size_t n = pAnd->_children.size();
// ExpressionNary::optimize() generates an ExpressionConstant for {$and:[]}.
verify(n > 0);
- intrusive_ptr<Expression> pLast(pAnd->vpOperand[n - 1]);
+ intrusive_ptr<Expression> pLast(pAnd->_children[n - 1]);
const ExpressionConstant* pConst = dynamic_cast<ExpressionConstant*>(pLast.get());
if (!pConst)
return pE;
@@ -408,7 +409,7 @@ intrusive_ptr<Expression> ExpressionAnd::optimize() {
*/
if (n == 2) {
intrusive_ptr<Expression> pFinal(
- ExpressionCoerceToBool::create(getExpressionContext(), pAnd->vpOperand[0]));
+ ExpressionCoerceToBool::create(getExpressionContext(), std::move(pAnd->_children[0])));
return pFinal;
}
@@ -419,14 +420,14 @@ intrusive_ptr<Expression> ExpressionAnd::optimize() {
Note that because of any implicit conversions, we may need to
apply an implicit boolean conversion.
*/
- pAnd->vpOperand.resize(n - 1);
+ pAnd->_children.resize(n - 1);
return pE;
}
Value ExpressionAnd::evaluate(const Document& root) const {
- const size_t n = vpOperand.size();
+ const size_t n = _children.size();
for (size_t i = 0; i < n; ++i) {
- Value pValue(vpOperand[i]->evaluate(root));
+ Value pValue(_children[i]->evaluate(root));
if (!pValue.coerceToBool())
return Value(false);
}
@@ -442,7 +443,7 @@ const char* ExpressionAnd::getOpName() const {
/* ------------------------- ExpressionAnyElementTrue -------------------------- */
Value ExpressionAnyElementTrue::evaluate(const Document& root) const {
- const Value arr = vpOperand[0]->evaluate(root);
+ const Value arr = _children[0]->evaluate(root);
uassert(17041,
str::stream() << getOpName() << "'s argument must be an array, but is "
<< typeName(arr.getType()),
@@ -465,8 +466,8 @@ const char* ExpressionAnyElementTrue::getOpName() const {
Value ExpressionArray::evaluate(const Document& root) const {
vector<Value> values;
- values.reserve(vpOperand.size());
- for (auto&& expr : vpOperand) {
+ values.reserve(_children.size());
+ for (auto&& expr : _children) {
Value elemVal = expr->evaluate(root);
values.push_back(elemVal.missing() ? Value(BSONNULL) : std::move(elemVal));
}
@@ -475,8 +476,8 @@ Value ExpressionArray::evaluate(const Document& root) const {
Value ExpressionArray::serialize(bool explain) const {
vector<Value> expressions;
- expressions.reserve(vpOperand.size());
- for (auto&& expr : vpOperand) {
+ expressions.reserve(_children.size());
+ for (auto&& expr : _children) {
expressions.push_back(expr->serialize(explain));
}
return Value(std::move(expressions));
@@ -485,7 +486,7 @@ Value ExpressionArray::serialize(bool explain) const {
intrusive_ptr<Expression> ExpressionArray::optimize() {
bool allValuesConstant = true;
- for (auto&& expr : vpOperand) {
+ for (auto&& expr : _children) {
expr = expr->optimize();
if (!dynamic_cast<ExpressionConstant*>(expr.get())) {
allValuesConstant = false;
@@ -507,8 +508,8 @@ const char* ExpressionArray::getOpName() const {
/* ------------------------- ExpressionArrayElemAt -------------------------- */
Value ExpressionArrayElemAt::evaluate(const Document& root) const {
- const Value array = vpOperand[0]->evaluate(root);
- const Value indexArg = vpOperand[1]->evaluate(root);
+ const Value array = _children[0]->evaluate(root);
+ const Value indexArg = _children[1]->evaluate(root);
if (array.nullish() || indexArg.nullish()) {
return Value(BSONNULL);
@@ -549,7 +550,7 @@ const char* ExpressionArrayElemAt::getOpName() const {
/* ------------------------- ExpressionObjectToArray -------------------------- */
Value ExpressionObjectToArray::evaluate(const Document& root) const {
- const Value targetVal = vpOperand[0]->evaluate(root);
+ const Value targetVal = _children[0]->evaluate(root);
if (targetVal.nullish()) {
return Value(BSONNULL);
@@ -581,7 +582,7 @@ const char* ExpressionObjectToArray::getOpName() const {
/* ------------------------- ExpressionArrayToObject -------------------------- */
Value ExpressionArrayToObject::evaluate(const Document& root) const {
- const Value input = vpOperand[0]->evaluate(root);
+ const Value input = _children[0]->evaluate(root);
if (input.nullish()) {
return Value(BSONNULL);
}
@@ -702,14 +703,13 @@ const char* ExpressionCeil::getOpName() const {
/* -------------------- ExpressionCoerceToBool ------------------------- */
intrusive_ptr<ExpressionCoerceToBool> ExpressionCoerceToBool::create(
- const intrusive_ptr<ExpressionContext>& expCtx, const intrusive_ptr<Expression>& pExpression) {
- intrusive_ptr<ExpressionCoerceToBool> pNew(new ExpressionCoerceToBool(expCtx, pExpression));
- return pNew;
+ const intrusive_ptr<ExpressionContext>& expCtx, intrusive_ptr<Expression> pExpression) {
+ return new ExpressionCoerceToBool(expCtx, std::move(pExpression));
}
ExpressionCoerceToBool::ExpressionCoerceToBool(const intrusive_ptr<ExpressionContext>& expCtx,
- const intrusive_ptr<Expression>& pTheExpression)
- : Expression(expCtx), pExpression(pTheExpression) {}
+ intrusive_ptr<Expression> pExpression)
+ : Expression(expCtx, {std::move(pExpression)}), pExpression(_children[0]) {}
intrusive_ptr<Expression> ExpressionCoerceToBool::optimize() {
/* optimize the operand */
@@ -774,7 +774,7 @@ intrusive_ptr<Expression> ExpressionCompare::parse(
intrusive_ptr<ExpressionCompare> expr = new ExpressionCompare(expCtx, op);
ExpressionVector args = parseArguments(expCtx, bsonExpr, vps);
expr->validateArguments(args);
- expr->vpOperand = args;
+ expr->_children = args;
return expr;
}
@@ -784,7 +784,7 @@ boost::intrusive_ptr<ExpressionCompare> ExpressionCompare::create(
const boost::intrusive_ptr<Expression>& exprLeft,
const boost::intrusive_ptr<Expression>& exprRight) {
boost::intrusive_ptr<ExpressionCompare> expr = new ExpressionCompare(expCtx, cmpOp);
- expr->vpOperand = {exprLeft, exprRight};
+ expr->_children = {exprLeft, exprRight};
return expr;
}
@@ -810,8 +810,8 @@ static const CmpLookup cmpLookup[7] = {
}
Value ExpressionCompare::evaluate(const Document& root) const {
- Value pLeft(vpOperand[0]->evaluate(root));
- Value pRight(vpOperand[1]->evaluate(root));
+ Value pLeft(_children[0]->evaluate(root));
+ Value pRight(_children[1]->evaluate(root));
int cmp = getExpressionContext()->getValueComparator().compare(pLeft, pRight);
@@ -838,11 +838,11 @@ const char* ExpressionCompare::getOpName() const {
/* ------------------------- ExpressionConcat ----------------------------- */
Value ExpressionConcat::evaluate(const Document& root) const {
- const size_t n = vpOperand.size();
+ const size_t n = _children.size();
StringBuilder result;
for (size_t i = 0; i < n; ++i) {
- Value val = vpOperand[i]->evaluate(root);
+ Value val = _children[i]->evaluate(root);
if (val.nullish())
return Value(BSONNULL);
@@ -864,11 +864,11 @@ const char* ExpressionConcat::getOpName() const {
/* ------------------------- ExpressionConcatArrays ----------------------------- */
Value ExpressionConcatArrays::evaluate(const Document& root) const {
- const size_t n = vpOperand.size();
+ const size_t n = _children.size();
vector<Value> values;
for (size_t i = 0; i < n; ++i) {
- Value val = vpOperand[i]->evaluate(root);
+ Value val = _children[i]->evaluate(root);
if (val.nullish()) {
return Value(BSONNULL);
}
@@ -892,9 +892,9 @@ const char* ExpressionConcatArrays::getOpName() const {
/* ----------------------- ExpressionCond ------------------------------ */
Value ExpressionCond::evaluate(const Document& root) const {
- Value pCond(vpOperand[0]->evaluate(root));
+ Value pCond(_children[0]->evaluate(root));
int idx = pCond.coerceToBool() ? 1 : 2;
- return vpOperand[idx]->evaluate(root);
+ return _children[idx]->evaluate(root);
}
intrusive_ptr<Expression> ExpressionCond::parse(
@@ -907,25 +907,25 @@ intrusive_ptr<Expression> ExpressionCond::parse(
verify(expr.fieldNameStringData() == "$cond");
intrusive_ptr<ExpressionCond> ret = new ExpressionCond(expCtx);
- ret->vpOperand.resize(3);
+ ret->_children.resize(3);
const BSONObj args = expr.embeddedObject();
BSONForEach(arg, args) {
if (arg.fieldNameStringData() == "if") {
- ret->vpOperand[0] = parseOperand(expCtx, arg, vps);
+ ret->_children[0] = parseOperand(expCtx, arg, vps);
} else if (arg.fieldNameStringData() == "then") {
- ret->vpOperand[1] = parseOperand(expCtx, arg, vps);
+ ret->_children[1] = parseOperand(expCtx, arg, vps);
} else if (arg.fieldNameStringData() == "else") {
- ret->vpOperand[2] = parseOperand(expCtx, arg, vps);
+ ret->_children[2] = parseOperand(expCtx, arg, vps);
} else {
uasserted(17083,
str::stream() << "Unrecognized parameter to $cond: " << arg.fieldName());
}
}
- 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]);
+ uassert(17080, "Missing 'if' parameter to $cond", ret->_children[0]);
+ uassert(17081, "Missing 'then' parameter to $cond", ret->_children[1]);
+ uassert(17082, "Missing 'else' parameter to $cond", ret->_children[2]);
return ret;
}
@@ -1106,18 +1106,29 @@ ExpressionDateFromParts::ExpressionDateFromParts(
intrusive_ptr<Expression> isoWeek,
intrusive_ptr<Expression> isoDayOfWeek,
intrusive_ptr<Expression> timeZone)
- : Expression(expCtx),
- _year(std::move(year)),
- _month(std::move(month)),
- _day(std::move(day)),
- _hour(std::move(hour)),
- _minute(std::move(minute)),
- _second(std::move(second)),
- _millisecond(std::move(millisecond)),
- _isoWeekYear(std::move(isoWeekYear)),
- _isoWeek(std::move(isoWeek)),
- _isoDayOfWeek(std::move(isoDayOfWeek)),
- _timeZone(std::move(timeZone)) {}
+ : Expression(expCtx,
+ {std::move(year),
+ std::move(month),
+ std::move(day),
+ std::move(hour),
+ std::move(minute),
+ std::move(second),
+ std::move(millisecond),
+ std::move(isoWeekYear),
+ std::move(isoWeek),
+ std::move(isoDayOfWeek),
+ std::move(timeZone)}),
+ _year(_children[0]),
+ _month(_children[1]),
+ _day(_children[2]),
+ _hour(_children[3]),
+ _minute(_children[4]),
+ _second(_children[5]),
+ _millisecond(_children[6]),
+ _isoWeekYear(_children[7]),
+ _isoWeek(_children[8]),
+ _isoDayOfWeek(_children[9]),
+ _timeZone(_children[10]) {}
intrusive_ptr<Expression> ExpressionDateFromParts::optimize() {
if (_year) {
@@ -1361,12 +1372,17 @@ ExpressionDateFromString::ExpressionDateFromString(
intrusive_ptr<Expression> format,
intrusive_ptr<Expression> onNull,
intrusive_ptr<Expression> onError)
- : Expression(expCtx),
- _dateString(std::move(dateString)),
- _timeZone(std::move(timeZone)),
- _format(std::move(format)),
- _onNull(std::move(onNull)),
- _onError(std::move(onError)) {}
+ : Expression(expCtx,
+ {std::move(dateString),
+ std::move(timeZone),
+ std::move(format),
+ std::move(onNull),
+ std::move(onError)}),
+ _dateString(_children[0]),
+ _timeZone(_children[1]),
+ _format(_children[2]),
+ _onNull(_children[3]),
+ _onError(_children[4]) {}
intrusive_ptr<Expression> ExpressionDateFromString::optimize() {
_dateString = _dateString->optimize();
@@ -1531,10 +1547,10 @@ ExpressionDateToParts::ExpressionDateToParts(const boost::intrusive_ptr<Expressi
intrusive_ptr<Expression> date,
intrusive_ptr<Expression> timeZone,
intrusive_ptr<Expression> iso8601)
- : Expression(expCtx),
- _date(std::move(date)),
- _timeZone(std::move(timeZone)),
- _iso8601(std::move(iso8601)) {}
+ : Expression(expCtx, {std::move(date), std::move(timeZone), std::move(iso8601)}),
+ _date(_children[0]),
+ _timeZone(_children[1]),
+ _iso8601(_children[2]) {}
intrusive_ptr<Expression> ExpressionDateToParts::optimize() {
_date = _date->optimize();
@@ -1679,11 +1695,12 @@ ExpressionDateToString::ExpressionDateToString(
intrusive_ptr<Expression> format,
intrusive_ptr<Expression> timeZone,
intrusive_ptr<Expression> onNull)
- : Expression(expCtx),
- _format(std::move(format)),
- _date(std::move(date)),
- _timeZone(std::move(timeZone)),
- _onNull(std::move(onNull)) {}
+ : Expression(expCtx,
+ {std::move(format), std::move(date), std::move(timeZone), std::move(onNull)}),
+ _format(_children[0]),
+ _date(_children[1]),
+ _timeZone(_children[2]),
+ _onNull(_children[3]) {}
intrusive_ptr<Expression> ExpressionDateToString::optimize() {
_date = _date->optimize();
@@ -1777,8 +1794,8 @@ void ExpressionDateToString::_doAddDependencies(DepsTracker* deps) const {
/* ----------------------- ExpressionDivide ---------------------------- */
Value ExpressionDivide::evaluate(const Document& root) const {
- Value lhs = vpOperand[0]->evaluate(root);
- Value rhs = vpOperand[1]->evaluate(root);
+ Value lhs = _children[0]->evaluate(root);
+ Value rhs = _children[1]->evaluate(root);
auto assertNonZero = [](bool nonZero) { uassert(16608, "can't $divide by zero", nonZero); };
@@ -1830,13 +1847,15 @@ const char* ExpressionExp::getOpName() const {
/* ---------------------- ExpressionObject --------------------------- */
ExpressionObject::ExpressionObject(const boost::intrusive_ptr<ExpressionContext>& expCtx,
- vector<pair<string, intrusive_ptr<Expression>>>&& expressions)
- : Expression(expCtx), _expressions(std::move(expressions)) {}
+ std::vector<boost::intrusive_ptr<Expression>> _children,
+ vector<pair<string, intrusive_ptr<Expression>&>>&& expressions)
+ : Expression(expCtx, std::move(_children)), _expressions(std::move(expressions)) {}
intrusive_ptr<ExpressionObject> ExpressionObject::create(
const boost::intrusive_ptr<ExpressionContext>& expCtx,
- vector<pair<string, intrusive_ptr<Expression>>>&& expressions) {
- return new ExpressionObject(expCtx, std::move(expressions));
+ std::vector<boost::intrusive_ptr<Expression>> _children,
+ vector<pair<string, intrusive_ptr<Expression>&>>&& expressions) {
+ return new ExpressionObject(expCtx, std::move(_children), std::move(expressions));
}
intrusive_ptr<ExpressionObject> ExpressionObject::parse(
@@ -1846,7 +1865,8 @@ intrusive_ptr<ExpressionObject> ExpressionObject::parse(
// Make sure we don't have any duplicate field names.
stdx::unordered_set<string> specifiedFields;
- vector<pair<string, intrusive_ptr<Expression>>> expressions;
+ std::vector<boost::intrusive_ptr<Expression>> children;
+ vector<pair<string, intrusive_ptr<Expression>&>> expressions;
for (auto&& elem : obj) {
// Make sure this element has a valid field name. Use StringData here so that we can detect
// if the field name contains a null byte.
@@ -1858,10 +1878,16 @@ intrusive_ptr<ExpressionObject> ExpressionObject::parse(
<< obj.toString(),
specifiedFields.find(fieldName) == specifiedFields.end());
specifiedFields.insert(fieldName);
- expressions.emplace_back(fieldName, parseOperand(expCtx, elem, vps));
+ children.push_back(parseOperand(expCtx, elem, vps));
}
- return new ExpressionObject{expCtx, std::move(expressions)};
+ std::vector<boost::intrusive_ptr<Expression>>::size_type index = 0;
+ for (auto&& elem : obj) {
+ expressions.emplace_back(elem.fieldName(), children[index]);
+ ++index;
+ }
+
+ return new ExpressionObject{expCtx, std::move(children), std::move(expressions)};
}
intrusive_ptr<Expression> ExpressionObject::optimize() {
@@ -1880,8 +1906,8 @@ intrusive_ptr<Expression> ExpressionObject::optimize() {
}
void ExpressionObject::_doAddDependencies(DepsTracker* deps) const {
- for (auto&& pair : _expressions) {
- pair.second->addDependencies(deps);
+ for (auto&& child : _children) {
+ child->addDependencies(deps);
}
}
@@ -2156,11 +2182,11 @@ ExpressionFilter::ExpressionFilter(const boost::intrusive_ptr<ExpressionContext>
Variables::Id varId,
intrusive_ptr<Expression> input,
intrusive_ptr<Expression> filter)
- : Expression(expCtx),
+ : Expression(expCtx, {std::move(input), std::move(filter)}),
_varName(std::move(varName)),
_varId(varId),
- _input(std::move(input)),
- _filter(std::move(filter)) {}
+ _input(_children[0]),
+ _filter(_children[1]) {}
intrusive_ptr<Expression> ExpressionFilter::optimize() {
// TODO handle when _input is constant.
@@ -2245,7 +2271,7 @@ intrusive_ptr<Expression> ExpressionLet::parse(
// varsElem must be parsed before inElem regardless of BSON order.
BSONElement varsElem;
BSONElement inElem;
- BSONForEach(arg, args) {
+ for (auto&& arg : args) {
if (arg.fieldNameStringData() == "vars") {
varsElem = arg;
} else if (arg.fieldNameStringData() == "in") {
@@ -2262,25 +2288,36 @@ intrusive_ptr<Expression> ExpressionLet::parse(
// parse "vars"
VariablesParseState vpsSub(vpsIn); // vpsSub gets our vars, vpsIn doesn't.
VariableMap vars;
- BSONForEach(varElem, varsElem.embeddedObjectUserCheck()) {
+ std::vector<boost::intrusive_ptr<Expression>> children;
+ auto&& varsObj = varsElem.embeddedObjectUserCheck();
+ for (auto&& varElem : varsObj)
+ children.push_back(parseOperand(expCtx, varElem, vpsIn));
+
+ // Make a place in the vector for "in".
+ auto& inPtr = children.emplace_back(nullptr);
+
+ std::vector<boost::intrusive_ptr<Expression>>::size_type index = 0;
+ for (auto&& varElem : varsObj) {
const string varName = varElem.fieldName();
Variables::uassertValidNameForUserWrite(varName);
Variables::Id id = vpsSub.defineVariable(varName);
- vars[id] = NameAndExpression(varName,
- parseOperand(expCtx, varElem, vpsIn)); // only has outer vars
+ vars.emplace(id, NameAndExpression{varName, children[index]}); // only has outer vars
+ ++index;
}
// parse "in"
- intrusive_ptr<Expression> subExpression = parseOperand(expCtx, inElem, vpsSub); // has our vars
+ inPtr = parseOperand(expCtx, inElem, vpsSub); // has our vars
- return new ExpressionLet(expCtx, vars, subExpression);
+ return new ExpressionLet(expCtx, std::move(vars), std::move(children));
}
ExpressionLet::ExpressionLet(const boost::intrusive_ptr<ExpressionContext>& expCtx,
- const VariableMap& vars,
- intrusive_ptr<Expression> subExpression)
- : Expression(expCtx), _variables(vars), _subExpression(subExpression) {}
+ VariableMap&& vars,
+ std::vector<boost::intrusive_ptr<Expression>> children)
+ : Expression(expCtx, std::move(children)),
+ _variables(std::move(vars)),
+ _subExpression(_children.back()) {}
intrusive_ptr<Expression> ExpressionLet::optimize() {
if (_variables.empty()) {
@@ -2386,7 +2423,11 @@ ExpressionMap::ExpressionMap(const boost::intrusive_ptr<ExpressionContext>& expC
Variables::Id varId,
intrusive_ptr<Expression> input,
intrusive_ptr<Expression> each)
- : Expression(expCtx), _varName(varName), _varId(varId), _input(input), _each(each) {}
+ : Expression(expCtx, {std::move(input), std::move(each)}),
+ _varName(varName),
+ _varId(varId),
+ _input(_children[0]),
+ _each(_children[1]) {}
intrusive_ptr<Expression> ExpressionMap::optimize() {
// TODO handle when _input is constant
@@ -2516,8 +2557,8 @@ void ExpressionMeta::_doAddDependencies(DepsTracker* deps) const {
/* ----------------------- ExpressionMod ---------------------------- */
Value ExpressionMod::evaluate(const Document& root) const {
- Value lhs = vpOperand[0]->evaluate(root);
- Value rhs = vpOperand[1]->evaluate(root);
+ Value lhs = _children[0]->evaluate(root);
+ Value rhs = _children[1]->evaluate(root);
BSONType leftType = lhs.getType();
BSONType rightType = rhs.getType();
@@ -2585,9 +2626,9 @@ Value ExpressionMultiply::evaluate(const Document& root) const {
BSONType productType = NumberInt;
- const size_t n = vpOperand.size();
+ const size_t n = _children.size();
for (size_t i = 0; i < n; ++i) {
- Value val = vpOperand[i]->evaluate(root);
+ Value val = _children[i]->evaluate(root);
if (val.numeric()) {
BSONType oldProductType = productType;
@@ -2636,11 +2677,11 @@ const char* ExpressionMultiply::getOpName() const {
/* ----------------------- ExpressionIfNull ---------------------------- */
Value ExpressionIfNull::evaluate(const Document& root) const {
- Value pLeft(vpOperand[0]->evaluate(root));
+ Value pLeft(_children[0]->evaluate(root));
if (!pLeft.nullish())
return pLeft;
- Value pRight(vpOperand[1]->evaluate(root));
+ Value pRight(_children[1]->evaluate(root));
return pRight;
}
@@ -2652,8 +2693,8 @@ const char* ExpressionIfNull::getOpName() const {
/* ----------------------- ExpressionIn ---------------------------- */
Value ExpressionIn::evaluate(const Document& root) const {
- Value argument(vpOperand[0]->evaluate(root));
- Value arrayOfValues(vpOperand[1]->evaluate(root));
+ Value argument(_children[0]->evaluate(root));
+ Value arrayOfValues(_children[1]->evaluate(root));
uassert(40081,
str::stream() << "$in requires an array as a second argument, found: "
@@ -2696,7 +2737,7 @@ void uassertIfNotIntegralAndNonNegative(Value val,
} // namespace
Value ExpressionIndexOfArray::evaluate(const Document& root) const {
- Value arrayArg = vpOperand[0]->evaluate(root);
+ Value arrayArg = _children[0]->evaluate(root);
if (arrayArg.nullish()) {
return Value(BSONNULL);
@@ -2708,7 +2749,7 @@ Value ExpressionIndexOfArray::evaluate(const Document& root) const {
arrayArg.isArray());
std::vector<Value> array = arrayArg.getArray();
- auto args = evaluateAndValidateArguments(root, vpOperand, array.size());
+ auto args = evaluateAndValidateArguments(root, _children, array.size());
for (int i = args.startIndex; i < args.endIndex; i++) {
if (getExpressionContext()->getValueComparator().evaluate(array[i] ==
args.targetOfSearch)) {
@@ -2739,7 +2780,7 @@ ExpressionIndexOfArray::Arguments ExpressionIndexOfArray::evaluateAndValidateArg
endIndex = std::min(static_cast<int>(arrayLength), endIndexArg.coerceToInt());
}
- return {vpOperand[1]->evaluate(root), startIndex, endIndex};
+ return {_children[1]->evaluate(root), startIndex, endIndex};
}
/**
@@ -2753,12 +2794,12 @@ public:
const ValueUnorderedMap<vector<int>>& indexMap,
const ExpressionVector& operands)
: ExpressionIndexOfArray(expCtx), _indexMap(std::move(indexMap)) {
- vpOperand = operands;
+ _children = operands;
}
virtual Value evaluate(const Document& root) const {
- auto args = evaluateAndValidateArguments(root, vpOperand, _indexMap.size());
+ auto args = evaluateAndValidateArguments(root, _children, _indexMap.size());
auto indexVec = _indexMap.find(args.targetOfSearch);
if (indexVec == _indexMap.end())
@@ -2789,7 +2830,7 @@ intrusive_ptr<Expression> ExpressionIndexOfArray::optimize() {
// If the input array is an ExpressionConstant we can optimize using a unordered_map instead of
// an
// array.
- if (auto constantArray = dynamic_cast<ExpressionConstant*>(vpOperand[0].get())) {
+ if (auto constantArray = dynamic_cast<ExpressionConstant*>(_children[0].get())) {
const Value valueArray = constantArray->getValue();
if (valueArray.nullish()) {
return ExpressionConstant::create(getExpressionContext(), Value(BSONNULL));
@@ -2812,7 +2853,7 @@ intrusive_ptr<Expression> ExpressionIndexOfArray::optimize() {
}
indexMap[arr[i]].push_back(i);
}
- return new Optimized(getExpressionContext(), indexMap, vpOperand);
+ return new Optimized(getExpressionContext(), indexMap, _children);
}
return this;
}
@@ -2836,7 +2877,7 @@ bool stringHasTokenAtIndex(size_t index, const std::string& input, const std::st
} // namespace
Value ExpressionIndexOfBytes::evaluate(const Document& root) const {
- Value stringArg = vpOperand[0]->evaluate(root);
+ Value stringArg = _children[0]->evaluate(root);
if (stringArg.nullish()) {
return Value(BSONNULL);
@@ -2848,7 +2889,7 @@ Value ExpressionIndexOfBytes::evaluate(const Document& root) const {
stringArg.getType() == String);
const std::string& input = stringArg.getString();
- Value tokenArg = vpOperand[1]->evaluate(root);
+ Value tokenArg = _children[1]->evaluate(root);
uassert(40092,
str::stream() << "$indexOfBytes requires a string as the second argument, found: "
<< typeName(tokenArg.getType()),
@@ -2856,15 +2897,15 @@ Value ExpressionIndexOfBytes::evaluate(const Document& root) const {
const std::string& token = tokenArg.getString();
size_t startIndex = 0;
- if (vpOperand.size() > 2) {
- Value startIndexArg = vpOperand[2]->evaluate(root);
+ if (_children.size() > 2) {
+ Value startIndexArg = _children[2]->evaluate(root);
uassertIfNotIntegralAndNonNegative(startIndexArg, getOpName(), "starting index");
startIndex = static_cast<size_t>(startIndexArg.coerceToInt());
}
size_t endIndex = input.size();
- if (vpOperand.size() > 3) {
- Value endIndexArg = vpOperand[3]->evaluate(root);
+ if (_children.size() > 3) {
+ Value endIndexArg = _children[3]->evaluate(root);
uassertIfNotIntegralAndNonNegative(endIndexArg, getOpName(), "ending index");
// Don't let 'endIndex' exceed the length of the string.
endIndex = std::min(input.size(), static_cast<size_t>(endIndexArg.coerceToInt()));
@@ -2890,7 +2931,7 @@ const char* ExpressionIndexOfBytes::getOpName() const {
/* ----------------------- ExpressionIndexOfCP --------------------- */
Value ExpressionIndexOfCP::evaluate(const Document& root) const {
- Value stringArg = vpOperand[0]->evaluate(root);
+ Value stringArg = _children[0]->evaluate(root);
if (stringArg.nullish()) {
return Value(BSONNULL);
@@ -2902,7 +2943,7 @@ Value ExpressionIndexOfCP::evaluate(const Document& root) const {
stringArg.getType() == String);
const std::string& input = stringArg.getString();
- Value tokenArg = vpOperand[1]->evaluate(root);
+ Value tokenArg = _children[1]->evaluate(root);
uassert(40094,
str::stream() << "$indexOfCP requires a string as the second argument, found: "
<< typeName(tokenArg.getType()),
@@ -2910,8 +2951,8 @@ Value ExpressionIndexOfCP::evaluate(const Document& root) const {
const std::string& token = tokenArg.getString();
size_t startCodePointIndex = 0;
- if (vpOperand.size() > 2) {
- Value startIndexArg = vpOperand[2]->evaluate(root);
+ if (_children.size() > 2) {
+ Value startIndexArg = _children[2]->evaluate(root);
uassertIfNotIntegralAndNonNegative(startIndexArg, getOpName(), "starting index");
startCodePointIndex = static_cast<size_t>(startIndexArg.coerceToInt());
}
@@ -2933,8 +2974,8 @@ Value ExpressionIndexOfCP::evaluate(const Document& root) const {
}
size_t endCodePointIndex = codePointLength;
- if (vpOperand.size() > 3) {
- Value endIndexArg = vpOperand[3]->evaluate(root);
+ if (_children.size() > 3) {
+ Value endIndexArg = _children[3]->evaluate(root);
uassertIfNotIntegralAndNonNegative(endIndexArg, getOpName(), "ending index");
// Don't let 'endCodePointIndex' exceed the number of code points in the string.
@@ -2992,8 +3033,8 @@ const char* ExpressionLn::getOpName() const {
/* ----------------------- ExpressionLog ---------------------------- */
Value ExpressionLog::evaluate(const Document& root) const {
- Value argVal = vpOperand[0]->evaluate(root);
- Value baseVal = vpOperand[1]->evaluate(root);
+ Value argVal = _children[0]->evaluate(root);
+ Value baseVal = _children[1]->evaluate(root);
if (argVal.nullish() || baseVal.nullish())
return Value(BSONNULL);
@@ -3076,7 +3117,7 @@ const char* ExpressionLog10::getOpName() const {
intrusive_ptr<Expression> ExpressionNary::optimize() {
uint32_t constOperandCount = 0;
- for (auto& operand : vpOperand) {
+ for (auto& operand : _children) {
operand = operand->optimize();
if (dynamic_cast<ExpressionConstant*>(operand.get())) {
++constOperandCount;
@@ -3084,7 +3125,7 @@ intrusive_ptr<Expression> ExpressionNary::optimize() {
}
// If all the operands are constant expressions, collapse the expression into one constant
// expression.
- if (constOperandCount == vpOperand.size()) {
+ if (constOperandCount == _children.size()) {
return intrusive_ptr<Expression>(
ExpressionConstant::create(getExpressionContext(), evaluate(Document())));
}
@@ -3097,8 +3138,8 @@ intrusive_ptr<Expression> ExpressionNary::optimize() {
if (isAssociative()) {
ExpressionVector constExpressions;
ExpressionVector optimizedOperands;
- for (size_t i = 0; i < vpOperand.size();) {
- intrusive_ptr<Expression> operand = vpOperand[i];
+ for (size_t i = 0; i < _children.size();) {
+ intrusive_ptr<Expression> operand = _children[i];
// If the operand is a constant one, add it to the current list of consecutive constant
// operands.
if (dynamic_cast<ExpressionConstant*>(operand.get())) {
@@ -3112,10 +3153,10 @@ intrusive_ptr<Expression> ExpressionNary::optimize() {
// E.g: sum(a, b, sum(c, d), e) => sum(a, b, c, d, e)
ExpressionNary* nary = dynamic_cast<ExpressionNary*>(operand.get());
if (nary && !strcmp(nary->getOpName(), getOpName()) && nary->isAssociative()) {
- invariant(!nary->vpOperand.empty());
- vpOperand[i] = std::move(nary->vpOperand[0]);
- vpOperand.insert(
- vpOperand.begin() + i + 1, nary->vpOperand.begin() + 1, nary->vpOperand.end());
+ invariant(!nary->_children.empty());
+ _children[i] = std::move(nary->_children[0]);
+ _children.insert(
+ _children.begin() + i + 1, nary->_children.begin() + 1, nary->_children.end());
continue;
}
@@ -3127,11 +3168,11 @@ intrusive_ptr<Expression> ExpressionNary::optimize() {
// back the operands.
if (!isCommutative()) {
if (constExpressions.size() > 1) {
- ExpressionVector vpOperandSave = std::move(vpOperand);
- vpOperand = std::move(constExpressions);
+ ExpressionVector childrenSave = std::move(_children);
+ _children = std::move(constExpressions);
optimizedOperands.emplace_back(
ExpressionConstant::create(getExpressionContext(), evaluate(Document())));
- vpOperand = std::move(vpOperandSave);
+ _children = std::move(childrenSave);
} else {
optimizedOperands.insert(
optimizedOperands.end(), constExpressions.begin(), constExpressions.end());
@@ -3143,7 +3184,7 @@ intrusive_ptr<Expression> ExpressionNary::optimize() {
}
if (constExpressions.size() > 1) {
- vpOperand = std::move(constExpressions);
+ _children = std::move(constExpressions);
optimizedOperands.emplace_back(
ExpressionConstant::create(getExpressionContext(), evaluate(Document())));
} else {
@@ -3151,27 +3192,27 @@ intrusive_ptr<Expression> ExpressionNary::optimize() {
optimizedOperands.end(), constExpressions.begin(), constExpressions.end());
}
- vpOperand = std::move(optimizedOperands);
+ _children = std::move(optimizedOperands);
}
return this;
}
void ExpressionNary::_doAddDependencies(DepsTracker* deps) const {
- for (auto&& operand : vpOperand) {
+ for (auto&& operand : _children) {
operand->addDependencies(deps);
}
}
void ExpressionNary::addOperand(const intrusive_ptr<Expression>& pExpression) {
- vpOperand.push_back(pExpression);
+ _children.push_back(pExpression);
}
Value ExpressionNary::serialize(bool explain) const {
- const size_t nOperand = vpOperand.size();
+ const size_t nOperand = _children.size();
vector<Value> array;
/* build up the array */
for (size_t i = 0; i < nOperand; i++)
- array.push_back(vpOperand[i]->serialize(explain));
+ array.push_back(_children[i]->serialize(explain));
return Value(DOC(getOpName() << array));
}
@@ -3179,7 +3220,7 @@ Value ExpressionNary::serialize(bool explain) const {
/* ------------------------- ExpressionNot ----------------------------- */
Value ExpressionNot::evaluate(const Document& root) const {
- Value pOp(vpOperand[0]->evaluate(root));
+ Value pOp(_children[0]->evaluate(root));
bool b = pOp.coerceToBool();
return Value(!b);
@@ -3193,9 +3234,9 @@ const char* ExpressionNot::getOpName() const {
/* -------------------------- ExpressionOr ----------------------------- */
Value ExpressionOr::evaluate(const Document& root) const {
- const size_t n = vpOperand.size();
+ const size_t n = _children.size();
for (size_t i = 0; i < n; ++i) {
- Value pValue(vpOperand[i]->evaluate(root));
+ Value pValue(_children[i]->evaluate(root));
if (pValue.coerceToBool())
return Value(true);
}
@@ -3217,10 +3258,10 @@ intrusive_ptr<Expression> ExpressionOr::optimize() {
promised by ExpressionNary::optimize(),) then there's nothing
we can do.
*/
- const size_t n = pOr->vpOperand.size();
+ const size_t n = pOr->_children.size();
// ExpressionNary::optimize() generates an ExpressionConstant for {$or:[]}.
verify(n > 0);
- intrusive_ptr<Expression> pLast(pOr->vpOperand[n - 1]);
+ intrusive_ptr<Expression> pLast(pOr->_children[n - 1]);
const ExpressionConstant* pConst = dynamic_cast<ExpressionConstant*>(pLast.get());
if (!pConst)
return pE;
@@ -3244,14 +3285,14 @@ intrusive_ptr<Expression> ExpressionOr::optimize() {
*/
if (n == 2) {
intrusive_ptr<Expression> pFinal(
- ExpressionCoerceToBool::create(getExpressionContext(), pOr->vpOperand[0]));
+ ExpressionCoerceToBool::create(getExpressionContext(), std::move(pOr->_children[0])));
return pFinal;
}
/*
Remove the final "false" value, and return the new expression.
*/
- pOr->vpOperand.resize(n - 1);
+ pOr->_children.resize(n - 1);
return pE;
}
@@ -3358,16 +3399,16 @@ bool representableAsLong(long long base, long long exp) {
intrusive_ptr<Expression> ExpressionPow::create(
const boost::intrusive_ptr<ExpressionContext>& expCtx, Value base, Value exp) {
intrusive_ptr<ExpressionPow> expr(new ExpressionPow(expCtx));
- expr->vpOperand.push_back(
+ expr->_children.push_back(
ExpressionConstant::create(expr->getExpressionContext(), std::move(base)));
- expr->vpOperand.push_back(
+ expr->_children.push_back(
ExpressionConstant::create(expr->getExpressionContext(), std::move(exp)));
return expr;
}
Value ExpressionPow::evaluate(const Document& root) const {
- Value baseVal = vpOperand[0]->evaluate(root);
- Value expVal = vpOperand[1]->evaluate(root);
+ Value baseVal = _children[0]->evaluate(root);
+ Value expVal = _children[1]->evaluate(root);
if (baseVal.nullish() || expVal.nullish())
return Value(BSONNULL);
@@ -3484,8 +3525,8 @@ const char* ExpressionPow::getOpName() const {
/* ------------------------- ExpressionRange ------------------------------ */
Value ExpressionRange::evaluate(const Document& root) const {
- Value startVal(vpOperand[0]->evaluate(root));
- Value endVal(vpOperand[1]->evaluate(root));
+ Value startVal(_children[0]->evaluate(root));
+ Value endVal(_children[1]->evaluate(root));
uassert(34443,
str::stream() << "$range requires a numeric starting value, found value of type: "
@@ -3510,9 +3551,9 @@ Value ExpressionRange::evaluate(const Document& root) const {
int end = endVal.coerceToInt();
int step = 1;
- if (vpOperand.size() == 3) {
+ if (_children.size() == 3) {
// A step was specified by the user.
- Value stepVal(vpOperand[2]->evaluate(root));
+ Value stepVal(_children[2]->evaluate(root));
uassert(34447,
str::stream() << "$range requires a numeric step value, found value of type:"
@@ -3555,32 +3596,35 @@ intrusive_ptr<Expression> ExpressionReduce::parse(
<< typeName(expr.type()),
expr.type() == Object);
- intrusive_ptr<ExpressionReduce> reduce(new ExpressionReduce(expCtx));
// vpsSub is used only to parse 'in', which must have access to $$this and $$value.
VariablesParseState vpsSub(vps);
- reduce->_thisVar = vpsSub.defineVariable("this");
- reduce->_valueVar = vpsSub.defineVariable("value");
+ auto thisVar = vpsSub.defineVariable("this");
+ auto valueVar = vpsSub.defineVariable("value");
+ boost::intrusive_ptr<Expression> input;
+ boost::intrusive_ptr<Expression> initial;
+ boost::intrusive_ptr<Expression> in;
for (auto&& elem : expr.Obj()) {
auto field = elem.fieldNameStringData();
if (field == "input") {
- reduce->_input = parseOperand(expCtx, elem, vps);
+ input = parseOperand(expCtx, elem, vps);
} else if (field == "initialValue") {
- reduce->_initial = parseOperand(expCtx, elem, vps);
+ initial = parseOperand(expCtx, elem, vps);
} else if (field == "in") {
- reduce->_in = parseOperand(expCtx, elem, vpsSub);
+ in = parseOperand(expCtx, elem, vpsSub);
} else {
uasserted(40076, str::stream() << "$reduce found an unknown argument: " << field);
}
}
- uassert(40077, "$reduce requires 'input' to be specified", reduce->_input);
- uassert(40078, "$reduce requires 'initialValue' to be specified", reduce->_initial);
- uassert(40079, "$reduce requires 'in' to be specified", reduce->_in);
+ uassert(40077, "$reduce requires 'input' to be specified", input);
+ uassert(40078, "$reduce requires 'initialValue' to be specified", initial);
+ uassert(40079, "$reduce requires 'in' to be specified", in);
- return reduce;
+ return new ExpressionReduce(
+ expCtx, std::move(input), std::move(initial), std::move(in), thisVar, valueVar);
}
Value ExpressionReduce::evaluate(const Document& root) const {
@@ -3631,7 +3675,7 @@ Value ExpressionReduce::serialize(bool explain) const {
/* ------------------------ ExpressionReverseArray ------------------------ */
Value ExpressionReverseArray::evaluate(const Document& root) const {
- Value input(vpOperand[0]->evaluate(root));
+ Value input(_children[0]->evaluate(root));
if (input.nullish()) {
return Value(BSONNULL);
@@ -3668,8 +3712,8 @@ ValueSet arrayToSet(const Value& val, const ValueComparator& valueComparator) {
/* ----------------------- ExpressionSetDifference ---------------------------- */
Value ExpressionSetDifference::evaluate(const Document& root) const {
- const Value lhs = vpOperand[0]->evaluate(root);
- const Value rhs = vpOperand[1]->evaluate(root);
+ const Value lhs = _children[0]->evaluate(root);
+ const Value rhs = _children[1]->evaluate(root);
if (lhs.nullish() || rhs.nullish()) {
return Value(BSONNULL);
@@ -3714,12 +3758,12 @@ void ExpressionSetEquals::validateArguments(const ExpressionVector& args) const
}
Value ExpressionSetEquals::evaluate(const Document& root) const {
- const size_t n = vpOperand.size();
+ const size_t n = _children.size();
const auto& valueComparator = getExpressionContext()->getValueComparator();
ValueSet lhs = valueComparator.makeOrderedValueSet();
for (size_t i = 0; i < n; i++) {
- const Value nextEntry = vpOperand[i]->evaluate(root);
+ const Value nextEntry = _children[i]->evaluate(root);
uassert(17044,
str::stream() << "All operands of $setEquals must be arrays. One "
<< "argument is of type: "
@@ -3751,11 +3795,11 @@ const char* ExpressionSetEquals::getOpName() const {
/* ----------------------- ExpressionSetIntersection ---------------------------- */
Value ExpressionSetIntersection::evaluate(const Document& root) const {
- const size_t n = vpOperand.size();
+ const size_t n = _children.size();
const auto& valueComparator = getExpressionContext()->getValueComparator();
ValueSet currentIntersection = valueComparator.makeOrderedValueSet();
for (size_t i = 0; i < n; i++) {
- const Value nextEntry = vpOperand[i]->evaluate(root);
+ const Value nextEntry = _children[i]->evaluate(root);
if (nextEntry.nullish()) {
return Value(BSONNULL);
}
@@ -3812,8 +3856,8 @@ Value setIsSubsetHelper(const vector<Value>& lhs, const ValueSet& rhs) {
}
Value ExpressionSetIsSubset::evaluate(const Document& root) const {
- const Value lhs = vpOperand[0]->evaluate(root);
- const Value rhs = vpOperand[1]->evaluate(root);
+ const Value lhs = _children[0]->evaluate(root);
+ const Value rhs = _children[1]->evaluate(root);
uassert(17046,
str::stream() << "both operands of $setIsSubset must be arrays. First "
@@ -3843,11 +3887,11 @@ public:
const ValueSet& cachedRhsSet,
const ExpressionVector& operands)
: ExpressionSetIsSubset(expCtx), _cachedRhsSet(cachedRhsSet) {
- vpOperand = operands;
+ _children = operands;
}
virtual Value evaluate(const Document& root) const {
- const Value lhs = vpOperand[0]->evaluate(root);
+ const Value lhs = _children[0]->evaluate(root);
uassert(17310,
str::stream() << "both operands of $setIsSubset must be arrays. First "
@@ -3870,7 +3914,7 @@ intrusive_ptr<Expression> ExpressionSetIsSubset::optimize() {
if (optimized.get() != this)
return optimized;
- if (ExpressionConstant* ec = dynamic_cast<ExpressionConstant*>(vpOperand[1].get())) {
+ if (ExpressionConstant* ec = dynamic_cast<ExpressionConstant*>(_children[1].get())) {
const Value rhs = ec->getValue();
uassert(17311,
str::stream() << "both operands of $setIsSubset must be arrays. Second "
@@ -3881,7 +3925,7 @@ intrusive_ptr<Expression> ExpressionSetIsSubset::optimize() {
intrusive_ptr<Expression> optimizedWithConstant(
new Optimized(this->getExpressionContext(),
arrayToSet(rhs, getExpressionContext()->getValueComparator()),
- vpOperand));
+ _children));
return optimizedWithConstant;
}
return optimized;
@@ -3896,9 +3940,9 @@ const char* ExpressionSetIsSubset::getOpName() const {
Value ExpressionSetUnion::evaluate(const Document& root) const {
ValueSet unionedSet = getExpressionContext()->getValueComparator().makeOrderedValueSet();
- const size_t n = vpOperand.size();
+ const size_t n = _children.size();
for (size_t i = 0; i < n; i++) {
- const Value newEntries = vpOperand[i]->evaluate(root);
+ const Value newEntries = _children[i]->evaluate(root);
if (newEntries.nullish()) {
return Value(BSONNULL);
}
@@ -3921,7 +3965,7 @@ const char* ExpressionSetUnion::getOpName() const {
/* ----------------------- ExpressionIsArray ---------------------------- */
Value ExpressionIsArray::evaluate(const Document& root) const {
- Value argument = vpOperand[0]->evaluate(root);
+ Value argument = _children[0]->evaluate(root);
return Value(argument.isArray());
}
@@ -3933,11 +3977,11 @@ const char* ExpressionIsArray::getOpName() const {
/* ----------------------- ExpressionSlice ---------------------------- */
Value ExpressionSlice::evaluate(const Document& root) const {
- const size_t n = vpOperand.size();
+ const size_t n = _children.size();
- Value arrayVal = vpOperand[0]->evaluate(root);
+ Value arrayVal = _children[0]->evaluate(root);
// Could be either a start index or the length from 0.
- Value arg2 = vpOperand[1]->evaluate(root);
+ Value arg2 = _children[1]->evaluate(root);
if (arrayVal.nullish() || arg2.nullish()) {
return Value(BSONNULL);
@@ -3988,7 +4032,7 @@ Value ExpressionSlice::evaluate(const Document& root) const {
start = std::min(array.size(), size_t(startInt));
}
- Value countVal = vpOperand[2]->evaluate(root);
+ Value countVal = _children[2]->evaluate(root);
if (countVal.nullish()) {
return Value(BSONNULL);
@@ -4024,7 +4068,7 @@ const char* ExpressionSlice::getOpName() const {
/* ----------------------- ExpressionSize ---------------------------- */
Value ExpressionSize::evaluate(const Document& root) const {
- Value array = vpOperand[0]->evaluate(root);
+ Value array = _children[0]->evaluate(root);
uassert(17124,
str::stream() << "The argument to $size must be an array, but was of type: "
@@ -4041,8 +4085,8 @@ const char* ExpressionSize::getOpName() const {
/* ----------------------- ExpressionSplit --------------------------- */
Value ExpressionSplit::evaluate(const Document& root) const {
- Value inputArg = vpOperand[0]->evaluate(root);
- Value separatorArg = vpOperand[1]->evaluate(root);
+ Value inputArg = _children[0]->evaluate(root);
+ Value separatorArg = _children[1]->evaluate(root);
if (inputArg.nullish() || separatorArg.nullish()) {
return Value(BSONNULL);
@@ -4119,8 +4163,8 @@ const char* ExpressionSqrt::getOpName() const {
/* ----------------------- ExpressionStrcasecmp ---------------------------- */
Value ExpressionStrcasecmp::evaluate(const Document& root) const {
- Value pString1(vpOperand[0]->evaluate(root));
- Value pString2(vpOperand[1]->evaluate(root));
+ Value pString1(_children[0]->evaluate(root));
+ Value pString2(_children[1]->evaluate(root));
/* boost::iequals returns a bool not an int so strings must actually be allocated */
string str1 = boost::to_upper_copy(pString1.coerceToString());
@@ -4143,9 +4187,9 @@ const char* ExpressionStrcasecmp::getOpName() const {
/* ----------------------- ExpressionSubstrBytes ---------------------------- */
Value ExpressionSubstrBytes::evaluate(const Document& root) const {
- Value pString(vpOperand[0]->evaluate(root));
- Value pLower(vpOperand[1]->evaluate(root));
- Value pLength(vpOperand[2]->evaluate(root));
+ Value pString(_children[0]->evaluate(root));
+ Value pLower(_children[1]->evaluate(root));
+ Value pLength(_children[2]->evaluate(root));
string str = pString.coerceToString();
uassert(16034,
@@ -4208,9 +4252,9 @@ const char* ExpressionSubstrBytes::getOpName() const {
/* ----------------------- ExpressionSubstrCP ---------------------------- */
Value ExpressionSubstrCP::evaluate(const Document& root) const {
- Value inputVal(vpOperand[0]->evaluate(root));
- Value lowerVal(vpOperand[1]->evaluate(root));
- Value lengthVal(vpOperand[2]->evaluate(root));
+ Value inputVal(_children[0]->evaluate(root));
+ Value lowerVal(_children[1]->evaluate(root));
+ Value lengthVal(_children[2]->evaluate(root));
std::string str = inputVal.coerceToString();
uassert(34450,
@@ -4283,7 +4327,7 @@ const char* ExpressionSubstrCP::getOpName() const {
/* ----------------------- ExpressionStrLenBytes ------------------------- */
Value ExpressionStrLenBytes::evaluate(const Document& root) const {
- Value str(vpOperand[0]->evaluate(root));
+ Value str(_children[0]->evaluate(root));
uassert(34473,
str::stream() << "$strLenBytes requires a string argument, found: "
@@ -4306,7 +4350,7 @@ const char* ExpressionStrLenBytes::getOpName() const {
/* ----------------------- ExpressionStrLenCP ------------------------- */
Value ExpressionStrLenCP::evaluate(const Document& root) const {
- Value val(vpOperand[0]->evaluate(root));
+ Value val(_children[0]->evaluate(root));
uassert(34471,
str::stream() << "$strLenCP requires a string argument, found: "
@@ -4331,8 +4375,8 @@ const char* ExpressionStrLenCP::getOpName() const {
/* ----------------------- ExpressionSubtract ---------------------------- */
Value ExpressionSubtract::evaluate(const Document& root) const {
- Value lhs = vpOperand[0]->evaluate(root);
- Value rhs = vpOperand[1]->evaluate(root);
+ Value lhs = _children[0]->evaluate(root);
+ Value rhs = _children[1]->evaluate(root);
BSONType diffType = Value::getWidestNumeric(rhs.getType(), lhs.getType());
@@ -4405,8 +4449,8 @@ boost::intrusive_ptr<Expression> ExpressionSwitch::parse(
<< typeName(expr.type()),
expr.type() == Object);
- intrusive_ptr<ExpressionSwitch> expression(new ExpressionSwitch(expCtx));
-
+ boost::intrusive_ptr<Expression> expDefault;
+ std::vector<boost::intrusive_ptr<Expression>> children;
for (auto&& elem : expr.Obj()) {
auto field = elem.fieldNameStringData();
@@ -4423,15 +4467,15 @@ boost::intrusive_ptr<Expression> ExpressionSwitch::parse(
<< typeName(branch.type()),
branch.type() == Object);
- ExpressionPair branchExpression;
+ boost::intrusive_ptr<Expression> switchCase, switchThen;
for (auto&& branchElement : branch.Obj()) {
auto branchField = branchElement.fieldNameStringData();
if (branchField == "case") {
- branchExpression.first = parseOperand(expCtx, branchElement, vps);
+ switchCase = parseOperand(expCtx, branchElement, vps);
} else if (branchField == "then") {
- branchExpression.second = parseOperand(expCtx, branchElement, vps);
+ switchThen = parseOperand(expCtx, branchElement, vps);
} else {
uasserted(40063,
str::stream() << "$switch found an unknown argument to a branch: "
@@ -4439,26 +4483,37 @@ boost::intrusive_ptr<Expression> ExpressionSwitch::parse(
}
}
- uassert(40064,
- "$switch requires each branch have a 'case' expression",
- branchExpression.first);
- uassert(40065,
- "$switch requires each branch have a 'then' expression.",
- branchExpression.second);
+ uassert(40064, "$switch requires each branch have a 'case' expression", switchCase);
+ uassert(
+ 40065, "$switch requires each branch have a 'then' expression.", switchThen);
- expression->_branches.push_back(branchExpression);
+ children.push_back(std::move(switchCase));
+ children.push_back(std::move(switchThen));
}
} else if (field == "default") {
// Optional, arbitrary expression.
- expression->_default = parseOperand(expCtx, elem, vps);
+ expDefault = parseOperand(expCtx, elem, vps);
} else {
uasserted(40067, str::stream() << "$switch found an unknown argument: " << field);
}
}
+ children.push_back(std::move(expDefault));
+ // Obtain references to the case and branch expressions two-by-two from the children vector,
+ // ignore the last.
+ std::vector<ExpressionPair> branches;
+ boost::optional<boost::intrusive_ptr<Expression>&> first;
+ for (auto&& child : children) {
+ if (first) {
+ branches.emplace_back(*first, child);
+ first = boost::none;
+ } else {
+ first = child;
+ }
+ }
- uassert(40068, "$switch requires at least one branch.", !expression->_branches.empty());
+ uassert(40068, "$switch requires at least one branch.", !branches.empty());
- return expression;
+ return new ExpressionSwitch(expCtx, std::move(children), std::move(branches));
}
void ExpressionSwitch::_doAddDependencies(DepsTracker* deps) const {
@@ -4477,12 +4532,10 @@ boost::intrusive_ptr<Expression> ExpressionSwitch::optimize() {
_default = _default->optimize();
}
- std::transform(_branches.begin(),
- _branches.end(),
- _branches.begin(),
- [](ExpressionPair branch) -> ExpressionPair {
- return {branch.first->optimize(), branch.second->optimize()};
- });
+ for (auto&& [switchCase, switchThen] : _branches) {
+ switchCase = switchCase->optimize();
+ switchThen = switchThen->optimize();
+ }
return this;
}
@@ -4508,7 +4561,7 @@ Value ExpressionSwitch::serialize(bool explain) const {
/* ------------------------- ExpressionToLower ----------------------------- */
Value ExpressionToLower::evaluate(const Document& root) const {
- Value pString(vpOperand[0]->evaluate(root));
+ Value pString(_children[0]->evaluate(root));
string str = pString.coerceToString();
boost::to_lower(str);
return Value(str);
@@ -4522,7 +4575,7 @@ const char* ExpressionToLower::getOpName() const {
/* ------------------------- ExpressionToUpper -------------------------- */
Value ExpressionToUpper::evaluate(const Document& root) const {
- Value pString(vpOperand[0]->evaluate(root));
+ Value pString(_children[0]->evaluate(root));
string str(pString.coerceToString());
boost::to_upper(str);
return Value(str);
@@ -4776,13 +4829,13 @@ void assertFlagsValid(uint32_t flags,
}
static Value evaluateRoundOrTrunc(const Document& root,
- const std::vector<boost::intrusive_ptr<Expression>>& vpOperand,
+ const std::vector<boost::intrusive_ptr<Expression>>& children,
const std::string& opName,
Decimal128::RoundingMode roundingMode,
double (*doubleOp)(double)) {
constexpr auto maxPrecision = 100LL;
constexpr auto minPrecision = -20LL;
- auto numericArg = Value(vpOperand[0]->evaluate(root));
+ auto numericArg = Value(children[0]->evaluate(root));
if (numericArg.nullish()) {
return Value(BSONNULL);
}
@@ -4790,7 +4843,7 @@ static Value evaluateRoundOrTrunc(const Document& root,
str::stream() << opName << " only supports numeric types, not "
<< typeName(numericArg.getType()),
numericArg.numeric());
- if (vpOperand.size() == 1) {
+ if (children.size() == 1) {
switch (numericArg.getType()) {
case BSONType::NumberDecimal:
return Value(
@@ -4804,7 +4857,7 @@ static Value evaluateRoundOrTrunc(const Document& root,
}
}
// Else, if precision is specified, round to the specified precision.
- auto precisionArg = Value(vpOperand[1]->evaluate(root));
+ auto precisionArg = Value(children[1]->evaluate(root));
if (precisionArg.nullish()) {
return Value(BSONNULL);
}
@@ -4859,7 +4912,7 @@ static Value evaluateRoundOrTrunc(const Document& root,
Value ExpressionRound::evaluate(const Document& root) const {
return evaluateRoundOrTrunc(
- root, vpOperand, getOpName(), Decimal128::kRoundTiesToEven, &std::round);
+ root, _children, getOpName(), Decimal128::kRoundTiesToEven, &std::round);
}
REGISTER_EXPRESSION(round, ExpressionRound::parse);
@@ -4869,7 +4922,7 @@ const char* ExpressionRound::getOpName() const {
Value ExpressionTrunc::evaluate(const Document& root) const {
return evaluateRoundOrTrunc(
- root, vpOperand, getOpName(), Decimal128::kRoundTowardZero, &std::trunc);
+ root, _children, getOpName(), Decimal128::kRoundTowardZero, &std::trunc);
}
REGISTER_EXPRESSION(trunc, ExpressionTrunc::parse);
@@ -4880,7 +4933,7 @@ const char* ExpressionTrunc::getOpName() const {
/* ------------------------- ExpressionType ----------------------------- */
Value ExpressionType::evaluate(const Document& root) const {
- Value val(vpOperand[0]->evaluate(root));
+ Value val(_children[0]->evaluate(root));
return Value(StringData(typeName(val.getType())));
}
@@ -4901,7 +4954,11 @@ intrusive_ptr<Expression> ExpressionZip::parse(
<< typeName(expr.type()),
expr.type() == Object);
- intrusive_ptr<ExpressionZip> newZip(new ExpressionZip(expCtx));
+ auto useLongestLength = false;
+ std::vector<boost::intrusive_ptr<Expression>> children;
+ // We need to ensure defaults appear after inputs so we build them seperately and then
+ // concatenate them.
+ std::vector<boost::intrusive_ptr<Expression>> tempDefaultChildren;
for (auto&& elem : expr.Obj()) {
const auto field = elem.fieldNameStringData();
@@ -4911,7 +4968,7 @@ intrusive_ptr<Expression> ExpressionZip::parse(
<< typeName(elem.type()),
elem.type() == Array);
for (auto&& subExpr : elem.Array()) {
- newZip->_inputs.push_back(parseOperand(expCtx, subExpr, vps));
+ children.push_back(parseOperand(expCtx, subExpr, vps));
}
} else if (field == "defaults") {
uassert(34462,
@@ -4919,29 +4976,44 @@ intrusive_ptr<Expression> ExpressionZip::parse(
<< typeName(elem.type()),
elem.type() == Array);
for (auto&& subExpr : elem.Array()) {
- newZip->_defaults.push_back(parseOperand(expCtx, subExpr, vps));
+ tempDefaultChildren.push_back(parseOperand(expCtx, subExpr, vps));
}
} else if (field == "useLongestLength") {
uassert(34463,
str::stream() << "useLongestLength must be a bool, found "
<< typeName(expr.type()),
elem.type() == Bool);
- newZip->_useLongestLength = elem.Bool();
+ useLongestLength = elem.Bool();
} else {
uasserted(34464,
str::stream() << "$zip found an unknown argument: " << elem.fieldName());
}
}
- uassert(34465, "$zip requires at least one input array", !newZip->_inputs.empty());
+ auto numInputs = children.size();
+ std::move(tempDefaultChildren.begin(), tempDefaultChildren.end(), std::back_inserter(children));
+
+ std::vector<std::reference_wrapper<boost::intrusive_ptr<Expression>>> inputs;
+ std::vector<std::reference_wrapper<boost::intrusive_ptr<Expression>>> defaults;
+ for (auto&& child : children) {
+ if (numInputs == 0) {
+ defaults.push_back(child);
+ } else {
+ inputs.push_back(child);
+ numInputs--;
+ }
+ }
+
+ uassert(34465, "$zip requires at least one input array", !inputs.empty());
uassert(34466,
"cannot specify defaults unless useLongestLength is true",
- (newZip->_useLongestLength || newZip->_defaults.empty()));
+ (useLongestLength || defaults.empty()));
uassert(34467,
"defaults and inputs must have the same length",
- (newZip->_defaults.empty() || newZip->_defaults.size() == newZip->_inputs.size()));
+ (defaults.empty() || defaults.size() == inputs.size()));
- return std::move(newZip);
+ return new ExpressionZip(
+ expCtx, useLongestLength, std::move(children), std::move(inputs), std::move(defaults));
}
Value ExpressionZip::evaluate(const Document& root) const {
@@ -4952,7 +5024,7 @@ Value ExpressionZip::evaluate(const Document& root) const {
size_t minArraySize = 0;
size_t maxArraySize = 0;
for (size_t i = 0; i < _inputs.size(); i++) {
- Value evalExpr = _inputs[i]->evaluate(root);
+ Value evalExpr = _inputs[i].get()->evaluate(root);
if (evalExpr.nullish()) {
return Value(BSONNULL);
}
@@ -4981,7 +5053,7 @@ Value ExpressionZip::evaluate(const Document& root) const {
// If we need default values, evaluate each expression.
if (minArraySize != maxArraySize) {
for (size_t i = 0; i < _defaults.size(); i++) {
- evaluatedDefaults[i] = _defaults[i]->evaluate(root);
+ evaluatedDefaults[i] = _defaults[i].get()->evaluate(root);
}
}
@@ -5014,20 +5086,10 @@ Value ExpressionZip::evaluate(const Document& root) const {
}
boost::intrusive_ptr<Expression> ExpressionZip::optimize() {
- std::transform(_inputs.begin(),
- _inputs.end(),
- _inputs.begin(),
- [](intrusive_ptr<Expression> inputExpression) -> intrusive_ptr<Expression> {
- return inputExpression->optimize();
- });
-
- std::transform(_defaults.begin(),
- _defaults.end(),
- _defaults.begin(),
- [](intrusive_ptr<Expression> defaultExpression) -> intrusive_ptr<Expression> {
- return defaultExpression->optimize();
- });
-
+ for (auto&& input : _inputs)
+ input.get() = input.get()->optimize();
+ for (auto&& zipDefault : _defaults)
+ zipDefault.get() = zipDefault.get()->optimize();
return this;
}
@@ -5037,11 +5099,11 @@ Value ExpressionZip::serialize(bool explain) const {
Value serializedUseLongestLength = Value(_useLongestLength);
for (auto&& expr : _inputs) {
- serializedInput.push_back(expr->serialize(explain));
+ serializedInput.push_back(expr.get()->serialize(explain));
}
for (auto&& expr : _defaults) {
- serializedDefaults.push_back(expr->serialize(explain));
+ serializedDefaults.push_back(expr.get()->serialize(explain));
}
return Value(DOC("$zip" << DOC("inputs" << Value(serializedInput) << "defaults"
@@ -5496,7 +5558,7 @@ Expression::Parser makeConversionAlias(const StringData shortcutName, BSONType t
str::stream() << shortcutName << " requires a single argument, got "
<< operands.size(),
operands.size() == 1);
- return ExpressionConvert::create(expCtx, operands[0], toType);
+ return ExpressionConvert::create(expCtx, std::move(operands[0]), toType);
};
}
@@ -5517,17 +5579,26 @@ REGISTER_EXPRESSION(toBool, makeConversionAlias("$toBool"_sd, BSONType::Bool));
boost::intrusive_ptr<Expression> ExpressionConvert::create(
const boost::intrusive_ptr<ExpressionContext>& expCtx,
- const boost::intrusive_ptr<Expression>& input,
+ boost::intrusive_ptr<Expression> input,
BSONType toType) {
- return new ExpressionConvert(expCtx, input, toType);
+ return new ExpressionConvert(
+ expCtx,
+ std::move(input),
+ ExpressionConstant::create(expCtx, Value(StringData(typeName(toType)))),
+ nullptr,
+ nullptr);
}
ExpressionConvert::ExpressionConvert(const boost::intrusive_ptr<ExpressionContext>& expCtx,
- const boost::intrusive_ptr<Expression>& input,
- BSONType toType)
- : Expression(expCtx),
- _input(input),
- _to(ExpressionConstant::create(expCtx, Value(StringData(typeName(toType))))) {}
+ boost::intrusive_ptr<Expression> input,
+ boost::intrusive_ptr<Expression> to,
+ boost::intrusive_ptr<Expression> onError,
+ boost::intrusive_ptr<Expression> onNull)
+ : Expression(expCtx, {std::move(input), std::move(to), std::move(onError), std::move(onNull)}),
+ _input(_children[0]),
+ _to(_children[1]),
+ _onError(_children[2]),
+ _onNull(_children[3]) {}
intrusive_ptr<Expression> ExpressionConvert::parse(
const boost::intrusive_ptr<ExpressionContext>& expCtx,
@@ -5538,18 +5609,20 @@ intrusive_ptr<Expression> ExpressionConvert::parse(
<< typeName(expr.type()),
expr.type() == BSONType::Object);
- intrusive_ptr<ExpressionConvert> newConvert(new ExpressionConvert(expCtx));
-
+ boost::intrusive_ptr<Expression> input;
+ boost::intrusive_ptr<Expression> to;
+ boost::intrusive_ptr<Expression> onError;
+ boost::intrusive_ptr<Expression> onNull;
for (auto&& elem : expr.embeddedObject()) {
const auto field = elem.fieldNameStringData();
if (field == "input"_sd) {
- newConvert->_input = parseOperand(expCtx, elem, vps);
+ input = parseOperand(expCtx, elem, vps);
} else if (field == "to"_sd) {
- newConvert->_to = parseOperand(expCtx, elem, vps);
+ to = parseOperand(expCtx, elem, vps);
} else if (field == "onError"_sd) {
- newConvert->_onError = parseOperand(expCtx, elem, vps);
+ onError = parseOperand(expCtx, elem, vps);
} else if (field == "onNull"_sd) {
- newConvert->_onNull = parseOperand(expCtx, elem, vps);
+ onNull = parseOperand(expCtx, elem, vps);
} else {
uasserted(ErrorCodes::FailedToParse,
str::stream() << "$convert found an unknown argument: "
@@ -5557,10 +5630,11 @@ intrusive_ptr<Expression> ExpressionConvert::parse(
}
}
- uassert(ErrorCodes::FailedToParse, "Missing 'input' parameter to $convert", newConvert->_input);
- uassert(ErrorCodes::FailedToParse, "Missing 'to' parameter to $convert", newConvert->_to);
+ uassert(ErrorCodes::FailedToParse, "Missing 'input' parameter to $convert", input);
+ uassert(ErrorCodes::FailedToParse, "Missing 'to' parameter to $convert", to);
- return std::move(newConvert);
+ return new ExpressionConvert(
+ expCtx, std::move(input), std::move(to), std::move(onError), std::move(onNull));
}
Value ExpressionConvert::evaluate(const Document& root) const {
@@ -5670,36 +5744,46 @@ Value ExpressionConvert::performConversion(BSONType targetType, Value inputValue
return table.findConversionFunc(inputType, targetType)(getExpressionContext(), inputValue);
}
-/* -------------------------- ExpressionRegex ------------------------------ */
+namespace {
-ExpressionRegex::ExpressionRegex(const boost::intrusive_ptr<ExpressionContext>& expCtx,
- BSONElement expr,
- const VariablesParseState& vpsIn,
- const std::string& opName)
- : Expression(expCtx), _opName(std::move(opName)) {
+auto CommonRegexParse(const boost::intrusive_ptr<ExpressionContext>& expCtx,
+ BSONElement expr,
+ const VariablesParseState& vpsIn,
+ StringData opName) {
uassert(51103,
- str::stream() << _opName << " expects an object of named arguments but found: "
+ str::stream() << opName << " expects an object of named arguments but found: "
<< expr.type(),
expr.type() == BSONType::Object);
+ struct {
+ boost::intrusive_ptr<Expression> input;
+ boost::intrusive_ptr<Expression> regex;
+ boost::intrusive_ptr<Expression> options;
+ } parsed;
for (auto&& elem : expr.embeddedObject()) {
const auto field = elem.fieldNameStringData();
if (field == "input"_sd) {
- _input = parseOperand(expCtx, elem, vpsIn);
+ parsed.input = Expression::parseOperand(expCtx, elem, vpsIn);
} else if (field == "regex"_sd) {
- _regex = parseOperand(expCtx, elem, vpsIn);
+ parsed.regex = Expression::parseOperand(expCtx, elem, vpsIn);
} else if (field == "options"_sd) {
- _options = parseOperand(expCtx, elem, vpsIn);
+ parsed.options = Expression::parseOperand(expCtx, elem, vpsIn);
} else {
uasserted(31024,
- str::stream() << _opName << " found an unknown argument: "
+ str::stream() << opName << " found an unknown argument: "
<< elem.fieldNameStringData());
}
}
- uassert(31022, str::stream() << _opName << " requires 'input' parameter", _input);
- uassert(31023, str::stream() << _opName << " requires 'regex' parameter", _regex);
+ uassert(31022, str::stream() << opName << " requires 'input' parameter", parsed.input);
+ uassert(31023, str::stream() << opName << " requires 'regex' parameter", parsed.regex);
+
+ return parsed;
}
+} // namespace
+
+/* -------------------------- ExpressionRegex ------------------------------ */
+
ExpressionRegex::RegexExecutionState ExpressionRegex::buildInitialState(
const Document& root) const {
Value textInput = _input->evaluate(root);
@@ -5922,6 +6006,17 @@ void ExpressionRegex::_doAddDependencies(DepsTracker* deps) const {
/* -------------------------- ExpressionRegexFind ------------------------------ */
+REGISTER_EXPRESSION(regexFind, ExpressionRegexFind::parse);
+boost::intrusive_ptr<Expression> ExpressionRegexFind::parse(
+ const boost::intrusive_ptr<ExpressionContext>& expCtx,
+ BSONElement expr,
+ const VariablesParseState& vpsIn) {
+ auto opName = "$regexFind"_sd;
+ auto[input, regex, options] = CommonRegexParse(expCtx, expr, vpsIn, opName);
+ return new ExpressionRegexFind(
+ expCtx, std::move(input), std::move(regex), std::move(options), opName);
+}
+
Value ExpressionRegexFind::evaluate(const Document& root) const {
auto executionState = buildInitialState(root);
if (executionState.nullish()) {
@@ -5930,10 +6025,19 @@ Value ExpressionRegexFind::evaluate(const Document& root) const {
return nextMatch(&executionState);
}
-REGISTER_EXPRESSION(regexFind, ExpressionRegexFind::parse);
-
/* -------------------------- ExpressionRegexFindAll ------------------------------ */
+REGISTER_EXPRESSION(regexFindAll, ExpressionRegexFindAll::parse);
+boost::intrusive_ptr<Expression> ExpressionRegexFindAll::parse(
+ const boost::intrusive_ptr<ExpressionContext>& expCtx,
+ BSONElement expr,
+ const VariablesParseState& vpsIn) {
+ auto opName = "$regexFindAll"_sd;
+ auto[input, regex, options] = CommonRegexParse(expCtx, expr, vpsIn, opName);
+ return new ExpressionRegexFindAll(
+ expCtx, std::move(input), std::move(regex), std::move(options), opName);
+}
+
Value ExpressionRegexFindAll::evaluate(const Document& root) const {
std::vector<Value> output;
auto executionState = buildInitialState(root);
@@ -5982,16 +6086,23 @@ Value ExpressionRegexFindAll::evaluate(const Document& root) const {
return Value(output);
}
-REGISTER_EXPRESSION(regexFindAll, ExpressionRegexFindAll::parse);
-
/* -------------------------- ExpressionRegexMatch ------------------------------ */
+REGISTER_EXPRESSION(regexMatch, ExpressionRegexMatch::parse);
+boost::intrusive_ptr<Expression> ExpressionRegexMatch::parse(
+ const boost::intrusive_ptr<ExpressionContext>& expCtx,
+ BSONElement expr,
+ const VariablesParseState& vpsIn) {
+ auto opName = "$regexMatch"_sd;
+ auto[input, regex, options] = CommonRegexParse(expCtx, expr, vpsIn, opName);
+ return new ExpressionRegexMatch(
+ expCtx, std::move(input), std::move(regex), std::move(options), opName);
+}
+
Value ExpressionRegexMatch::evaluate(const Document& root) const {
auto executionState = buildInitialState(root);
// Return output of execute only if regex is not nullish.
return executionState.nullish() ? Value(false) : Value(execute(&executionState) > 0);
}
-REGISTER_EXPRESSION(regexMatch, ExpressionRegexMatch::parse);
-
} // namespace mongo
diff --git a/src/mongo/db/pipeline/expression.h b/src/mongo/db/pipeline/expression.h
index 9bf881ea241..47829732ee4 100644
--- a/src/mongo/db/pipeline/expression.h
+++ b/src/mongo/db/pipeline/expression.h
@@ -36,6 +36,7 @@
#include <map>
#include <pcre.h>
#include <string>
+#include <utility>
#include <vector>
#include "mongo/base/init.h"
@@ -240,15 +241,23 @@ public:
Parser parser,
boost::optional<ServerGlobalParams::FeatureCompatibility::Version> requiredMinVersion);
+ const auto& getChildren() const {
+ return _children;
+ }
+
protected:
- Expression(const boost::intrusive_ptr<ExpressionContext>& expCtx) : _expCtx(expCtx) {
+ using ExpressionVector = std::vector<boost::intrusive_ptr<Expression>>;
+
+ Expression(const boost::intrusive_ptr<ExpressionContext>& expCtx) : Expression(expCtx, {}) {}
+
+ Expression(const boost::intrusive_ptr<ExpressionContext>& expCtx, ExpressionVector&& children)
+ : _children(std::move(children)), _expCtx(expCtx) {
auto varIds = _expCtx->variablesParseState.getDefinedVariableIDs();
if (!varIds.empty()) {
_boundaryVariableId = *std::prev(varIds.end());
}
}
- typedef std::vector<boost::intrusive_ptr<Expression>> ExpressionVector;
const boost::intrusive_ptr<ExpressionContext>& getExpressionContext() const {
return _expCtx;
@@ -256,12 +265,25 @@ protected:
virtual void _doAddDependencies(DepsTracker* deps) const = 0;
+ /**
+ * Owning container for all sub-Expressions.
+ *
+ * Some derived classes contain named fields since they orginate from user syntax containing
+ * field names. These classes contain alternate data structures or object members for accessing
+ * children. These structures or object memebers are expected to reference this data structure.
+ * In addition this structure should not be modified by named-field derivied classes to avoid
+ * invalidating references.
+ */
+ ExpressionVector _children;
+
private:
boost::optional<Variables::Id> _boundaryVariableId;
boost::intrusive_ptr<ExpressionContext> _expCtx;
};
-// Inherit from ExpressionVariadic or ExpressionFixedArity instead of directly from this class.
+/**
+ * Inherit from ExpressionVariadic or ExpressionFixedArity instead of directly from this class.
+ */
class ExpressionNary : public Expression {
public:
boost::intrusive_ptr<Expression> optimize() override;
@@ -282,16 +304,8 @@ public:
return false;
}
- /*
- Get the name of the operator.
-
- @returns the name of the operator; this std::string belongs to the class
- implementation, and should not be deleted
- and should not
- */
virtual const char* getOpName() const = 0;
- /// Allow subclasses the opportunity to validate arguments at parse time.
virtual void validateArguments(const ExpressionVector& args) const {}
static ExpressionVector parseArguments(const boost::intrusive_ptr<ExpressionContext>& expCtx,
@@ -299,7 +313,7 @@ public:
const VariablesParseState& vps);
const ExpressionVector& getOperandList() const {
- return vpOperand;
+ return _children;
}
protected:
@@ -307,8 +321,6 @@ protected:
: Expression(expCtx) {}
void _doAddDependencies(DepsTracker* deps) const override;
-
- ExpressionVector vpOperand;
};
/// Inherit from ExpressionVariadic or ExpressionFixedArity instead of directly from this class.
@@ -322,7 +334,7 @@ public:
auto expr = make_intrusive<SubClass>(expCtx);
ExpressionVector args = parseArguments(expCtx, bsonExpr, vps);
expr->validateArguments(args);
- expr->vpOperand = args;
+ expr->_children = std::move(args);
return std::move(expr);
}
@@ -391,11 +403,11 @@ public:
Value evaluate(const Document& root) const final {
Accumulator accum(this->getExpressionContext());
- const size_t n = this->vpOperand.size();
+ const auto n = this->_children.size();
// If a single array arg is given, loop through it passing each member to the accumulator.
// If a single, non-array arg is given, pass it directly to the accumulator.
if (n == 1) {
- Value singleVal = this->vpOperand[0]->evaluate(root);
+ Value singleVal = this->_children[0]->evaluate(root);
if (singleVal.getType() == Array) {
for (const Value& val : singleVal.getArray()) {
accum.process(val, false);
@@ -405,7 +417,7 @@ public:
}
} else {
// If multiple arguments are given, pass all arguments to the accumulator.
- for (auto&& argument : this->vpOperand) {
+ for (auto&& argument : this->_children) {
accum.process(argument->evaluate(root), false);
}
}
@@ -415,7 +427,7 @@ public:
bool isAssociative() const final {
// Return false if a single argument is given to avoid a single array argument being treated
// as an array instead of as a list of arguments.
- if (this->vpOperand.size() == 1) {
+ if (this->_children.size() == 1) {
return false;
}
return Accumulator(this->getExpressionContext()).isAssociative();
@@ -446,7 +458,7 @@ public:
virtual ~ExpressionSingleNumericArg() = default;
Value evaluate(const Document& root) const final {
- Value arg = this->vpOperand[0]->evaluate(root);
+ Value arg = this->_children[0]->evaluate(root);
if (arg.nullish())
return Value(BSONNULL);
@@ -480,14 +492,14 @@ public:
* 3. Call evaluateNumericArgs on the two numeric args.
*/
Value evaluate(const Document& root) const final {
- Value arg1 = this->vpOperand[0]->evaluate(root);
+ Value arg1 = this->_children[0]->evaluate(root);
if (arg1.nullish())
return Value(BSONNULL);
uassert(51044,
str::stream() << this->getOpName() << " only supports numeric types, not "
<< typeName(arg1.getType()),
arg1.numeric());
- Value arg2 = this->vpOperand[1]->evaluate(root);
+ Value arg2 = this->_children[1]->evaluate(root);
if (arg2.nullish())
return Value(BSONNULL);
uassert(51045,
@@ -631,26 +643,26 @@ public:
const boost::intrusive_ptr<ExpressionContext>& expCtx,
BSONElement operatorElem,
const VariablesParseState& variablesParseState) {
- boost::intrusive_ptr<DateExpressionAcceptingTimeZone> dateExpression{new SubClass(expCtx)};
if (operatorElem.type() == BSONType::Object) {
if (operatorElem.embeddedObject().firstElementFieldName()[0] == '$') {
// Assume this is an expression specification representing the date argument
// like {$add: [<date>, 1000]}.
- dateExpression->_date = Expression::parseObject(
- expCtx, operatorElem.embeddedObject(), variablesParseState);
- return dateExpression;
+ return new SubClass(expCtx,
+ Expression::parseObject(expCtx,
+ operatorElem.embeddedObject(),
+ variablesParseState));
} else {
// It's an object specifying the date and timezone options like {date: <date>,
// timezone: <timezone>}.
auto opName = operatorElem.fieldNameStringData();
+ boost::intrusive_ptr<Expression> date;
+ boost::intrusive_ptr<Expression> timeZone;
for (const auto& subElem : operatorElem.embeddedObject()) {
auto argName = subElem.fieldNameStringData();
if (argName == "date"_sd) {
- dateExpression->_date =
- Expression::parseOperand(expCtx, subElem, variablesParseState);
+ date = Expression::parseOperand(expCtx, subElem, variablesParseState);
} else if (argName == "timezone"_sd) {
- dateExpression->_timeZone =
- Expression::parseOperand(expCtx, subElem, variablesParseState);
+ timeZone = Expression::parseOperand(expCtx, subElem, variablesParseState);
} else {
uasserted(40535,
str::stream() << "unrecognized option to " << opName << ": \""
@@ -661,31 +673,37 @@ public:
uassert(40539,
str::stream() << "missing 'date' argument to " << opName << ", provided: "
<< operatorElem,
- dateExpression->_date);
+ date);
+ return new SubClass(expCtx, std::move(date), std::move(timeZone));
}
- return dateExpression;
} else if (operatorElem.type() == BSONType::Array) {
auto elems = operatorElem.Array();
uassert(
40536,
- str::stream() << dateExpression->_opName
+ str::stream() << operatorElem.fieldNameStringData()
<< " accepts exactly one argument if given an array, but was given "
<< elems.size(),
elems.size() == 1);
// We accept an argument wrapped in a single array. For example, either {$week: <date>}
// or {$week: [<date>]} are valid, but not {$week: [{date: <date>}]}.
- operatorElem = elems[0];
+ return new SubClass(expCtx,
+ Expression::parseOperand(expCtx, elems[0], variablesParseState));
}
- // It's some literal value, or the first element of a user-specified array. Either way it
- // should be treated as the date argument.
- dateExpression->_date = Expression::parseOperand(expCtx, operatorElem, variablesParseState);
- return dateExpression;
+ // Exhausting the other possibilities, we are left with a literal value which should be
+ // treated as the date argument.
+ return new SubClass(expCtx,
+ Expression::parseOperand(expCtx, operatorElem, variablesParseState));
}
protected:
- explicit DateExpressionAcceptingTimeZone(StringData opName,
- const boost::intrusive_ptr<ExpressionContext>& expCtx)
- : Expression(expCtx), _opName(opName) {}
+ explicit DateExpressionAcceptingTimeZone(const boost::intrusive_ptr<ExpressionContext>& expCtx,
+ const StringData opName,
+ boost::intrusive_ptr<Expression> date,
+ boost::intrusive_ptr<Expression> timeZone)
+ : Expression(expCtx, {date, timeZone}),
+ _opName(opName),
+ _date(_children[0]),
+ _timeZone(_children[1]) {}
void _doAddDependencies(DepsTracker* deps) const final {
_date->addDependencies(deps);
@@ -706,9 +724,9 @@ private:
StringData _opName;
// The expression representing the date argument.
- boost::intrusive_ptr<Expression> _date;
- // The expression representing the timezone argument, nullptr if not specified.
- boost::intrusive_ptr<Expression> _timeZone = nullptr;
+ boost::intrusive_ptr<Expression>& _date;
+ // The expression representing the timezone argument.
+ boost::intrusive_ptr<Expression>& _timeZone;
};
class ExpressionAbs final : public ExpressionSingleNumericArg<ExpressionAbs> {
@@ -874,7 +892,7 @@ public:
static boost::intrusive_ptr<ExpressionCoerceToBool> create(
const boost::intrusive_ptr<ExpressionContext>& expCtx,
- const boost::intrusive_ptr<Expression>& pExpression);
+ boost::intrusive_ptr<Expression> pExpression);
void acceptVisitor(ExpressionVisitor* visitor) final {
return visitor->visit(this);
@@ -885,9 +903,9 @@ protected:
private:
ExpressionCoerceToBool(const boost::intrusive_ptr<ExpressionContext>& expCtx,
- const boost::intrusive_ptr<Expression>& pExpression);
+ boost::intrusive_ptr<Expression> pExpression);
- boost::intrusive_ptr<Expression> pExpression;
+ boost::intrusive_ptr<Expression>& pExpression;
};
@@ -1020,11 +1038,11 @@ private:
boost::intrusive_ptr<Expression> onNull,
boost::intrusive_ptr<Expression> onError);
- boost::intrusive_ptr<Expression> _dateString;
- boost::intrusive_ptr<Expression> _timeZone;
- boost::intrusive_ptr<Expression> _format;
- boost::intrusive_ptr<Expression> _onNull;
- boost::intrusive_ptr<Expression> _onError;
+ boost::intrusive_ptr<Expression>& _dateString;
+ boost::intrusive_ptr<Expression>& _timeZone;
+ boost::intrusive_ptr<Expression>& _format;
+ boost::intrusive_ptr<Expression>& _onNull;
+ boost::intrusive_ptr<Expression>& _onError;
};
class ExpressionDateFromParts final : public Expression {
@@ -1077,17 +1095,17 @@ private:
long long defaultValue,
long long* returnValue) const;
- boost::intrusive_ptr<Expression> _year;
- boost::intrusive_ptr<Expression> _month;
- boost::intrusive_ptr<Expression> _day;
- boost::intrusive_ptr<Expression> _hour;
- boost::intrusive_ptr<Expression> _minute;
- boost::intrusive_ptr<Expression> _second;
- boost::intrusive_ptr<Expression> _millisecond;
- boost::intrusive_ptr<Expression> _isoWeekYear;
- boost::intrusive_ptr<Expression> _isoWeek;
- boost::intrusive_ptr<Expression> _isoDayOfWeek;
- boost::intrusive_ptr<Expression> _timeZone;
+ boost::intrusive_ptr<Expression>& _year;
+ boost::intrusive_ptr<Expression>& _month;
+ boost::intrusive_ptr<Expression>& _day;
+ boost::intrusive_ptr<Expression>& _hour;
+ boost::intrusive_ptr<Expression>& _minute;
+ boost::intrusive_ptr<Expression>& _second;
+ boost::intrusive_ptr<Expression>& _millisecond;
+ boost::intrusive_ptr<Expression>& _isoWeekYear;
+ boost::intrusive_ptr<Expression>& _isoWeek;
+ boost::intrusive_ptr<Expression>& _isoDayOfWeek;
+ boost::intrusive_ptr<Expression>& _timeZone;
};
class ExpressionDateToParts final : public Expression {
@@ -1119,9 +1137,9 @@ private:
boost::optional<int> evaluateIso8601Flag(const Document& root) const;
- boost::intrusive_ptr<Expression> _date;
- boost::intrusive_ptr<Expression> _timeZone;
- boost::intrusive_ptr<Expression> _iso8601;
+ boost::intrusive_ptr<Expression>& _date;
+ boost::intrusive_ptr<Expression>& _timeZone;
+ boost::intrusive_ptr<Expression>& _iso8601;
};
class ExpressionDateToString final : public Expression {
@@ -1149,16 +1167,19 @@ private:
boost::intrusive_ptr<Expression> timeZone,
boost::intrusive_ptr<Expression> onNull);
- boost::intrusive_ptr<Expression> _format;
- boost::intrusive_ptr<Expression> _date;
- boost::intrusive_ptr<Expression> _timeZone;
- boost::intrusive_ptr<Expression> _onNull;
+ boost::intrusive_ptr<Expression>& _format;
+ boost::intrusive_ptr<Expression>& _date;
+ boost::intrusive_ptr<Expression>& _timeZone;
+ boost::intrusive_ptr<Expression>& _onNull;
};
class ExpressionDayOfMonth final : public DateExpressionAcceptingTimeZone<ExpressionDayOfMonth> {
public:
- explicit ExpressionDayOfMonth(const boost::intrusive_ptr<ExpressionContext>& expCtx)
- : DateExpressionAcceptingTimeZone<ExpressionDayOfMonth>("$dayOfMonth", expCtx) {}
+ explicit ExpressionDayOfMonth(const boost::intrusive_ptr<ExpressionContext>& expCtx,
+ boost::intrusive_ptr<Expression> date,
+ boost::intrusive_ptr<Expression> timeZone = nullptr)
+ : DateExpressionAcceptingTimeZone<ExpressionDayOfMonth>(
+ expCtx, "$dayOfMonth", std::move(date), std::move(timeZone)) {}
Value evaluateDate(Date_t date, const TimeZone& timeZone) const final {
return Value(timeZone.dateParts(date).dayOfMonth);
@@ -1172,8 +1193,11 @@ public:
class ExpressionDayOfWeek final : public DateExpressionAcceptingTimeZone<ExpressionDayOfWeek> {
public:
- explicit ExpressionDayOfWeek(const boost::intrusive_ptr<ExpressionContext>& expCtx)
- : DateExpressionAcceptingTimeZone<ExpressionDayOfWeek>("$dayOfWeek", expCtx) {}
+ explicit ExpressionDayOfWeek(const boost::intrusive_ptr<ExpressionContext>& expCtx,
+ boost::intrusive_ptr<Expression> date,
+ boost::intrusive_ptr<Expression> timeZone = nullptr)
+ : DateExpressionAcceptingTimeZone<ExpressionDayOfWeek>(
+ expCtx, "$dayOfWeek", std::move(date), std::move(timeZone)) {}
Value evaluateDate(Date_t date, const TimeZone& timeZone) const final {
return Value(timeZone.dayOfWeek(date));
@@ -1187,8 +1211,11 @@ public:
class ExpressionDayOfYear final : public DateExpressionAcceptingTimeZone<ExpressionDayOfYear> {
public:
- explicit ExpressionDayOfYear(const boost::intrusive_ptr<ExpressionContext>& expCtx)
- : DateExpressionAcceptingTimeZone<ExpressionDayOfYear>("$dayOfYear", expCtx) {}
+ explicit ExpressionDayOfYear(const boost::intrusive_ptr<ExpressionContext>& expCtx,
+ boost::intrusive_ptr<Expression> date,
+ boost::intrusive_ptr<Expression> timeZone = nullptr)
+ : DateExpressionAcceptingTimeZone<ExpressionDayOfYear>(
+ expCtx, "$dayOfYear", std::move(date), std::move(timeZone)) {}
Value evaluateDate(Date_t date, const TimeZone& timeZone) const final {
return Value(timeZone.dayOfYear(date));
@@ -1338,9 +1365,9 @@ private:
// The id of the variable to set.
Variables::Id _varId;
// The array to iterate over.
- boost::intrusive_ptr<Expression> _input;
+ boost::intrusive_ptr<Expression>& _input;
// The expression determining whether each element should be present in the result array.
- boost::intrusive_ptr<Expression> _filter;
+ boost::intrusive_ptr<Expression>& _filter;
};
@@ -1360,8 +1387,11 @@ public:
class ExpressionHour final : public DateExpressionAcceptingTimeZone<ExpressionHour> {
public:
- explicit ExpressionHour(const boost::intrusive_ptr<ExpressionContext>& expCtx)
- : DateExpressionAcceptingTimeZone<ExpressionHour>("$hour", expCtx) {}
+ explicit ExpressionHour(const boost::intrusive_ptr<ExpressionContext>& expCtx,
+ boost::intrusive_ptr<Expression> date,
+ boost::intrusive_ptr<Expression> timeZone = nullptr)
+ : DateExpressionAcceptingTimeZone<ExpressionHour>(
+ expCtx, "$hour", std::move(date), std::move(timeZone)) {}
Value evaluateDate(Date_t date, const TimeZone& timeZone) const final {
return Value(timeZone.dateParts(date).hour);
@@ -1484,12 +1514,8 @@ public:
const VariablesParseState& vps);
struct NameAndExpression {
- NameAndExpression() {}
- NameAndExpression(std::string name, boost::intrusive_ptr<Expression> expression)
- : name(name), expression(expression) {}
-
std::string name;
- boost::intrusive_ptr<Expression> expression;
+ boost::intrusive_ptr<Expression>& expression;
};
typedef std::map<Variables::Id, NameAndExpression> VariableMap;
@@ -1503,11 +1529,11 @@ protected:
private:
ExpressionLet(const boost::intrusive_ptr<ExpressionContext>& expCtx,
- const VariableMap& vars,
- boost::intrusive_ptr<Expression> subExpression);
+ VariableMap&& vars,
+ std::vector<boost::intrusive_ptr<Expression>> children);
VariableMap _variables;
- boost::intrusive_ptr<Expression> _subExpression;
+ boost::intrusive_ptr<Expression>& _subExpression;
};
class ExpressionLn final : public ExpressionSingleNumericArg<ExpressionLn> {
@@ -1580,8 +1606,8 @@ private:
std::string _varName;
Variables::Id _varId;
- boost::intrusive_ptr<Expression> _input;
- boost::intrusive_ptr<Expression> _each;
+ boost::intrusive_ptr<Expression>& _input;
+ boost::intrusive_ptr<Expression>& _each;
};
class ExpressionMeta final : public Expression {
@@ -1614,8 +1640,11 @@ private:
class ExpressionMillisecond final : public DateExpressionAcceptingTimeZone<ExpressionMillisecond> {
public:
- explicit ExpressionMillisecond(const boost::intrusive_ptr<ExpressionContext>& expCtx)
- : DateExpressionAcceptingTimeZone<ExpressionMillisecond>("$millisecond", expCtx) {}
+ explicit ExpressionMillisecond(const boost::intrusive_ptr<ExpressionContext>& expCtx,
+ boost::intrusive_ptr<Expression> date,
+ boost::intrusive_ptr<Expression> timeZone = nullptr)
+ : DateExpressionAcceptingTimeZone<ExpressionMillisecond>(
+ expCtx, "$millisecond", std::move(date), std::move(timeZone)) {}
Value evaluateDate(Date_t date, const TimeZone& timeZone) const final {
return Value(timeZone.dateParts(date).millisecond);
@@ -1629,8 +1658,11 @@ public:
class ExpressionMinute final : public DateExpressionAcceptingTimeZone<ExpressionMinute> {
public:
- explicit ExpressionMinute(const boost::intrusive_ptr<ExpressionContext>& expCtx)
- : DateExpressionAcceptingTimeZone<ExpressionMinute>("$minute", expCtx) {}
+ explicit ExpressionMinute(const boost::intrusive_ptr<ExpressionContext>& expCtx,
+ boost::intrusive_ptr<Expression> date,
+ boost::intrusive_ptr<Expression> timeZone = nullptr)
+ : DateExpressionAcceptingTimeZone<ExpressionMinute>(
+ expCtx, "$minute", std::move(date), std::move(timeZone)) {}
Value evaluateDate(Date_t date, const TimeZone& timeZone) const final {
return Value(timeZone.dateParts(date).minute);
@@ -1680,8 +1712,11 @@ public:
class ExpressionMonth final : public DateExpressionAcceptingTimeZone<ExpressionMonth> {
public:
- explicit ExpressionMonth(const boost::intrusive_ptr<ExpressionContext>& expCtx)
- : DateExpressionAcceptingTimeZone<ExpressionMonth>("$month", expCtx) {}
+ explicit ExpressionMonth(const boost::intrusive_ptr<ExpressionContext>& expCtx,
+ boost::intrusive_ptr<Expression> date,
+ boost::intrusive_ptr<Expression> timeZone = nullptr)
+ : DateExpressionAcceptingTimeZone<ExpressionMonth>(
+ expCtx, "$month", std::move(date), std::move(timeZone)) {}
Value evaluateDate(Date_t date, const TimeZone& timeZone) const final {
return Value(timeZone.dateParts(date).month);
@@ -1723,7 +1758,8 @@ public:
static boost::intrusive_ptr<ExpressionObject> create(
const boost::intrusive_ptr<ExpressionContext>& expCtx,
- std::vector<std::pair<std::string, boost::intrusive_ptr<Expression>>>&& expressions);
+ std::vector<boost::intrusive_ptr<Expression>> children,
+ std::vector<std::pair<std::string, boost::intrusive_ptr<Expression>&>>&& expressions);
/**
* Parses and constructs an ExpressionObject from 'obj'.
@@ -1736,8 +1772,7 @@ public:
/**
* This ExpressionObject must outlive the returned vector.
*/
- const std::vector<std::pair<std::string, boost::intrusive_ptr<Expression>>>&
- getChildExpressions() const {
+ const auto& getChildExpressions() const {
return _expressions;
}
@@ -1754,11 +1789,12 @@ protected:
private:
ExpressionObject(
const boost::intrusive_ptr<ExpressionContext>& expCtx,
- std::vector<std::pair<std::string, boost::intrusive_ptr<Expression>>>&& expressions);
+ std::vector<boost::intrusive_ptr<Expression>> children,
+ std::vector<std::pair<std::string, boost::intrusive_ptr<Expression>&>>&& expressions);
// The mapping from field name to expression within this object. This needs to respect the order
// in which the fields were specified in the input BSON.
- std::vector<std::pair<std::string, boost::intrusive_ptr<Expression>>> _expressions;
+ std::vector<std::pair<std::string, boost::intrusive_ptr<Expression>&>> _expressions;
};
@@ -1818,15 +1854,25 @@ public:
class ExpressionReduce final : public Expression {
public:
- explicit ExpressionReduce(const boost::intrusive_ptr<ExpressionContext>& expCtx)
- : Expression(expCtx) {}
+ ExpressionReduce(const boost::intrusive_ptr<ExpressionContext>& expCtx,
+ boost::intrusive_ptr<Expression> input,
+ boost::intrusive_ptr<Expression> initial,
+ boost::intrusive_ptr<Expression> in,
+ Variables::Id thisVar,
+ Variables::Id valueVar)
+ : Expression(expCtx, {std::move(input), std::move(initial), std::move(in)}),
+ _input(_children[0]),
+ _initial(_children[1]),
+ _in(_children[2]),
+ _thisVar(thisVar),
+ _valueVar(valueVar) {}
Value evaluate(const Document& root) const final;
boost::intrusive_ptr<Expression> optimize() final;
static boost::intrusive_ptr<Expression> parse(
const boost::intrusive_ptr<ExpressionContext>& expCtx,
BSONElement expr,
- const VariablesParseState& vpsIn);
+ const VariablesParseState& vps);
Value serialize(bool explain) const final;
void acceptVisitor(ExpressionVisitor* visitor) final {
@@ -1837,19 +1883,22 @@ protected:
void _doAddDependencies(DepsTracker* deps) const final;
private:
- boost::intrusive_ptr<Expression> _input;
- boost::intrusive_ptr<Expression> _initial;
- boost::intrusive_ptr<Expression> _in;
+ boost::intrusive_ptr<Expression>& _input;
+ boost::intrusive_ptr<Expression>& _initial;
+ boost::intrusive_ptr<Expression>& _in;
- Variables::Id _valueVar;
Variables::Id _thisVar;
+ Variables::Id _valueVar;
};
class ExpressionSecond final : public DateExpressionAcceptingTimeZone<ExpressionSecond> {
public:
- explicit ExpressionSecond(const boost::intrusive_ptr<ExpressionContext>& expCtx)
- : DateExpressionAcceptingTimeZone<ExpressionSecond>("$second", expCtx) {}
+ ExpressionSecond(const boost::intrusive_ptr<ExpressionContext>& expCtx,
+ boost::intrusive_ptr<Expression> date,
+ boost::intrusive_ptr<Expression> timeZone = nullptr)
+ : DateExpressionAcceptingTimeZone<ExpressionSecond>(
+ expCtx, "$second", std::move(date), std::move(timeZone)) {}
Value evaluateDate(Date_t date, const TimeZone& timeZone) const final {
return Value(timeZone.dateParts(date).second);
@@ -2135,8 +2184,15 @@ public:
class ExpressionSwitch final : public Expression {
public:
- explicit ExpressionSwitch(const boost::intrusive_ptr<ExpressionContext>& expCtx)
- : Expression(expCtx) {}
+ using ExpressionPair =
+ std::pair<boost::intrusive_ptr<Expression>&, boost::intrusive_ptr<Expression>&>;
+
+ ExpressionSwitch(const boost::intrusive_ptr<ExpressionContext>& expCtx,
+ std::vector<boost::intrusive_ptr<Expression>> children,
+ std::vector<ExpressionPair> branches)
+ : Expression(expCtx, std::move(children)),
+ _default(_children.back()),
+ _branches(std::move(branches)) {}
Value evaluate(const Document& root) const final;
boost::intrusive_ptr<Expression> optimize() final;
@@ -2154,10 +2210,7 @@ protected:
void _doAddDependencies(DepsTracker* deps) const final;
private:
- using ExpressionPair =
- std::pair<boost::intrusive_ptr<Expression>, boost::intrusive_ptr<Expression>>;
-
- boost::intrusive_ptr<Expression> _default;
+ boost::intrusive_ptr<Expression>& _default;
std::vector<ExpressionPair> _branches;
};
@@ -2205,13 +2258,13 @@ public:
ExpressionTrim(const boost::intrusive_ptr<ExpressionContext>& expCtx,
TrimType trimType,
StringData name,
- const boost::intrusive_ptr<Expression>& input,
- const boost::intrusive_ptr<Expression>& charactersToTrim)
- : Expression(expCtx),
+ boost::intrusive_ptr<Expression> input,
+ boost::intrusive_ptr<Expression> charactersToTrim)
+ : Expression(expCtx, {std::move(input), std::move(charactersToTrim)}),
_trimType(trimType),
_name(name.toString()),
- _input(input),
- _characters(charactersToTrim) {}
+ _input(_children[0]),
+ _characters(_children[1]) {}
Value evaluate(const Document& root) const final;
boost::intrusive_ptr<Expression> optimize() final;
@@ -2257,8 +2310,8 @@ private:
TrimType _trimType;
std::string _name; // "$trim", "$ltrim", or "$rtrim".
- boost::intrusive_ptr<Expression> _input;
- boost::intrusive_ptr<Expression> _characters; // Optional, null if not specified.
+ boost::intrusive_ptr<Expression>& _input;
+ boost::intrusive_ptr<Expression>& _characters; // Optional, null if not specified.
};
@@ -2292,8 +2345,11 @@ public:
class ExpressionWeek final : public DateExpressionAcceptingTimeZone<ExpressionWeek> {
public:
- explicit ExpressionWeek(const boost::intrusive_ptr<ExpressionContext>& expCtx)
- : DateExpressionAcceptingTimeZone<ExpressionWeek>("$week"_sd, expCtx) {}
+ ExpressionWeek(const boost::intrusive_ptr<ExpressionContext>& expCtx,
+ boost::intrusive_ptr<Expression> date,
+ boost::intrusive_ptr<Expression> timeZone = nullptr)
+ : DateExpressionAcceptingTimeZone<ExpressionWeek>(
+ expCtx, "$week", std::move(date), std::move(timeZone)) {}
Value evaluateDate(Date_t date, const TimeZone& timeZone) const final {
return Value(timeZone.week(date));
@@ -2307,8 +2363,11 @@ public:
class ExpressionIsoWeekYear final : public DateExpressionAcceptingTimeZone<ExpressionIsoWeekYear> {
public:
- explicit ExpressionIsoWeekYear(const boost::intrusive_ptr<ExpressionContext>& expCtx)
- : DateExpressionAcceptingTimeZone<ExpressionIsoWeekYear>("$isoWeekYear"_sd, expCtx) {}
+ explicit ExpressionIsoWeekYear(const boost::intrusive_ptr<ExpressionContext>& expCtx,
+ boost::intrusive_ptr<Expression> date,
+ boost::intrusive_ptr<Expression> timeZone = nullptr)
+ : DateExpressionAcceptingTimeZone<ExpressionIsoWeekYear>(
+ expCtx, "$isoWeekYear", std::move(date), std::move(timeZone)) {}
Value evaluateDate(Date_t date, const TimeZone& timeZone) const final {
return Value(timeZone.isoYear(date));
@@ -2323,8 +2382,11 @@ public:
class ExpressionIsoDayOfWeek final
: public DateExpressionAcceptingTimeZone<ExpressionIsoDayOfWeek> {
public:
- explicit ExpressionIsoDayOfWeek(const boost::intrusive_ptr<ExpressionContext>& expCtx)
- : DateExpressionAcceptingTimeZone<ExpressionIsoDayOfWeek>("$isoDayOfWeek"_sd, expCtx) {}
+ ExpressionIsoDayOfWeek(const boost::intrusive_ptr<ExpressionContext>& expCtx,
+ boost::intrusive_ptr<Expression> date,
+ boost::intrusive_ptr<Expression> timeZone = nullptr)
+ : DateExpressionAcceptingTimeZone<ExpressionIsoDayOfWeek>(
+ expCtx, "$isoDayOfWeek", std::move(date), std::move(timeZone)) {}
Value evaluateDate(Date_t date, const TimeZone& timeZone) const final {
return Value(timeZone.isoDayOfWeek(date));
@@ -2338,8 +2400,11 @@ public:
class ExpressionIsoWeek final : public DateExpressionAcceptingTimeZone<ExpressionIsoWeek> {
public:
- explicit ExpressionIsoWeek(const boost::intrusive_ptr<ExpressionContext>& expCtx)
- : DateExpressionAcceptingTimeZone<ExpressionIsoWeek>("$isoWeek", expCtx) {}
+ ExpressionIsoWeek(const boost::intrusive_ptr<ExpressionContext>& expCtx,
+ boost::intrusive_ptr<Expression> date,
+ boost::intrusive_ptr<Expression> timeZone = nullptr)
+ : DateExpressionAcceptingTimeZone<ExpressionIsoWeek>(
+ expCtx, "$isoWeek", std::move(date), std::move(timeZone)) {}
Value evaluateDate(Date_t date, const TimeZone& timeZone) const final {
return Value(timeZone.isoWeek(date));
@@ -2353,8 +2418,11 @@ public:
class ExpressionYear final : public DateExpressionAcceptingTimeZone<ExpressionYear> {
public:
- explicit ExpressionYear(const boost::intrusive_ptr<ExpressionContext>& expCtx)
- : DateExpressionAcceptingTimeZone<ExpressionYear>("$year", expCtx) {}
+ ExpressionYear(const boost::intrusive_ptr<ExpressionContext>& expCtx,
+ boost::intrusive_ptr<Expression> date,
+ boost::intrusive_ptr<Expression> timeZone = nullptr)
+ : DateExpressionAcceptingTimeZone<ExpressionYear>(
+ expCtx, "$year", std::move(date), std::move(timeZone)) {}
Value evaluateDate(Date_t date, const TimeZone& timeZone) const final {
return Value(timeZone.dateParts(date).year);
@@ -2368,8 +2436,15 @@ public:
class ExpressionZip final : public Expression {
public:
- explicit ExpressionZip(const boost::intrusive_ptr<ExpressionContext>& expCtx)
- : Expression(expCtx) {}
+ ExpressionZip(const boost::intrusive_ptr<ExpressionContext>& expCtx,
+ bool useLongestLength,
+ std::vector<boost::intrusive_ptr<Expression>> children,
+ std::vector<std::reference_wrapper<boost::intrusive_ptr<Expression>>> inputs,
+ std::vector<std::reference_wrapper<boost::intrusive_ptr<Expression>>> defaults)
+ : Expression(expCtx, std::move(children)),
+ _useLongestLength(useLongestLength),
+ _inputs(std::move(inputs)),
+ _defaults(std::move(defaults)) {}
Value evaluate(const Document& root) const final;
boost::intrusive_ptr<Expression> optimize() final;
@@ -2387,9 +2462,9 @@ protected:
void _doAddDependencies(DepsTracker* deps) const final;
private:
- bool _useLongestLength = false;
- ExpressionVector _inputs;
- ExpressionVector _defaults;
+ bool _useLongestLength;
+ std::vector<std::reference_wrapper<boost::intrusive_ptr<Expression>>> _inputs;
+ std::vector<std::reference_wrapper<boost::intrusive_ptr<Expression>>> _defaults;
};
class ExpressionConvert final : public Expression {
@@ -2399,7 +2474,7 @@ public:
* 'onNull' and 'onError' unspecified.
*/
static boost::intrusive_ptr<Expression> create(const boost::intrusive_ptr<ExpressionContext>&,
- const boost::intrusive_ptr<Expression>& input,
+ boost::intrusive_ptr<Expression> input,
BSONType toType);
static boost::intrusive_ptr<Expression> parse(
@@ -2407,8 +2482,6 @@ public:
BSONElement expr,
const VariablesParseState& vpsIn);
- explicit ExpressionConvert(const boost::intrusive_ptr<ExpressionContext>& expCtx)
- : Expression(expCtx) {}
Value evaluate(const Document& root) const final;
boost::intrusive_ptr<Expression> optimize() final;
Value serialize(bool explain) const final;
@@ -2422,16 +2495,18 @@ protected:
private:
ExpressionConvert(const boost::intrusive_ptr<ExpressionContext>&,
- const boost::intrusive_ptr<Expression>& input,
- BSONType toType);
+ boost::intrusive_ptr<Expression> input,
+ boost::intrusive_ptr<Expression> to,
+ boost::intrusive_ptr<Expression> onError,
+ boost::intrusive_ptr<Expression> onNull);
BSONType computeTargetType(Value typeName) const;
Value performConversion(BSONType targetType, Value inputValue) const;
- boost::intrusive_ptr<Expression> _input;
- boost::intrusive_ptr<Expression> _to;
- boost::intrusive_ptr<Expression> _onError;
- boost::intrusive_ptr<Expression> _onNull;
+ boost::intrusive_ptr<Expression>& _input;
+ boost::intrusive_ptr<Expression>& _to;
+ boost::intrusive_ptr<Expression>& _onError;
+ boost::intrusive_ptr<Expression>& _onNull;
};
class ExpressionRegex : public Expression {
@@ -2511,11 +2586,16 @@ public:
return _opName;
}
-protected:
ExpressionRegex(const boost::intrusive_ptr<ExpressionContext>& expCtx,
- BSONElement expr,
- const VariablesParseState& vpsIn,
- const std::string& opName);
+ boost::intrusive_ptr<Expression> input,
+ boost::intrusive_ptr<Expression> regex,
+ boost::intrusive_ptr<Expression> options,
+ const StringData opName)
+ : Expression(expCtx, {std::move(input), std::move(regex), std::move(options)}),
+ _input(_children[0]),
+ _regex(_children[1]),
+ _options(_children[2]),
+ _opName(opName) {}
private:
void _extractInputField(RegexExecutionState* executionState, const Value& textInput) const;
@@ -2531,9 +2611,9 @@ private:
* Expressions which, when evaluated for a given document, produce the the regex pattern, the
* regex option flags, and the input text to which the regex should be applied.
*/
- boost::intrusive_ptr<Expression> _input;
- boost::intrusive_ptr<Expression> _regex;
- boost::intrusive_ptr<Expression> _options;
+ boost::intrusive_ptr<Expression>& _input;
+ boost::intrusive_ptr<Expression>& _regex;
+ boost::intrusive_ptr<Expression>& _options;
/**
* This variable will be set when the $regex* expressions have constant values for their 'regex'
@@ -2553,9 +2633,7 @@ public:
static boost::intrusive_ptr<Expression> parse(
const boost::intrusive_ptr<ExpressionContext>& expCtx,
BSONElement expr,
- const VariablesParseState& vpsIn) {
- return new ExpressionRegexFind(expCtx, expr, vpsIn);
- }
+ const VariablesParseState& vpsIn);
Value evaluate(const Document& root) const final;
@@ -2563,11 +2641,7 @@ public:
return visitor->visit(this);
}
-private:
- ExpressionRegexFind(const boost::intrusive_ptr<ExpressionContext>& expCtx,
- BSONElement expr,
- const VariablesParseState& vpsIn)
- : ExpressionRegex(expCtx, expr, vpsIn, "$regexFind") {}
+ using ExpressionRegex::ExpressionRegex;
};
class ExpressionRegexFindAll final : public ExpressionRegex {
@@ -2575,20 +2649,14 @@ public:
static boost::intrusive_ptr<Expression> parse(
const boost::intrusive_ptr<ExpressionContext>& expCtx,
BSONElement expr,
- const VariablesParseState& vpsIn) {
- return new ExpressionRegexFindAll(expCtx, expr, vpsIn);
- }
+ const VariablesParseState& vpsIn);
Value evaluate(const Document& root) const final;
void acceptVisitor(ExpressionVisitor* visitor) final {
return visitor->visit(this);
}
-private:
- ExpressionRegexFindAll(const boost::intrusive_ptr<ExpressionContext>& expCtx,
- BSONElement expr,
- const VariablesParseState& vpsIn)
- : ExpressionRegex(expCtx, expr, vpsIn, "$regexFindAll") {}
+ using ExpressionRegex::ExpressionRegex;
};
class ExpressionRegexMatch final : public ExpressionRegex {
@@ -2596,9 +2664,7 @@ public:
static boost::intrusive_ptr<Expression> parse(
const boost::intrusive_ptr<ExpressionContext>& expCtx,
BSONElement expr,
- const VariablesParseState& vpsIn) {
- return new ExpressionRegexMatch(expCtx, expr, vpsIn);
- }
+ const VariablesParseState& vpsIn);
Value evaluate(const Document& root) const final;
@@ -2606,10 +2672,6 @@ public:
return visitor->visit(this);
}
-private:
- ExpressionRegexMatch(const boost::intrusive_ptr<ExpressionContext>& expCtx,
- BSONElement expr,
- const VariablesParseState& vpsIn)
- : ExpressionRegex(expCtx, expr, vpsIn, "$regexMatch") {}
+ using ExpressionRegex::ExpressionRegex;
};
}
diff --git a/src/mongo/db/pipeline/expression_test.cpp b/src/mongo/db/pipeline/expression_test.cpp
index 02bd8a55d8a..ceff19672a2 100644
--- a/src/mongo/db/pipeline/expression_test.cpp
+++ b/src/mongo/db/pipeline/expression_test.cpp
@@ -204,9 +204,8 @@ public:
// By default, this is not associative/commutative so the results will change if
// instantiated as commutative or associative and operations are reordered.
vector<Value> values;
- for (ExpressionVector::const_iterator i = vpOperand.begin(); i != vpOperand.end(); ++i) {
- values.push_back((*i)->evaluate(root));
- }
+ for (auto&& child : _children)
+ values.push_back(child->evaluate(root));
return Value(values);
}
@@ -3306,9 +3305,32 @@ TEST(ParseObject, ShouldRejectExpressionAsTheSecondField) {
// Evaluation.
//
+namespace {
+/**
+ * ExpressionObject builds two vectors within it's ::parse() method, one owning and one with names
+ * and references to the former. Since the ::create() method bypasses this step, we have to mimic
+ * the behavior here.
+ */
+auto expressionObjectCreateHelper(
+ const boost::intrusive_ptr<ExpressionContext>& expCtx,
+ std::vector<std::pair<std::string, boost::intrusive_ptr<Expression>>>&&
+ expressionsWithChildrenInPlace) {
+ std::vector<boost::intrusive_ptr<Expression>> children;
+ std::vector<std::pair<std::string, boost::intrusive_ptr<Expression>&>> expressions;
+ for (auto & [ unused, expression ] : expressionsWithChildrenInPlace)
+ children.push_back(std::move(expression));
+ std::vector<boost::intrusive_ptr<Expression>>::size_type index = 0;
+ for (auto & [ fieldName, unused ] : expressionsWithChildrenInPlace) {
+ expressions.emplace_back(fieldName, children[index]);
+ ++index;
+ }
+ return ExpressionObject::create(expCtx, std::move(children), std::move(expressions));
+}
+} // namespace
+
TEST(ExpressionObjectEvaluate, EmptyObjectShouldEvaluateToEmptyDocument) {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- auto object = ExpressionObject::create(expCtx, {});
+ auto object = expressionObjectCreateHelper(expCtx, {});
ASSERT_VALUE_EQ(Value(Document()), object->evaluate(Document()));
ASSERT_VALUE_EQ(Value(Document()), object->evaluate(Document{{"a", 1}}));
ASSERT_VALUE_EQ(Value(Document()), object->evaluate(Document{{"_id", "ID"_sd}}));
@@ -3317,7 +3339,7 @@ TEST(ExpressionObjectEvaluate, EmptyObjectShouldEvaluateToEmptyDocument) {
TEST(ExpressionObjectEvaluate, ShouldEvaluateEachField) {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
auto object =
- ExpressionObject::create(expCtx, {{"a", makeConstant(1)}, {"b", makeConstant(5)}});
+ expressionObjectCreateHelper(expCtx, {{"a", makeConstant(1)}, {"b", makeConstant(5)}});
ASSERT_VALUE_EQ(Value(Document{{"a", 1}, {"b", 5}}), object->evaluate(Document()));
ASSERT_VALUE_EQ(Value(Document{{"a", 1}, {"b", 5}}), object->evaluate(Document{{"a", 1}}));
ASSERT_VALUE_EQ(Value(Document{{"a", 1}, {"b", 5}}),
@@ -3326,10 +3348,10 @@ TEST(ExpressionObjectEvaluate, ShouldEvaluateEachField) {
TEST(ExpressionObjectEvaluate, OrderOfFieldsInOutputShouldMatchOrderInSpecification) {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- auto object = ExpressionObject::create(expCtx,
- {{"a", ExpressionFieldPath::create(expCtx, "a")},
- {"b", ExpressionFieldPath::create(expCtx, "b")},
- {"c", ExpressionFieldPath::create(expCtx, "c")}});
+ auto object = expressionObjectCreateHelper(expCtx,
+ {{"a", ExpressionFieldPath::create(expCtx, "a")},
+ {"b", ExpressionFieldPath::create(expCtx, "b")},
+ {"c", ExpressionFieldPath::create(expCtx, "c")}});
ASSERT_VALUE_EQ(
Value(Document{{"a", "A"_sd}, {"b", "B"_sd}, {"c", "C"_sd}}),
object->evaluate(Document{{"c", "C"_sd}, {"a", "A"_sd}, {"b", "B"_sd}, {"_id", "ID"_sd}}));
@@ -3337,19 +3359,20 @@ TEST(ExpressionObjectEvaluate, OrderOfFieldsInOutputShouldMatchOrderInSpecificat
TEST(ExpressionObjectEvaluate, ShouldRemoveFieldsThatHaveMissingValues) {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- auto object = ExpressionObject::create(expCtx,
- {{"a", ExpressionFieldPath::create(expCtx, "a.b")},
- {"b", ExpressionFieldPath::create(expCtx, "missing")}});
+ auto object =
+ expressionObjectCreateHelper(expCtx,
+ {{"a", ExpressionFieldPath::create(expCtx, "a.b")},
+ {"b", ExpressionFieldPath::create(expCtx, "missing")}});
ASSERT_VALUE_EQ(Value(Document{}), object->evaluate(Document()));
ASSERT_VALUE_EQ(Value(Document{}), object->evaluate(Document{{"a", 1}}));
}
TEST(ExpressionObjectEvaluate, ShouldEvaluateFieldsWithinNestedObject) {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- auto object = ExpressionObject::create(
+ auto object = expressionObjectCreateHelper(
expCtx,
{{"a",
- ExpressionObject::create(
+ expressionObjectCreateHelper(
expCtx,
{{"b", makeConstant(1)}, {"c", ExpressionFieldPath::create(expCtx, "_id")}})}});
ASSERT_VALUE_EQ(Value(Document{{"a", Document{{"b", 1}}}}), object->evaluate(Document()));
@@ -3359,11 +3382,11 @@ TEST(ExpressionObjectEvaluate, ShouldEvaluateFieldsWithinNestedObject) {
TEST(ExpressionObjectEvaluate, ShouldEvaluateToEmptyDocumentIfAllFieldsAreMissing) {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- auto object =
- ExpressionObject::create(expCtx, {{"a", ExpressionFieldPath::create(expCtx, "missing")}});
+ auto object = expressionObjectCreateHelper(
+ expCtx, {{"a", ExpressionFieldPath::create(expCtx, "missing")}});
ASSERT_VALUE_EQ(Value(Document{}), object->evaluate(Document()));
- auto objectWithNestedObject = ExpressionObject::create(expCtx, {{"nested", object}});
+ auto objectWithNestedObject = expressionObjectCreateHelper(expCtx, {{"nested", object}});
ASSERT_VALUE_EQ(Value(Document{{"nested", Document{}}}),
objectWithNestedObject->evaluate(Document()));
}
@@ -3374,7 +3397,7 @@ TEST(ExpressionObjectEvaluate, ShouldEvaluateToEmptyDocumentIfAllFieldsAreMissin
TEST(ExpressionObjectDependencies, ConstantValuesShouldNotBeAddedToDependencies) {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- auto object = ExpressionObject::create(expCtx, {{"a", makeConstant(5)}});
+ auto object = expressionObjectCreateHelper(expCtx, {{"a", makeConstant(5)}});
DepsTracker deps;
object->addDependencies(&deps);
ASSERT_EQ(deps.fields.size(), 0UL);
@@ -3383,7 +3406,7 @@ TEST(ExpressionObjectDependencies, ConstantValuesShouldNotBeAddedToDependencies)
TEST(ExpressionObjectDependencies, FieldPathsShouldBeAddedToDependencies) {
intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
auto object =
- ExpressionObject::create(expCtx, {{"x", ExpressionFieldPath::create(expCtx, "c.d")}});
+ expressionObjectCreateHelper(expCtx, {{"x", ExpressionFieldPath::create(expCtx, "c.d")}});
DepsTracker deps;
object->addDependencies(&deps);
ASSERT_EQ(deps.fields.size(), 1UL);
@@ -3462,7 +3485,7 @@ TEST(ExpressionObjectOptimizations, OptimizingAnObjectShouldOptimizeSubExpressio
VariablesParseState vps = expCtx->variablesParseState;
auto addExpression =
ExpressionAdd::parse(expCtx, BSON("$add" << BSON_ARRAY(1 << 2)).firstElement(), vps);
- auto object = ExpressionObject::create(expCtx, {{"a", addExpression}});
+ auto object = expressionObjectCreateHelper(expCtx, {{"a", addExpression}});
ASSERT_EQ(object->getChildExpressions().size(), 1UL);
auto optimized = object->optimize();