summaryrefslogtreecommitdiff
path: root/src/mongo/db
diff options
context:
space:
mode:
authorTed Tuckman <ted.tuckman@mongodb.com>2023-03-28 16:20:15 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2023-03-28 20:30:16 +0000
commitdf7e8314403e4e23c80a384e2413d32d66d6f4d2 (patch)
tree65c43eb80406eff0e3ef570299cac152f8a0c411 /src/mongo/db
parent3e44409d23b9924ceca60bb2429382fbcc8fc0c3 (diff)
downloadmongo-df7e8314403e4e23c80a384e2413d32d66d6f4d2.tar.gz
SERVER-73678 Implement and verify redaction for JsonSchema MatchExpressions
Diffstat (limited to 'src/mongo/db')
-rw-r--r--src/mongo/db/matcher/expression_arity.h1
-rw-r--r--src/mongo/db/matcher/expression_path.h6
-rw-r--r--src/mongo/db/matcher/expression_serialization_test.cpp279
-rw-r--r--src/mongo/db/matcher/expression_type.h12
-rw-r--r--src/mongo/db/matcher/expression_type_test.cpp18
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.cpp7
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index_test.cpp1
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_allowed_properties.cpp17
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_eq.cpp12
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_fmod.cpp11
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_match_array_index.cpp14
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_num_array_items.cpp7
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_num_properties.cpp7
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_object_match.cpp1
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_root_doc_eq.cpp3
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_str_length.cpp7
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_unique_items.cpp1
-rw-r--r--src/mongo/db/pipeline/expression.cpp7
-rw-r--r--src/mongo/db/query/serialization_options.h44
19 files changed, 414 insertions, 41 deletions
diff --git a/src/mongo/db/matcher/expression_arity.h b/src/mongo/db/matcher/expression_arity.h
index ebe68bd9255..fce1508c224 100644
--- a/src/mongo/db/matcher/expression_arity.h
+++ b/src/mongo/db/matcher/expression_arity.h
@@ -108,7 +108,6 @@ public:
* Serializes each subexpression sequentially in a BSONArray.
*/
void serialize(BSONObjBuilder* builder, SerializationOptions opts) const final {
- // TODO SERVER-73678 respect 'opts'.
BSONArrayBuilder exprArray(builder->subarrayStart(name()));
for (const auto& expr : _expressions) {
BSONObjBuilder exprBuilder(exprArray.subobjStart());
diff --git a/src/mongo/db/matcher/expression_path.h b/src/mongo/db/matcher/expression_path.h
index 07db4a167ed..33f4cb751a2 100644
--- a/src/mongo/db/matcher/expression_path.h
+++ b/src/mongo/db/matcher/expression_path.h
@@ -159,12 +159,6 @@ public:
}
void serialize(BSONObjBuilder* out, SerializationOptions opts) const override {
- // TODO SERVER-73678 do we need to pass 'includePath' or other options to
- // `getSerializedRightHandSide()` here? I don't think we need 'includePath' for
- // LeafMatchExpression subclasses, but the class comment on PathMatchExpression leaves me a
- // 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) {
if (opts.redactFieldNames) {
diff --git a/src/mongo/db/matcher/expression_serialization_test.cpp b/src/mongo/db/matcher/expression_serialization_test.cpp
index 214c0d31ffb..1551039194b 100644
--- a/src/mongo/db/matcher/expression_serialization_test.cpp
+++ b/src/mongo/db/matcher/expression_serialization_test.cpp
@@ -37,7 +37,16 @@
#include "mongo/db/matcher/expression_parser.h"
#include "mongo/db/matcher/extensions_callback_noop.h"
#include "mongo/db/matcher/matcher.h"
+#include "mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.h"
+#include "mongo/db/matcher/schema/expression_internal_schema_cond.h"
+#include "mongo/db/matcher/schema/expression_internal_schema_eq.h"
+#include "mongo/db/matcher/schema/expression_internal_schema_fmod.h"
+#include "mongo/db/matcher/schema/expression_internal_schema_max_items.h"
#include "mongo/db/matcher/schema/expression_internal_schema_max_length.h"
+#include "mongo/db/matcher/schema/expression_internal_schema_max_properties.h"
+#include "mongo/db/matcher/schema/expression_internal_schema_min_items.h"
+#include "mongo/db/matcher/schema/expression_internal_schema_min_length.h"
+#include "mongo/db/matcher/schema/expression_internal_schema_min_properties.h"
#include "mongo/db/pipeline/expression_context_for_test.h"
#include "mongo/unittest/unittest.h"
@@ -1846,5 +1855,275 @@ TEST(SerializeInternalBinDataSubType, ExpressionBinDataSubTypeSerializesCorrectl
ASSERT_TRUE(original.matches(obj));
}
+std::string redactFieldNameForTest(StringData s) {
+ return str::stream() << "HASH(" << s << ")";
+}
+TEST(SerializeInternalSchema, AllowedPropertiesRedactsCorrectly) {
+
+ auto query = fromjson(
+ "{$_internalSchemaAllowedProperties: {properties: ['a', 'b'],"
+ "namePlaceholder: 'i', patternProperties: [], otherwise: {i: 0}}}");
+ boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ auto objMatch = MatchExpressionParser::parse(query, expCtx);
+ ASSERT_OK(objMatch.getStatus());
+
+ SerializationOptions opts;
+ opts.redactFieldNames = true;
+ opts.redactFieldNamesStrategy = redactFieldNameForTest;
+ opts.replacementForLiteralArgs = "?";
+
+ ASSERT_BSONOBJ_EQ(
+ fromjson(
+ "{ $_internalSchemaAllowedProperties: { properties: \"?\", namePlaceholder: \"?\", "
+ "patternProperties: [], otherwise: { \"HASH(i)\": { $eq: \"?\" } } } }"),
+ objMatch.getValue()->serialize(opts));
+}
+
+/**
+ * Helper function for parsing and creating MatchExpressions.
+ */
+std::unique_ptr<InternalSchemaCondMatchExpression> createCondMatchExpression(BSONObj condition,
+ BSONObj thenBranch,
+ BSONObj elseBranch) {
+ boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ auto conditionExpr = MatchExpressionParser::parse(condition, expCtx);
+ ASSERT_OK(conditionExpr.getStatus());
+ auto thenBranchExpr = MatchExpressionParser::parse(thenBranch, expCtx);
+ ASSERT_OK(thenBranchExpr.getStatus());
+ auto elseBranchExpr = MatchExpressionParser::parse(elseBranch, expCtx);
+
+ std::array<std::unique_ptr<MatchExpression>, 3> expressions = {
+ {std::move(conditionExpr.getValue()),
+ std::move(thenBranchExpr.getValue()),
+ std::move(elseBranchExpr.getValue())}};
+
+ auto cond = std::make_unique<InternalSchemaCondMatchExpression>(std::move(expressions));
+
+ return cond;
+}
+
+TEST(SerializeInternalSchema, CondMatchRedactsCorrectly) {
+ SerializationOptions opts;
+ opts.redactFieldNames = true;
+ opts.replacementForLiteralArgs = "?";
+ opts.redactFieldNamesStrategy = redactFieldNameForTest;
+ auto conditionQuery = BSON("age" << BSON("$lt" << 18));
+ auto thenQuery = BSON("job"
+ << "student");
+ auto elseQuery = BSON("job"
+ << "engineer");
+ auto cond = createCondMatchExpression(conditionQuery, thenQuery, elseQuery);
+ BSONObjBuilder bob;
+ cond->serialize(&bob, opts);
+ auto expectedResult =
+ BSON("$_internalSchemaCond" << BSON_ARRAY(BSON("HASH(age)" << BSON("$lt"
+ << "?"))
+ << BSON("HASH(job)" << BSON("$eq"
+ << "?"))
+ << BSON("HASH(job)" << BSON("$eq"
+ << "?"))));
+ ASSERT_BSONOBJ_EQ(expectedResult, bob.done());
+}
+
+TEST(SerializeInternalSchema, FmodMatchRedactsCorrectly) {
+ InternalSchemaFmodMatchExpression m("a"_sd, Decimal128(1.7), Decimal128(2));
+ SerializationOptions opts;
+ opts.replacementForLiteralArgs = "?";
+ BSONObjBuilder bob;
+ m.serialize(&bob, opts);
+ ASSERT_BSONOBJ_EQ(BSON("a" << BSON("$_internalSchemaFmod" << BSON_ARRAY("?"
+ << "?"))),
+ bob.done());
+}
+
+TEST(SerializeInternalSchema, MatchArrayIndexRedactsCorrectly) {
+ auto query = fromjson(
+ "{foo: {$_internalSchemaMatchArrayIndex:"
+ "{index: 0, namePlaceholder: 'i', expression: {i: {$type: 'number'}}}}}");
+ boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ auto objMatch = MatchExpressionParser::parse(query, expCtx);
+ ASSERT_OK(objMatch.getStatus());
+
+ BSONObjBuilder bob;
+ SerializationOptions opts;
+ opts.redactFieldNames = true;
+ opts.redactFieldNamesStrategy = redactFieldNameForTest;
+ opts.replacementForLiteralArgs = "?";
+ objMatch.getValue()->serialize(&bob, opts);
+
+ ASSERT_BSONOBJ_EQ(bob.done(),
+ BSON("HASH(foo)" << BSON("$_internalSchemaMatchArrayIndex"
+ << BSON("index"
+ << "?"
+ << "namePlaceholder"
+ << "HASH(i)"
+ << "expression"
+ << BSON("HASH(i)" << BSON("$type"
+ << "?"))))));
+}
+
+TEST(SerializeInternalSchema, MaxItemsRedactsCorrectly) {
+ InternalSchemaMaxItemsMatchExpression maxItems("a.b"_sd, 2);
+ SerializationOptions opts;
+ opts.redactFieldNames = true;
+ opts.replacementForLiteralArgs = "?";
+ opts.redactFieldNamesStrategy = redactFieldNameForTest;
+
+ ASSERT_BSONOBJ_EQ(maxItems.getSerializedRightHandSide(opts),
+ BSON("$_internalSchemaMaxItems"
+ << "?"));
+}
+
+TEST(SerializeInternalSchema, MaxLengthRedactsCorrectly) {
+ InternalSchemaMaxLengthMatchExpression maxLength("a"_sd, 2);
+ SerializationOptions opts;
+ opts.replacementForLiteralArgs = "?";
+ ASSERT_BSONOBJ_EQ(maxLength.getSerializedRightHandSide(opts),
+ BSON("$_internalSchemaMaxLength"
+ << "?"));
+}
+
+TEST(SerializeInternalSchema, MinItemsRedactsCorrectly) {
+ InternalSchemaMinItemsMatchExpression minItems("a.b"_sd, 2);
+ SerializationOptions opts;
+ opts.redactFieldNames = true;
+ opts.replacementForLiteralArgs = "?";
+ opts.redactFieldNamesStrategy = redactFieldNameForTest;
+
+ ASSERT_BSONOBJ_EQ(minItems.getSerializedRightHandSide(opts),
+ BSON("$_internalSchemaMinItems"
+ << "?"));
+}
+
+TEST(SerializeInternalSchema, MinLengthRedactsCorrectly) {
+ InternalSchemaMinLengthMatchExpression minLength("a"_sd, 2);
+ SerializationOptions opts;
+ opts.replacementForLiteralArgs = "?";
+ ASSERT_BSONOBJ_EQ(minLength.getSerializedRightHandSide(opts),
+ BSON("$_internalSchemaMinLength"
+ << "?"));
+}
+
+TEST(SerializeInternalSchema, MinPropertiesRedactsCorrectly) {
+ InternalSchemaMinPropertiesMatchExpression minProperties(5);
+ SerializationOptions opts;
+ opts.replacementForLiteralArgs = "?";
+
+ BSONObjBuilder bob;
+ minProperties.serialize(&bob, opts);
+ ASSERT_BSONOBJ_EQ(bob.done(),
+ BSON("$_internalSchemaMinProperties"
+ << "?"));
+}
+
+TEST(SerializeInternalSchema, ObjectMatchRedactsCorrectly) {
+ SerializationOptions opts;
+ opts.redactFieldNames = true;
+ opts.redactFieldNamesStrategy = redactFieldNameForTest;
+ opts.replacementForLiteralArgs = "?";
+ auto query = fromjson(
+ " {a: {$_internalSchemaObjectMatch: {"
+ " c: {$eq: 3}"
+ " }}}");
+ boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ auto objMatch = MatchExpressionParser::parse(query, expCtx);
+ ASSERT_OK(objMatch.getStatus());
+
+ ASSERT_BSONOBJ_EQ(
+ objMatch.getValue()->serialize(opts),
+ BSON("HASH(a)" << BSON("$_internalSchemaObjectMatch" << BSON("HASH(c)" << BSON("$eq"
+ << "?")))));
+}
+
+TEST(SerializeInternalSchema, RootDocEqRedactsCorrectly) {
+ auto query = fromjson("{$_internalSchemaRootDocEq: {a:1, b: {c: 1, d: [1]}}}");
+ boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ SerializationOptions opts;
+ opts.redactFieldNames = true;
+ opts.redactFieldNamesStrategy = redactFieldNameForTest;
+ opts.replacementForLiteralArgs = "?";
+ auto objMatch = MatchExpressionParser::parse(query, expCtx);
+ ASSERT_OK(objMatch.getStatus());
+
+ ASSERT_BSONOBJ_EQ(
+ objMatch.getValue()->serialize(opts),
+ BSON("$_internalSchemaRootDocEq" << BSON("HASH(a)"
+ << "?"
+ << "HASH(b)"
+ << BSON("HASH(c)"
+ << "?"
+ << "HASH(d)" << BSON_ARRAY("?")))));
+}
+
+TEST(SerializeInternalSchema, BinDataEncryptedTypeRedactsCorrectly) {
+ MatcherTypeSet typeSet;
+ typeSet.bsonTypes.insert(BSONType::String);
+ typeSet.bsonTypes.insert(BSONType::Date);
+ InternalSchemaBinDataEncryptedTypeExpression e("a"_sd, std::move(typeSet));
+ SerializationOptions opts;
+ opts.replacementForLiteralArgs = "?";
+ ASSERT_BSONOBJ_EQ(BSON("$_internalSchemaBinDataEncryptedType"
+ << "?"),
+ e.getSerializedRightHandSide(opts));
+}
+
+TEST(SerializeInternalSchema, BinDataFLE2EncryptedTypeRedactsCorrectly) {
+ InternalSchemaBinDataFLE2EncryptedTypeExpression e("ssn"_sd, BSONType::String);
+ SerializationOptions opts;
+ opts.replacementForLiteralArgs = "?";
+ ASSERT_BSONOBJ_EQ(BSON("$_internalSchemaBinDataFLE2EncryptedType"
+ << "?"),
+ e.getSerializedRightHandSide(opts));
+}
+
+TEST(SerializesInternalSchema, MaxPropertiesRedactsCorrectly) {
+ InternalSchemaMaxPropertiesMatchExpression maxProperties(5);
+ SerializationOptions opts;
+ opts.replacementForLiteralArgs = "?";
+
+ BSONObjBuilder bob;
+ maxProperties.serialize(&bob, opts);
+ ASSERT_BSONOBJ_EQ(bob.done(),
+ BSON("$_internalSchemaMaxProperties"
+ << "?"));
+}
+
+TEST(SerializesInternalSchema, EqRedactsCorrectly) {
+ SerializationOptions opts;
+ opts.redactFieldNamesStrategy = redactFieldNameForTest;
+ opts.redactFieldNames = true;
+ opts.replacementForLiteralArgs = "?";
+ auto query = fromjson("{$_internalSchemaEq: {a:1, b: {c: 1, d: [1]}}}");
+ BSONObjBuilder bob;
+ InternalSchemaEqMatchExpression e("a"_sd, query.firstElement());
+ e.serialize(&bob, opts);
+ ASSERT_BSONOBJ_EQ(bob.done(),
+ BSON("HASH(a)" << BSON("$_internalSchemaEq"
+ << BSON("HASH(a)"
+ << "?"
+ << "HASH(b)"
+ << BSON("HASH(c)"
+ << "?"
+ << "HASH(d)" << BSON_ARRAY("?"))))));
+}
+
+TEST(InternalSchemaAllElemMatchFromIndexMatchExpression, RedactsExpressionCorrectly) {
+ auto query = fromjson("{a: {$_internalSchemaAllElemMatchFromIndex: [2, {a: {$lt: 5}}]}}");
+ boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ auto expr = MatchExpressionParser::parse(query, expCtx);
+ ASSERT_OK(expr.getStatus());
+ auto elemMatchExpr = dynamic_cast<const InternalSchemaAllElemMatchFromIndexMatchExpression*>(
+ expr.getValue().get());
+
+ SerializationOptions opts;
+ opts.redactFieldNames = true;
+ opts.redactFieldNamesStrategy = redactFieldNameForTest;
+ opts.replacementForLiteralArgs = "?";
+
+ ASSERT_BSONOBJ_EQ(BSON("$_internalSchemaAllElemMatchFromIndex"
+ << BSON_ARRAY("?" << BSON("HASH(a)" << BSON("$lt"
+ << "?")))),
+ elemMatchExpr->getSerializedRightHandSide(opts));
+}
} // namespace
} // namespace mongo
diff --git a/src/mongo/db/matcher/expression_type.h b/src/mongo/db/matcher/expression_type.h
index 75f6155f7fa..bc2a758caf7 100644
--- a/src/mongo/db/matcher/expression_type.h
+++ b/src/mongo/db/matcher/expression_type.h
@@ -77,8 +77,11 @@ public:
}
BSONObj getSerializedRightHandSide(SerializationOptions opts) const final {
- // TODO SERVER-73678 respect 'replacementForLiteralArgs'.
BSONObjBuilder subBuilder;
+ if (opts.replacementForLiteralArgs) {
+ subBuilder.append(name(), opts.replacementForLiteralArgs.get());
+ return subBuilder.obj();
+ }
BSONArrayBuilder arrBuilder(subBuilder.subarrayStart(name()));
_typeSet.toBSONArray(&arrBuilder);
arrBuilder.doneFast();
@@ -244,9 +247,12 @@ public:
}
BSONObj getSerializedRightHandSide(SerializationOptions opts) const final {
- // TODO SERVER-73678 respect 'replacementForLiteralArgs'.
BSONObjBuilder bob;
- bob.append(name(), _binDataSubType);
+ if (opts.replacementForLiteralArgs) {
+ bob.append(name(), opts.replacementForLiteralArgs.get());
+ } else {
+ bob.append(name(), _binDataSubType);
+ }
return bob.obj();
}
diff --git a/src/mongo/db/matcher/expression_type_test.cpp b/src/mongo/db/matcher/expression_type_test.cpp
index 0ddee9a24b5..d934f8a402c 100644
--- a/src/mongo/db/matcher/expression_type_test.cpp
+++ b/src/mongo/db/matcher/expression_type_test.cpp
@@ -217,6 +217,15 @@ TEST(ExpressionTypeTest, InternalSchemaTypeExprWithMultipleTypesMatchesAllSuchTy
ASSERT_FALSE(expr.matchesBSON(fromjson("{a: ['str']}")));
}
+TEST(ExpressionTypeTest, RedactsTypesCorrectly) {
+ TypeMatchExpression type(""_sd, String);
+ SerializationOptions opts;
+ opts.replacementForLiteralArgs = "?";
+ ASSERT_BSONOBJ_EQ(BSON("$type"
+ << "?"),
+ type.getSerializedRightHandSide(opts));
+}
+
TEST(ExpressionBinDataSubTypeTest, MatchesBinDataGeneral) {
BSONObj match = BSON("a" << BSONBinData(nullptr, 0, BinDataType::BinDataGeneral));
BSONObj notMatch = BSON("a" << BSONBinData(nullptr, 0, BinDataType::bdtCustom));
@@ -300,6 +309,15 @@ TEST(ExpressionBinDataSubTypeTest, Equivalent) {
ASSERT(!e1.equivalent(&e3));
}
+TEST(ExpressionBinDataSubTypeTest, RedactsCorrectly) {
+ InternalSchemaBinDataSubTypeExpression e("b"_sd, BinDataType::newUUID);
+ SerializationOptions opts;
+ opts.replacementForLiteralArgs = "?";
+ ASSERT_BSONOBJ_EQ(BSON("$_internalSchemaBinDataSubType"
+ << "?"),
+ e.getSerializedRightHandSide(opts));
+}
+
TEST(InternalSchemaBinDataEncryptedTypeTest, DoesNotTraverseLeafArrays) {
MatcherTypeSet typeSet;
typeSet.bsonTypes.insert(BSONType::String);
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 8c533d8cc95..97ceacf1e01 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
@@ -78,10 +78,13 @@ void InternalSchemaAllElemMatchFromIndexMatchExpression::debugString(StringBuild
BSONObj InternalSchemaAllElemMatchFromIndexMatchExpression::getSerializedRightHandSide(
SerializationOptions opts) const {
- // TODO SERVER-73678 respect 'replacementForLiteralArgs'.
BSONObjBuilder allElemMatchBob;
BSONArrayBuilder subArray(allElemMatchBob.subarrayStart(kName));
- subArray.append(_index);
+ if (opts.replacementForLiteralArgs) {
+ subArray.append(opts.replacementForLiteralArgs.get());
+ } else {
+ subArray.append(_index);
+ }
{
BSONObjBuilder eBuilder(subArray.subobjStart());
_expression->getFilter()->serialize(&eBuilder, opts);
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index_test.cpp b/src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index_test.cpp
index dd1be3e030f..46e498fb67c 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index_test.cpp
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index_test.cpp
@@ -136,6 +136,5 @@ DEATH_TEST_REGEX(InternalSchemaAllElemMatchFromIndexMatchExpression,
ASSERT_EQ(objMatch.getValue()->numChildren(), 1);
ASSERT_THROWS_CODE(objMatch.getValue()->getChild(1), AssertionException, 6400200);
}
-
} // namespace
} // namespace mongo
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_allowed_properties.cpp b/src/mongo/db/matcher/schema/expression_internal_schema_allowed_properties.cpp
index 2a656b15269..c9a76a5d61f 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_allowed_properties.cpp
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_allowed_properties.cpp
@@ -123,20 +123,27 @@ bool InternalSchemaAllowedPropertiesMatchExpression::_matchesBSONObj(const BSONO
void InternalSchemaAllowedPropertiesMatchExpression::serialize(BSONObjBuilder* builder,
SerializationOptions opts) const {
- // TODO SERVER-73678 respect 'opts'.
BSONObjBuilder expressionBuilder(
builder->subobjStart(InternalSchemaAllowedPropertiesMatchExpression::kName));
std::vector<StringData> sortedProperties(_properties.begin(), _properties.end());
std::sort(sortedProperties.begin(), sortedProperties.end());
- expressionBuilder.append("properties", sortedProperties);
-
- expressionBuilder.append("namePlaceholder", _namePlaceholder);
+ if (opts.replacementForLiteralArgs) {
+ expressionBuilder.append("properties", opts.replacementForLiteralArgs.get());
+ expressionBuilder.append("namePlaceholder", opts.replacementForLiteralArgs.get());
+ } else {
+ expressionBuilder.append("properties", sortedProperties);
+ expressionBuilder.append("namePlaceholder", _namePlaceholder);
+ }
BSONArrayBuilder patternPropertiesBuilder(expressionBuilder.subarrayStart("patternProperties"));
for (auto&& item : _patternProperties) {
BSONObjBuilder itemBuilder(patternPropertiesBuilder.subobjStart());
- itemBuilder.appendRegex("regex", item.first.rawRegex);
+ if (opts.replacementForLiteralArgs) {
+ itemBuilder.appendRegex("regex", opts.replacementForLiteralArgs.get());
+ } else {
+ itemBuilder.appendRegex("regex", item.first.rawRegex);
+ }
BSONObjBuilder subexpressionBuilder(itemBuilder.subobjStart("expression"));
item.second->getFilter()->serialize(&subexpressionBuilder, opts);
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 d28ad725d00..d3e57b11bb2 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_eq.cpp
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_eq.cpp
@@ -64,8 +64,18 @@ void InternalSchemaEqMatchExpression::debugString(StringBuilder& debug,
BSONObj InternalSchemaEqMatchExpression::getSerializedRightHandSide(
SerializationOptions opts) const {
- // TODO SERVER-73678 respect 'replacementForLiteralArgs.'
BSONObjBuilder eqObj;
+ if (opts.redactFieldNames || opts.replacementForLiteralArgs) {
+ if (_rhsElem.isABSONObj()) {
+ BSONObjBuilder exprSpec(eqObj.subobjStart(kName));
+ opts.redactObjToBuilder(&exprSpec, _rhsElem.Obj());
+ exprSpec.done();
+ return eqObj.obj();
+ } else if (opts.replacementForLiteralArgs) {
+ // If the element is not an object it must be a literal.
+ return BSON(kName << opts.replacementForLiteralArgs.get());
+ }
+ }
eqObj.appendAs(_rhsElem, kName);
return eqObj.obj();
}
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 df257512525..93280d7cb5a 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_fmod.cpp
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_fmod.cpp
@@ -73,11 +73,16 @@ void InternalSchemaFmodMatchExpression::debugString(StringBuilder& debug,
BSONObj InternalSchemaFmodMatchExpression::getSerializedRightHandSide(
SerializationOptions opts) const {
- // TODO SERVER-73678 respect 'replacementForLiteralArgs'.
BSONObjBuilder objMatchBob;
BSONArrayBuilder arrBuilder(objMatchBob.subarrayStart("$_internalSchemaFmod"));
- arrBuilder.append(_divisor);
- arrBuilder.append(_remainder);
+ // Divisor and Remainder are always literals.
+ if (opts.replacementForLiteralArgs) {
+ arrBuilder.append(opts.replacementForLiteralArgs.get());
+ arrBuilder.append(opts.replacementForLiteralArgs.get());
+ } else {
+ arrBuilder.append(_divisor);
+ arrBuilder.append(_remainder);
+ }
arrBuilder.doneFast();
return objMatchBob.obj();
}
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 115c95eafb8..2f000c524e9 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
@@ -68,12 +68,20 @@ bool InternalSchemaMatchArrayIndexMatchExpression::equivalent(const MatchExpress
BSONObj InternalSchemaMatchArrayIndexMatchExpression::getSerializedRightHandSide(
SerializationOptions opts) const {
- // TODO SERVER-73678 respect 'replacementForLiteralArgs'.
BSONObjBuilder objBuilder;
{
BSONObjBuilder matchArrayElemSubobj(objBuilder.subobjStart(kName));
- matchArrayElemSubobj.append("index", _index);
- matchArrayElemSubobj.append("namePlaceholder", _expression->getPlaceholder().value_or(""));
+ if (opts.replacementForLiteralArgs) {
+ matchArrayElemSubobj.append("index", opts.replacementForLiteralArgs.get());
+ } else {
+ matchArrayElemSubobj.append("index", _index);
+ }
+ if (auto placeHolder = _expression->getPlaceholder()) {
+ matchArrayElemSubobj.append("namePlaceholder",
+ opts.serializeFieldName(placeHolder.get()));
+ } else {
+ matchArrayElemSubobj.append("namePlaceholder", "");
+ }
{
BSONObjBuilder subexprSubObj(matchArrayElemSubobj.subobjStart("expression"));
_expression->getFilter()->serialize(&subexprSubObj, opts);
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 9871c44a1c9..3ef7921faf3 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
@@ -52,9 +52,12 @@ void InternalSchemaNumArrayItemsMatchExpression::debugString(StringBuilder& debu
BSONObj InternalSchemaNumArrayItemsMatchExpression::getSerializedRightHandSide(
SerializationOptions opts) const {
- // TODO SERVER-73678 respect 'replacementForLiteralArgs'.
BSONObjBuilder objBuilder;
- objBuilder.append(_name, _numItems);
+ if (opts.replacementForLiteralArgs) {
+ objBuilder.append(_name, opts.replacementForLiteralArgs.get());
+ } else {
+ objBuilder.append(_name, _numItems);
+ }
return objBuilder.obj();
}
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_num_properties.cpp b/src/mongo/db/matcher/schema/expression_internal_schema_num_properties.cpp
index e086cc7f34b..94fa8206616 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_num_properties.cpp
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_num_properties.cpp
@@ -44,8 +44,11 @@ void InternalSchemaNumPropertiesMatchExpression::debugString(StringBuilder& debu
void InternalSchemaNumPropertiesMatchExpression::serialize(BSONObjBuilder* out,
SerializationOptions opts) const {
- // TODO SERVER-73678 respect 'opts'.
- out->append(_name, _numProperties);
+ if (opts.replacementForLiteralArgs) {
+ out->append(_name, opts.replacementForLiteralArgs.get());
+ } else {
+ out->append(_name, _numProperties);
+ }
}
bool InternalSchemaNumPropertiesMatchExpression::equivalent(const MatchExpression* other) const {
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 1bf30c432c0..8e8bf6c6f7a 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
@@ -64,7 +64,6 @@ void InternalSchemaObjectMatchExpression::debugString(StringBuilder& debug,
BSONObj InternalSchemaObjectMatchExpression::getSerializedRightHandSide(
SerializationOptions opts) const {
- // TODO SERVER-73678 respect 'replacementForLiteralArgs'.
BSONObjBuilder objMatchBob;
BSONObjBuilder subBob(objMatchBob.subobjStart(kName));
_sub->serialize(&subBob, opts);
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_root_doc_eq.cpp b/src/mongo/db/matcher/schema/expression_internal_schema_root_doc_eq.cpp
index 18d1fe747e1..f9e40a3856d 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_root_doc_eq.cpp
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_root_doc_eq.cpp
@@ -49,9 +49,8 @@ void InternalSchemaRootDocEqMatchExpression::debugString(StringBuilder& debug,
void InternalSchemaRootDocEqMatchExpression::serialize(BSONObjBuilder* out,
SerializationOptions opts) const {
- // TODO SERVER-73678 respect 'opts.'
BSONObjBuilder subObj(out->subobjStart(kName));
- subObj.appendElements(_rhsObj);
+ opts.redactObjToBuilder(&subObj, _rhsObj);
subObj.doneFast();
}
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 97031addb3c..bb8d3351e87 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
@@ -52,9 +52,12 @@ void InternalSchemaStrLengthMatchExpression::debugString(StringBuilder& debug,
BSONObj InternalSchemaStrLengthMatchExpression::getSerializedRightHandSide(
SerializationOptions opts) const {
- // TODO SERVER-73678 respect 'replacementForLiteralArgs'.
BSONObjBuilder objBuilder;
- objBuilder.append(_name, _strLen);
+ if (opts.replacementForLiteralArgs) {
+ objBuilder.append(opts.serializeFieldName(_name), opts.replacementForLiteralArgs.get());
+ } else {
+ objBuilder.append(opts.serializeFieldName(_name), _strLen);
+ }
return objBuilder.obj();
}
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 05ea89e39b4..4915d1ba2ab 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
@@ -55,7 +55,6 @@ bool InternalSchemaUniqueItemsMatchExpression::equivalent(const MatchExpression*
BSONObj InternalSchemaUniqueItemsMatchExpression::getSerializedRightHandSide(
SerializationOptions opts) const {
- // TODO SERVER-73678 respect 'replacementForLiteralArgs.'
BSONObjBuilder bob;
bob.append(kName, true);
return bob.obj();
diff --git a/src/mongo/db/pipeline/expression.cpp b/src/mongo/db/pipeline/expression.cpp
index 5672003d034..c239ab05d1e 100644
--- a/src/mongo/db/pipeline/expression.cpp
+++ b/src/mongo/db/pipeline/expression.cpp
@@ -2390,12 +2390,7 @@ Value ExpressionObject::serialize(SerializationOptions options) const {
}
MutableDocument outputDoc;
for (auto&& pair : _expressions) {
- if (options.redactFieldNames) {
- outputDoc.addField(options.redactFieldNamesStrategy(pair.first),
- pair.second->serialize(options));
- } else {
- outputDoc.addField(pair.first, pair.second->serialize(options));
- }
+ outputDoc.addField(options.serializeFieldName(pair.first), pair.second->serialize(options));
}
return outputDoc.freezeToValue();
}
diff --git a/src/mongo/db/query/serialization_options.h b/src/mongo/db/query/serialization_options.h
index 30efc9294bf..6858ddd0c30 100644
--- a/src/mongo/db/query/serialization_options.h
+++ b/src/mongo/db/query/serialization_options.h
@@ -29,6 +29,8 @@
#pragma once
#include "mongo/base/string_data.h"
+#include "mongo/bson/bsonobj.h"
+#include "mongo/bson/bsonobjbuilder.h"
#include "mongo/db/exec/document_value/value.h"
#include "mongo/db/query/explain_options.h"
#include "mongo/util/assert_util.h"
@@ -73,6 +75,48 @@ struct SerializationOptions {
return str.toString();
}
+ // Helper functions for redacting BSONObj. Does not take into account anything to do with MQL
+ // semantics, redacts all field names and literals in the passed in obj.
+ void redactArrayToBuilder(BSONArrayBuilder* bab, std::vector<BSONElement> array) {
+ for (const auto& elem : array) {
+ if (elem.type() == BSONType::Object) {
+ BSONObjBuilder subObj(bab->subobjStart());
+ redactObjToBuilder(&subObj, elem.Obj());
+ subObj.done();
+ } else if (elem.type() == BSONType::Array) {
+ BSONArrayBuilder subArr(bab->subarrayStart());
+ redactArrayToBuilder(&subArr, elem.Array());
+ subArr.done();
+ } else {
+ if (replacementForLiteralArgs) {
+ bab->append(replacementForLiteralArgs.get());
+ } else {
+ bab->append(elem);
+ }
+ }
+ }
+ }
+ void redactObjToBuilder(BSONObjBuilder* bob, BSONObj objToRedact) {
+ for (const auto& elem : objToRedact) {
+ auto fieldName = serializeFieldName(elem.fieldName());
+ if (elem.type() == BSONType::Object) {
+ BSONObjBuilder subObj(bob->subobjStart(fieldName));
+ redactObjToBuilder(&subObj, elem.Obj());
+ subObj.done();
+ } else if (elem.type() == BSONType::Array) {
+ BSONArrayBuilder subArr(bob->subarrayStart(fieldName));
+ redactArrayToBuilder(&subArr, elem.Array());
+ subArr.done();
+ } else {
+ if (replacementForLiteralArgs) {
+ bob->append(fieldName, replacementForLiteralArgs.get());
+ } else {
+ bob->appendAs(elem, fieldName);
+ }
+ }
+ }
+ }
+
template <class T>
Value serializeLiteralValue(T n) {
if (replacementForLiteralArgs) {