summaryrefslogtreecommitdiff
path: root/src/mongo/db/matcher/expression_serialization_test.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/matcher/expression_serialization_test.cpp')
-rw-r--r--src/mongo/db/matcher/expression_serialization_test.cpp352
1 files changed, 345 insertions, 7 deletions
diff --git a/src/mongo/db/matcher/expression_serialization_test.cpp b/src/mongo/db/matcher/expression_serialization_test.cpp
index 78517a52634..3b25631cd0f 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"
@@ -49,9 +58,7 @@ using std::string;
using std::unique_ptr;
BSONObj serialize(MatchExpression* match) {
- BSONObjBuilder bob;
- match->serialize(&bob, true);
- return bob.obj();
+ return match->serialize();
}
TEST(SerializeBasic, AndExpressionWithOneChildSerializesCorrectly) {
@@ -355,7 +362,6 @@ TEST(SerializeBasic, ExpressionElemMatchValueWithTripleNotSerializesCorrectly) {
ASSERT_EQ(original.matches(obj), reserialized.matches(obj));
}
-
TEST(SerializeBasic, ExpressionSizeSerializesCorrectly) {
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
Matcher original(fromjson("{x: {$size: 2}}"),
@@ -630,6 +636,32 @@ TEST(SerializeBasic, ExpressionRegexWithValueAndOptionsSerializesCorrectly) {
ASSERT_EQ(original.matches(obj), reserialized.matches(obj));
}
+TEST(SerializeBasic, ExpressionRegexWithoutOptionsSerializesShapeCorrectly) {
+ auto query = fromjson(R"({x: {$regex: ".*"}})");
+ boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ auto objMatch = MatchExpressionParser::parse(query, expCtx);
+ ASSERT_OK(objMatch.getStatus());
+ SerializationOptions opts;
+ opts.literalPolicy = LiteralSerializationPolicy::kToRepresentativeParseableValue;
+ ASSERT_BSONOBJ_EQ(BSON("x" << BSON("$regex"
+ << "\\?")),
+ objMatch.getValue()->serialize(opts));
+}
+
+TEST(SerializeBasic, ExpressionRegexWithOptionsSerializesShapeCorrectly) {
+ auto query = fromjson(R"({x: {$regex: ".*", $options: "m"}})");
+ boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ auto objMatch = MatchExpressionParser::parse(query, expCtx);
+ ASSERT_OK(objMatch.getStatus());
+ SerializationOptions opts;
+ opts.literalPolicy = LiteralSerializationPolicy::kToRepresentativeParseableValue;
+ ASSERT_BSONOBJ_EQ(BSON("x" << BSON("$regex"
+ << "\\?"
+ << "$options"
+ << "i")),
+ objMatch.getValue()->serialize(opts));
+}
+
TEST(SerializeBasic, ExpressionRegexWithValueSerializesCorrectly) {
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
Matcher original(fromjson("{x: /a.b/}"),
@@ -1093,7 +1125,7 @@ TEST(SerializeBasic, ExpressionNotWithDirectPathExpSerializesCorrectly) {
// direct path expression child, instead creating a NOT -> AND -> path expression. This test
// manually constructs such an expression in case it ever turns up, since that should still be
// able to serialize.
- auto originalBSON = fromjson("{a: {$not: {$eq: 2}}}}");
+ auto originalBSON = fromjson("{a: {$not: {$eq: 2}}}");
auto equalityRHSElem = originalBSON["a"]["$not"]["$eq"];
auto equalityExpression = std::make_unique<EqualityMatchExpression>("a"_sd, equalityRHSElem);
@@ -1679,7 +1711,7 @@ TEST(SerializeInternalSchema, ExpressionInternalSchemaMaxLengthSerializesCorrect
TEST(SerializeInternalSchema, ExpressionInternalSchemaCondSerializesCorrectly) {
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- Matcher original(fromjson("{$_internalSchemaCond: [{a: 1}, {b: 2}, {c: 3}]}}"),
+ Matcher original(fromjson("{$_internalSchemaCond: [{a: 1}, {b: 2}, {c: 3}]}"),
expCtx,
ExtensionsCallbackNoop(),
MatchExpressionParser::kAllowAllSpecialFeatures);
@@ -1690,7 +1722,7 @@ TEST(SerializeInternalSchema, ExpressionInternalSchemaCondSerializesCorrectly) {
BSONObjBuilder builder;
ASSERT_BSONOBJ_EQ(
*reserialized.getQuery(),
- fromjson("{$_internalSchemaCond: [{a: {$eq: 1}}, {b: {$eq: 2}}, {c: {$eq: 3}}]}}"));
+ fromjson("{$_internalSchemaCond: [{a: {$eq: 1}}, {b: {$eq: 2}}, {c: {$eq: 3}}]}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
}
@@ -1848,5 +1880,311 @@ TEST(SerializeInternalBinDataSubType, ExpressionBinDataSubTypeSerializesCorrectl
ASSERT_TRUE(original.matches(obj));
}
+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 = SerializationOptions::kDebugShapeAndMarkIdentifiers_FOR_TEST;
+
+ ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
+ R"({
+ "$_internalSchemaAllowedProperties": {
+ "properties": "?array<?string>",
+ "namePlaceholder": "i",
+ "patternProperties": [],
+ "otherwise": {
+ "HASH<i>": {
+ "$eq": "?number"
+ }
+ }
+ }
+ })",
+ 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) {
+ auto opts = SerializationOptions::kDebugShapeAndMarkIdentifiers_FOR_TEST;
+ 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);
+ ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
+ R"({
+ "$_internalSchemaCond": [
+ {
+ "HASH<age>": {
+ "$lt": "?number"
+ }
+ },
+ {
+ "HASH<job>": {
+ "$eq": "?string"
+ }
+ },
+ {
+ "HASH<job>": {
+ "$eq": "?string"
+ }
+ }
+ ]
+ })",
+ bob.done());
+}
+
+TEST(SerializeInternalSchema, FmodMatchRedactsCorrectly) {
+ InternalSchemaFmodMatchExpression m("a"_sd, Decimal128(1.7), Decimal128(2));
+ auto opts = SerializationOptions{LiteralSerializationPolicy::kToDebugTypeString};
+ BSONObjBuilder bob;
+ m.serialize(&bob, opts);
+ ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
+ R"({"a":{"$_internalSchemaFmod":["?number","?number"]}})",
+ 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;
+ auto opts = SerializationOptions::kDebugShapeAndMarkIdentifiers_FOR_TEST;
+ objMatch.getValue()->serialize(&bob, opts);
+ ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
+ R"({
+ "HASH<foo>": {
+ "$_internalSchemaMatchArrayIndex": {
+ "index": "?number",
+ "namePlaceholder": "HASH<i>",
+ "expression": {
+ "HASH<i>": {
+ "$type": ['number']
+ }
+ }
+ }
+ }
+ })",
+ bob.done());
+}
+
+TEST(SerializeInternalSchema, MaxItemsRedactsCorrectly) {
+ InternalSchemaMaxItemsMatchExpression maxItems("a.b"_sd, 2);
+ auto opts = SerializationOptions::kDebugShapeAndMarkIdentifiers_FOR_TEST;
+ ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
+ R"({"$_internalSchemaMaxItems":"?number"})",
+ maxItems.getSerializedRightHandSide(opts));
+}
+
+TEST(SerializeInternalSchema, MaxLengthRedactsCorrectly) {
+ InternalSchemaMaxLengthMatchExpression maxLength("a"_sd, 2);
+ auto opts = SerializationOptions::kDebugShapeAndMarkIdentifiers_FOR_TEST;
+ ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
+ R"({"$_internalSchemaMaxLength":"?number"})",
+ maxLength.getSerializedRightHandSide(opts));
+}
+
+TEST(SerializeInternalSchema, MinItemsRedactsCorrectly) {
+ InternalSchemaMinItemsMatchExpression minItems("a.b"_sd, 2);
+ auto opts = SerializationOptions::kDebugShapeAndMarkIdentifiers_FOR_TEST;
+
+ ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
+ R"({"$_internalSchemaMinItems":"?number"})",
+ minItems.getSerializedRightHandSide(opts));
+}
+
+TEST(SerializeInternalSchema, MinLengthRedactsCorrectly) {
+ InternalSchemaMinLengthMatchExpression minLength("a"_sd, 2);
+ auto opts = SerializationOptions{LiteralSerializationPolicy::kToDebugTypeString};
+ ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
+ R"({"$_internalSchemaMinLength":"?number"})",
+ minLength.getSerializedRightHandSide(opts));
+}
+
+TEST(SerializeInternalSchema, MinPropertiesRedactsCorrectly) {
+ InternalSchemaMinPropertiesMatchExpression minProperties(5);
+ auto opts = SerializationOptions{LiteralSerializationPolicy::kToDebugTypeString};
+
+ BSONObjBuilder bob;
+ minProperties.serialize(&bob, opts);
+ ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
+ R"({"$_internalSchemaMinProperties":"?number"})",
+ bob.done());
+}
+
+TEST(SerializeInternalSchema, ObjectMatchRedactsCorrectly) {
+ auto opts = SerializationOptions::kDebugShapeAndMarkIdentifiers_FOR_TEST;
+ 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_AUTO( // NOLINT
+ R"({"HASH<a>":{"$_internalSchemaObjectMatch":{"HASH<c>":{"$eq":"?number"}}}})",
+ objMatch.getValue()->serialize(opts));
+}
+
+TEST(SerializeInternalSchema, RootDocEqRedactsCorrectly) {
+ auto query = fromjson("{$_internalSchemaRootDocEq: {a:1, b: {c: 1, d: [1]}}}");
+ boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ auto opts = SerializationOptions::kDebugShapeAndMarkIdentifiers_FOR_TEST;
+ auto objMatch = MatchExpressionParser::parse(query, expCtx);
+ ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
+ R"({
+ "$_internalSchemaRootDocEq": {
+ "HASH<a>": "?number",
+ "HASH<b>": {
+ "HASH<c>": "?number",
+ "HASH<d>": [
+ "?number"
+ ]
+ }
+ }
+ })",
+ objMatch.getValue()->serialize(opts));
+}
+
+TEST(SerializeInternalSchema, BinDataEncryptedTypeRedactsCorrectly) {
+ MatcherTypeSet typeSet;
+ typeSet.bsonTypes.insert(BSONType::String);
+ typeSet.bsonTypes.insert(BSONType::Date);
+ InternalSchemaBinDataEncryptedTypeExpression e("a"_sd, std::move(typeSet));
+ auto opts = SerializationOptions{LiteralSerializationPolicy::kToDebugTypeString};
+ ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
+ R"({"$_internalSchemaBinDataEncryptedType":[2,9]})",
+ e.getSerializedRightHandSide(opts));
+}
+
+TEST(SerializeInternalSchema, BinDataFLE2EncryptedTypeRedactsCorrectly) {
+ InternalSchemaBinDataFLE2EncryptedTypeExpression e("ssn"_sd, BSONType::String);
+ auto opts = SerializationOptions{LiteralSerializationPolicy::kToDebugTypeString};
+ ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
+ R"({"$_internalSchemaBinDataFLE2EncryptedType":[2]})",
+ e.getSerializedRightHandSide(opts));
+}
+
+TEST(SerializesInternalSchema, MaxPropertiesRedactsCorrectly) {
+ InternalSchemaMaxPropertiesMatchExpression maxProperties(5);
+ auto opts = SerializationOptions{LiteralSerializationPolicy::kToDebugTypeString};
+
+ BSONObjBuilder bob;
+ maxProperties.serialize(&bob, opts);
+ ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
+ R"({"$_internalSchemaMaxProperties":"?number"})",
+ bob.done());
+}
+
+TEST(SerializesInternalSchema, EqRedactsCorrectly) {
+ auto opts = SerializationOptions::kDebugShapeAndMarkIdentifiers_FOR_TEST;
+ 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_AUTO( // NOLINT
+ R"({
+ "HASH<a>": {
+ "$_internalSchemaEq": {
+ "HASH<a>": "?number",
+ "HASH<b>": {
+ "HASH<c>": "?number",
+ "HASH<d>": [
+ "?number"
+ ]
+ }
+ }
+ }
+ })",
+ bob.done());
+}
+
+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());
+
+ auto opts = SerializationOptions::kDebugShapeAndMarkIdentifiers_FOR_TEST;
+
+ ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
+ R"({
+ "$_internalSchemaAllElemMatchFromIndex": [
+ "?number",
+ {
+ "HASH<a>": {
+ "$lt": "?number"
+ }
+ }
+ ]
+ })",
+ elemMatchExpr->getSerializedRightHandSide(opts));
+}
+
+TEST(SerializeBasic, SerializesNestedElemMatchCorrectly) {
+ auto query = fromjson(R"({a: {$elemMatch: {$elemMatch: {b: {$lt: 6, $gt: 4}}}}})");
+ boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ auto objMatch = MatchExpressionParser::parse(query, expCtx);
+ ASSERT_OK(objMatch.getStatus());
+ SerializationOptions opts;
+ opts.literalPolicy = LiteralSerializationPolicy::kToDebugTypeString;
+ ASSERT_BSONOBJ_EQ_AUTO(
+ R"({"a": {
+ "$elemMatch": {
+ "$elemMatch": {
+ "$and": [
+ {
+ "b": {
+ "$lt": "?number"
+ }
+ },
+ {
+ "b": {
+ "$gt": "?number"
+ }
+ }
+ ]
+ }
+ }
+ }
+ })",
+ objMatch.getValue()->serialize(opts));
+}
} // namespace
} // namespace mongo