summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTed Tuckman <ted.tuckman@mongodb.com>2023-02-14 16:26:06 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2023-02-14 20:28:18 +0000
commit9355b6a325e2606e41198caa07ded85e68a5b353 (patch)
tree3ecd3efe943493524b16fd085cb28a77f66c0a75
parent7923c15b7d285d18a0c81a1e55dc939c7320af39 (diff)
downloadmongo-9355b6a325e2606e41198caa07ded85e68a5b353.tar.gz
SERVER-73324 Literal and field name redaction for ExpressionFieldPath and ExpressionConstant
-rw-r--r--src/mongo/db/pipeline/accumulator_multi.cpp6
-rw-r--r--src/mongo/db/pipeline/accumulator_multi.h2
-rw-r--r--src/mongo/db/pipeline/expression.cpp255
-rw-r--r--src/mongo/db/pipeline/expression.h75
-rw-r--r--src/mongo/db/pipeline/expression_field_path_test.cpp65
-rw-r--r--src/mongo/db/pipeline/expression_find_internal.h6
-rw-r--r--src/mongo/db/pipeline/expression_function.cpp4
-rw-r--r--src/mongo/db/pipeline/expression_function.h2
-rw-r--r--src/mongo/db/pipeline/expression_js_emit.cpp4
-rw-r--r--src/mongo/db/pipeline/expression_js_emit.h2
-rw-r--r--src/mongo/db/pipeline/expression_test.cpp25
-rw-r--r--src/mongo/db/pipeline/expression_test_api_version.cpp2
-rw-r--r--src/mongo/db/pipeline/expression_test_api_version.h2
-rw-r--r--src/mongo/db/query/serialization_options.h22
-rw-r--r--src/mongo/s/commands/sharding_expressions.cpp4
-rw-r--r--src/mongo/s/commands/sharding_expressions.h2
16 files changed, 304 insertions, 174 deletions
diff --git a/src/mongo/db/pipeline/accumulator_multi.cpp b/src/mongo/db/pipeline/accumulator_multi.cpp
index 5a478e7e7d5..c8b33426cb8 100644
--- a/src/mongo/db/pipeline/accumulator_multi.cpp
+++ b/src/mongo/db/pipeline/accumulator_multi.cpp
@@ -158,10 +158,10 @@ void AccumulatorN::updateAndCheckMemUsage(size_t memAdded) {
void AccumulatorN::serializeHelper(const boost::intrusive_ptr<Expression>& initializer,
const boost::intrusive_ptr<Expression>& argument,
- bool explain,
+ SerializationOptions options,
MutableDocument& md) {
- md.addField(kFieldNameN, Value(initializer->serialize(explain)));
- md.addField(kFieldNameInput, Value(argument->serialize(explain)));
+ md.addField(kFieldNameN, Value(initializer->serialize(options)));
+ md.addField(kFieldNameInput, Value(argument->serialize(options)));
}
template <MinMaxSense s>
diff --git a/src/mongo/db/pipeline/accumulator_multi.h b/src/mongo/db/pipeline/accumulator_multi.h
index 1118a9421ff..f7d5e6b68bb 100644
--- a/src/mongo/db/pipeline/accumulator_multi.h
+++ b/src/mongo/db/pipeline/accumulator_multi.h
@@ -85,7 +85,7 @@ public:
*/
static void serializeHelper(const boost::intrusive_ptr<Expression>& initializer,
const boost::intrusive_ptr<Expression>& argument,
- bool explain,
+ SerializationOptions options,
MutableDocument& md);
protected:
diff --git a/src/mongo/db/pipeline/expression.cpp b/src/mongo/db/pipeline/expression.cpp
index 3a5e4107e27..8981a1c6731 100644
--- a/src/mongo/db/pipeline/expression.cpp
+++ b/src/mongo/db/pipeline/expression.cpp
@@ -639,11 +639,11 @@ Value ExpressionArray::evaluate(const Document& root, Variables* variables) cons
return Value(std::move(values));
}
-Value ExpressionArray::serialize(bool explain) const {
+Value ExpressionArray::serialize(SerializationOptions options) const {
vector<Value> expressions;
expressions.reserve(_children.size());
for (auto&& expr : _children) {
- expressions.push_back(expr->serialize(explain));
+ expressions.push_back(expr->serialize(options));
}
return Value(std::move(expressions));
}
@@ -962,11 +962,11 @@ Value ExpressionCoerceToBool::evaluate(const Document& root, Variables* variable
return Value(false);
}
-Value ExpressionCoerceToBool::serialize(bool explain) const {
+Value ExpressionCoerceToBool::serialize(SerializationOptions options) const {
// When not explaining, serialize to an $and expression. When parsed, the $and expression
// will be optimized back into a ExpressionCoerceToBool.
- const char* name = explain ? "$coerceToBool" : "$and";
- return Value(DOC(name << DOC_ARRAY(_children[_kExpression]->serialize(explain))));
+ const char* name = options.explain ? "$coerceToBool" : "$and";
+ return Value(DOC(name << DOC_ARRAY(_children[_kExpression]->serialize(options))));
}
/* ----------------------- ExpressionCompare --------------------------- */
@@ -1211,7 +1211,10 @@ Value ExpressionConstant::evaluate(const Document& root, Variables* variables) c
return _value;
}
-Value ExpressionConstant::serialize(bool explain) const {
+Value ExpressionConstant::serialize(SerializationOptions options) const {
+ if (options.replacementForLiteralArgs) {
+ return Value(DOC("$const" << *options.replacementForLiteralArgs));
+ }
return serializeConstant(_value);
}
@@ -1424,25 +1427,25 @@ intrusive_ptr<Expression> ExpressionDateFromParts::optimize() {
return this;
}
-Value ExpressionDateFromParts::serialize(bool explain) const {
+Value ExpressionDateFromParts::serialize(SerializationOptions options) const {
return Value(Document{
{"$dateFromParts",
Document{
- {"year", _children[_kYear] ? _children[_kYear]->serialize(explain) : Value()},
- {"month", _children[_kMonth] ? _children[_kMonth]->serialize(explain) : Value()},
- {"day", _children[_kDay] ? _children[_kDay]->serialize(explain) : Value()},
- {"hour", _children[_kHour] ? _children[_kHour]->serialize(explain) : Value()},
- {"minute", _children[_kMinute] ? _children[_kMinute]->serialize(explain) : Value()},
- {"second", _children[_kSecond] ? _children[_kSecond]->serialize(explain) : Value()},
+ {"year", _children[_kYear] ? _children[_kYear]->serialize(options) : Value()},
+ {"month", _children[_kMonth] ? _children[_kMonth]->serialize(options) : Value()},
+ {"day", _children[_kDay] ? _children[_kDay]->serialize(options) : Value()},
+ {"hour", _children[_kHour] ? _children[_kHour]->serialize(options) : Value()},
+ {"minute", _children[_kMinute] ? _children[_kMinute]->serialize(options) : Value()},
+ {"second", _children[_kSecond] ? _children[_kSecond]->serialize(options) : Value()},
{"millisecond",
- _children[_kMillisecond] ? _children[_kMillisecond]->serialize(explain) : Value()},
+ _children[_kMillisecond] ? _children[_kMillisecond]->serialize(options) : Value()},
{"isoWeekYear",
- _children[_kIsoWeekYear] ? _children[_kIsoWeekYear]->serialize(explain) : Value()},
- {"isoWeek", _children[_kIsoWeek] ? _children[_kIsoWeek]->serialize(explain) : Value()},
+ _children[_kIsoWeekYear] ? _children[_kIsoWeekYear]->serialize(options) : Value()},
+ {"isoWeek", _children[_kIsoWeek] ? _children[_kIsoWeek]->serialize(options) : Value()},
{"isoDayOfWeek",
- _children[_kIsoDayOfWeek] ? _children[_kIsoDayOfWeek]->serialize(explain) : Value()},
+ _children[_kIsoDayOfWeek] ? _children[_kIsoDayOfWeek]->serialize(options) : Value()},
{"timezone",
- _children[_kTimeZone] ? _children[_kTimeZone]->serialize(explain) : Value()}}}});
+ _children[_kTimeZone] ? _children[_kTimeZone]->serialize(options) : Value()}}}});
}
bool ExpressionDateFromParts::evaluateNumberWithDefault(const Document& root,
@@ -1668,17 +1671,17 @@ intrusive_ptr<Expression> ExpressionDateFromString::optimize() {
return this;
}
-Value ExpressionDateFromString::serialize(bool explain) const {
+Value ExpressionDateFromString::serialize(SerializationOptions options) const {
return Value(Document{
{"$dateFromString",
Document{
- {"dateString", _children[_kDateString]->serialize(explain)},
+ {"dateString", _children[_kDateString]->serialize(options)},
{"timezone",
- _children[_kTimeZone] ? _children[_kTimeZone]->serialize(explain) : Value()},
- {"format", _children[_kFormat] ? _children[_kFormat]->serialize(explain) : Value()},
- {"onNull", _children[_kOnNull] ? _children[_kOnNull]->serialize(explain) : Value()},
+ _children[_kTimeZone] ? _children[_kTimeZone]->serialize(options) : Value()},
+ {"format", _children[_kFormat] ? _children[_kFormat]->serialize(options) : Value()},
+ {"onNull", _children[_kOnNull] ? _children[_kOnNull]->serialize(options) : Value()},
{"onError",
- _children[_kOnError] ? _children[_kOnError]->serialize(explain) : Value()}}}});
+ _children[_kOnError] ? _children[_kOnError]->serialize(options) : Value()}}}});
}
Value ExpressionDateFromString::evaluate(const Document& root, Variables* variables) const {
@@ -1821,14 +1824,14 @@ intrusive_ptr<Expression> ExpressionDateToParts::optimize() {
return this;
}
-Value ExpressionDateToParts::serialize(bool explain) const {
+Value ExpressionDateToParts::serialize(SerializationOptions options) const {
return Value(Document{
{"$dateToParts",
- Document{{"date", _children[_kDate]->serialize(explain)},
+ Document{{"date", _children[_kDate]->serialize(options)},
{"timezone",
- _children[_kTimeZone] ? _children[_kTimeZone]->serialize(explain) : Value()},
+ _children[_kTimeZone] ? _children[_kTimeZone]->serialize(options) : Value()},
{"iso8601",
- _children[_kIso8601] ? _children[_kIso8601]->serialize(explain) : Value()}}}});
+ _children[_kIso8601] ? _children[_kIso8601]->serialize(options) : Value()}}}});
}
boost::optional<int> ExpressionDateToParts::evaluateIso8601Flag(const Document& root,
@@ -1976,16 +1979,16 @@ intrusive_ptr<Expression> ExpressionDateToString::optimize() {
return this;
}
-Value ExpressionDateToString::serialize(bool explain) const {
+Value ExpressionDateToString::serialize(SerializationOptions options) const {
return Value(Document{
{"$dateToString",
Document{
- {"date", _children[_kDate]->serialize(explain)},
- {"format", _children[_kFormat] ? _children[_kFormat]->serialize(explain) : Value()},
+ {"date", _children[_kDate]->serialize(options)},
+ {"format", _children[_kFormat] ? _children[_kFormat]->serialize(options) : Value()},
{"timezone",
- _children[_kTimeZone] ? _children[_kTimeZone]->serialize(explain) : Value()},
+ _children[_kTimeZone] ? _children[_kTimeZone]->serialize(options) : Value()},
{"onNull",
- _children[_kOnNull] ? _children[_kOnNull]->serialize(explain) : Value()}}}});
+ _children[_kOnNull] ? _children[_kOnNull]->serialize(options) : Value()}}}});
}
Value ExpressionDateToString::evaluate(const Document& root, Variables* variables) const {
@@ -2142,16 +2145,16 @@ boost::intrusive_ptr<Expression> ExpressionDateDiff::optimize() {
return this;
};
-Value ExpressionDateDiff::serialize(bool explain) const {
+Value ExpressionDateDiff::serialize(SerializationOptions options) const {
return Value{Document{
{"$dateDiff"_sd,
- Document{{"startDate"_sd, _children[_kStartDate]->serialize(explain)},
- {"endDate"_sd, _children[_kEndDate]->serialize(explain)},
- {"unit"_sd, _children[_kUnit]->serialize(explain)},
+ Document{{"startDate"_sd, _children[_kStartDate]->serialize(options)},
+ {"endDate"_sd, _children[_kEndDate]->serialize(options)},
+ {"unit"_sd, _children[_kUnit]->serialize(options)},
{"timezone"_sd,
- _children[_kTimeZone] ? _children[_kTimeZone]->serialize(explain) : Value{}},
+ _children[_kTimeZone] ? _children[_kTimeZone]->serialize(options) : Value{}},
{"startOfWeek"_sd,
- _children[_kStartOfWeek] ? _children[_kStartOfWeek]->serialize(explain)
+ _children[_kStartOfWeek] ? _children[_kStartOfWeek]->serialize(options)
: Value{}}}}}};
};
@@ -2365,10 +2368,10 @@ Value ExpressionObject::evaluate(const Document& root, Variables* variables) con
return outputDoc.freezeToValue();
}
-Value ExpressionObject::serialize(bool explain) const {
+Value ExpressionObject::serialize(SerializationOptions options) const {
MutableDocument outputDoc;
for (auto&& pair : _expressions) {
- outputDoc.addField(pair.first, pair.second->serialize(explain));
+ outputDoc.addField(pair.first, pair.second->serialize(options));
}
return outputDoc.freezeToValue();
}
@@ -2531,12 +2534,34 @@ Value ExpressionFieldPath::evaluate(const Document& root, Variables* variables)
}
}
-Value ExpressionFieldPath::serialize(bool explain) const {
- if (_fieldPath.getFieldName(0) == "CURRENT" && _fieldPath.getPathLength() > 1) {
- // use short form for "$$CURRENT.foo" but not just "$$CURRENT"
- return Value("$" + _fieldPath.tail().fullPath());
+Value ExpressionFieldPath::serialize(SerializationOptions options) const {
+ auto&& [prefix, path] = [&]() {
+ if (_fieldPath.getFieldName(0) == "CURRENT" && _fieldPath.getPathLength() > 1) {
+ // use short form for "$$CURRENT.foo" but not just "$$CURRENT"
+ return std::make_pair(std::string("$"), _fieldPath.tail());
+ } else {
+ return std::make_pair(std::string("$$"), _fieldPath);
+ }
+ }();
+ if (options.redactFieldNames) {
+ std::stringstream redacted;
+ redacted << prefix;
+ size_t startPos = 0;
+ // Check if our prefix indicates this path begins with a system variable.
+ if (prefix.length() == 2) {
+ // Don't redact a variable reference.
+ redacted << path.getFieldName(0);
+ ++startPos;
+ }
+ for (size_t i = startPos; i < path.getPathLength(); ++i) {
+ if (i > 0) {
+ redacted << ".";
+ }
+ redacted << options.redactFieldNamesStrategy(path.getFieldName(i));
+ }
+ return Value(redacted.str());
} else {
- return Value("$$" + _fieldPath.fullPath());
+ return Value(prefix + path.fullPath());
}
}
@@ -2677,16 +2702,16 @@ intrusive_ptr<Expression> ExpressionFilter::optimize() {
return this;
}
-Value ExpressionFilter::serialize(bool explain) const {
+Value ExpressionFilter::serialize(SerializationOptions options) const {
if (_limit) {
return Value(DOC(
- "$filter" << DOC("input" << _children[_kInput]->serialize(explain) << "as" << _varName
- << "cond" << _children[_kCond]->serialize(explain) << "limit"
- << (_children[*_limit])->serialize(explain))));
+ "$filter" << DOC("input" << _children[_kInput]->serialize(options) << "as" << _varName
+ << "cond" << _children[_kCond]->serialize(options) << "limit"
+ << (_children[*_limit])->serialize(options))));
}
return Value(
- DOC("$filter" << DOC("input" << _children[_kInput]->serialize(explain) << "as" << _varName
- << "cond" << _children[_kCond]->serialize(explain))));
+ DOC("$filter" << DOC("input" << _children[_kInput]->serialize(options) << "as" << _varName
+ << "cond" << _children[_kCond]->serialize(options))));
}
Value ExpressionFilter::evaluate(const Document& root, Variables* variables) const {
@@ -2870,15 +2895,15 @@ intrusive_ptr<Expression> ExpressionLet::optimize() {
return this;
}
-Value ExpressionLet::serialize(bool explain) const {
+Value ExpressionLet::serialize(SerializationOptions options) const {
MutableDocument vars;
for (VariableMap::const_iterator it = _variables.begin(), end = _variables.end(); it != end;
++it) {
- vars[it->second.name] = it->second.expression->serialize(explain);
+ vars[it->second.name] = it->second.expression->serialize(options);
}
return Value(DOC("$let" << DOC("vars" << vars.freeze() << "in"
- << _children[_kSubExpression]->serialize(explain))));
+ << _children[_kSubExpression]->serialize(options))));
}
Value ExpressionLet::evaluate(const Document& root, Variables* variables) const {
@@ -2958,10 +2983,10 @@ intrusive_ptr<Expression> ExpressionMap::optimize() {
return this;
}
-Value ExpressionMap::serialize(bool explain) const {
+Value ExpressionMap::serialize(SerializationOptions options) const {
return Value(
- DOC("$map" << DOC("input" << _children[_kInput]->serialize(explain) << "as" << _varName
- << "in" << _children[_kEach]->serialize(explain))));
+ DOC("$map" << DOC("input" << _children[_kInput]->serialize(options) << "as" << _varName
+ << "in" << _children[_kEach]->serialize(options))));
}
Value ExpressionMap::evaluate(const Document& root, Variables* variables) const {
@@ -3109,7 +3134,7 @@ ExpressionMeta::ExpressionMeta(ExpressionContext* const expCtx, MetaType metaTyp
expCtx->sbeCompatible = false;
}
-Value ExpressionMeta::serialize(bool explain) const {
+Value ExpressionMeta::serialize(SerializationOptions options) const {
const auto nameIter = kMetaTypeToMetaName.find(_metaType);
invariant(nameIter != kMetaTypeToMetaName.end());
return Value(DOC("$meta" << nameIter->second));
@@ -3859,10 +3884,10 @@ Value toValue(const std::array<std::uint8_t, 32>& buf) {
return Value(BSONBinData(vec.data(), vec.size(), BinDataType::Encrypt));
}
-Value ExpressionInternalFLEEqual::serialize(bool explain) const {
+Value ExpressionInternalFLEEqual::serialize(SerializationOptions options) const {
return Value(Document{
{kInternalFleEq,
- Document{{"field", _children[0]->serialize(explain)},
+ Document{{"field", _children[0]->serialize(options)},
{"edc", toValue(_evaluator.edcTokens()[0])},
{"counter", Value(static_cast<long long>(_evaluator.contentionFactor()))},
{"server", toValue(_evaluator.serverToken())}}}});
@@ -3935,7 +3960,7 @@ intrusive_ptr<Expression> ExpressionInternalFLEBetween::parse(ExpressionContext*
expCtx, std::move(fieldExpr), serverTokenPair.second, cf, edcTokens);
}
-Value ExpressionInternalFLEBetween::serialize(bool explain) const {
+Value ExpressionInternalFLEBetween::serialize(SerializationOptions options) const {
std::vector<Value> edcValues;
edcValues.reserve(_evaluator.edcTokens().size());
for (auto& token : _evaluator.edcTokens()) {
@@ -3943,7 +3968,7 @@ Value ExpressionInternalFLEBetween::serialize(bool explain) const {
}
return Value(Document{
{kInternalFleBetween,
- Document{{"field", _children[0]->serialize(explain)},
+ Document{{"field", _children[0]->serialize(options)},
{"edc", Value(edcValues)},
{"counter", Value(static_cast<long long>(_evaluator.contentionFactor()))},
{"server", toValue(_evaluator.serverToken())}}}});
@@ -4097,12 +4122,12 @@ void ExpressionNary::addOperand(const intrusive_ptr<Expression>& pExpression) {
_children.push_back(pExpression);
}
-Value ExpressionNary::serialize(bool explain) const {
+Value ExpressionNary::serialize(SerializationOptions options) const {
const size_t nOperand = _children.size();
vector<Value> array;
/* build up the array */
for (size_t i = 0; i < nOperand; i++)
- array.push_back(_children[i]->serialize(explain));
+ array.push_back(_children[i]->serialize(options));
return Value(DOC(getOpName() << array));
}
@@ -4562,21 +4587,21 @@ intrusive_ptr<Expression> ExpressionReduce::optimize() {
return this;
}
-Value ExpressionReduce::serialize(bool explain) const {
+Value ExpressionReduce::serialize(SerializationOptions options) const {
return Value(Document{{"$reduce",
- Document{{"input", _children[_kInput]->serialize(explain)},
- {"initialValue", _children[_kInitial]->serialize(explain)},
- {"in", _children[_kIn]->serialize(explain)}}}});
+ Document{{"input", _children[_kInput]->serialize(options)},
+ {"initialValue", _children[_kInitial]->serialize(options)},
+ {"in", _children[_kIn]->serialize(options)}}}});
}
/* ------------------------ ExpressionReplaceBase ------------------------ */
-Value ExpressionReplaceBase::serialize(bool explain) const {
+Value ExpressionReplaceBase::serialize(SerializationOptions options) const {
return Value(
Document{{getOpName(),
- Document{{"input", _children[_kInput]->serialize(explain)},
- {"find", _children[_kFind]->serialize(explain)},
- {"replacement", _children[_kReplacement]->serialize(explain)}}}});
+ Document{{"input", _children[_kInput]->serialize(options)},
+ {"find", _children[_kFind]->serialize(options)},
+ {"replacement", _children[_kReplacement]->serialize(options)}}}});
}
namespace {
@@ -4860,9 +4885,9 @@ intrusive_ptr<Expression> ExpressionSortArray::optimize() {
return this;
}
-Value ExpressionSortArray::serialize(bool explain) const {
+Value ExpressionSortArray::serialize(SerializationOptions options) const {
return Value(Document{{kName,
- Document{{"input", _children[_kInput]->serialize(explain)},
+ Document{{"input", _children[_kInput]->serialize(options)},
{"sortBy", _sortBy.getOriginalElement()}}}});
}
@@ -5807,20 +5832,20 @@ boost::intrusive_ptr<Expression> ExpressionSwitch::optimize() {
return this;
}
-Value ExpressionSwitch::serialize(bool explain) const {
+Value ExpressionSwitch::serialize(SerializationOptions options) const {
std::vector<Value> serializedBranches;
serializedBranches.reserve(numBranches());
for (int i = 0; i < numBranches(); ++i) {
auto [caseExpr, thenExpr] = getBranch(i);
- serializedBranches.push_back(Value(Document{{"case", caseExpr->serialize(explain)},
- {"then", thenExpr->serialize(explain)}}));
+ serializedBranches.push_back(Value(Document{{"case", caseExpr->serialize(options)},
+ {"then", thenExpr->serialize(options)}}));
}
if (defaultExpr()) {
return Value(Document{{"$switch",
Document{{"branches", Value(serializedBranches)},
- {"default", defaultExpr()->serialize(explain)}}}});
+ {"default", defaultExpr()->serialize(options)}}}});
}
return Value(Document{{"$switch", Document{{"branches", Value(serializedBranches)}}}});
@@ -6062,12 +6087,12 @@ boost::intrusive_ptr<Expression> ExpressionTrim::optimize() {
return this;
}
-Value ExpressionTrim::serialize(bool explain) const {
+Value ExpressionTrim::serialize(SerializationOptions options) const {
return Value(
Document{{_name,
- Document{{"input", _children[_kInput]->serialize(explain)},
+ Document{{"input", _children[_kInput]->serialize(options)},
{"chars",
- _children[_kCharacters] ? _children[_kCharacters]->serialize(explain)
+ _children[_kCharacters] ? _children[_kCharacters]->serialize(options)
: Value()}}}});
}
@@ -6359,17 +6384,17 @@ boost::intrusive_ptr<Expression> ExpressionZip::optimize() {
return this;
}
-Value ExpressionZip::serialize(bool explain) const {
+Value ExpressionZip::serialize(SerializationOptions options) const {
vector<Value> serializedInput;
vector<Value> serializedDefaults;
Value serializedUseLongestLength = Value(_useLongestLength);
for (auto&& expr : _inputs) {
- serializedInput.push_back(expr.get()->serialize(explain));
+ serializedInput.push_back(expr.get()->serialize(options));
}
for (auto&& expr : _defaults) {
- serializedDefaults.push_back(expr.get()->serialize(explain));
+ serializedDefaults.push_back(expr.get()->serialize(options));
}
return Value(DOC("$zip" << DOC("inputs" << Value(serializedInput) << "defaults"
@@ -6938,15 +6963,15 @@ boost::intrusive_ptr<Expression> ExpressionConvert::optimize() {
return this;
}
-Value ExpressionConvert::serialize(bool explain) const {
+Value ExpressionConvert::serialize(SerializationOptions options) const {
return Value(Document{
{"$convert",
Document{
- {"input", _children[_kInput]->serialize(explain)},
- {"to", _children[_kTo]->serialize(explain)},
- {"onError", _children[_kOnError] ? _children[_kOnError]->serialize(explain) : Value()},
+ {"input", _children[_kInput]->serialize(options)},
+ {"to", _children[_kTo]->serialize(options)},
+ {"onError", _children[_kOnError] ? _children[_kOnError]->serialize(options) : Value()},
{"onNull",
- _children[_kOnNull] ? _children[_kOnNull]->serialize(explain) : Value()}}}});
+ _children[_kOnNull] ? _children[_kOnNull]->serialize(options) : Value()}}}});
}
BSONType ExpressionConvert::computeTargetType(Value targetTypeName) const {
@@ -7134,13 +7159,13 @@ void ExpressionRegex::_compile(RegexExecutionState* executionState) const {
executionState->numCaptures = executionState->pcrePtr->captureCount();
}
-Value ExpressionRegex::serialize(bool explain) const {
+Value ExpressionRegex::serialize(SerializationOptions options) const {
return Value(Document{
{_opName,
- Document{{"input", _children[_kInput]->serialize(explain)},
- {"regex", _children[_kRegex]->serialize(explain)},
+ Document{{"input", _children[_kInput]->serialize(options)},
+ {"regex", _children[_kRegex]->serialize(options)},
{"options",
- _children[_kOptions] ? _children[_kOptions]->serialize(explain) : Value()}}}});
+ _children[_kOptions] ? _children[_kOptions]->serialize(options) : Value()}}}});
}
void ExpressionRegex::_extractInputField(RegexExecutionState* executionState,
@@ -7394,7 +7419,7 @@ intrusive_ptr<Expression> ExpressionRandom::optimize() {
return intrusive_ptr<Expression>(this);
}
-Value ExpressionRandom::serialize(const bool explain) const {
+Value ExpressionRandom::serialize(SerializationOptions options) const {
return Value(DOC(getOpName() << Document()));
}
@@ -7417,8 +7442,8 @@ Value ExpressionToHashedIndexKey::evaluate(const Document& root, Variables* vari
BSONElementHasher::DEFAULT_HASH_SEED));
}
-Value ExpressionToHashedIndexKey::serialize(bool explain) const {
- return Value(DOC("$toHashedIndexKey" << _children[0]->serialize(explain)));
+Value ExpressionToHashedIndexKey::serialize(SerializationOptions options) const {
+ return Value(DOC("$toHashedIndexKey" << _children[0]->serialize(options)));
}
/* ------------------------- ExpressionDateArithmetics -------------------------- */
@@ -7501,14 +7526,14 @@ boost::intrusive_ptr<Expression> ExpressionDateArithmetics::optimize() {
return intrusive_ptr<Expression>(this);
}
-Value ExpressionDateArithmetics::serialize(bool explain) const {
+Value ExpressionDateArithmetics::serialize(SerializationOptions options) const {
return Value(Document{
{_opName,
- Document{{"startDate", _children[_kStartDate]->serialize(explain)},
- {"unit", _children[_kUnit]->serialize(explain)},
- {"amount", _children[_kAmount]->serialize(explain)},
+ Document{{"startDate", _children[_kStartDate]->serialize(options)},
+ {"unit", _children[_kUnit]->serialize(options)},
+ {"amount", _children[_kAmount]->serialize(options)},
{"timezone",
- _children[_kTimeZone] ? _children[_kTimeZone]->serialize(explain) : Value()}}}});
+ _children[_kTimeZone] ? _children[_kTimeZone]->serialize(options) : Value()}}}});
}
Value ExpressionDateArithmetics::evaluate(const Document& root, Variables* variables) const {
@@ -7744,17 +7769,17 @@ boost::intrusive_ptr<Expression> ExpressionDateTrunc::optimize() {
return this;
};
-Value ExpressionDateTrunc::serialize(bool explain) const {
+Value ExpressionDateTrunc::serialize(SerializationOptions options) const {
return Value{Document{
{"$dateTrunc"_sd,
- Document{{"date"_sd, _children[_kDate]->serialize(explain)},
- {"unit"_sd, _children[_kUnit]->serialize(explain)},
+ Document{{"date"_sd, _children[_kDate]->serialize(options)},
+ {"unit"_sd, _children[_kUnit]->serialize(options)},
{"binSize"_sd,
- _children[_kBinSize] ? _children[_kBinSize]->serialize(explain) : Value{}},
+ _children[_kBinSize] ? _children[_kBinSize]->serialize(options) : Value{}},
{"timezone"_sd,
- _children[_kTimeZone] ? _children[_kTimeZone]->serialize(explain) : Value{}},
+ _children[_kTimeZone] ? _children[_kTimeZone]->serialize(options) : Value{}},
{"startOfWeek"_sd,
- _children[_kStartOfWeek] ? _children[_kStartOfWeek]->serialize(explain)
+ _children[_kStartOfWeek] ? _children[_kStartOfWeek]->serialize(options)
: Value{}}}}}};
};
@@ -7948,10 +7973,10 @@ intrusive_ptr<Expression> ExpressionGetField::optimize() {
return intrusive_ptr<Expression>(this);
}
-Value ExpressionGetField::serialize(const bool explain) const {
+Value ExpressionGetField::serialize(SerializationOptions options) const {
return Value(Document{{"$getField"_sd,
- Document{{"field"_sd, _children[_kField]->serialize(explain)},
- {"input"_sd, _children[_kInput]->serialize(explain)}}}});
+ Document{{"field"_sd, _children[_kField]->serialize(options)},
+ {"input"_sd, _children[_kInput]->serialize(options)}}}});
}
/* -------------------------- ExpressionSetField ------------------------------ */
@@ -8061,11 +8086,11 @@ intrusive_ptr<Expression> ExpressionSetField::optimize() {
return intrusive_ptr<Expression>(this);
}
-Value ExpressionSetField::serialize(const bool explain) const {
+Value ExpressionSetField::serialize(SerializationOptions options) const {
return Value(Document{{"$setField"_sd,
- Document{{"field"_sd, _children[_kField]->serialize(explain)},
- {"input"_sd, _children[_kInput]->serialize(explain)},
- {"value"_sd, _children[_kValue]->serialize(explain)}}}});
+ Document{{"field"_sd, _children[_kField]->serialize(options)},
+ {"input"_sd, _children[_kInput]->serialize(options)},
+ {"value"_sd, _children[_kValue]->serialize(options)}}}});
}
/* ------------------------- ExpressionTsSecond ----------------------------- */
diff --git a/src/mongo/db/pipeline/expression.h b/src/mongo/db/pipeline/expression.h
index 4224063ae04..f000c07bd2d 100644
--- a/src/mongo/db/pipeline/expression.h
+++ b/src/mongo/db/pipeline/expression.h
@@ -54,6 +54,7 @@
#include "mongo/db/query/allowed_contexts.h"
#include "mongo/db/query/datetime/date_time_support.h"
#include "mongo/db/query/query_feature_flags_gen.h"
+#include "mongo/db/query/serialization_options.h"
#include "mongo/db/query/sort_pattern.h"
#include "mongo/db/server_options.h"
#include "mongo/db/update/pattern_cmp.h"
@@ -207,7 +208,7 @@ public:
* If 'explain' is false, the returned Value must result in the same Expression when parsed by
* parseOperand().
*/
- virtual Value serialize(bool explain) const = 0;
+ virtual Value serialize(SerializationOptions options) const = 0;
/**
* Evaluate the expression with respect to the Document given by 'root' and the Variables given
@@ -373,7 +374,7 @@ private:
class ExpressionNary : public Expression {
public:
boost::intrusive_ptr<Expression> optimize() override;
- Value serialize(bool explain) const override;
+ Value serialize(SerializationOptions options) const override;
/*
Add an operand to the n-ary expression.
@@ -553,9 +554,9 @@ public:
return AccumulatorN::kName.rawData();
}
- Value serialize(bool explain) const {
+ Value serialize(SerializationOptions options) const {
MutableDocument md;
- AccumulatorN::serializeHelper(_n, _output, explain, md);
+ AccumulatorN::serializeHelper(_n, _output, options, md);
return Value(DOC(getOpName() << md.freeze()));
}
@@ -673,7 +674,7 @@ public:
boost::intrusive_ptr<Expression> optimize() final;
Value evaluate(const Document& root, Variables* variables) const final;
- Value serialize(bool explain) const final;
+ Value serialize(SerializationOptions options) const final;
const char* getOpName() const;
@@ -770,10 +771,10 @@ public:
* Always serializes to the full {date: <date arg>, timezone: <timezone arg>} format, leaving
* off the timezone if not specified.
*/
- Value serialize(bool explain) const final {
- auto timezone = _children[_kTimeZone] ? _children[_kTimeZone]->serialize(explain) : Value();
+ Value serialize(SerializationOptions options) const final {
+ auto timezone = _children[_kTimeZone] ? _children[_kTimeZone]->serialize(options) : Value();
return Value(Document{{_opName,
- Document{{"date", _children[_kDate]->serialize(explain)},
+ Document{{"date", _children[_kDate]->serialize(options)},
{"timezone", std::move(timezone)}}}});
}
@@ -1047,7 +1048,7 @@ public:
}
Value evaluate(const Document& root, Variables* variables) const final;
- Value serialize(bool explain) const final;
+ Value serialize(SerializationOptions options) const final;
static boost::intrusive_ptr<ExpressionArray> create(
ExpressionContext* const expCtx, std::vector<boost::intrusive_ptr<Expression>>&& children) {
@@ -1223,7 +1224,7 @@ class ExpressionCoerceToBool final : public Expression {
public:
boost::intrusive_ptr<Expression> optimize() final;
Value evaluate(const Document& root, Variables* variables) const final;
- Value serialize(bool explain) const final;
+ Value serialize(SerializationOptions options) const final;
static boost::intrusive_ptr<ExpressionCoerceToBool> create(
ExpressionContext* expCtx, boost::intrusive_ptr<Expression> pExpression);
@@ -1384,7 +1385,7 @@ public:
boost::intrusive_ptr<Expression> onError);
boost::intrusive_ptr<Expression> optimize() final;
- Value serialize(bool explain) const final;
+ Value serialize(SerializationOptions options) const final;
Value evaluate(const Document& root, Variables* variables) const final;
static boost::intrusive_ptr<Expression> parse(ExpressionContext* expCtx,
@@ -1426,7 +1427,7 @@ public:
boost::intrusive_ptr<Expression> timeZone);
boost::intrusive_ptr<Expression> optimize() final;
- Value serialize(bool explain) const final;
+ Value serialize(SerializationOptions options) const final;
Value evaluate(const Document& root, Variables* variables) const final;
static boost::intrusive_ptr<Expression> parse(ExpressionContext* expCtx,
@@ -1506,7 +1507,7 @@ public:
boost::intrusive_ptr<Expression> iso8601);
boost::intrusive_ptr<Expression> optimize() final;
- Value serialize(bool explain) const final;
+ Value serialize(SerializationOptions options) const final;
Value evaluate(const Document& root, Variables* variables) const final;
static boost::intrusive_ptr<Expression> parse(ExpressionContext* expCtx,
@@ -1540,7 +1541,7 @@ public:
boost::intrusive_ptr<Expression> timeZone,
boost::intrusive_ptr<Expression> onNull);
boost::intrusive_ptr<Expression> optimize() final;
- Value serialize(bool explain) const final;
+ Value serialize(SerializationOptions options) const final;
Value evaluate(const Document& root, Variables* variables) const final;
static boost::intrusive_ptr<Expression> parse(ExpressionContext* expCtx,
@@ -1652,7 +1653,7 @@ public:
boost::intrusive_ptr<Expression> timezone,
boost::intrusive_ptr<Expression> startOfWeek);
boost::intrusive_ptr<Expression> optimize() final;
- Value serialize(bool explain) const final;
+ Value serialize(SerializationOptions options) const final;
Value evaluate(const Document& root, Variables* variables) const final;
static boost::intrusive_ptr<Expression> parse(ExpressionContext* expCtx,
BSONElement expr,
@@ -1791,7 +1792,7 @@ public:
boost::intrusive_ptr<Expression> optimize() final;
Value evaluate(const Document& root, Variables* variables) const;
- Value serialize(bool explain) const final;
+ Value serialize(SerializationOptions options) const final;
/*
Create a field path expression using old semantics (rooted off of CURRENT).
@@ -1892,7 +1893,7 @@ private:
class ExpressionFilter final : public Expression {
public:
boost::intrusive_ptr<Expression> optimize() final;
- Value serialize(bool explain) const final;
+ Value serialize(SerializationOptions options) const final;
Value evaluate(const Document& root, Variables* variables) const final;
static boost::intrusive_ptr<Expression> parse(ExpressionContext* expCtx,
@@ -2127,7 +2128,7 @@ public:
class ExpressionLet final : public Expression {
public:
boost::intrusive_ptr<Expression> optimize() final;
- Value serialize(bool explain) const final;
+ Value serialize(SerializationOptions options) const final;
Value evaluate(const Document& root, Variables* variables) const final;
static boost::intrusive_ptr<Expression> parse(ExpressionContext* expCtx,
@@ -2240,7 +2241,7 @@ public:
ConstDataRange serverToken,
int64_t contentionFactor,
std::vector<ConstDataRange> edcTokens);
- Value serialize(bool explain) const final;
+ Value serialize(SerializationOptions options) const final;
Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const;
@@ -2268,7 +2269,7 @@ public:
ConstDataRange serverToken,
int64_t contentionFactor,
ConstDataRange edcToken);
- Value serialize(bool explain) const final;
+ Value serialize(SerializationOptions options) const final;
Value evaluate(const Document& root, Variables* variables) const final;
const char* getOpName() const;
@@ -2299,7 +2300,7 @@ public:
boost::intrusive_ptr<Expression> each); // yields results to be added to output array
boost::intrusive_ptr<Expression> optimize() final;
- Value serialize(bool explain) const final;
+ Value serialize(SerializationOptions options) const final;
Value evaluate(const Document& root, Variables* variables) const final;
static boost::intrusive_ptr<Expression> parse(ExpressionContext* expCtx,
@@ -2328,7 +2329,7 @@ class ExpressionMeta final : public Expression {
public:
ExpressionMeta(ExpressionContext* expCtx, DocumentMetadataFields::MetaType metaType);
- Value serialize(bool explain) const final;
+ Value serialize(SerializationOptions options) const final;
Value evaluate(const Document& root, Variables* variables) const final;
static boost::intrusive_ptr<Expression> parse(ExpressionContext* expCtx,
@@ -2518,7 +2519,7 @@ class ExpressionObject final : public Expression {
public:
boost::intrusive_ptr<Expression> optimize() final;
Value evaluate(const Document& root, Variables* variables) const final;
- Value serialize(bool explain) const final;
+ Value serialize(SerializationOptions options) const final;
static boost::intrusive_ptr<ExpressionObject> create(
ExpressionContext* expCtx,
@@ -2655,7 +2656,7 @@ public:
static boost::intrusive_ptr<Expression> parse(ExpressionContext* expCtx,
BSONElement expr,
const VariablesParseState& vps);
- Value serialize(bool explain) const final;
+ Value serialize(SerializationOptions options) const final;
void acceptVisitor(ExpressionMutableVisitor* visitor) final {
return visitor->visit(this);
@@ -2686,7 +2687,7 @@ public:
virtual const char* getOpName() const = 0;
Value evaluate(const Document& root, Variables* variables) const final;
boost::intrusive_ptr<Expression> optimize() final;
- Value serialize(bool explain) const final;
+ Value serialize(SerializationOptions options) const final;
protected:
virtual Value _doEval(StringData input, StringData find, StringData replacement) const = 0;
@@ -2963,7 +2964,7 @@ public:
static boost::intrusive_ptr<Expression> parse(ExpressionContext* expCtx,
BSONElement expr,
const VariablesParseState& vps);
- Value serialize(bool explain) const final;
+ Value serialize(SerializationOptions options) const final;
void acceptVisitor(ExpressionMutableVisitor* visitor) final {
return visitor->visit(this);
@@ -3325,7 +3326,7 @@ public:
static boost::intrusive_ptr<Expression> parse(ExpressionContext* expCtx,
BSONElement expr,
const VariablesParseState& vpsIn);
- Value serialize(bool explain) const final;
+ Value serialize(SerializationOptions options) const final;
void acceptVisitor(ExpressionMutableVisitor* visitor) final {
return visitor->visit(this);
@@ -3435,7 +3436,7 @@ public:
static boost::intrusive_ptr<Expression> parse(ExpressionContext* expCtx,
BSONElement expr,
const VariablesParseState& vpsIn);
- Value serialize(bool explain) const final;
+ Value serialize(SerializationOptions options) const final;
void acceptVisitor(ExpressionMutableVisitor* visitor) final {
return visitor->visit(this);
@@ -3684,7 +3685,7 @@ public:
static boost::intrusive_ptr<Expression> parse(ExpressionContext* expCtx,
BSONElement expr,
const VariablesParseState& vpsIn);
- Value serialize(bool explain) const final;
+ Value serialize(SerializationOptions options) const final;
void acceptVisitor(ExpressionMutableVisitor* visitor) final {
return visitor->visit(this);
@@ -3721,7 +3722,7 @@ public:
Value evaluate(const Document& root, Variables* variables) const final;
boost::intrusive_ptr<Expression> optimize() final;
- Value serialize(bool explain) const final;
+ Value serialize(SerializationOptions options) const final;
void acceptVisitor(ExpressionMutableVisitor* visitor) final {
return visitor->visit(this);
@@ -3823,7 +3824,7 @@ public:
boost::optional<std::pair<boost::optional<std::string>, std::string>>
getConstantPatternAndOptions() const;
- Value serialize(bool explain) const;
+ Value serialize(SerializationOptions options) const;
const std::string& getOpName() const {
return _opName;
@@ -3935,7 +3936,7 @@ public:
BSONElement exprElement,
const VariablesParseState& vps);
- Value serialize(bool explain) const final;
+ Value serialize(SerializationOptions options) const final;
Value evaluate(const Document& root, Variables* variables) const final;
@@ -3978,7 +3979,7 @@ public:
}
Value evaluate(const Document& root, Variables* variables) const;
- Value serialize(bool explain) const final;
+ Value serialize(SerializationOptions options) const final;
};
class ExpressionDateArithmetics : public Expression {
@@ -3995,7 +3996,7 @@ public:
_opName(opName) {}
boost::intrusive_ptr<Expression> optimize() final;
- Value serialize(bool explain) const final;
+ Value serialize(SerializationOptions options) const final;
Value evaluate(const Document& root, Variables* variables) const final;
protected:
@@ -4127,7 +4128,7 @@ public:
boost::intrusive_ptr<Expression> timezone,
boost::intrusive_ptr<Expression> startOfWeek);
boost::intrusive_ptr<Expression> optimize() final;
- Value serialize(bool explain) const final;
+ Value serialize(SerializationOptions options) const final;
Value evaluate(const Document& root, Variables* variables) const final;
void acceptVisitor(ExpressionMutableVisitor* visitor) final {
return visitor->visit(this);
@@ -4225,7 +4226,7 @@ public:
expCtx->sbeCompatible = false;
}
- Value serialize(bool explain) const final;
+ Value serialize(SerializationOptions options) const final;
Value evaluate(const Document& root, Variables* variables) const final;
@@ -4264,7 +4265,7 @@ public:
expCtx->sbeCompatible = false;
}
- Value serialize(bool explain) const final;
+ Value serialize(SerializationOptions options) const final;
Value evaluate(const Document& root, Variables* variables) const final;
diff --git a/src/mongo/db/pipeline/expression_field_path_test.cpp b/src/mongo/db/pipeline/expression_field_path_test.cpp
index 13b59c1b4c4..9507cc0000a 100644
--- a/src/mongo/db/pipeline/expression_field_path_test.cpp
+++ b/src/mongo/db/pipeline/expression_field_path_test.cpp
@@ -61,6 +61,10 @@ Document fromBson(BSONObj obj) {
return Document(obj);
}
+std::string redactFieldNameForTest(StringData s) {
+ return str::stream() << "HASH(" << s << ")";
+}
+
namespace FieldPath {
/** The provided field path does not pass validation. */
@@ -200,6 +204,67 @@ TEST(FieldPath, ScalarVariableWithDottedFieldPathOptimizesToConstantMissingValue
ASSERT_VALUE_EQ(Value(), constantExpr->getValue());
}
+TEST(FieldPath, SerializeWithRedaction) {
+ SerializationOptions options;
+ options.redactFieldNamesStrategy = redactFieldNameForTest;
+ options.redactFieldNames = true;
+
+ auto expCtx = ExpressionContextForTest{};
+ intrusive_ptr<Expression> expression =
+ ExpressionFieldPath::createPathFromString(&expCtx, "bar", expCtx.variablesParseState);
+ ASSERT_BSONOBJ_BINARY_EQ(BSON("foo"
+ << "$HASH(bar)"),
+ BSON("foo" << expression->serialize(options)));
+ // Repeat with a dotted path.
+ expression =
+ ExpressionFieldPath::createPathFromString(&expCtx, "a.b.c", expCtx.variablesParseState);
+ ASSERT_BSONOBJ_BINARY_EQ(BSON("foo"
+ << "$HASH(a).HASH(b).HASH(c)"),
+ BSON("foo" << expression->serialize(options)));
+
+ // Expression with multiple field paths.
+ // {$and: [{$gt: ["$foo", 5]}, {$lt: [$foo, 10]}]} => {$and: [{$gt: ["$HASH(foo)", 5]}, {$lt:
+ // [$HASH(foo), 10]}]}
+ auto expressionBSON = BSON("$and" << BSON_ARRAY(BSON("$gt" << BSON_ARRAY("$foo" << 5))
+ << BSON("$lt" << BSON_ARRAY("$foo" << 10))));
+ expression = Expression::parseExpression(&expCtx, expressionBSON, expCtx.variablesParseState);
+ auto redactedBSON = BSON(
+ "$and" << BSON_ARRAY(BSON("$gt" << BSON_ARRAY("$HASH(foo)" << BSON("$const" << 5)))
+ << BSON("$lt" << BSON_ARRAY("$HASH(foo)" << BSON("$const" << 10)))));
+ ASSERT_BSONOBJ_BINARY_EQ(BSON("field" << redactedBSON),
+ BSON("field" << expression->serialize(options)));
+
+ // Test that a variable followed by user fields is properly hashed.
+ std::string replacementChar = "?";
+ options.replacementForLiteralArgs = replacementChar;
+ expressionBSON = BSON("$gt" << BSON_ARRAY("$$ROOT.a.b"
+ << "5"));
+ expression = Expression::parseExpression(&expCtx, expressionBSON, expCtx.variablesParseState);
+ redactedBSON = BSON("$gt" << BSON_ARRAY("$$ROOT.HASH(a).HASH(b)" << BSON("$const"
+ << "?")));
+ ASSERT_BSONOBJ_BINARY_EQ(BSON("field" << redactedBSON),
+ BSON("field" << expression->serialize(options)));
+
+ // Test that a system variable is preserved.
+ expressionBSON = BSON("$gt" << BSON_ARRAY("$foo"
+ << "$$NOW"));
+ expression = Expression::parseExpression(&expCtx, expressionBSON, expCtx.variablesParseState);
+ redactedBSON = BSON("$gt" << BSON_ARRAY("$HASH(foo)"
+ << "$$NOW"));
+ ASSERT_BSONOBJ_BINARY_EQ(BSON("field" << redactedBSON),
+ BSON("field" << expression->serialize(options)));
+
+
+ expressionBSON = BSON("$gt" << BSON_ARRAY("$foo.a.b"
+ << "$$NOW"));
+ // Repeat the above test with a dotted path.
+ expression = Expression::parseExpression(&expCtx, expressionBSON, expCtx.variablesParseState);
+ redactedBSON = BSON("$gt" << BSON_ARRAY("$HASH(foo).HASH(a).HASH(b)"
+ << "$$NOW"));
+ ASSERT_BSONOBJ_BINARY_EQ(BSON("field" << redactedBSON),
+ BSON("field" << expression->serialize(options)));
+}
+
/** The field path itself is a dependency. */
class Dependencies {
public:
diff --git a/src/mongo/db/pipeline/expression_find_internal.h b/src/mongo/db/pipeline/expression_find_internal.h
index 7eb727a55ae..9baf15b3958 100644
--- a/src/mongo/db/pipeline/expression_find_internal.h
+++ b/src/mongo/db/pipeline/expression_find_internal.h
@@ -78,7 +78,7 @@ public:
return visitor->visit(this);
}
- Value serialize(bool explain) const final {
+ Value serialize(SerializationOptions options) const final {
MONGO_UNREACHABLE;
}
@@ -148,7 +148,7 @@ public:
return visitor->visit(this);
}
- Value serialize(bool explain) const final {
+ Value serialize(SerializationOptions options) const final {
MONGO_UNREACHABLE;
}
@@ -201,7 +201,7 @@ public:
return visitor->visit(this);
}
- Value serialize(bool explain) const final {
+ Value serialize(SerializationOptions options) const final {
MONGO_UNREACHABLE;
}
diff --git a/src/mongo/db/pipeline/expression_function.cpp b/src/mongo/db/pipeline/expression_function.cpp
index 4b35b9fa0b3..da3a0b1e374 100644
--- a/src/mongo/db/pipeline/expression_function.cpp
+++ b/src/mongo/db/pipeline/expression_function.cpp
@@ -46,10 +46,10 @@ ExpressionFunction::ExpressionFunction(ExpressionContext* const expCtx,
expCtx->sbeCompatible = false;
}
-Value ExpressionFunction::serialize(bool explain) const {
+Value ExpressionFunction::serialize(SerializationOptions options) const {
MutableDocument d;
d["body"] = Value(_funcSource);
- d["args"] = Value(_passedArgs->serialize(explain));
+ d["args"] = Value(_passedArgs->serialize(options));
d["lang"] = Value(_lang);
// This field will only be seralized when desugaring $where in $expr + $_internalJs
if (_assignFirstArgToThis) {
diff --git a/src/mongo/db/pipeline/expression_function.h b/src/mongo/db/pipeline/expression_function.h
index c6d5987820b..29111b21f64 100644
--- a/src/mongo/db/pipeline/expression_function.h
+++ b/src/mongo/db/pipeline/expression_function.h
@@ -69,7 +69,7 @@ public:
Value evaluate(const Document& root, Variables* variables) const final;
- Value serialize(bool explain) const final;
+ Value serialize(SerializationOptions options) const final;
void acceptVisitor(ExpressionMutableVisitor* visitor) final {
return visitor->visit(this);
diff --git a/src/mongo/db/pipeline/expression_js_emit.cpp b/src/mongo/db/pipeline/expression_js_emit.cpp
index 5af7c69b456..3a45690f302 100644
--- a/src/mongo/db/pipeline/expression_js_emit.cpp
+++ b/src/mongo/db/pipeline/expression_js_emit.cpp
@@ -128,10 +128,10 @@ boost::intrusive_ptr<Expression> ExpressionInternalJsEmit::parse(ExpressionConte
return new ExpressionInternalJsEmit(expCtx, std::move(thisRef), std::move(funcSourceString));
}
-Value ExpressionInternalJsEmit::serialize(bool explain) const {
+Value ExpressionInternalJsEmit::serialize(SerializationOptions options) const {
return Value(
Document{{kExpressionName,
- Document{{"eval", _funcSource}, {"this", _thisRef->serialize(explain)}}}});
+ Document{{"eval", _funcSource}, {"this", _thisRef->serialize(options)}}}});
}
Value ExpressionInternalJsEmit::evaluate(const Document& root, Variables* variables) const {
diff --git a/src/mongo/db/pipeline/expression_js_emit.h b/src/mongo/db/pipeline/expression_js_emit.h
index 5b346baf871..18540866001 100644
--- a/src/mongo/db/pipeline/expression_js_emit.h
+++ b/src/mongo/db/pipeline/expression_js_emit.h
@@ -55,7 +55,7 @@ public:
Value evaluate(const Document& root, Variables* variables) const final;
- Value serialize(bool explain) const final;
+ Value serialize(SerializationOptions options) const final;
void acceptVisitor(ExpressionMutableVisitor* visitor) final {
return visitor->visit(this);
diff --git a/src/mongo/db/pipeline/expression_test.cpp b/src/mongo/db/pipeline/expression_test.cpp
index ae017b10df3..f9e7cb5a234 100644
--- a/src/mongo/db/pipeline/expression_test.cpp
+++ b/src/mongo/db/pipeline/expression_test.cpp
@@ -813,6 +813,31 @@ TEST(ExpressionConstantTest, ConstantOfValueMissingSerializesToRemoveSystemVar)
BSON("field" << expression->serialize(false)));
}
+TEST(ExpressionConstantTest, ConstantRedaction) {
+ SerializationOptions options;
+ std::string replacementChar = "?";
+ options.replacementForLiteralArgs = replacementChar;
+
+ // Test that a constant is replaced.
+ auto expCtx = ExpressionContextForTest{};
+ intrusive_ptr<Expression> expression = ExpressionConstant::create(&expCtx, Value("my_ssn"_sd));
+ ASSERT_BSONOBJ_BINARY_EQ(BSON("field" << BSON("$const" << replacementChar)),
+ BSON("field" << expression->serialize(options)));
+
+ // Test an expression with multiple ExpressionConst children.
+ // {$and: [{$gt: ["$foo", 5]}, {$lt: [$foo, 10]}]} => {$and: [{$gt: ["$foo", "?"]}, {$lt: [$foo,
+ // "?"]}]}
+ auto expressionBSON = BSON("$and" << BSON_ARRAY(BSON("$gt" << BSON_ARRAY("$foo" << 5))
+ << BSON("$lt" << BSON_ARRAY("$foo" << 10))));
+ expression = Expression::parseExpression(&expCtx, expressionBSON, expCtx.variablesParseState);
+ auto redactedBSON =
+ BSON("$and" << BSON_ARRAY(
+ BSON("$gt" << BSON_ARRAY("$foo" << BSON("$const" << replacementChar)))
+ << BSON("$lt" << BSON_ARRAY("$foo" << BSON("$const" << replacementChar)))));
+ ASSERT_BSONOBJ_BINARY_EQ(BSON("field" << redactedBSON),
+ BSON("field" << expression->serialize(options)));
+}
+
} // namespace Constant
TEST(ExpressionFromAccumulators, Avg) {
diff --git a/src/mongo/db/pipeline/expression_test_api_version.cpp b/src/mongo/db/pipeline/expression_test_api_version.cpp
index 7602535be84..6a4c46b73c4 100644
--- a/src/mongo/db/pipeline/expression_test_api_version.cpp
+++ b/src/mongo/db/pipeline/expression_test_api_version.cpp
@@ -89,7 +89,7 @@ boost::intrusive_ptr<Expression> ExpressionTestApiVersion::parse(ExpressionConte
return new ExpressionTestApiVersion(expCtx, unstableField, deprecatedField);
}
-Value ExpressionTestApiVersion::serialize(bool explain) const {
+Value ExpressionTestApiVersion::serialize(SerializationOptions options) const {
return Value(Document{{"$_testApiVersion",
Document{{"unstable", _unstable ? Value(_unstable) : Value()},
{"deprecated", _deprecated ? Value(_deprecated) : Value()}}}});
diff --git a/src/mongo/db/pipeline/expression_test_api_version.h b/src/mongo/db/pipeline/expression_test_api_version.h
index 2608bb2dd01..19f2dfc891e 100644
--- a/src/mongo/db/pipeline/expression_test_api_version.h
+++ b/src/mongo/db/pipeline/expression_test_api_version.h
@@ -48,7 +48,7 @@ public:
Value evaluate(const Document& root, Variables* variables) const final;
- Value serialize(bool explain) const final;
+ Value serialize(SerializationOptions options) const final;
void acceptVisitor(ExpressionMutableVisitor* visitor) final {
return visitor->visit(this);
diff --git a/src/mongo/db/query/serialization_options.h b/src/mongo/db/query/serialization_options.h
index 9ab6fbc1c33..f72116bd7fb 100644
--- a/src/mongo/db/query/serialization_options.h
+++ b/src/mongo/db/query/serialization_options.h
@@ -29,17 +29,24 @@
#pragma once
+namespace mongo {
+namespace {
+// Should never be called, throw to ensure we catch this in tests.
+std::string defaultRedactionStrategy(StringData s) {
+ MONGO_UNREACHABLE_TASSERT(7332410);
+}
+} // namespace
+
#include <boost/optional.hpp>
#include "mongo/base/string_data.h"
-namespace mongo {
-
/**
- * A struct with options for how you want to serialize a match expression.
+ * A struct with options for how you want to serialize a match or aggregation expression.
*/
struct SerializationOptions {
SerializationOptions() {}
+ SerializationOptions(bool explain_) : explain(explain_) {}
// 'replacementForLiteralArgs' is an independent option to serialize in a genericized format
// with the aim of similar "shaped" queries serializing to the same object. For example, if
@@ -51,7 +58,11 @@ struct SerializationOptions {
// 4, so the serialization expected would be {$and: [{a: {$gt: '?'}}, {b: {$lt: '?'}}]}.
boost::optional<StringData> replacementForLiteralArgs = boost::none;
- // TODO SERVER-73663 'redactFieldNames' could be here - a callback function?
+ // If true the caller must set redactFieldNamesStrategy. 'redactFieldNames' if set along with
+ // a strategy the redaction strategy will be called on any field paths/names encountered
+ // before serializing them.
+ bool redactFieldNames = false;
+ std::function<std::string(StringData)> redactFieldNamesStrategy = defaultRedactionStrategy;
// If set, serializes without including the path. For example {a: {$gt: 2}} would serialize
// as just {$gt: 2}.
@@ -66,6 +77,9 @@ struct SerializationOptions {
// The $elemMatch will serialize {a: {$elemMatch: <recurse>}} and the EQ will serialize just
// {$eq: 2} instead of its usual {a: {$eq: 2}}.
bool includePath = true;
+
+ // For aggregation indicate whether we should use the more verbose serialization format.
+ bool explain = false;
};
} // namespace mongo
diff --git a/src/mongo/s/commands/sharding_expressions.cpp b/src/mongo/s/commands/sharding_expressions.cpp
index cf2bdac9a99..407dd622eab 100644
--- a/src/mongo/s/commands/sharding_expressions.cpp
+++ b/src/mongo/s/commands/sharding_expressions.cpp
@@ -463,7 +463,7 @@ boost::intrusive_ptr<Expression> ExpressionInternalIndexKey::optimize() {
return this;
}
-Value ExpressionInternalIndexKey::serialize(bool explain) const {
+Value ExpressionInternalIndexKey::serialize(SerializationOptions options) const {
invariant(_doc);
invariant(_spec);
@@ -472,7 +472,7 @@ Value ExpressionInternalIndexKey::serialize(bool explain) const {
// The 'spec' is always treated as a constant so do not call '_spec->serialize()' which would
// wrap the value in an unnecessary '$const' object.
- return Value(DOC(opName << DOC(kDocField << _doc->serialize(explain) << kSpecField
+ return Value(DOC(opName << DOC(kDocField << _doc->serialize(options) << kSpecField
<< specExprConstant->getValue())));
}
diff --git a/src/mongo/s/commands/sharding_expressions.h b/src/mongo/s/commands/sharding_expressions.h
index e46218132a8..809d3a057d2 100644
--- a/src/mongo/s/commands/sharding_expressions.h
+++ b/src/mongo/s/commands/sharding_expressions.h
@@ -131,7 +131,7 @@ public:
boost::intrusive_ptr<Expression> optimize() final;
- Value serialize(bool explain) const final;
+ Value serialize(SerializationOptions options) const final;
Value evaluate(const Document& root, Variables* variables) const final;