summaryrefslogtreecommitdiff
path: root/src/mongo/db
diff options
context:
space:
mode:
authorJess Balint <jbalint@gmail.com>2023-02-23 22:59:47 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2023-02-24 05:30:37 +0000
commit7c793c6e717490b64bfd08dfccddc65567e94824 (patch)
treea0da99a3fb399e29f8a24cdb229899c07c2879b3 /src/mongo/db
parent3b0428bc373dc939cd156ca7fc5cdd4409a71534 (diff)
downloadmongo-7c793c6e717490b64bfd08dfccddc65567e94824.tar.gz
SERVER-73663 Field name redaction for LeafMatchExpressions #10765
SERVER-73676 Query shape (literal redaction) for leftover non-leaf MatchExpressions, pt. 2
Diffstat (limited to 'src/mongo/db')
-rw-r--r--src/mongo/db/matcher/expression_array.cpp21
-rw-r--r--src/mongo/db/matcher/expression_array.h9
-rw-r--r--src/mongo/db/matcher/expression_geo.cpp6
-rw-r--r--src/mongo/db/matcher/expression_geo.h12
-rw-r--r--src/mongo/db/matcher/expression_internal_bucket_geo_within.cpp16
-rw-r--r--src/mongo/db/matcher/expression_leaf.cpp40
-rw-r--r--src/mongo/db/matcher/expression_leaf.h18
-rw-r--r--src/mongo/db/matcher/expression_path.h20
-rw-r--r--src/mongo/db/matcher/expression_text_base.cpp17
-rw-r--r--src/mongo/db/matcher/expression_text_base.h4
-rw-r--r--src/mongo/db/matcher/expression_tree.cpp20
-rw-r--r--src/mongo/db/matcher/expression_type.h6
-rw-r--r--src/mongo/db/matcher/expression_where_base.cpp7
-rw-r--r--src/mongo/db/matcher/parsed_match_expression_for_test.h7
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.cpp4
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.h3
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_eq.cpp2
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_eq.h3
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_fmod.cpp2
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_fmod.h3
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_match_array_index.cpp4
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_match_array_index.h3
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_num_array_items.cpp2
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_num_array_items.h3
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_object_match.cpp4
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_object_match.h3
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_str_length.cpp2
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_str_length.h3
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_unique_items.cpp2
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_unique_items.h3
-rw-r--r--src/mongo/db/query/query_shape.cpp10
-rw-r--r--src/mongo/db/query/query_shape.h3
-rw-r--r--src/mongo/db/query/query_shape_test.cpp81
-rw-r--r--src/mongo/db/query/serialization_options.h7
34 files changed, 224 insertions, 126 deletions
diff --git a/src/mongo/db/matcher/expression_array.cpp b/src/mongo/db/matcher/expression_array.cpp
index 5f69a1211de..db1ef7dc7b6 100644
--- a/src/mongo/db/matcher/expression_array.cpp
+++ b/src/mongo/db/matcher/expression_array.cpp
@@ -104,9 +104,7 @@ void ElemMatchObjectMatchExpression::debugString(StringBuilder& debug, int inden
}
BSONObj ElemMatchObjectMatchExpression::getSerializedRightHandSide(
- boost::optional<StringData> replacementForLiteralArgs) const {
- SerializationOptions opts;
- opts.replacementForLiteralArgs = replacementForLiteralArgs;
+ SerializationOptions opts) const {
return BSON("$elemMatch" << _sub->serialize(opts));
}
@@ -175,14 +173,11 @@ void ElemMatchValueMatchExpression::debugString(StringBuilder& debug, int indent
}
}
-BSONObj ElemMatchValueMatchExpression::getSerializedRightHandSide(
- boost::optional<StringData> replacementForLiteralArgs) const {
+BSONObj ElemMatchValueMatchExpression::getSerializedRightHandSide(SerializationOptions opts) const {
BSONObjBuilder emBob;
+ opts.includePath = false;
for (auto&& child : _subs) {
- SerializationOptions opts;
- opts.includePath = false;
- opts.replacementForLiteralArgs = replacementForLiteralArgs;
child->serialize(&emBob, opts);
}
@@ -224,10 +219,12 @@ void SizeMatchExpression::debugString(StringBuilder& debug, int indentationLevel
}
}
-BSONObj SizeMatchExpression::getSerializedRightHandSide(
- boost::optional<StringData> replacementForLiteralArgs) const {
- // TODO SERVER-73676 respect 'replacementForLiteralArgs.'
- return BSON("$size" << _size);
+BSONObj SizeMatchExpression::getSerializedRightHandSide(SerializationOptions opts) const {
+ const char* opName = "$size";
+ if (opts.replacementForLiteralArgs) {
+ return BSON(opName << *opts.replacementForLiteralArgs);
+ }
+ return BSON(opName << _size);
}
bool SizeMatchExpression::equivalent(const MatchExpression* other) const {
diff --git a/src/mongo/db/matcher/expression_array.h b/src/mongo/db/matcher/expression_array.h
index c8f1ede74a3..4c26756d33f 100644
--- a/src/mongo/db/matcher/expression_array.h
+++ b/src/mongo/db/matcher/expression_array.h
@@ -90,8 +90,7 @@ public:
virtual void debugString(StringBuilder& debug, int indentationLevel) const;
- BSONObj getSerializedRightHandSide(
- boost::optional<StringData> replacementForLiteralArgs) const final;
+ BSONObj getSerializedRightHandSide(SerializationOptions opts) const final;
std::vector<std::unique_ptr<MatchExpression>>* getChildVector() final {
return nullptr;
@@ -159,8 +158,7 @@ public:
virtual void debugString(StringBuilder& debug, int indentationLevel) const;
- BSONObj getSerializedRightHandSide(
- boost::optional<StringData> replacementForLiteralArgs) const final;
+ BSONObj getSerializedRightHandSide(SerializationOptions opts) const final;
std::vector<std::unique_ptr<MatchExpression>>* getChildVector() final {
return &_subs;
@@ -235,8 +233,7 @@ public:
virtual void debugString(StringBuilder& debug, int indentationLevel) const;
- BSONObj getSerializedRightHandSide(
- boost::optional<StringData> replacementForLiteralArgs) const final;
+ BSONObj getSerializedRightHandSide(SerializationOptions opts) const final;
virtual bool equivalent(const MatchExpression* other) const;
diff --git a/src/mongo/db/matcher/expression_geo.cpp b/src/mongo/db/matcher/expression_geo.cpp
index 22eaccdf1ff..36a9e3f3107 100644
--- a/src/mongo/db/matcher/expression_geo.cpp
+++ b/src/mongo/db/matcher/expression_geo.cpp
@@ -447,8 +447,7 @@ void GeoMatchExpression::debugString(StringBuilder& debug, int indentationLevel)
debug << "\n";
}
-BSONObj GeoMatchExpression::getSerializedRightHandSide(
- boost::optional<StringData> replacementForLiteralArgs) const {
+BSONObj GeoMatchExpression::getSerializedRightHandSide(SerializationOptions opts) const {
BSONObjBuilder subobj;
// TODO SERVER-73672 looks like we'll need to traverse '_rawObj' if 'replacementForLiteralArgs'
// is set.
@@ -508,8 +507,7 @@ void GeoNearMatchExpression::debugString(StringBuilder& debug, int indentationLe
debug << "\n";
}
-BSONObj GeoNearMatchExpression::getSerializedRightHandSide(
- boost::optional<StringData> replacementForLiteralArgs) const {
+BSONObj GeoNearMatchExpression::getSerializedRightHandSide(SerializationOptions opts) const {
// TODO SERVER-73672 looks like we'll need to traverse '_rawObj' if 'replacementForLiteralArgs'
// is set.
BSONObjBuilder objBuilder;
diff --git a/src/mongo/db/matcher/expression_geo.h b/src/mongo/db/matcher/expression_geo.h
index 2c92e2250ad..1e784e0ba0b 100644
--- a/src/mongo/db/matcher/expression_geo.h
+++ b/src/mongo/db/matcher/expression_geo.h
@@ -106,8 +106,7 @@ public:
virtual void debugString(StringBuilder& debug, int indentationLevel = 0) const;
- BSONObj getSerializedRightHandSide(
- boost::optional<StringData> replacementForLiteralArgs) const final;
+ BSONObj getSerializedRightHandSide(SerializationOptions opts) const final;
virtual bool equivalent(const MatchExpression* other) const;
@@ -211,8 +210,7 @@ public:
virtual void debugString(StringBuilder& debug, int indentationLevel = 0) const;
- BSONObj getSerializedRightHandSide(
- boost::optional<StringData> replacementForLiteralArgs) const final;
+ BSONObj getSerializedRightHandSide(SerializationOptions opts) const final;
virtual bool equivalent(const MatchExpression* other) const;
@@ -255,7 +253,7 @@ public:
: LeafMatchExpression(INTERNAL_2D_POINT_IN_ANNULUS, twoDPath), _annulus(annulus) {}
void serialize(BSONObjBuilder* out, SerializationOptions opts) const final {
- out->append("TwoDPtInAnnulusExpression", true);
+ out->append("$TwoDPtInAnnulusExpression", true);
}
bool matchesSingleElement(const BSONElement& e, MatchDetails* details = nullptr) const final {
@@ -273,9 +271,7 @@ public:
// These won't be called.
//
- BSONObj getSerializedRightHandSide(
- boost::optional<StringData> replacementForLiteralArgs) const final {
- // TODO SERVER-73676 is this going to be a problem? I don't see how this is unreachable...
+ BSONObj getSerializedRightHandSide(SerializationOptions opts) const final {
MONGO_UNREACHABLE;
}
diff --git a/src/mongo/db/matcher/expression_internal_bucket_geo_within.cpp b/src/mongo/db/matcher/expression_internal_bucket_geo_within.cpp
index 001992fafa0..e7618378833 100644
--- a/src/mongo/db/matcher/expression_internal_bucket_geo_within.cpp
+++ b/src/mongo/db/matcher/expression_internal_bucket_geo_within.cpp
@@ -193,13 +193,23 @@ bool InternalBucketGeoWithinMatchExpression::_matchesBSONObj(const BSONObj& obj)
void InternalBucketGeoWithinMatchExpression::serialize(BSONObjBuilder* builder,
SerializationOptions opts) const {
- // TODO SERVER-73676 respect 'opts'.
BSONObjBuilder bob(builder->subobjStart(InternalBucketGeoWithinMatchExpression::kName));
+ // Serialize the geometry shape.
BSONObjBuilder withinRegionBob(
bob.subobjStart(InternalBucketGeoWithinMatchExpression::kWithinRegion));
- withinRegionBob.append(_geoContainer->getGeoElement());
+ if (opts.replacementForLiteralArgs) {
+ bob.append(_geoContainer->getGeoElement().fieldName(), *opts.replacementForLiteralArgs);
+ } else {
+ withinRegionBob.append(_geoContainer->getGeoElement());
+ }
withinRegionBob.doneFast();
- bob.append(InternalBucketGeoWithinMatchExpression::kField, _field);
+ // Serialize the field which is being searched over.
+ if (opts.redactFieldNames) {
+ bob.append(InternalBucketGeoWithinMatchExpression::kField,
+ opts.redactFieldNamesStrategy(_field));
+ } else {
+ bob.append(InternalBucketGeoWithinMatchExpression::kField, _field);
+ }
bob.doneFast();
}
diff --git a/src/mongo/db/matcher/expression_leaf.cpp b/src/mongo/db/matcher/expression_leaf.cpp
index 7e0bd6f2791..fe39d9298ce 100644
--- a/src/mongo/db/matcher/expression_leaf.cpp
+++ b/src/mongo/db/matcher/expression_leaf.cpp
@@ -94,10 +94,9 @@ void ComparisonMatchExpressionBase::debugString(StringBuilder& debug, int indent
debug << "\n";
}
-BSONObj ComparisonMatchExpressionBase::getSerializedRightHandSide(
- boost::optional<StringData> replacementForLiteralArgs) const {
- if (replacementForLiteralArgs) {
- return BSON(name() << *replacementForLiteralArgs);
+BSONObj ComparisonMatchExpressionBase::getSerializedRightHandSide(SerializationOptions opts) const {
+ if (opts.replacementForLiteralArgs) {
+ return BSON(name() << *opts.replacementForLiteralArgs);
} else {
return BSON(name() << _rhs);
}
@@ -290,13 +289,12 @@ void RegexMatchExpression::debugString(StringBuilder& debug, int indentationLeve
debug << "\n";
}
-BSONObj RegexMatchExpression::getSerializedRightHandSide(
- boost::optional<StringData> replacementForLiteralArgs) const {
+BSONObj RegexMatchExpression::getSerializedRightHandSide(SerializationOptions opts) const {
BSONObjBuilder regexBuilder;
- regexBuilder.append("$regex", replacementForLiteralArgs.value_or(_regex));
+ regexBuilder.append("$regex", opts.replacementForLiteralArgs.value_or(_regex));
if (!_flags.empty()) {
- regexBuilder.append("$options", replacementForLiteralArgs.value_or(_flags));
+ regexBuilder.append("$options", opts.replacementForLiteralArgs.value_or(_flags));
}
return regexBuilder.obj();
@@ -372,9 +370,8 @@ void ModMatchExpression::debugString(StringBuilder& debug, int indentationLevel)
debug << "\n";
}
-BSONObj ModMatchExpression::getSerializedRightHandSide(
- boost::optional<StringData> replacementForLiteralArgs) const {
- if (auto str = replacementForLiteralArgs) {
+BSONObj ModMatchExpression::getSerializedRightHandSide(SerializationOptions opts) const {
+ if (auto str = opts.replacementForLiteralArgs) {
return BSON("$mod" << *str);
} else {
return BSON("$mod" << BSON_ARRAY(_divisor << _remainder));
@@ -413,10 +410,9 @@ void ExistsMatchExpression::debugString(StringBuilder& debug, int indentationLev
debug << "\n";
}
-BSONObj ExistsMatchExpression::getSerializedRightHandSide(
- boost::optional<StringData> replacementForLiteralArgs) const {
- if (replacementForLiteralArgs) {
- return BSON("$exists" << *replacementForLiteralArgs);
+BSONObj ExistsMatchExpression::getSerializedRightHandSide(SerializationOptions opts) const {
+ if (opts.replacementForLiteralArgs) {
+ return BSON("$exists" << *opts.replacementForLiteralArgs);
} else {
return BSON("$exists" << true);
}
@@ -503,11 +499,10 @@ void InMatchExpression::debugString(StringBuilder& debug, int indentationLevel)
debug << "\n";
}
-BSONObj InMatchExpression::getSerializedRightHandSide(
- boost::optional<StringData> replacementForLiteralArgs) const {
- if (replacementForLiteralArgs) {
+BSONObj InMatchExpression::getSerializedRightHandSide(SerializationOptions opts) const {
+ if (opts.replacementForLiteralArgs) {
// In this case, treat an '$in' with any number of arguments as equivalent.
- return BSON("$in" << BSON_ARRAY(*replacementForLiteralArgs));
+ return BSON("$in" << BSON_ARRAY(*opts.replacementForLiteralArgs));
}
BSONObjBuilder inBob;
@@ -863,8 +858,7 @@ void BitTestMatchExpression::debugString(StringBuilder& debug, int indentationLe
}
}
-BSONObj BitTestMatchExpression::getSerializedRightHandSide(
- boost::optional<StringData> replacementForLiteralArgs) const {
+BSONObj BitTestMatchExpression::getSerializedRightHandSide(SerializationOptions opts) const {
std::string opString = "";
switch (matchType()) {
@@ -884,8 +878,8 @@ BSONObj BitTestMatchExpression::getSerializedRightHandSide(
MONGO_UNREACHABLE;
}
- if (replacementForLiteralArgs) {
- return BSON(opString << *replacementForLiteralArgs);
+ if (opts.replacementForLiteralArgs) {
+ return BSON(opString << *opts.replacementForLiteralArgs);
}
BSONArrayBuilder arrBob;
diff --git a/src/mongo/db/matcher/expression_leaf.h b/src/mongo/db/matcher/expression_leaf.h
index ad2556a44e1..82b62000365 100644
--- a/src/mongo/db/matcher/expression_leaf.h
+++ b/src/mongo/db/matcher/expression_leaf.h
@@ -161,8 +161,7 @@ public:
virtual void debugString(StringBuilder& debug, int indentationLevel = 0) const;
- BSONObj getSerializedRightHandSide(
- boost::optional<StringData> replacementForLiteralArgs) const final;
+ BSONObj getSerializedRightHandSide(SerializationOptions opts) const final;
virtual bool equivalent(const MatchExpression* other) const;
@@ -509,8 +508,7 @@ public:
virtual void debugString(StringBuilder& debug, int indentationLevel) const;
- BSONObj getSerializedRightHandSide(
- boost::optional<StringData> replacementForLiteralArgs) const final;
+ BSONObj getSerializedRightHandSide(SerializationOptions opts) const final;
void serializeToBSONTypeRegex(BSONObjBuilder* out) const;
@@ -592,8 +590,7 @@ public:
virtual void debugString(StringBuilder& debug, int indentationLevel) const;
- BSONObj getSerializedRightHandSide(
- boost::optional<StringData> replacementForLiteralArgs) const final;
+ BSONObj getSerializedRightHandSide(SerializationOptions opts) const final;
virtual bool equivalent(const MatchExpression* other) const;
@@ -660,8 +657,7 @@ public:
virtual void debugString(StringBuilder& debug, int indentationLevel) const;
- BSONObj getSerializedRightHandSide(
- boost::optional<StringData> replacementForLiteralArgs) const final;
+ BSONObj getSerializedRightHandSide(SerializationOptions opts) const final;
virtual bool equivalent(const MatchExpression* other) const;
@@ -695,8 +691,7 @@ public:
virtual void debugString(StringBuilder& debug, int indentationLevel) const;
- BSONObj getSerializedRightHandSide(
- boost::optional<StringData> replacementForLiteralArgs) const final;
+ BSONObj getSerializedRightHandSide(SerializationOptions opts) const final;
virtual bool equivalent(const MatchExpression* other) const;
@@ -839,8 +834,7 @@ public:
virtual void debugString(StringBuilder& debug, int indentationLevel) const;
- BSONObj getSerializedRightHandSide(
- boost::optional<StringData> replacementForLiteralArgs) const final;
+ BSONObj getSerializedRightHandSide(SerializationOptions opts) const final;
virtual bool equivalent(const MatchExpression* other) const;
diff --git a/src/mongo/db/matcher/expression_path.h b/src/mongo/db/matcher/expression_path.h
index 295495bc305..07db4a167ed 100644
--- a/src/mongo/db/matcher/expression_path.h
+++ b/src/mongo/db/matcher/expression_path.h
@@ -165,10 +165,16 @@ public:
// bit confused over 'includePath' semantics here. Before we changed anything for query
// shape, it looks like 'includePath' was not forwarded through, so it's either not needed
// or there was a pre-existing bug.
+ auto&& rhs = getSerializedRightHandSide(opts);
if (opts.includePath) {
- out->append(path(), getSerializedRightHandSide(opts.replacementForLiteralArgs));
+ if (opts.redactFieldNames) {
+ auto redactedFieldName = opts.redactFieldNamesStrategy(path());
+ out->append(redactedFieldName, rhs);
+ } else {
+ out->append(path(), rhs);
+ }
} else {
- out->appendElements(getSerializedRightHandSide(opts.replacementForLiteralArgs));
+ out->appendElements(rhs);
}
}
@@ -178,12 +184,12 @@ public:
* line with the expression. For example {x: {$not: {$eq: 1}}}, where $eq is the
* PathMatchExpression.
*
- * If the 'replacementForLiteralArgs' option is set, then any literal argument (like the number
- * 1 in the example above), should be replaced with this string. 'literal' here is in contrast
- * to another expression, if that is possible syntactically.
+ * Serialization options should be respected for any descendent expressions. Eg, if the
+ * 'replacementForLiteralArgs' option is set, then any literal argument (like the number 1 in
+ * the example above), should be replaced with this string. 'literal' here is in contrast to
+ * another expression, if that is possible syntactically.
*/
- virtual BSONObj getSerializedRightHandSide(
- boost::optional<StringData> replacementForLiteralArgs = boost::none) const = 0;
+ virtual BSONObj getSerializedRightHandSide(SerializationOptions opts = {}) const = 0;
private:
// ElementPath holds a FieldRef, which owns the underlying path string.
diff --git a/src/mongo/db/matcher/expression_text_base.cpp b/src/mongo/db/matcher/expression_text_base.cpp
index 68af83a2932..b5c3825c679 100644
--- a/src/mongo/db/matcher/expression_text_base.cpp
+++ b/src/mongo/db/matcher/expression_text_base.cpp
@@ -57,12 +57,19 @@ void TextMatchExpressionBase::debugString(StringBuilder& debug, int indentationL
}
void TextMatchExpressionBase::serialize(BSONObjBuilder* out, SerializationOptions opts) const {
- // TODO support 'opts'
const fts::FTSQuery& ftsQuery = getFTSQuery();
- out->append("$text",
- BSON("$search" << ftsQuery.getQuery() << "$language" << ftsQuery.getLanguage()
- << "$caseSensitive" << ftsQuery.getCaseSensitive()
- << "$diacriticSensitive" << ftsQuery.getDiacriticSensitive()));
+ if (opts.replacementForLiteralArgs) {
+ out->append("$text",
+ BSON("$search" << *opts.replacementForLiteralArgs << "$language"
+ << *opts.replacementForLiteralArgs << "$caseSensitive"
+ << *opts.replacementForLiteralArgs << "$diacriticSensitive"
+ << *opts.replacementForLiteralArgs));
+ } else {
+ out->append("$text",
+ BSON("$search" << ftsQuery.getQuery() << "$language" << ftsQuery.getLanguage()
+ << "$caseSensitive" << ftsQuery.getCaseSensitive()
+ << "$diacriticSensitive" << ftsQuery.getDiacriticSensitive()));
+ }
}
bool TextMatchExpressionBase::equivalent(const MatchExpression* other) const {
diff --git a/src/mongo/db/matcher/expression_text_base.h b/src/mongo/db/matcher/expression_text_base.h
index 954d5272d13..92b7b5a8c94 100644
--- a/src/mongo/db/matcher/expression_text_base.h
+++ b/src/mongo/db/matcher/expression_text_base.h
@@ -60,9 +60,7 @@ public:
*/
virtual const fts::FTSQuery& getFTSQuery() const = 0;
- BSONObj getSerializedRightHandSide(
- boost::optional<StringData> replacementForLiteralArgs) const final {
- // TODO SERVER-73676 is this going to be a problem? I don't see how this is unreachable...
+ BSONObj getSerializedRightHandSide(SerializationOptions opts) const final {
MONGO_UNREACHABLE;
}
diff --git a/src/mongo/db/matcher/expression_tree.cpp b/src/mongo/db/matcher/expression_tree.cpp
index 2f3f376931b..ba00bc78ad0 100644
--- a/src/mongo/db/matcher/expression_tree.cpp
+++ b/src/mongo/db/matcher/expression_tree.cpp
@@ -455,9 +455,12 @@ void NotMatchExpression::serializeNotExpressionToNor(MatchExpression* exp,
}
void NotMatchExpression::serialize(BSONObjBuilder* out, SerializationOptions opts) const {
- // TODO SERVER-73676 respect 'opts.'
if (_exp->matchType() == MatchType::AND && _exp->numChildren() == 0) {
- out->append("$alwaysFalse", 1);
+ if (opts.replacementForLiteralArgs) {
+ out->append("$alwaysFalse", *opts.replacementForLiteralArgs);
+ } else {
+ out->append("$alwaysFalse", 1);
+ }
return;
}
@@ -492,10 +495,15 @@ void NotMatchExpression::serialize(BSONObjBuilder* out, SerializationOptions opt
// 1}}.
if (auto pathMatch = dynamic_cast<PathMatchExpression*>(expressionToNegate);
pathMatch && !dynamic_cast<TextMatchExpressionBase*>(expressionToNegate)) {
- const auto path = pathMatch->path();
- BSONObjBuilder pathBob(out->subobjStart(path));
- pathBob.append("$not",
- pathMatch->getSerializedRightHandSide(opts.replacementForLiteralArgs));
+ auto append = [&](StringData path) {
+ BSONObjBuilder pathBob(out->subobjStart(path));
+ pathBob.append("$not", pathMatch->getSerializedRightHandSide(opts));
+ };
+ if (opts.redactFieldNames) {
+ append(opts.redactFieldNamesStrategy(pathMatch->path()));
+ } else {
+ append(pathMatch->path());
+ }
return;
}
return serializeNotExpressionToNor(expressionToNegate, out, opts);
diff --git a/src/mongo/db/matcher/expression_type.h b/src/mongo/db/matcher/expression_type.h
index b45b0e90774..3e8eecf836b 100644
--- a/src/mongo/db/matcher/expression_type.h
+++ b/src/mongo/db/matcher/expression_type.h
@@ -82,8 +82,7 @@ public:
debug << "\n";
}
- BSONObj getSerializedRightHandSide(
- boost::optional<StringData> replacementForLiteralArgs) const final {
+ BSONObj getSerializedRightHandSide(SerializationOptions opts) const final {
// TODO SERVER-73678 respect 'replacementForLiteralArgs'.
BSONObjBuilder subBuilder;
BSONArrayBuilder arrBuilder(subBuilder.subarrayStart(name()));
@@ -256,8 +255,7 @@ public:
debug << "\n";
}
- BSONObj getSerializedRightHandSide(
- boost::optional<StringData> replacementForLiteralArgs) const final {
+ BSONObj getSerializedRightHandSide(SerializationOptions opts) const final {
// TODO SERVER-73678 respect 'replacementForLiteralArgs'.
BSONObjBuilder bob;
bob.append(name(), _binDataSubType);
diff --git a/src/mongo/db/matcher/expression_where_base.cpp b/src/mongo/db/matcher/expression_where_base.cpp
index 17568f76321..3508260bb16 100644
--- a/src/mongo/db/matcher/expression_where_base.cpp
+++ b/src/mongo/db/matcher/expression_where_base.cpp
@@ -47,8 +47,11 @@ void WhereMatchExpressionBase::debugString(StringBuilder& debug, int indentation
}
void WhereMatchExpressionBase::serialize(BSONObjBuilder* out, SerializationOptions opts) const {
- // TODO SERVER-73676 respect 'opts.'
- out->appendCode("$where", getCode());
+ if (opts.replacementForLiteralArgs) {
+ out->append("$where", *opts.replacementForLiteralArgs);
+ } else {
+ out->appendCode("$where", getCode());
+ }
}
bool WhereMatchExpressionBase::equivalent(const MatchExpression* other) const {
diff --git a/src/mongo/db/matcher/parsed_match_expression_for_test.h b/src/mongo/db/matcher/parsed_match_expression_for_test.h
index 89e965a45ca..4c4d8a9dc43 100644
--- a/src/mongo/db/matcher/parsed_match_expression_for_test.h
+++ b/src/mongo/db/matcher/parsed_match_expression_for_test.h
@@ -48,7 +48,12 @@ public:
: _obj(fromjson(str)) {
_expCtx = make_intrusive<ExpressionContextForTest>();
_expCtx->setCollator(CollatorInterface::cloneCollator(collator));
- StatusWithMatchExpression result = MatchExpressionParser::parse(_obj, _expCtx);
+ StatusWithMatchExpression result =
+ MatchExpressionParser::parse(_obj,
+ _expCtx,
+ ExtensionsCallbackNoop(),
+ MatchExpressionParser::kDefaultSpecialFeatures |
+ MatchExpressionParser::AllowedFeatures::kJavascript);
ASSERT_OK(result.getStatus());
_expr = std::move(result.getValue());
}
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.cpp b/src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.cpp
index 6fbeb59be73..9b0c0fa6f74 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.cpp
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.cpp
@@ -77,14 +77,14 @@ void InternalSchemaAllElemMatchFromIndexMatchExpression::debugString(StringBuild
}
BSONObj InternalSchemaAllElemMatchFromIndexMatchExpression::getSerializedRightHandSide(
- boost::optional<StringData> replacementForLiteralArgs) const {
+ SerializationOptions opts) const {
// TODO SERVER-73678 respect 'replacementForLiteralArgs'.
BSONObjBuilder allElemMatchBob;
BSONArrayBuilder subArray(allElemMatchBob.subarrayStart(kName));
subArray.append(_index);
{
BSONObjBuilder eBuilder(subArray.subobjStart());
- _expression->getFilter()->serialize(&eBuilder, {});
+ _expression->getFilter()->serialize(&eBuilder, opts);
eBuilder.doneFast();
}
subArray.doneFast();
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.h b/src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.h
index 9ca495448f9..1a385142116 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.h
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.h
@@ -78,8 +78,7 @@ public:
void debugString(StringBuilder& debug, int indentationLevel) const final;
- BSONObj getSerializedRightHandSide(
- boost::optional<StringData> replacementForLiteralArgs) const final;
+ BSONObj getSerializedRightHandSide(SerializationOptions opts) const final;
bool equivalent(const MatchExpression* other) const final;
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_eq.cpp b/src/mongo/db/matcher/schema/expression_internal_schema_eq.cpp
index 496dac6a29d..2919bf1a65f 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_eq.cpp
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_eq.cpp
@@ -70,7 +70,7 @@ void InternalSchemaEqMatchExpression::debugString(StringBuilder& debug,
}
BSONObj InternalSchemaEqMatchExpression::getSerializedRightHandSide(
- boost::optional<StringData> replacementForLiteralArgs) const {
+ SerializationOptions opts) const {
// TODO SERVER-73678 respect 'replacementForLiteralArgs.'
BSONObjBuilder eqObj;
eqObj.appendAs(_rhsElem, kName);
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_eq.h b/src/mongo/db/matcher/schema/expression_internal_schema_eq.h
index e53110c3aa6..c4cb5f0d235 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_eq.h
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_eq.h
@@ -59,8 +59,7 @@ public:
void debugString(StringBuilder& debug, int indentationLevel) const final;
- BSONObj getSerializedRightHandSide(
- boost::optional<StringData> replacementForLiteralArgs) const final;
+ BSONObj getSerializedRightHandSide(SerializationOptions opts) const final;
bool equivalent(const MatchExpression* other) const final;
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_fmod.cpp b/src/mongo/db/matcher/schema/expression_internal_schema_fmod.cpp
index 887f382a0ad..4dd56a4884f 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_fmod.cpp
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_fmod.cpp
@@ -77,7 +77,7 @@ void InternalSchemaFmodMatchExpression::debugString(StringBuilder& debug,
}
BSONObj InternalSchemaFmodMatchExpression::getSerializedRightHandSide(
- boost::optional<StringData> replacementForLiteralArgs) const {
+ SerializationOptions opts) const {
// TODO SERVER-73678 respect 'replacementForLiteralArgs'.
BSONObjBuilder objMatchBob;
BSONArrayBuilder arrBuilder(objMatchBob.subarrayStart("$_internalSchemaFmod"));
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_fmod.h b/src/mongo/db/matcher/schema/expression_internal_schema_fmod.h
index a48a5ce88c7..46e3f39f860 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_fmod.h
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_fmod.h
@@ -58,8 +58,7 @@ public:
void debugString(StringBuilder& debug, int indentationLevel) const final;
- BSONObj getSerializedRightHandSide(
- boost::optional<StringData> replacementForLiteralArgs) const final;
+ BSONObj getSerializedRightHandSide(SerializationOptions opts) const final;
bool equivalent(const MatchExpression* other) const final;
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_match_array_index.cpp b/src/mongo/db/matcher/schema/expression_internal_schema_match_array_index.cpp
index a3b53e6890f..0792b33b2fe 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_match_array_index.cpp
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_match_array_index.cpp
@@ -73,7 +73,7 @@ bool InternalSchemaMatchArrayIndexMatchExpression::equivalent(const MatchExpress
}
BSONObj InternalSchemaMatchArrayIndexMatchExpression::getSerializedRightHandSide(
- boost::optional<StringData> replacementForLiteralArgs) const {
+ SerializationOptions opts) const {
// TODO SERVER-73678 respect 'replacementForLiteralArgs'.
BSONObjBuilder objBuilder;
{
@@ -82,7 +82,7 @@ BSONObj InternalSchemaMatchArrayIndexMatchExpression::getSerializedRightHandSide
matchArrayElemSubobj.append("namePlaceholder", _expression->getPlaceholder().value_or(""));
{
BSONObjBuilder subexprSubObj(matchArrayElemSubobj.subobjStart("expression"));
- _expression->getFilter()->serialize(&subexprSubObj, {});
+ _expression->getFilter()->serialize(&subexprSubObj, opts);
subexprSubObj.doneFast();
}
matchArrayElemSubobj.doneFast();
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_match_array_index.h b/src/mongo/db/matcher/schema/expression_internal_schema_match_array_index.h
index 56d115cc5ab..f3230bf0cbd 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_match_array_index.h
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_match_array_index.h
@@ -74,8 +74,7 @@ public:
return _expression->matchesBSONElement(element, details);
}
- BSONObj getSerializedRightHandSide(
- boost::optional<StringData> replacementForLiteralArgs) const final;
+ BSONObj getSerializedRightHandSide(SerializationOptions opts) const final;
std::unique_ptr<MatchExpression> shallowClone() const final;
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_num_array_items.cpp b/src/mongo/db/matcher/schema/expression_internal_schema_num_array_items.cpp
index 705f726e447..b4d0d069bad 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_num_array_items.cpp
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_num_array_items.cpp
@@ -57,7 +57,7 @@ void InternalSchemaNumArrayItemsMatchExpression::debugString(StringBuilder& debu
}
BSONObj InternalSchemaNumArrayItemsMatchExpression::getSerializedRightHandSide(
- boost::optional<StringData> replacementForLiteralArgs) const {
+ SerializationOptions opts) const {
// TODO SERVER-73678 respect 'replacementForLiteralArgs'.
BSONObjBuilder objBuilder;
objBuilder.append(_name, _numItems);
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_num_array_items.h b/src/mongo/db/matcher/schema/expression_internal_schema_num_array_items.h
index beb9cadd3f7..24eb787df55 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_num_array_items.h
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_num_array_items.h
@@ -52,8 +52,7 @@ public:
void debugString(StringBuilder& debug, int indentationLevel) const final;
- BSONObj getSerializedRightHandSide(
- boost::optional<StringData> replacementForLiteralArgs) const final;
+ BSONObj getSerializedRightHandSide(SerializationOptions opts) const final;
bool equivalent(const MatchExpression* other) const final;
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_object_match.cpp b/src/mongo/db/matcher/schema/expression_internal_schema_object_match.cpp
index e057d5c2d26..d1716c00b4f 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_object_match.cpp
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_object_match.cpp
@@ -62,11 +62,11 @@ void InternalSchemaObjectMatchExpression::debugString(StringBuilder& debug,
}
BSONObj InternalSchemaObjectMatchExpression::getSerializedRightHandSide(
- boost::optional<StringData> replacementForLiteralArgs) const {
+ SerializationOptions opts) const {
// TODO SERVER-73678 respect 'replacementForLiteralArgs'.
BSONObjBuilder objMatchBob;
BSONObjBuilder subBob(objMatchBob.subobjStart(kName));
- _sub->serialize(&subBob, {});
+ _sub->serialize(&subBob, opts);
subBob.doneFast();
return objMatchBob.obj();
}
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_object_match.h b/src/mongo/db/matcher/schema/expression_internal_schema_object_match.h
index 44e74fdd8bc..081f3382721 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_object_match.h
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_object_match.h
@@ -50,8 +50,7 @@ public:
void debugString(StringBuilder& debug, int indentationLevel = 0) const final;
- BSONObj getSerializedRightHandSide(
- boost::optional<StringData> replacementForLiteralArgs) const final;
+ BSONObj getSerializedRightHandSide(SerializationOptions opts) const final;
bool equivalent(const MatchExpression* other) const final;
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_str_length.cpp b/src/mongo/db/matcher/schema/expression_internal_schema_str_length.cpp
index b643393b548..f5b90ecd350 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_str_length.cpp
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_str_length.cpp
@@ -57,7 +57,7 @@ void InternalSchemaStrLengthMatchExpression::debugString(StringBuilder& debug,
}
BSONObj InternalSchemaStrLengthMatchExpression::getSerializedRightHandSide(
- boost::optional<StringData> replacementForLiteralArgs) const {
+ SerializationOptions opts) const {
// TODO SERVER-73678 respect 'replacementForLiteralArgs'.
BSONObjBuilder objBuilder;
objBuilder.append(_name, _strLen);
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_str_length.h b/src/mongo/db/matcher/schema/expression_internal_schema_str_length.h
index 9dd2493da99..6292f567ccc 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_str_length.h
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_str_length.h
@@ -62,8 +62,7 @@ public:
void debugString(StringBuilder& debug, int indentationLevel) const final;
- BSONObj getSerializedRightHandSide(
- boost::optional<StringData> replacementForLiteralArgs) const final;
+ BSONObj getSerializedRightHandSide(SerializationOptions opts) const final;
bool equivalent(const MatchExpression* other) const final;
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_unique_items.cpp b/src/mongo/db/matcher/schema/expression_internal_schema_unique_items.cpp
index a5dcfd9c9cd..90c7a4b69c4 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_unique_items.cpp
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_unique_items.cpp
@@ -60,7 +60,7 @@ bool InternalSchemaUniqueItemsMatchExpression::equivalent(const MatchExpression*
}
BSONObj InternalSchemaUniqueItemsMatchExpression::getSerializedRightHandSide(
- boost::optional<StringData> replacementForLiteralArgs) const {
+ SerializationOptions opts) const {
// TODO SERVER-73678 respect 'replacementForLiteralArgs.'
BSONObjBuilder bob;
bob.append(kName, true);
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_unique_items.h b/src/mongo/db/matcher/schema/expression_internal_schema_unique_items.h
index eaf12148879..98f7d779377 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_unique_items.h
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_unique_items.h
@@ -86,8 +86,7 @@ public:
bool equivalent(const MatchExpression* other) const final;
- BSONObj getSerializedRightHandSide(
- boost::optional<StringData> replacementForLiteralArgs) const final;
+ BSONObj getSerializedRightHandSide(SerializationOptions opts) const final;
std::unique_ptr<MatchExpression> shallowClone() const final;
diff --git a/src/mongo/db/query/query_shape.cpp b/src/mongo/db/query/query_shape.cpp
index cc6829a72f4..fcfe0cb361e 100644
--- a/src/mongo/db/query/query_shape.cpp
+++ b/src/mongo/db/query/query_shape.cpp
@@ -36,4 +36,14 @@ BSONObj predicateShape(const MatchExpression* predicate) {
opts.replacementForLiteralArgs = kLiteralArgString;
return predicate->serialize(opts);
}
+
+BSONObj predicateShape(const MatchExpression* predicate,
+ std::function<std::string(StringData)> redactFieldNamesStrategy) {
+ SerializationOptions opts;
+ opts.replacementForLiteralArgs = kLiteralArgString;
+ opts.redactFieldNamesStrategy = redactFieldNamesStrategy;
+ opts.redactFieldNames = true;
+ return predicate->serialize(opts);
+}
+
} // namespace mongo::query_shape
diff --git a/src/mongo/db/query/query_shape.h b/src/mongo/db/query/query_shape.h
index 78733ac54b9..c072fa8b95e 100644
--- a/src/mongo/db/query/query_shape.h
+++ b/src/mongo/db/query/query_shape.h
@@ -51,4 +51,7 @@ constexpr StringData kLiteralArgString = "?"_sd;
*/
BSONObj predicateShape(const MatchExpression* predicate);
+BSONObj predicateShape(const MatchExpression* predicate,
+ std::function<std::string(StringData)> redactFieldNamesStrategy);
+
} // namespace mongo::query_shape
diff --git a/src/mongo/db/query/query_shape_test.cpp b/src/mongo/db/query/query_shape_test.cpp
index 47c5e2bdea6..c764831200a 100644
--- a/src/mongo/db/query/query_shape_test.cpp
+++ b/src/mongo/db/query/query_shape_test.cpp
@@ -29,16 +29,26 @@
#include "mongo/bson/bsonmisc.h"
#include "mongo/bson/bsonobj.h"
+#include "mongo/db/matcher/expression_geo.h"
+#include "mongo/db/matcher/extensions_callback_real.h"
#include "mongo/db/matcher/parsed_match_expression_for_test.h"
-#include "mongo/db/operation_context.h"
#include "mongo/db/pipeline/expression_context_for_test.h"
-#include "mongo/db/query/optimizer/utils/unit_test_utils.h"
-#include "mongo/db/query/query_planner_params.h"
#include "mongo/db/query/query_shape.h"
#include "mongo/unittest/unittest.h"
namespace mongo {
namespace {
+
+/**
+ * Simplistic redaction strategy for testing which appends the field name to the prefix "REDACT_".
+ */
+std::string redactFieldNameForTest(StringData sd) {
+ return "REDACT_" + sd.toString();
+}
+
+static const SerializationOptions literalAndFieldRedactOpts{redactFieldNameForTest,
+ query_shape::kLiteralArgString};
+
void assertShapeIs(std::string filterJson, BSONObj expectedShape) {
ParsedMatchExpressionForTest expr(filterJson);
ASSERT_BSONOBJ_EQ(expectedShape, query_shape::predicateShape(expr.get()));
@@ -48,12 +58,26 @@ void assertShapeIs(std::string filterJson, std::string expectedShapeJson) {
return assertShapeIs(filterJson, fromjson(expectedShapeJson));
}
+void assertRedactedShapeIs(std::string filterJson, BSONObj expectedShape) {
+ ParsedMatchExpressionForTest expr(filterJson);
+ ASSERT_BSONOBJ_EQ(expectedShape,
+ query_shape::predicateShape(expr.get(), redactFieldNameForTest));
+}
+
+void assertRedactedShapeIs(std::string filterJson, std::string expectedShapeJson) {
+ return assertRedactedShapeIs(filterJson, fromjson(expectedShapeJson));
+}
+
} // namespace
TEST(QueryPredicateShape, Equals) {
assertShapeIs("{a: 5}", "{a: {$eq: '?'}}"); // Implicit equals
assertShapeIs("{a: {$eq: 5}}", "{a: {$eq: '?'}}"); // Explicit equals
assertShapeIs("{a: 5, b: 6}", "{$and: [{a: {$eq: '?'}}, {b: {$eq: '?'}}]}"); // implicit $and
+ assertRedactedShapeIs("{a: 5}", "{REDACT_a: {$eq: '?'}}"); // Implicit equals
+ assertRedactedShapeIs("{a: {$eq: 5}}", "{REDACT_a: {$eq: '?'}}"); // Explicit equals
+ assertRedactedShapeIs("{a: 5, b: 6}",
+ "{$and: [{REDACT_a: {$eq: '?'}}, {REDACT_b: {$eq: '?'}}]}");
}
TEST(QueryPredicateShape, Comparisons) {
@@ -68,6 +92,8 @@ TEST(QueryPredicateShape, Regex) {
assertShapeIs("{a: /a+/i}",
BSON("a" << BSON("$regex" << query_shape::kLiteralArgString << "$options"
<< query_shape::kLiteralArgString)));
+ assertRedactedShapeIs("{a: /a+/}",
+ BSON("REDACT_a" << BSON("$regex" << query_shape::kLiteralArgString)));
}
TEST(QueryPredicateShape, Mod) {
@@ -120,6 +146,55 @@ TEST(QueryPredicateShape, ElemMatch) {
// ElemMatchValueMatchExpression
assertShapeIs("{a: {$elemMatch: {$gt: 5, $lt: 10}}}", "{a: {$elemMatch: {$gt: '?', $lt:'?'}}}");
+
+ // Nested
+ assertRedactedShapeIs("{a: {$elemMatch: {$elemMatch: {$gt: 5, $lt: 10}}}}",
+ "{REDACT_a: {$elemMatch: {$elemMatch: {$gt: '?', $lt:'?'}}}}");
+}
+
+TEST(QueryPredicateShape, InternalBucketGeoWithinMatchExpression) {
+ assertRedactedShapeIs(
+ "{ $_internalBucketGeoWithin: {withinRegion: {$centerSphere: [[0, 0], 10]}, field: \"a\"} "
+ "}",
+ "{ $_internalBucketGeoWithin: { withinRegion: { $centerSphere: \"?\" }, "
+ "field: \"REDACT_a\" } }");
+}
+
+TEST(QueryPredicateShape, NorMatchExpression) {
+ assertRedactedShapeIs("{ $nor: [ { a: {$lt: 5} }, { b: {$gt: 4} } ] }",
+ "{ $nor: [ { REDACT_a: {$lt: \"?\"} }, { REDACT_b: {$gt: \"?\"} } ] }");
+}
+
+TEST(QueryPredicateShape, NotMatchExpression) {
+ assertRedactedShapeIs("{ price: { $not: { $gt: 1.99 } } }",
+ "{ REDACT_price: { $not: { $gt: \"?\" } } }");
+ // Test the special case where NotMatchExpression::serialize() reduces to $alwaysFalse.
+ auto emptyAnd = std::make_unique<AndMatchExpression>();
+ const MatchExpression& notExpr = NotMatchExpression(std::move(emptyAnd));
+ auto serialized = notExpr.serialize(literalAndFieldRedactOpts);
+ ASSERT_BSONOBJ_EQ(fromjson("{$alwaysFalse: '?'}"), serialized);
+}
+
+TEST(QueryPredicateShape, SizeMatchExpression) {
+ assertRedactedShapeIs("{ price: { $size: 2 } }", "{ REDACT_price: { $size: \"?\" } }");
+}
+
+TEST(QueryPredicateShape, TextMatchExpression) {
+ TextMatchExpressionBase::TextParams params = {"coffee"};
+ auto expr = ExtensionsCallbackNoop().createText(params);
+ ASSERT_BSONOBJ_EQ(fromjson("{ $text: { $search: \"?\", $language: \"?\", $caseSensitive: "
+ "\"?\", $diacriticSensitive: \"?\" } }"),
+ expr->serialize(literalAndFieldRedactOpts));
+}
+
+TEST(QueryPredicateShape, TwoDPtInAnnulusExpression) {
+ const MatchExpression& expr = TwoDPtInAnnulusExpression({}, {});
+ ASSERT_BSONOBJ_EQ(fromjson("{ $TwoDPtInAnnulusExpression: true }"),
+ expr.serialize(literalAndFieldRedactOpts));
+}
+
+TEST(QueryPredicateShape, WhereMatchExpression) {
+ assertShapeIs("{$where: \"some_code()\"}", "{$where: \"?\"}");
}
BSONObj queryShapeForOptimizedExprExpression(std::string exprPredicateJson) {
diff --git a/src/mongo/db/query/serialization_options.h b/src/mongo/db/query/serialization_options.h
index 2086a25ae8c..4ac22617ecd 100644
--- a/src/mongo/db/query/serialization_options.h
+++ b/src/mongo/db/query/serialization_options.h
@@ -46,8 +46,15 @@ std::string defaultRedactionStrategy(StringData s) {
*/
struct SerializationOptions {
SerializationOptions() {}
+
SerializationOptions(bool explain_) : explain(explain_) {}
+ SerializationOptions(std::function<std::string(StringData)> redactFieldNamesStrategy_,
+ boost::optional<StringData> replacementForLiteralArgs_)
+ : replacementForLiteralArgs(replacementForLiteralArgs_),
+ redactFieldNames(redactFieldNamesStrategy_),
+ redactFieldNamesStrategy(redactFieldNamesStrategy_) {}
+
// '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
// set to '?' then the serialization of {a: {$gt: 2}} will result in {a: {$gt: '?'}}, as