summaryrefslogtreecommitdiff
path: root/src/mongo/db/pipeline/expression.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/pipeline/expression.cpp')
-rw-r--r--src/mongo/db/pipeline/expression.cpp942
1 files changed, 279 insertions, 663 deletions
diff --git a/src/mongo/db/pipeline/expression.cpp b/src/mongo/db/pipeline/expression.cpp
index 658fd9051b4..bf0915deddd 100644
--- a/src/mongo/db/pipeline/expression.cpp
+++ b/src/mongo/db/pipeline/expression.cpp
@@ -72,25 +72,13 @@ using std::pair;
using std::string;
using std::vector;
-Value ExpressionConstant::serializeConstant(const SerializationOptions& opts,
- Value val,
- bool wrapRepresentativeValue) {
+/// Helper function to easily wrap constants with $const.
+static Value serializeConstant(Value val) {
if (val.missing()) {
return Value("$$REMOVE"_sd);
}
- // It's safer to wrap constants in $const when generating representative shapes to avoid
- // ambiguity when re-parsing (SERVER-88296, SERVER-85376). However, we allow certain expressions
- // to override this behavior in order to reduce shape verbosity if the expression takes many
- // constant arguments (e.g. variadic expressions - SERVER-84159).
- // Debug shapes never wrap constants in $const to reduce shape size (and because re-parsing
- // support is not a consideration there).
- if ((opts.literalPolicy == LiteralSerializationPolicy::kUnchanged) ||
- (wrapRepresentativeValue &&
- opts.literalPolicy == LiteralSerializationPolicy::kToRepresentativeParseableValue)) {
- return Value(DOC("$const" << opts.serializeLiteral(val)));
- }
- return opts.serializeLiteral(val);
+ return Value(DOC("$const" << val));
}
/* --------------------------- Expression ------------------------------ */
@@ -200,7 +188,7 @@ void Expression::registerExpression(
parserMap[key] =
ParserRegistration{parser, allowedWithApiStrict, allowedWithClientType, requiredMinVersion};
// Add this expression to the global map of operator counters for expressions.
- operatorCountersAggExpressions.addCounter(key);
+ operatorCountersAggExpressions.addAggExpressionCounter(key);
}
intrusive_ptr<Expression> Expression::parseExpression(ExpressionContext* const expCtx,
@@ -320,204 +308,111 @@ const char* ExpressionAbs::getOpName() const {
/* ------------------------- ExpressionAdd ----------------------------- */
-namespace {
+StatusWith<Value> ExpressionAdd::apply(Value lhs, Value rhs) {
+ BSONType diffType = Value::getWidestNumeric(rhs.getType(), lhs.getType());
-/**
- * We'll try to return the narrowest possible result value while avoiding overflow or implicit use
- * of decimal types. To do that, compute separate sums for long, double and decimal values, and
- * track the current widest type. The long sum will be converted to double when the first double
- * value is seen or when long arithmetic would overflow.
- */
-class AddState {
-public:
- /**
- * Update the internal state with another operand. It is up to the caller to validate that the
- * operand is of a proper type.
- */
- void operator+=(const Value& operand) {
- auto oldWidestType = widestType;
- // Dates are represented by the long number of milliseconds since the unix epoch, so we can
- // treat them as regular numeric values for the purposes of addition after making sure that
- // only one date is present in the operand list.
- Value valToAdd;
- if (operand.getType() == Date) {
- uassert(16612, "only one date allowed in an $add expression", !isDate);
- Value oldValue = getValue();
- longTotal = 0;
- addToDateValue(oldValue);
- isDate = true;
- valToAdd = Value(operand.getDate().toMillisSinceEpoch());
- } else {
- widestType = Value::getWidestNumeric(widestType, operand.getType());
- valToAdd = operand;
- }
+ if (diffType == NumberDecimal) {
+ Decimal128 left = lhs.coerceToDecimal();
+ Decimal128 right = rhs.coerceToDecimal();
+ return Value(left.add(right));
+ } else if (diffType == NumberDouble) {
+ double right = rhs.coerceToDouble();
+ double left = lhs.coerceToDouble();
+ return Value(left + right);
+ } else if (diffType == NumberLong) {
+ long long result;
- if (isDate) {
- addToDateValue(valToAdd);
- return;
+ // If there is an overflow, convert the values to doubles.
+ if (overflow::add(lhs.coerceToLong(), rhs.coerceToLong(), &result)) {
+ return Value(lhs.coerceToDouble() + rhs.coerceToDouble());
}
+ return Value(result);
+ } else if (diffType == NumberInt) {
+ long long right = rhs.coerceToLong();
+ long long left = lhs.coerceToLong();
+ return Value::createIntOrLong(left + right);
+ } else if (lhs.nullish() || rhs.nullish()) {
+ return Value(BSONNULL);
+ } else {
+ return Status(ErrorCodes::TypeMismatch,
+ str::stream() << "cannot $add a" << typeName(rhs.getType()) << " from a "
+ << typeName(lhs.getType()));
+ }
+}
- // If this operation widens the return type, perform any necessary type conversions.
- if (oldWidestType != widestType) {
- switch (widestType) {
- case NumberLong:
- // Int -> Long is handled by the same sum.
- break;
- case NumberDouble:
- // Int/Long -> Double converts the existing longTotal to a doubleTotal.
- doubleTotal = longTotal;
- break;
- case NumberDecimal:
- // Convert the right total to NumberDecimal by looking at the old widest type.
- switch (oldWidestType) {
- case NumberInt:
- case NumberLong:
- decimalTotal = Decimal128(longTotal);
- break;
- case NumberDouble:
- decimalTotal = Decimal128(doubleTotal);
- break;
- default:
- MONGO_UNREACHABLE;
- }
- break;
- default:
- MONGO_UNREACHABLE;
- }
- }
+Value ExpressionAdd::evaluate(const Document& root, Variables* variables) const {
+ // We'll try to return the narrowest possible result value while avoiding overflow, loss
+ // of precision due to intermediate rounding or implicit use of decimal types. To do that,
+ // compute a compensated sum for non-decimal values and a separate decimal sum for decimal
+ // values, and track the current narrowest type.
+ DoubleDoubleSummation nonDecimalTotal;
+ Decimal128 decimalTotal;
+ BSONType totalType = NumberInt;
+ bool haveDate = false;
- // Perform the add operation.
- switch (widestType) {
- case NumberInt:
- case NumberLong:
- // If the long long arithmetic overflows, promote the result to a NumberDouble and
- // start incrementing the doubleTotal.
- long long newLongTotal;
- if (overflow::add(longTotal, valToAdd.coerceToLong(), &newLongTotal)) {
- widestType = NumberDouble;
- doubleTotal = longTotal + valToAdd.coerceToDouble();
- } else {
- longTotal = newLongTotal;
- }
+ const size_t n = _children.size();
+ for (size_t i = 0; i < n; ++i) {
+ Value val = _children[i]->evaluate(root, variables);
+
+ switch (val.getType()) {
+ case NumberDecimal:
+ decimalTotal = decimalTotal.add(val.getDecimal());
+ totalType = NumberDecimal;
break;
case NumberDouble:
- doubleTotal += valToAdd.coerceToDouble();
- break;
- case NumberDecimal:
- decimalTotal = decimalTotal.add(valToAdd.coerceToDecimal());
+ nonDecimalTotal.addDouble(val.getDouble());
+ if (totalType != NumberDecimal)
+ totalType = NumberDouble;
break;
- default:
- uasserted(ErrorCodes::TypeMismatch,
- str::stream() << "$add only supports numeric or date types, not "
- << typeName(valToAdd.getType()));
- }
- }
-
- Value getValue() const {
- // If one of the operands was a date, then return long value as Date.
- if (isDate) {
- return Value(Date_t::fromMillisSinceEpoch(longTotal));
- } else {
- switch (widestType) {
- case NumberInt:
- return Value::createIntOrLong(longTotal);
- case NumberLong:
- return Value(longTotal);
- case NumberDouble:
- return Value(doubleTotal);
- case NumberDecimal:
- return Value(decimalTotal);
- default:
- MONGO_UNREACHABLE;
- }
- }
- }
-
-private:
- // Convert 'valToAdd' into the data type used for dates (long long) and add it to 'longTotal'.
- void addToDateValue(Value valToAdd) {
- switch (valToAdd.getType()) {
- case NumberInt:
case NumberLong:
- if (overflow::add(longTotal, valToAdd.coerceToLong(), &longTotal)) {
- uasserted(ErrorCodes::Overflow, "date overflow");
- }
+ nonDecimalTotal.addLong(val.getLong());
+ if (totalType == NumberInt)
+ totalType = NumberLong;
break;
- case NumberDouble: {
- using limits = std::numeric_limits<long long>;
- double doubleToAdd = valToAdd.coerceToDouble();
- uassert(ErrorCodes::Overflow,
- "date overflow",
- // The upper bound is exclusive because it rounds up when it is cast to
- // a double.
- doubleToAdd >= static_cast<double>(limits::min()) &&
- doubleToAdd < static_cast<double>(limits::max()));
-
- if (overflow::add(longTotal, llround(doubleToAdd), &longTotal)) {
- uasserted(ErrorCodes::Overflow, "date overflow");
- }
+ case NumberInt:
+ nonDecimalTotal.addDouble(val.getInt());
break;
- }
- case NumberDecimal: {
- Decimal128 decimalToAdd = valToAdd.coerceToDecimal();
-
- std::uint32_t signalingFlags = Decimal128::SignalingFlag::kNoFlag;
- std::int64_t longToAdd = decimalToAdd.toLong(&signalingFlags);
- if (signalingFlags != Decimal128::SignalingFlag::kNoFlag ||
- overflow::add(longTotal, longToAdd, &longTotal)) {
- uasserted(ErrorCodes::Overflow, "date overflow");
- }
+ case Date:
+ uassert(16612, "only one date allowed in an $add expression", !haveDate);
+ haveDate = true;
+ nonDecimalTotal.addLong(val.getDate().toMillisSinceEpoch());
break;
- }
default:
- MONGO_UNREACHABLE;
+ uassert(16554,
+ str::stream() << "$add only supports numeric or date types, not "
+ << typeName(val.getType()),
+ val.nullish());
+ return Value(BSONNULL);
}
}
- long long longTotal = 0;
- double doubleTotal = 0;
- Decimal128 decimalTotal;
- BSONType widestType = NumberInt;
- bool isDate = false;
-};
-
-Status checkAddOperandType(Value val) {
- if (!val.numeric() && val.getType() != Date) {
- return Status(ErrorCodes::TypeMismatch,
- str::stream() << "$add only supports numeric or date types, not "
- << typeName(val.getType()));
+ if (haveDate) {
+ int64_t longTotal;
+ if (totalType == NumberDecimal) {
+ longTotal = decimalTotal.add(nonDecimalTotal.getDecimal()).toLong();
+ } else {
+ uassert(ErrorCodes::Overflow, "date overflow in $add", nonDecimalTotal.fitsLong());
+ longTotal = nonDecimalTotal.getLong();
+ }
+ return Value(Date_t::fromMillisSinceEpoch(longTotal));
}
-
- return Status::OK();
-}
-} // namespace
-
-StatusWith<Value> ExpressionAdd::apply(Value lhs, Value rhs) {
- if (lhs.nullish())
- return Value(BSONNULL);
- if (Status s = checkAddOperandType(lhs); !s.isOK())
- return s;
- if (rhs.nullish())
- return Value(BSONNULL);
- if (Status s = checkAddOperandType(rhs); !s.isOK())
- return s;
-
- AddState state;
- state += lhs;
- state += rhs;
- return state.getValue();
-}
-
-Value ExpressionAdd::evaluate(const Document& root, Variables* variables) const {
- AddState state;
- for (auto&& child : _children) {
- Value val = child->evaluate(root, variables);
- if (val.nullish())
- return Value(BSONNULL);
- uassertStatusOK(checkAddOperandType(val));
- state += val;
+ switch (totalType) {
+ case NumberDecimal:
+ return Value(decimalTotal.add(nonDecimalTotal.getDecimal()));
+ case NumberLong:
+ dassert(nonDecimalTotal.isInteger());
+ if (nonDecimalTotal.fitsLong())
+ return Value(nonDecimalTotal.getLong());
+ // Fallthrough.
+ case NumberInt:
+ if (nonDecimalTotal.fitsLong())
+ return Value::createIntOrLong(nonDecimalTotal.getLong());
+ // Fallthrough.
+ case NumberDouble:
+ return Value(nonDecimalTotal.getDouble());
+ default:
+ massert(16417, "$add resulted in a non-numeric type", false);
}
- return state.getValue();
}
REGISTER_STABLE_EXPRESSION(add, ExpressionAdd::parse);
@@ -655,16 +550,11 @@ Value ExpressionArray::evaluate(const Document& root, Variables* variables) cons
return Value(std::move(values));
}
-Value ExpressionArray::serialize(const SerializationOptions& options) const {
- if (options.literalPolicy != LiteralSerializationPolicy::kUnchanged &&
- selfAndChildrenAreConstant()) {
- return ExpressionConstant::serializeConstant(
- options, evaluate(Document{}, &(getExpressionContext()->variables)));
- }
+Value ExpressionArray::serialize(bool explain) const {
vector<Value> expressions;
expressions.reserve(_children.size());
for (auto&& expr : _children) {
- expressions.push_back(expr->serialize(options));
+ expressions.push_back(expr->serialize(explain));
}
return Value(std::move(expressions));
}
@@ -687,15 +577,6 @@ intrusive_ptr<Expression> ExpressionArray::optimize() {
return this;
}
-bool ExpressionArray::selfAndChildrenAreConstant() const {
- for (auto&& exprPointer : _children) {
- if (!exprPointer->selfAndChildrenAreConstant()) {
- return false;
- }
- }
- return true;
-}
-
const char* ExpressionArray::getOpName() const {
// This should never be called, but is needed to inherit from ExpressionNary.
return "$array";
@@ -996,11 +877,11 @@ Value ExpressionCoerceToBool::evaluate(const Document& root, Variables* variable
return Value(false);
}
-Value ExpressionCoerceToBool::serialize(const SerializationOptions& options) const {
+Value ExpressionCoerceToBool::serialize(bool explain) const {
// When not explaining, serialize to an $and expression. When parsed, the $and expression
// will be optimized back into a ExpressionCoerceToBool.
- const char* name = options.verbosity ? "$coerceToBool" : "$and";
- return Value(DOC(name << DOC_ARRAY(pExpression->serialize(options))));
+ const char* name = explain ? "$coerceToBool" : "$and";
+ return Value(DOC(name << DOC_ARRAY(pExpression->serialize(explain))));
}
/* ----------------------- ExpressionCompare --------------------------- */
@@ -1249,8 +1130,8 @@ Value ExpressionConstant::evaluate(const Document& root, Variables* variables) c
return _value;
}
-Value ExpressionConstant::serialize(const SerializationOptions& options) const {
- return ExpressionConstant::serializeConstant(options, _value);
+Value ExpressionConstant::serialize(bool explain) const {
+ return serializeConstant(_value);
}
REGISTER_STABLE_EXPRESSION(const, ExpressionConstant::parse);
@@ -1464,20 +1345,20 @@ intrusive_ptr<Expression> ExpressionDateFromParts::optimize() {
return this;
}
-Value ExpressionDateFromParts::serialize(const SerializationOptions& options) const {
+Value ExpressionDateFromParts::serialize(bool explain) const {
return Value(Document{
{"$dateFromParts",
- Document{{"year", _year ? _year->serialize(options) : Value()},
- {"month", _month ? _month->serialize(options) : Value()},
- {"day", _day ? _day->serialize(options) : Value()},
- {"hour", _hour ? _hour->serialize(options) : Value()},
- {"minute", _minute ? _minute->serialize(options) : Value()},
- {"second", _second ? _second->serialize(options) : Value()},
- {"millisecond", _millisecond ? _millisecond->serialize(options) : Value()},
- {"isoWeekYear", _isoWeekYear ? _isoWeekYear->serialize(options) : Value()},
- {"isoWeek", _isoWeek ? _isoWeek->serialize(options) : Value()},
- {"isoDayOfWeek", _isoDayOfWeek ? _isoDayOfWeek->serialize(options) : Value()},
- {"timezone", _timeZone ? _timeZone->serialize(options) : Value()}}}});
+ Document{{"year", _year ? _year->serialize(explain) : Value()},
+ {"month", _month ? _month->serialize(explain) : Value()},
+ {"day", _day ? _day->serialize(explain) : Value()},
+ {"hour", _hour ? _hour->serialize(explain) : Value()},
+ {"minute", _minute ? _minute->serialize(explain) : Value()},
+ {"second", _second ? _second->serialize(explain) : Value()},
+ {"millisecond", _millisecond ? _millisecond->serialize(explain) : Value()},
+ {"isoWeekYear", _isoWeekYear ? _isoWeekYear->serialize(explain) : Value()},
+ {"isoWeek", _isoWeek ? _isoWeek->serialize(explain) : Value()},
+ {"isoDayOfWeek", _isoDayOfWeek ? _isoDayOfWeek->serialize(explain) : Value()},
+ {"timezone", _timeZone ? _timeZone->serialize(explain) : Value()}}}});
}
bool ExpressionDateFromParts::evaluateNumberWithDefault(const Document& root,
@@ -1721,14 +1602,14 @@ intrusive_ptr<Expression> ExpressionDateFromString::optimize() {
return this;
}
-Value ExpressionDateFromString::serialize(const SerializationOptions& options) const {
+Value ExpressionDateFromString::serialize(bool explain) const {
return Value(
Document{{"$dateFromString",
- Document{{"dateString", _dateString->serialize(options)},
- {"timezone", _timeZone ? _timeZone->serialize(options) : Value()},
- {"format", _format ? _format->serialize(options) : Value()},
- {"onNull", _onNull ? _onNull->serialize(options) : Value()},
- {"onError", _onError ? _onError->serialize(options) : Value()}}}});
+ Document{{"dateString", _dateString->serialize(explain)},
+ {"timezone", _timeZone ? _timeZone->serialize(explain) : Value()},
+ {"format", _format ? _format->serialize(explain) : Value()},
+ {"onNull", _onNull ? _onNull->serialize(explain) : Value()},
+ {"onError", _onError ? _onError->serialize(explain) : Value()}}}});
}
Value ExpressionDateFromString::evaluate(const Document& root, Variables* variables) const {
@@ -1879,12 +1760,12 @@ intrusive_ptr<Expression> ExpressionDateToParts::optimize() {
return this;
}
-Value ExpressionDateToParts::serialize(const SerializationOptions& options) const {
+Value ExpressionDateToParts::serialize(bool explain) const {
return Value(
Document{{"$dateToParts",
- Document{{"date", _date->serialize(options)},
- {"timezone", _timeZone ? _timeZone->serialize(options) : Value()},
- {"iso8601", _iso8601 ? _iso8601->serialize(options) : Value()}}}});
+ Document{{"date", _date->serialize(explain)},
+ {"timezone", _timeZone ? _timeZone->serialize(explain) : Value()},
+ {"iso8601", _iso8601 ? _iso8601->serialize(explain) : Value()}}}});
}
boost::optional<int> ExpressionDateToParts::evaluateIso8601Flag(const Document& root,
@@ -2037,13 +1918,13 @@ intrusive_ptr<Expression> ExpressionDateToString::optimize() {
return this;
}
-Value ExpressionDateToString::serialize(const SerializationOptions& options) const {
+Value ExpressionDateToString::serialize(bool explain) const {
return Value(
Document{{"$dateToString",
- Document{{"date", _date->serialize(options)},
- {"format", _format ? _format->serialize(options) : Value()},
- {"timezone", _timeZone ? _timeZone->serialize(options) : Value()},
- {"onNull", _onNull ? _onNull->serialize(options) : Value()}}}});
+ Document{{"date", _date->serialize(explain)},
+ {"format", _format ? _format->serialize(explain) : Value()},
+ {"timezone", _timeZone ? _timeZone->serialize(explain) : Value()},
+ {"onNull", _onNull ? _onNull->serialize(explain) : Value()}}}});
}
Value ExpressionDateToString::evaluate(const Document& root, Variables* variables) const {
@@ -2184,14 +2065,14 @@ boost::intrusive_ptr<Expression> ExpressionDateDiff::optimize() {
return this;
};
-Value ExpressionDateDiff::serialize(const SerializationOptions& options) const {
+Value ExpressionDateDiff::serialize(bool explain) const {
return Value{Document{
{"$dateDiff"_sd,
- Document{{"startDate"_sd, _startDate->serialize(options)},
- {"endDate"_sd, _endDate->serialize(options)},
- {"unit"_sd, _unit->serialize(options)},
- {"timezone"_sd, _timeZone ? _timeZone->serialize(options) : Value{}},
- {"startOfWeek"_sd, _startOfWeek ? _startOfWeek->serialize(options) : Value{}}}}}};
+ Document{{"startDate"_sd, _startDate->serialize(explain)},
+ {"endDate"_sd, _endDate->serialize(explain)},
+ {"unit"_sd, _unit->serialize(explain)},
+ {"timezone"_sd, _timeZone ? _timeZone->serialize(explain) : Value{}},
+ {"startOfWeek"_sd, _startOfWeek ? _startOfWeek->serialize(explain) : Value{}}}}}};
};
Date_t ExpressionDateDiff::convertToDate(const Value& value, StringData parameterName) {
@@ -2253,17 +2134,6 @@ void ExpressionDateDiff::_doAddDependencies(DepsTracker* deps) const {
}
}
-monotonic::State ExpressionDateDiff::getMonotonicState(const FieldPath& sortedFieldPath) const {
- if (!ExpressionConstant::allNullOrConstant({_unit, _timeZone, _startOfWeek})) {
- return monotonic::State::NonMonotonic;
- }
- // Because the result of this expression can be negative, this works the same way as
- // ExpressionSubtract. Edge cases with DST and other timezone changes are handled correctly
- // according to dateDiff.
- return monotonic::combine(_endDate->getMonotonicState(sortedFieldPath),
- monotonic::opposite(_startDate->getMonotonicState(sortedFieldPath)));
-}
-
/* ----------------------- ExpressionDivide ---------------------------- */
Value ExpressionDivide::evaluate(const Document& root, Variables* variables) const {
@@ -2405,24 +2275,10 @@ Value ExpressionObject::evaluate(const Document& root, Variables* variables) con
return outputDoc.freezeToValue();
}
-bool ExpressionObject::selfAndChildrenAreConstant() const {
- for (auto&& [_, exprPointer] : _expressions) {
- if (!exprPointer->selfAndChildrenAreConstant()) {
- return false;
- }
- }
- return true;
-}
-
-Value ExpressionObject::serialize(const SerializationOptions& options) const {
- if (options.literalPolicy != LiteralSerializationPolicy::kUnchanged &&
- selfAndChildrenAreConstant()) {
- return ExpressionConstant::serializeConstant(options, Value(Document{}));
- }
+Value ExpressionObject::serialize(bool explain) const {
MutableDocument outputDoc;
for (auto&& pair : _expressions) {
- outputDoc.addField(options.serializeFieldPathFromString(pair.first),
- pair.second->serialize(options));
+ outputDoc.addField(pair.first, pair.second->serialize(explain));
}
return outputDoc.freezeToValue();
}
@@ -2597,34 +2453,14 @@ Value ExpressionFieldPath::evaluate(const Document& root, Variables* variables)
}
}
-namespace {
-// Shared among expressions that need to serialize dotted paths and redact the path components.
-auto getPrefixAndPath(FieldPath path) {
- if (path.getFieldName(0) == "CURRENT" && path.getPathLength() > 1) {
+Value ExpressionFieldPath::serialize(bool explain) const {
+ if (_fieldPath.getFieldName(0) == "CURRENT" && _fieldPath.getPathLength() > 1) {
// use short form for "$$CURRENT.foo" but not just "$$CURRENT"
- return std::make_pair(std::string("$"), path.tail());
+ return Value("$" + _fieldPath.tail().fullPath());
} else {
- return std::make_pair(std::string("$$"), path);
+ return Value("$$" + _fieldPath.fullPath());
}
}
-} // namespace
-
-Value ExpressionFieldPath::serialize(const SerializationOptions& options) const {
- auto [prefix, path] = getPrefixAndPath(_fieldPath);
- // First handles special cases for redaction of system variables. User variables will fall
- // through to the default full redaction case.
- if (options.transformIdentifiers && prefix.length() == 2) {
- if (path.getPathLength() == 1 && Variables::isBuiltin(_variable)) {
- // Nothing to redact for builtin variables.
- return Value(prefix + path.fullPath());
- } else if (path.getPathLength() > 1 && Variables::isBuiltin(_variable)) {
- // The first component of this path is a system variable, so keep that and redact
- // the rest.
- return Value(prefix + path.front() + "." + options.serializeFieldPath(path.tail()));
- }
- }
- return Value(prefix + options.serializeFieldPath(path));
-}
Expression::ComputedPaths ExpressionFieldPath::getComputedPaths(const std::string& exprFieldPath,
Variables::Id renamingVar) const {
@@ -2671,11 +2507,6 @@ std::unique_ptr<Expression> ExpressionFieldPath::copyWithSubstitution(
return nullptr;
}
-monotonic::State ExpressionFieldPath::getMonotonicState(const FieldPath& sortedFieldPath) const {
- return getFieldPathWithoutCurrentPrefix() == sortedFieldPath ? monotonic::State::Increasing
- : monotonic::State::NonMonotonic;
-}
-
/* ------------------------- ExpressionFilter ----------------------------- */
REGISTER_STABLE_EXPRESSION(filter, ExpressionFilter::parse);
@@ -2765,14 +2596,14 @@ intrusive_ptr<Expression> ExpressionFilter::optimize() {
return this;
}
-Value ExpressionFilter::serialize(const SerializationOptions& options) const {
+Value ExpressionFilter::serialize(bool explain) const {
if (_limit) {
- return Value(DOC("$filter" << DOC("input" << _input->serialize(options) << "as" << _varName
- << "cond" << _cond->serialize(options) << "limit"
- << (*_limit)->serialize(options))));
+ return Value(DOC("$filter" << DOC("input" << _input->serialize(explain) << "as" << _varName
+ << "cond" << _cond->serialize(explain) << "limit"
+ << (*_limit)->serialize(explain))));
}
- return Value(DOC("$filter" << DOC("input" << _input->serialize(options) << "as" << _varName
- << "cond" << _cond->serialize(options))));
+ return Value(DOC("$filter" << DOC("input" << _input->serialize(explain) << "as" << _varName
+ << "cond" << _cond->serialize(explain))));
}
Value ExpressionFilter::evaluate(const Document& root, Variables* variables) const {
@@ -2964,19 +2795,15 @@ intrusive_ptr<Expression> ExpressionLet::optimize() {
return this;
}
-Value ExpressionLet::serialize(const SerializationOptions& options) const {
+Value ExpressionLet::serialize(bool explain) const {
MutableDocument vars;
for (VariableMap::const_iterator it = _variables.begin(), end = _variables.end(); it != end;
++it) {
- auto key = it->second.name;
- if (options.transformIdentifiers) {
- key = options.transformIdentifiersCallback(key);
- }
- vars[key] = it->second.expression->serialize(options);
+ vars[it->second.name] = it->second.expression->serialize(explain);
}
return Value(
- DOC("$let" << DOC("vars" << vars.freeze() << "in" << _subExpression->serialize(options))));
+ DOC("$let" << DOC("vars" << vars.freeze() << "in" << _subExpression->serialize(explain))));
}
Value ExpressionLet::evaluate(const Document& root, Variables* variables) const {
@@ -3070,9 +2897,9 @@ intrusive_ptr<Expression> ExpressionMap::optimize() {
return this;
}
-Value ExpressionMap::serialize(const SerializationOptions& options) const {
- return Value(DOC("$map" << DOC("input" << _input->serialize(options) << "as" << _varName << "in"
- << _each->serialize(options))));
+Value ExpressionMap::serialize(bool explain) const {
+ return Value(DOC("$map" << DOC("input" << _input->serialize(explain) << "as" << _varName << "in"
+ << _each->serialize(explain))));
}
Value ExpressionMap::evaluate(const Document& root, Variables* variables) const {
@@ -3154,14 +2981,11 @@ const std::string recordIdName = "recordId";
const std::string indexKeyName = "indexKey";
const std::string sortKeyName = "sortKey";
const std::string searchScoreDetailsName = "searchScoreDetails";
-const std::string searchSequenceTokenName = "searchSequenceToken";
const std::string timeseriesBucketMinTimeName = "timeseriesBucketMinTime";
const std::string timeseriesBucketMaxTimeName = "timeseriesBucketMaxTime";
-const std::string vectorSearchScoreName = "vectorSearchScore";
using MetaType = DocumentMetadataFields::MetaType;
const StringMap<DocumentMetadataFields::MetaType> kMetaNameToMetaType = {
- {vectorSearchScoreName, MetaType::kVectorSearchScore},
{geoNearDistanceName, MetaType::kGeoNearDist},
{geoNearPointName, MetaType::kGeoNearPoint},
{indexKeyName, MetaType::kIndexKey},
@@ -3170,7 +2994,6 @@ const StringMap<DocumentMetadataFields::MetaType> kMetaNameToMetaType = {
{searchHighlightsName, MetaType::kSearchHighlights},
{searchScoreName, MetaType::kSearchScore},
{searchScoreDetailsName, MetaType::kSearchScoreDetails},
- {searchSequenceTokenName, MetaType::kSearchSequenceToken},
{sortKeyName, MetaType::kSortKey},
{textScoreName, MetaType::kTextScore},
{timeseriesBucketMinTimeName, MetaType::kTimeseriesBucketMinTime},
@@ -3178,7 +3001,6 @@ const StringMap<DocumentMetadataFields::MetaType> kMetaNameToMetaType = {
};
const stdx::unordered_map<DocumentMetadataFields::MetaType, StringData> kMetaTypeToMetaName = {
- {MetaType::kVectorSearchScore, vectorSearchScoreName},
{MetaType::kGeoNearDist, geoNearDistanceName},
{MetaType::kGeoNearPoint, geoNearPointName},
{MetaType::kIndexKey, indexKeyName},
@@ -3187,7 +3009,6 @@ const stdx::unordered_map<DocumentMetadataFields::MetaType, StringData> kMetaTyp
{MetaType::kSearchHighlights, searchHighlightsName},
{MetaType::kSearchScore, searchScoreName},
{MetaType::kSearchScoreDetails, searchScoreDetailsName},
- {MetaType::kSearchSequenceToken, searchSequenceTokenName},
{MetaType::kSortKey, sortKeyName},
{MetaType::kTextScore, textScoreName},
{MetaType::kTimeseriesBucketMinTime, timeseriesBucketMinTimeName},
@@ -3214,7 +3035,7 @@ ExpressionMeta::ExpressionMeta(ExpressionContext* const expCtx, MetaType metaTyp
expCtx->sbeCompatible = false;
}
-Value ExpressionMeta::serialize(const SerializationOptions& options) const {
+Value ExpressionMeta::serialize(bool explain) const {
const auto nameIter = kMetaTypeToMetaName.find(_metaType);
invariant(nameIter != kMetaTypeToMetaName.end());
return Value(DOC("$meta" << nameIter->second));
@@ -3223,9 +3044,6 @@ Value ExpressionMeta::serialize(const SerializationOptions& options) const {
Value ExpressionMeta::evaluate(const Document& root, Variables* variables) const {
const auto& metadata = root.metadata();
switch (_metaType) {
- case MetaType::kVectorSearchScore:
- return metadata.hasVectorSearchScore() ? Value(metadata.getVectorSearchScore())
- : Value();
case MetaType::kTextScore:
return metadata.hasTextScore() ? Value(metadata.getTextScore()) : Value();
case MetaType::kRandVal:
@@ -3260,9 +3078,6 @@ Value ExpressionMeta::evaluate(const Document& root, Variables* variables) const
case MetaType::kSearchScoreDetails:
return metadata.hasSearchScoreDetails() ? Value(metadata.getSearchScoreDetails())
: Value();
- case MetaType::kSearchSequenceToken:
- return metadata.hasSearchSequenceToken() ? Value(metadata.getSearchSequenceToken())
- : Value();
case MetaType::kTimeseriesBucketMinTime:
return metadata.hasTimeseriesBucketMinTime()
? Value(metadata.getTimeseriesBucketMinTime())
@@ -3850,7 +3665,7 @@ Value ExpressionLn::evaluateNumericArg(const Value& numericArg) const {
if (numericArg.getType() == NumberDecimal) {
Decimal128 argDecimal = numericArg.getDecimal();
if (argDecimal.isGreater(Decimal128::kNormalizedZero))
- return Value(argDecimal.naturalLogarithm());
+ return Value(argDecimal.logarithm());
// Fall through for error case.
}
double argDouble = numericArg.coerceToDouble();
@@ -4004,9 +3819,9 @@ Value toValue(const std::array<std::uint8_t, 32>& buf) {
return Value(BSONBinData(vec.data(), vec.size(), BinDataType::Encrypt));
}
-Value ExpressionInternalFLEEqual::serialize(const SerializationOptions& options) const {
+Value ExpressionInternalFLEEqual::serialize(bool explain) const {
return Value(Document{{kInternalFleEq,
- Document{{"field", _children[0]->serialize(options)},
+ Document{{"field", _children[0]->serialize(explain)},
{"edc", toValue(_edcToken)},
{"counter", Value(static_cast<long long>(_contentionFactor))},
{"server", toValue(_serverToken)}}}});
@@ -4161,22 +3976,13 @@ void ExpressionNary::addOperand(const intrusive_ptr<Expression>& pExpression) {
_children.push_back(pExpression);
}
-Value ExpressionNary::serialize(const SerializationOptions& options) const {
+Value ExpressionNary::serialize(bool explain) const {
const size_t nOperand = _children.size();
vector<Value> array;
/* build up the array */
- for (size_t i = 0; i < nOperand; i++) {
- // If this input is a constant, bypass the standard serialization that wraps the
- // representative value in $const. This does not lead to ambiguity for variadic operators
- // but avoids bloating the representative shape for operators that have many inputs.
- ExpressionConstant const* exprConst = dynamic_cast<ExpressionConstant*>(_children[i].get());
- if (exprConst) {
- array.push_back(exprConst->serializeConstant(
- options, exprConst->getValue(), false /* wrapRepresentativeValue */));
- } else {
- array.push_back(_children[i]->serialize(options));
- }
- }
+ for (size_t i = 0; i < nOperand; i++)
+ array.push_back(_children[i]->serialize(explain));
+
return Value(DOC(getOpName() << array));
}
@@ -4641,11 +4447,11 @@ void ExpressionReduce::_doAddDependencies(DepsTracker* deps) const {
_in->addDependencies(deps);
}
-Value ExpressionReduce::serialize(const SerializationOptions& options) const {
+Value ExpressionReduce::serialize(bool explain) const {
return Value(Document{{"$reduce",
- Document{{"input", _input->serialize(options)},
- {"initialValue", _initial->serialize(options)},
- {"in", _in->serialize(options)}}}});
+ Document{{"input", _input->serialize(explain)},
+ {"initialValue", _initial->serialize(explain)},
+ {"in", _in->serialize(explain)}}}});
}
/* ------------------------ ExpressionReplaceBase ------------------------ */
@@ -4656,11 +4462,11 @@ void ExpressionReplaceBase::_doAddDependencies(DepsTracker* deps) const {
_replacement->addDependencies(deps);
}
-Value ExpressionReplaceBase::serialize(const SerializationOptions& options) const {
+Value ExpressionReplaceBase::serialize(bool explain) const {
return Value(Document{{getOpName(),
- Document{{"input", _input->serialize(options)},
- {"find", _find->serialize(options)},
- {"replacement", _replacement->serialize(options)}}}});
+ Document{{"input", _input->serialize(explain)},
+ {"find", _find->serialize(explain)},
+ {"replacement", _replacement->serialize(explain)}}}});
}
namespace {
@@ -4953,9 +4759,9 @@ void ExpressionSortArray::_doAddDependencies(DepsTracker* deps) const {
_input->addDependencies(deps);
}
-Value ExpressionSortArray::serialize(const SerializationOptions& options) const {
+Value ExpressionSortArray::serialize(bool explain) const {
return Value(Document{{kName,
- Document{{"input", _input->serialize(options)},
+ Document{{"input", _input->serialize(explain)},
{"sortBy", _sortBy.getOriginalElement()}}}});
}
@@ -5729,45 +5535,14 @@ StatusWith<Value> ExpressionSubtract::apply(Value lhs, Value rhs) {
} else if (lhs.nullish() || rhs.nullish()) {
return Value(BSONNULL);
} else if (lhs.getType() == Date) {
- BSONType rhsType = rhs.getType();
- switch (rhsType) {
- case Date:
- return Value(durationCount<Milliseconds>(lhs.getDate() - rhs.getDate()));
- case NumberInt:
- case NumberLong: {
- long long longDiff = lhs.getDate().toMillisSinceEpoch();
- if (overflow::sub(longDiff, rhs.coerceToLong(), &longDiff)) {
- return Status(ErrorCodes::Overflow, str::stream() << "date overflow");
- }
- return Value(Date_t::fromMillisSinceEpoch(longDiff));
- }
- case NumberDouble: {
- using limits = std::numeric_limits<long long>;
- long long longDiff = lhs.getDate().toMillisSinceEpoch();
- double doubleRhs = rhs.coerceToDouble();
- // check the doubleRhs should not exceed int64 limit and result will not overflow
- if (doubleRhs >= static_cast<double>(limits::min()) &&
- doubleRhs < static_cast<double>(limits::max()) &&
- !overflow::sub(longDiff, llround(doubleRhs), &longDiff)) {
- return Value(Date_t::fromMillisSinceEpoch(longDiff));
- }
- return Status(ErrorCodes::Overflow, str::stream() << "date overflow");
- }
- case NumberDecimal: {
- long long longDiff = lhs.getDate().toMillisSinceEpoch();
- Decimal128 decimalRhs = rhs.coerceToDecimal();
- std::uint32_t signalingFlags = Decimal128::SignalingFlag::kNoFlag;
- std::int64_t longRhs = decimalRhs.toLong(&signalingFlags);
- if (signalingFlags != Decimal128::SignalingFlag::kNoFlag ||
- overflow::sub(longDiff, longRhs, &longDiff)) {
- return Status(ErrorCodes::Overflow, str::stream() << "date overflow");
- }
- return Value(Date_t::fromMillisSinceEpoch(longDiff));
- }
- default:
- return Status(ErrorCodes::TypeMismatch,
- str::stream()
- << "can't $subtract " << typeName(rhs.getType()) << " from Date");
+ if (rhs.getType() == Date) {
+ return Value(durationCount<Milliseconds>(lhs.getDate() - rhs.getDate()));
+ } else if (rhs.numeric()) {
+ return Value(lhs.getDate() - Milliseconds(rhs.coerceToLong()));
+ } else {
+ return Status(ErrorCodes::TypeMismatch,
+ str::stream()
+ << "can't $subtract " << typeName(rhs.getType()) << " from Date");
}
} else {
return Status(ErrorCodes::TypeMismatch,
@@ -5781,35 +5556,24 @@ const char* ExpressionSubtract::getOpName() const {
return "$subtract";
}
-monotonic::State ExpressionSubtract::getMonotonicState(const FieldPath& sortedFieldPath) const {
- // 1. Get monotonic states of the both children.
- // 2. Apply monotonic::opposite to the state of the second child, because it is negated.
- // 3. Combine children. Function monotonic::combine correctly handles all the cases where, for
- // example, argumemnts are both monotonic, but in the opposite directions.
- return monotonic::combine(
- getChildren()[0]->getMonotonicState(sortedFieldPath),
- monotonic::opposite(getChildren()[1]->getMonotonicState(sortedFieldPath)));
-}
-
/* ------------------------- ExpressionSwitch ------------------------------ */
REGISTER_STABLE_EXPRESSION(switch, ExpressionSwitch::parse);
Value ExpressionSwitch::evaluate(const Document& root, Variables* variables) const {
- for (int i = 0; i < numBranches(); ++i) {
- auto [caseExpr, thenExpr] = getBranch(i);
- Value caseResult = caseExpr->evaluate(root, variables);
+ for (auto&& branch : _branches) {
+ Value caseExpression(branch.first->evaluate(root, variables));
- if (caseResult.coerceToBool()) {
- return thenExpr->evaluate(root, variables);
+ if (caseExpression.coerceToBool()) {
+ return branch.second->evaluate(root, variables);
}
}
uassert(40066,
"$switch could not find a matching branch for an input, and no default was specified.",
- defaultExpr());
+ _default);
- return defaultExpr()->evaluate(root, variables);
+ return _default->evaluate(root, variables);
}
boost::intrusive_ptr<Expression> ExpressionSwitch::parse(ExpressionContext* const expCtx,
@@ -5818,7 +5582,7 @@ boost::intrusive_ptr<Expression> ExpressionSwitch::parse(ExpressionContext* cons
uassert(40060,
str::stream() << "$switch requires an object as an argument, found: "
<< typeName(expr.type()),
- expr.type() == BSONType::Object);
+ expr.type() == Object);
boost::intrusive_ptr<Expression> expDefault;
std::vector<boost::intrusive_ptr<Expression>> children;
@@ -5830,13 +5594,13 @@ boost::intrusive_ptr<Expression> ExpressionSwitch::parse(ExpressionContext* cons
uassert(40061,
str::stream() << "$switch expected an array for 'branches', found: "
<< typeName(elem.type()),
- elem.type() == BSONType::Array);
+ elem.type() == Array);
for (auto&& branch : elem.Array()) {
uassert(40062,
str::stream() << "$switch expected each branch to be an object, found: "
<< typeName(branch.type()),
- branch.type() == BSONType::Object);
+ branch.type() == Object);
boost::intrusive_ptr<Expression> switchCase, switchThen;
@@ -5868,96 +5632,98 @@ boost::intrusive_ptr<Expression> ExpressionSwitch::parse(ExpressionContext* cons
uasserted(40067, str::stream() << "$switch found an unknown argument: " << field);
}
}
-
- // The the 'default' expression is always the final child. If no 'default' expression is
- // provided, then the final child is nullptr.
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;
+ }
+ }
- return new ExpressionSwitch(expCtx, std::move(children));
-}
+ uassert(40068, "$switch requires at least one branch.", !branches.empty());
-void ExpressionSwitch::deleteBranch(int i) {
- invariant(i >= 0);
- invariant(i < numBranches());
- // Delete the two elements corresponding to this branch at positions 2i and 2i + 1.
- _children.erase(std::next(_children.begin(), i * 2), std::next(_children.begin(), i * 2 + 2));
+ return new ExpressionSwitch(expCtx, std::move(children), std::move(branches));
}
void ExpressionSwitch::_doAddDependencies(DepsTracker* deps) const {
- for (auto&& child : _children) {
- // Check for nullptr, since we leave a nullptr as the final child when the 'default'
- // expression is missing.
- if (child) {
- child->addDependencies(deps);
- }
+ for (auto&& branch : _branches) {
+ branch.first->addDependencies(deps);
+ branch.second->addDependencies(deps);
+ }
+
+ if (_default) {
+ _default->addDependencies(deps);
}
}
boost::intrusive_ptr<Expression> ExpressionSwitch::optimize() {
- if (defaultExpr()) {
- _children.back() = _children.back()->optimize();
+ if (_default) {
+ _default = _default->optimize();
}
- bool trueConst = false;
+ std::vector<ExpressionPair>::iterator it = _branches.begin();
+ bool true_const = false;
- int i = 0;
- while (!trueConst && i < numBranches()) {
- boost::intrusive_ptr<Expression>& caseExpr = _children[i * 2];
- boost::intrusive_ptr<Expression>& thenExpr = _children[i * 2 + 1];
- caseExpr = caseExpr->optimize();
+ while (!true_const && it != _branches.end()) {
+ (it->first) = (it->first)->optimize();
- if (auto* val = dynamic_cast<ExpressionConstant*>(caseExpr.get())) {
- if (!val->getValue().coerceToBool()) {
+ if (auto* val = dynamic_cast<ExpressionConstant*>((it->first).get())) {
+ if (!((val->getValue()).coerceToBool())) {
// Case is constant and evaluates to false, so it is removed.
- deleteBranch(i);
+ it = _branches.erase(it);
} else {
- // Case optimized to a constant true value. Set the optimized version of the
- // corresponding 'then' expression as the new 'default'. Break out of the loop and
- // fall through to the logic to remove this and all subsequent branches.
- trueConst = true;
- _children.back() = thenExpr->optimize();
- break;
+ // Case is constant and true so it is set to default and then removed.
+ true_const = true;
+
+ // Optimizing this case's then, so that default will remain optimized.
+ (it->second) = (it->second)->optimize();
+ _default = it->second;
+ it = _branches.erase(it);
}
} else {
// Since case is not removed from the switch, its then is now optimized.
- thenExpr = thenExpr->optimize();
- ++i;
+ (it->second) = (it->second)->optimize();
+ ++it;
}
}
// Erasing the rest of the cases because found a default true value.
- if (trueConst) {
- while (i < numBranches()) {
- deleteBranch(i);
- }
+ if (true_const) {
+ _branches.erase(it, _branches.end());
}
// If there are no cases, make the switch its default.
- if (numBranches() == 0) {
+ if (_branches.size() == 0 && _default) {
+ return _default;
+ } else if (_branches.size() == 0) {
uassert(40069,
- "Cannot execute a switch statement where all the cases evaluate to false "
- "without a default",
- defaultExpr());
- return _children.back();
+ "One cannot execute a switch statement where all the cases evaluate to false "
+ "without a default.",
+ _branches.size());
}
return this;
}
-Value ExpressionSwitch::serialize(const SerializationOptions& options) const {
+Value ExpressionSwitch::serialize(bool explain) const {
std::vector<Value> serializedBranches;
- serializedBranches.reserve(numBranches());
+ serializedBranches.reserve(_branches.size());
- for (int i = 0; i < numBranches(); ++i) {
- auto [caseExpr, thenExpr] = getBranch(i);
- serializedBranches.push_back(Value(Document{{"case", caseExpr->serialize(options)},
- {"then", thenExpr->serialize(options)}}));
+ for (auto&& branch : _branches) {
+ serializedBranches.push_back(Value(Document{{"case", branch.first->serialize(explain)},
+ {"then", branch.second->serialize(explain)}}));
}
- if (defaultExpr()) {
+ if (_default) {
return Value(Document{{"$switch",
Document{{"branches", Value(serializedBranches)},
- {"default", defaultExpr()->serialize(options)}}}});
+ {"default", _default->serialize(explain)}}}});
}
return Value(Document{{"$switch", Document{{"branches", Value(serializedBranches)}}}});
@@ -6199,11 +5965,11 @@ boost::intrusive_ptr<Expression> ExpressionTrim::optimize() {
return this;
}
-Value ExpressionTrim::serialize(const SerializationOptions& options) const {
+Value ExpressionTrim::serialize(bool explain) const {
return Value(
Document{{_name,
- Document{{"input", _input->serialize(options)},
- {"chars", _characters ? _characters->serialize(options) : Value()}}}});
+ Document{{"input", _input->serialize(explain)},
+ {"chars", _characters ? _characters->serialize(explain) : Value()}}}});
}
void ExpressionTrim::_doAddDependencies(DepsTracker* deps) const {
@@ -6501,17 +6267,17 @@ boost::intrusive_ptr<Expression> ExpressionZip::optimize() {
return this;
}
-Value ExpressionZip::serialize(const SerializationOptions& options) const {
+Value ExpressionZip::serialize(bool explain) const {
vector<Value> serializedInput;
vector<Value> serializedDefaults;
Value serializedUseLongestLength = Value(_useLongestLength);
for (auto&& expr : _inputs) {
- serializedInput.push_back(expr.get()->serialize(options));
+ serializedInput.push_back(expr.get()->serialize(explain));
}
for (auto&& expr : _defaults) {
- serializedDefaults.push_back(expr.get()->serialize(options));
+ serializedDefaults.push_back(expr.get()->serialize(explain));
}
return Value(DOC("$zip" << DOC("inputs" << Value(serializedInput) << "defaults"
@@ -6905,7 +6671,7 @@ private:
} else if (doubleValue == 0.0 && std::signbit(doubleValue)) {
return Value("-0"_sd);
} else {
- return Value(static_cast<std::string>(str::stream() << fmt::format("{}", doubleValue)));
+ return Value(static_cast<std::string>(str::stream() << doubleValue));
}
}
@@ -7092,6 +6858,14 @@ boost::intrusive_ptr<Expression> ExpressionConvert::optimize() {
return this;
}
+Value ExpressionConvert::serialize(bool explain) const {
+ return Value(Document{{"$convert",
+ Document{{"input", _input->serialize(explain)},
+ {"to", _to->serialize(explain)},
+ {"onError", _onError ? _onError->serialize(explain) : Value()},
+ {"onNull", _onNull ? _onNull->serialize(explain) : Value()}}}});
+}
+
void ExpressionConvert::_doAddDependencies(DepsTracker* deps) const {
_input->addDependencies(deps);
_to->addDependencies(deps);
@@ -7103,29 +6877,6 @@ void ExpressionConvert::_doAddDependencies(DepsTracker* deps) const {
}
}
-Value ExpressionConvert::serialize(const SerializationOptions& options) const {
- // Since the 'to' field is a parameter from a set of valid values and not free user input,
- // we want to avoid boiling it down to the representative value in the query shape. The first
- // condition is so that we can keep serializing correctly whenever the 'to' field is an
- // expression that gets resolved down to a string of a valid type, or its corresponding
- // numerical value. If it's just the constant, we want to wrap it in a $const except when the
- // serialization policy is debug.
- auto constExpr = dynamic_cast<ExpressionConstant*>(_to.get());
- Value toField = Value();
- if (!constExpr) {
- toField = _to->serialize(options);
- } else if (options.literalPolicy == LiteralSerializationPolicy::kToDebugTypeString) {
- toField = constExpr->getValue();
- } else {
- toField = Value(DOC("$const" << constExpr->getValue()));
- }
- return Value(Document{{"$convert",
- Document{{"input", _input->serialize(options)},
- {"to", toField},
- {"onError", _onError ? _onError->serialize(options) : Value()},
- {"onNull", _onNull ? _onNull->serialize(options) : Value()}}}});
-}
-
BSONType ExpressionConvert::computeTargetType(Value targetTypeName) const {
BSONType targetType;
if (targetTypeName.getType() == BSONType::String) {
@@ -7388,12 +7139,12 @@ void ExpressionRegex::_compile(RegexExecutionState* executionState) const {
executionState->capturesBuffer.resize((1 + executionState->numCaptures) * 3);
}
-Value ExpressionRegex::serialize(const SerializationOptions& options) const {
+Value ExpressionRegex::serialize(bool explain) const {
return Value(
Document{{_opName,
- Document{{"input", _input->serialize(options)},
- {"regex", _regex->serialize(options)},
- {"options", _options ? _options->serialize(options) : Value()}}}});
+ Document{{"input", _input->serialize(explain)},
+ {"regex", _regex->serialize(explain)},
+ {"options", _options ? _options->serialize(explain) : Value()}}}});
}
void ExpressionRegex::_extractInputField(RegexExecutionState* executionState,
@@ -7656,7 +7407,7 @@ void ExpressionRandom::_doAddDependencies(DepsTracker* deps) const {
deps->needRandomGenerator = true;
}
-Value ExpressionRandom::serialize(const SerializationOptions& options) const {
+Value ExpressionRandom::serialize(const bool explain) const {
return Value(DOC(getOpName() << Document()));
}
@@ -7679,8 +7430,8 @@ Value ExpressionToHashedIndexKey::evaluate(const Document& root, Variables* vari
BSONElementHasher::DEFAULT_HASH_SEED));
}
-Value ExpressionToHashedIndexKey::serialize(const SerializationOptions& options) const {
- return Value(DOC("$toHashedIndexKey" << _children[0]->serialize(options)));
+Value ExpressionToHashedIndexKey::serialize(bool explain) const {
+ return Value(DOC("$toHashedIndexKey" << _children[0]->serialize(explain)));
}
void ExpressionToHashedIndexKey::_doAddDependencies(DepsTracker* deps) const {
@@ -7756,13 +7507,13 @@ boost::intrusive_ptr<Expression> ExpressionDateArithmetics::optimize() {
return intrusive_ptr<Expression>(this);
}
-Value ExpressionDateArithmetics::serialize(const SerializationOptions& options) const {
+Value ExpressionDateArithmetics::serialize(bool explain) const {
return Value(
Document{{_opName,
- Document{{"startDate", _startDate->serialize(options)},
- {"unit", _unit->serialize(options)},
- {"amount", _amount->serialize(options)},
- {"timezone", _timeZone ? _timeZone->serialize(options) : Value()}}}});
+ Document{{"startDate", _startDate->serialize(explain)},
+ {"unit", _unit->serialize(explain)},
+ {"amount", _amount->serialize(explain)},
+ {"timezone", _timeZone ? _timeZone->serialize(explain) : Value()}}}});
}
Value ExpressionDateArithmetics::evaluate(const Document& root, Variables* variables) const {
@@ -7800,15 +7551,6 @@ Value ExpressionDateArithmetics::evaluate(const Document& root, Variables* varia
startDate.coerceToDate(), unit, amount.coerceToLong(), timezone.get());
}
-monotonic::State ExpressionDateArithmetics::getMonotonicState(
- const FieldPath& sortedFieldPath) const {
- if (!ExpressionConstant::allNullOrConstant({_unit, _timeZone})) {
- return monotonic::State::NonMonotonic;
- }
- return combineMonotonicStateOfArguments(_startDate->getMonotonicState(sortedFieldPath),
- _amount->getMonotonicState(sortedFieldPath));
-}
-
/* ----------------------- ExpressionDateAdd ---------------------------- */
REGISTER_STABLE_EXPRESSION(dateAdd, ExpressionDateAdd::parse);
@@ -7833,11 +7575,6 @@ Value ExpressionDateAdd::evaluateDateArithmetics(Date_t date,
return Value(dateAdd(date, unit, amount, timezone));
}
-monotonic::State ExpressionDateAdd::combineMonotonicStateOfArguments(
- monotonic::State startDataMonotonicState, monotonic::State amountMonotonicState) const {
- return monotonic::combine(startDataMonotonicState, amountMonotonicState);
-}
-
/* ----------------------- ExpressionDateSubtract ---------------------------- */
REGISTER_STABLE_EXPRESSION(dateSubtract, ExpressionDateSubtract::parse);
@@ -7867,11 +7604,6 @@ Value ExpressionDateSubtract::evaluateDateArithmetics(Date_t date,
return Value(dateAdd(date, unit, -amount, timezone));
}
-monotonic::State ExpressionDateSubtract::combineMonotonicStateOfArguments(
- monotonic::State startDataMonotonicState, monotonic::State amountMonotonicState) const {
- return monotonic::combine(startDataMonotonicState, amountMonotonicState);
-}
-
/* ----------------------- ExpressionDateTrunc ---------------------------- */
REGISTER_STABLE_EXPRESSION(dateTrunc, ExpressionDateTrunc::parse);
@@ -7955,14 +7687,14 @@ boost::intrusive_ptr<Expression> ExpressionDateTrunc::optimize() {
return this;
};
-Value ExpressionDateTrunc::serialize(const SerializationOptions& options) const {
+Value ExpressionDateTrunc::serialize(bool explain) const {
return Value{Document{
{"$dateTrunc"_sd,
- Document{{"date"_sd, _date->serialize(options)},
- {"unit"_sd, _unit->serialize(options)},
- {"binSize"_sd, _binSize ? _binSize->serialize(options) : Value{}},
- {"timezone"_sd, _timeZone ? _timeZone->serialize(options) : Value{}},
- {"startOfWeek"_sd, _startOfWeek ? _startOfWeek->serialize(options) : Value{}}}}}};
+ Document{{"date"_sd, _date->serialize(explain)},
+ {"unit"_sd, _unit->serialize(explain)},
+ {"binSize"_sd, _binSize ? _binSize->serialize(explain) : Value{}},
+ {"timezone"_sd, _timeZone ? _timeZone->serialize(explain) : Value{}},
+ {"startOfWeek"_sd, _startOfWeek ? _startOfWeek->serialize(explain) : Value{}}}}}};
};
Date_t ExpressionDateTrunc::convertToDate(const Value& value) {
@@ -8044,13 +7776,6 @@ void ExpressionDateTrunc::_doAddDependencies(DepsTracker* deps) const {
}
}
-monotonic::State ExpressionDateTrunc::getMonotonicState(const FieldPath& sortedFieldPath) const {
- if (!ExpressionConstant::allNullOrConstant({_unit, _binSize, _timeZone, _startOfWeek})) {
- return monotonic::State::NonMonotonic;
- }
- return _date->getMonotonicState(sortedFieldPath);
-}
-
/* -------------------------- ExpressionGetField ------------------------------ */
REGISTER_EXPRESSION_WITH_MIN_VERSION(
getField,
@@ -8144,6 +7869,7 @@ Value ExpressionGetField::evaluate(const Document& root, Variables* variables) c
return Value();
}
+
return inputValue.getDocument().getField(fieldValue.getString());
}
@@ -8156,22 +7882,10 @@ void ExpressionGetField::_doAddDependencies(DepsTracker* deps) const {
_field->addDependencies(deps);
}
-Value ExpressionGetField::serialize(const SerializationOptions& options) const {
- // The parser guarantees that the '_field' expression evaluates to a constant string.
- auto strPath = static_cast<ExpressionConstant*>(_field.get())->getValue().getString();
-
- Value maybeRedactedPath{options.serializeFieldPathFromString(strPath)};
- // This is a pretty unique option to serialize. It is both a constant and a field path, which
- // means that it:
- // - should be redacted (if that option is set).
- // - should *not* be wrapped in $const iff we are serializing for a debug string
- if (options.literalPolicy != LiteralSerializationPolicy::kToDebugTypeString) {
- maybeRedactedPath = Value(Document{{"$const"_sd, maybeRedactedPath}});
- }
-
+Value ExpressionGetField::serialize(const bool explain) const {
return Value(Document{{"$getField"_sd,
- Document{{"field"_sd, std::move(maybeRedactedPath)},
- {"input"_sd, _input->serialize(options)}}}});
+ Document{{"field"_sd, _field->serialize(explain)},
+ {"input"_sd, _input->serialize(explain)}}}});
}
/* -------------------------- ExpressionSetField ------------------------------ */
@@ -8295,23 +8009,11 @@ void ExpressionSetField::_doAddDependencies(DepsTracker* deps) const {
_value->addDependencies(deps);
}
-Value ExpressionSetField::serialize(const SerializationOptions& options) const {
- // The parser guarantees that the '_field' expression evaluates to a constant string.
- auto strPath = static_cast<ExpressionConstant*>(_field.get())->getValue().getString();
-
- Value maybeRedactedPath{options.serializeFieldPathFromString(strPath)};
- // This is a pretty unique option to serialize. It is both a constant and a field path, which
- // means that it:
- // - should be redacted (if that option is set).
- // - should *not* be wrapped in $const iff we are serializing for a debug string
- if (options.literalPolicy != LiteralSerializationPolicy::kToDebugTypeString) {
- maybeRedactedPath = Value(Document{{"$const"_sd, maybeRedactedPath}});
- }
-
+Value ExpressionSetField::serialize(const bool explain) const {
return Value(Document{{"$setField"_sd,
- Document{{"field"_sd, std::move(maybeRedactedPath)},
- {"input"_sd, _input->serialize(options)},
- {"value"_sd, _value->serialize(options)}}}});
+ Document{{"field"_sd, _field->serialize(explain)},
+ {"input"_sd, _input->serialize(explain)},
+ {"value"_sd, _value->serialize(explain)}}}});
}
/* ------------------------- ExpressionTsSecond ----------------------------- */
@@ -8364,90 +8066,4 @@ REGISTER_EXPRESSION_WITH_MIN_VERSION(
MONGO_INITIALIZER_GROUP(BeginExpressionRegistration, ("default"), ("EndExpressionRegistration"))
MONGO_INITIALIZER_GROUP(EndExpressionRegistration, ("BeginExpressionRegistration"), ())
-
-/* ----------------------- ExpressionInternalKeyStringValue ---------------------------- */
-
-REGISTER_STABLE_EXPRESSION(_internalKeyStringValue, ExpressionInternalKeyStringValue::parse);
-
-boost::intrusive_ptr<Expression> ExpressionInternalKeyStringValue::parse(
- ExpressionContext* expCtx, BSONElement expr, const VariablesParseState& vps) {
-
- uassert(
- 8281500,
- str::stream() << "$_internalKeyStringValue only supports an object as its argument, not "
- << typeName(expr.type()),
- expr.type() == BSONType::Object);
-
- boost::intrusive_ptr<Expression> inputExpr;
- boost::intrusive_ptr<Expression> collationExpr;
-
- for (auto&& element : expr.embeddedObject()) {
- auto field = element.fieldNameStringData();
- if ("input"_sd == field) {
- inputExpr = parseOperand(expCtx, element, vps);
- } else if ("collation"_sd == field) {
- collationExpr = parseOperand(expCtx, element, vps);
- } else {
- uasserted(8281501,
- str::stream() << "Unrecognized argument to $_internalKeyStringValue: "
- << element.fieldName());
- }
- }
- uassert(8281502,
- str::stream() << "$_internalKeyStringValue requires 'input' to be specified",
- inputExpr);
-
- return make_intrusive<ExpressionInternalKeyStringValue>(expCtx, inputExpr, collationExpr);
-}
-
-Value ExpressionInternalKeyStringValue::serialize(const SerializationOptions& options) const {
- return Value(
- Document{{getOpName(),
- Document{{"input", _children[_kInput]->serialize(options)},
- {"collation",
- _children[_kCollation] ? _children[_kCollation]->serialize(options)
- : Value()}}}});
-}
-
-Value ExpressionInternalKeyStringValue::evaluate(const Document& root, Variables* variables) const {
- const Value input = _children[_kInput]->evaluate(root, variables);
- auto inputBson = input.wrap("");
-
- std::unique_ptr<CollatorInterface> collator = nullptr;
- if (_children[_kCollation]) {
- const Value collation = _children[_kCollation]->evaluate(root, variables);
- uassert(8281503,
- str::stream() << "Collation spec must be an object, not "
- << typeName(collation.getType()),
- collation.isObject());
- auto collationBson = collation.getDocument().toBson();
-
- auto collatorFactory =
- CollatorFactoryInterface::get(getExpressionContext()->opCtx->getServiceContext());
- collator = uassertStatusOKWithContext(collatorFactory->makeFromBSON(collationBson),
- "Invalid collation spec");
- }
-
- KeyString::HeapBuilder ksBuilder(KeyString::Version::V1);
- if (collator) {
- ksBuilder.appendBSONElement(inputBson.firstElement(), [&](StringData str) {
- return collator->getComparisonString(str);
- });
- } else {
- ksBuilder.appendBSONElement(inputBson.firstElement());
- }
- auto ksValue = ksBuilder.release();
-
- // The result omits the typebits so that the numeric value of different types have the same
- // binary representation.
- return Value(
- BSONBinData{ksValue.getBuffer(), static_cast<int>(ksValue.getSize()), BinDataGeneral});
-}
-
-void ExpressionInternalKeyStringValue::_doAddDependencies(DepsTracker* deps) const {
- _children[_kInput]->addDependencies(deps);
- if (_children[_kCollation]) {
- _children[_kCollation]->addDependencies(deps);
- }
-}
} // namespace mongo