summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKyle Suarez <kyle.suarez@mongodb.com>2017-07-07 15:22:36 -0400
committerKyle Suarez <kyle.suarez@mongodb.com>2017-07-07 15:22:36 -0400
commit306e5376eaa4d48c6fc0ae1afb9a61e9eaffd498 (patch)
tree2987cb012208f32332f81e927c7f6b0c14016b73
parentce5e5fdfcad9ee0b3b08954982ded2a4bdfd8ac2 (diff)
downloadmongo-306e5376eaa4d48c6fc0ae1afb9a61e9eaffd498.tar.gz
SERVER-29588 create an $_internalSchemaUniqueItems MatchExpression
-rw-r--r--src/mongo/bson/bsonelement.cpp1
-rw-r--r--src/mongo/bson/bsonobj.h1
-rw-r--r--src/mongo/db/matcher/SConscript2
-rw-r--r--src/mongo/db/matcher/expression.h3
-rw-r--r--src/mongo/db/matcher/expression_parser.cpp15
-rw-r--r--src/mongo/db/matcher/expression_serialization_test.cpp327
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_unique_items.cpp74
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_unique_items.h74
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_unique_items_test.cpp111
-rw-r--r--src/mongo/db/matcher/schema/expression_parser_schema_test.cpp81
-rw-r--r--src/mongo/db/pipeline/document_source_match.cpp1
-rw-r--r--src/mongo/db/pipeline/document_source_match_test.cpp2
-rw-r--r--src/mongo/db/query/plan_cache.cpp4
13 files changed, 492 insertions, 204 deletions
diff --git a/src/mongo/bson/bsonelement.cpp b/src/mongo/bson/bsonelement.cpp
index f82ba99e8f3..b3f6ec79696 100644
--- a/src/mongo/bson/bsonelement.cpp
+++ b/src/mongo/bson/bsonelement.cpp
@@ -336,6 +336,7 @@ const StringMap<BSONObj::MatchType> queryOperatorMap{
{"bitsAnyClear", BSONObj::opBITS_ANY_CLEAR},
{"_internalSchemaMinItems", BSONObj::opINTERNAL_SCHEMA_MIN_ITEMS},
{"_internalSchemaMaxItems", BSONObj::opINTERNAL_SCHEMA_MAX_ITEMS},
+ {"_internalSchemaUniqueItems", BSONObj::opINTERNAL_SCHEMA_UNIQUE_ITEMS},
};
// Compares two string elements using a simple binary compare.
diff --git a/src/mongo/bson/bsonobj.h b/src/mongo/bson/bsonobj.h
index 49d0edf06bc..4be9c8ccba2 100644
--- a/src/mongo/bson/bsonobj.h
+++ b/src/mongo/bson/bsonobj.h
@@ -530,6 +530,7 @@ public:
opBITS_ANY_CLEAR = 0x1A,
opINTERNAL_SCHEMA_MIN_ITEMS = 0x1B,
opINTERNAL_SCHEMA_MAX_ITEMS = 0x1C,
+ opINTERNAL_SCHEMA_UNIQUE_ITEMS = 0x1D,
};
/** add all elements of the object to the specified vector */
diff --git a/src/mongo/db/matcher/SConscript b/src/mongo/db/matcher/SConscript
index 3b865fae670..4d7ee1f1b63 100644
--- a/src/mongo/db/matcher/SConscript
+++ b/src/mongo/db/matcher/SConscript
@@ -46,6 +46,7 @@ env.Library(
'matchable.cpp',
'matcher.cpp',
'schema/expression_internal_schema_num_array_items.cpp',
+ 'schema/expression_internal_schema_unique_items.cpp',
'schema/expression_internal_schema_xor.cpp',
],
LIBDEPS=[
@@ -68,6 +69,7 @@ env.CppUnitTest(
'expression_tree_test.cpp',
'schema/expression_internal_schema_max_items_test.cpp',
'schema/expression_internal_schema_min_items_test.cpp',
+ 'schema/expression_internal_schema_unique_items_test.cpp',
'schema/expression_internal_schema_xor_test.cpp',
],
LIBDEPS=[
diff --git a/src/mongo/db/matcher/expression.h b/src/mongo/db/matcher/expression.h
index 2c99218bdf7..f6299aa63c2 100644
--- a/src/mongo/db/matcher/expression.h
+++ b/src/mongo/db/matcher/expression.h
@@ -98,8 +98,9 @@ public:
INTERNAL_2D_POINT_IN_ANNULUS,
// JSON Schema expressions.
- INTERNAL_SCHEMA_MIN_ITEMS,
INTERNAL_SCHEMA_MAX_ITEMS,
+ INTERNAL_SCHEMA_MIN_ITEMS,
+ INTERNAL_SCHEMA_UNIQUE_ITEMS,
INTERNAL_SCHEMA_XOR,
};
diff --git a/src/mongo/db/matcher/expression_parser.cpp b/src/mongo/db/matcher/expression_parser.cpp
index 8b5b9aed1a0..f2c121fa96e 100644
--- a/src/mongo/db/matcher/expression_parser.cpp
+++ b/src/mongo/db/matcher/expression_parser.cpp
@@ -38,6 +38,7 @@
#include "mongo/db/matcher/expression_tree.h"
#include "mongo/db/matcher/schema/expression_internal_schema_max_items.h"
#include "mongo/db/matcher/schema/expression_internal_schema_min_items.h"
+#include "mongo/db/matcher/schema/expression_internal_schema_unique_items.h"
#include "mongo/db/matcher/schema/expression_internal_schema_xor.h"
#include "mongo/db/namespace_string.h"
#include "mongo/stdx/memory.h"
@@ -293,6 +294,20 @@ StatusWithMatchExpression MatchExpressionParser::_parseSubField(const BSONObj& c
return _parseInternalSchemaSingleIntegerArgument<InternalSchemaMaxItemsMatchExpression>(
name, e);
}
+ case BSONObj::opINTERNAL_SCHEMA_UNIQUE_ITEMS: {
+ if (!e.isBoolean() || !e.boolean()) {
+ return {ErrorCodes::FailedToParse,
+ str::stream() << name << " must be a boolean of value true"};
+ }
+
+
+ auto expr = stdx::make_unique<InternalSchemaUniqueItemsMatchExpression>();
+ auto status = expr->init(name);
+ if (!status.isOK()) {
+ return status;
+ }
+ return {std::move(expr)};
+ }
}
return {Status(ErrorCodes::BadValue,
diff --git a/src/mongo/db/matcher/expression_serialization_test.cpp b/src/mongo/db/matcher/expression_serialization_test.cpp
index b05cbe71bfc..40591d75dcb 100644
--- a/src/mongo/db/matcher/expression_serialization_test.cpp
+++ b/src/mongo/db/matcher/expression_serialization_test.cpp
@@ -45,6 +45,8 @@ using std::pair;
using std::string;
using std::unique_ptr;
+constexpr CollatorInterface* kSimpleCollator = nullptr;
+
BSONObj serialize(MatchExpression* match) {
BSONObjBuilder bob;
match->serialize(&bob);
@@ -52,10 +54,9 @@ BSONObj serialize(MatchExpression* match) {
}
TEST(SerializeBasic, AndExpressionWithOneChildSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
- Matcher original(fromjson("{$and: [{x: 0}]}"), ExtensionsCallbackNoop(), collator);
+ Matcher original(fromjson("{$and: [{x: 0}]}"), ExtensionsCallbackNoop(), kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{$and: [{x: {$eq: 0}}]}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
@@ -67,10 +68,10 @@ TEST(SerializeBasic, AndExpressionWithOneChildSerializesCorrectly) {
}
TEST(SerializeBasic, AndExpressionWithTwoChildrenSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
- Matcher original(fromjson("{$and: [{x: 1}, {x: 2}]}"), ExtensionsCallbackNoop(), collator);
+ Matcher original(
+ fromjson("{$and: [{x: 1}, {x: 2}]}"), ExtensionsCallbackNoop(), kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{$and: [{x: {$eq: 1}}, {x: {$eq: 2}}]}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
@@ -82,10 +83,10 @@ TEST(SerializeBasic, AndExpressionWithTwoChildrenSerializesCorrectly) {
}
TEST(SerializeBasic, AndExpressionWithTwoIdenticalChildrenSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
- Matcher original(fromjson("{$and: [{x: 1}, {x: 1}]}"), ExtensionsCallbackNoop(), collator);
+ Matcher original(
+ fromjson("{$and: [{x: 1}, {x: 1}]}"), ExtensionsCallbackNoop(), kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{$and: [{x: {$eq: 1}}, {x: {$eq: 1}}]}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
@@ -97,10 +98,10 @@ TEST(SerializeBasic, AndExpressionWithTwoIdenticalChildrenSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionOr) {
- const CollatorInterface* collator = nullptr;
- Matcher original(fromjson("{$or: [{x: 'A'}, {x: 'B'}]}"), ExtensionsCallbackNoop(), collator);
+ Matcher original(
+ fromjson("{$or: [{x: 'A'}, {x: 'B'}]}"), ExtensionsCallbackNoop(), kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(),
fromjson("{$or: [{x: {$eq: 'A'}}, {x: {$eq: 'B'}}]}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
@@ -113,12 +114,11 @@ TEST(SerializeBasic, ExpressionOr) {
}
TEST(SerializeBasic, ExpressionElemMatchObjectSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
Matcher original(fromjson("{x: {$elemMatch: {a: {$gt: 0}, b: {$gt: 0}}}}"),
ExtensionsCallbackNoop(),
- collator);
+ kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(),
fromjson("{x: {$elemMatch: {$and: [{a: {$gt: 0}}, {b: {$gt: 0}}]}}}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
@@ -131,12 +131,11 @@ TEST(SerializeBasic, ExpressionElemMatchObjectSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionElemMatchObjectWithEmptyStringSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
Matcher original(fromjson("{'': {$elemMatch: {a: {$gt: 0}, b: {$gt: 0}}}}"),
ExtensionsCallbackNoop(),
- collator);
+ kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(),
fromjson("{'': {$elemMatch: {$and: [{a: {$gt: 0}}, {b: {$gt: 0}}]}}}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
@@ -149,11 +148,11 @@ TEST(SerializeBasic, ExpressionElemMatchObjectWithEmptyStringSerializesCorrectly
}
TEST(SerializeBasic, ExpressionElemMatchValueSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
- Matcher original(
- fromjson("{x: {$elemMatch: {$lt: 1, $gt: -1}}}"), ExtensionsCallbackNoop(), collator);
+ Matcher original(fromjson("{x: {$elemMatch: {$lt: 1, $gt: -1}}}"),
+ ExtensionsCallbackNoop(),
+ kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{x: {$elemMatch: {$lt: 1, $gt: -1}}}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
@@ -168,14 +167,13 @@ TEST(SerializeBasic, ExpressionElemMatchValueSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionElemMatchValueWithRegexSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
const auto match = BSON("x" << BSON("$elemMatch" << BSON("$regex"
<< "abc"
<< "$options"
<< "i")));
- Matcher original(match, ExtensionsCallbackNoop(), collator);
+ Matcher original(match, ExtensionsCallbackNoop(), kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), match);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
@@ -190,11 +188,11 @@ TEST(SerializeBasic, ExpressionElemMatchValueWithRegexSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionElemMatchValueWithEmptyStringSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
- Matcher original(
- fromjson("{x: {$elemMatch: {$lt: 1, $gt: -1}}}"), ExtensionsCallbackNoop(), collator);
+ Matcher original(fromjson("{x: {$elemMatch: {$lt: 1, $gt: -1}}}"),
+ ExtensionsCallbackNoop(),
+ kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{x: {$elemMatch: {$lt: 1, $gt: -1}}}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
@@ -209,10 +207,9 @@ TEST(SerializeBasic, ExpressionElemMatchValueWithEmptyStringSerializesCorrectly)
}
TEST(SerializeBasic, ExpressionSizeSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
- Matcher original(fromjson("{x: {$size: 2}}"), ExtensionsCallbackNoop(), collator);
+ Matcher original(fromjson("{x: {$size: 2}}"), ExtensionsCallbackNoop(), kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{x: {$size: 2}}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
@@ -224,10 +221,9 @@ TEST(SerializeBasic, ExpressionSizeSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionAllSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
- Matcher original(fromjson("{x: {$all: [1, 2]}}"), ExtensionsCallbackNoop(), collator);
+ Matcher original(fromjson("{x: {$all: [1, 2]}}"), ExtensionsCallbackNoop(), kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{$and: [{x: {$eq: 1}}, {x: {$eq: 2}}]}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
@@ -239,10 +235,9 @@ TEST(SerializeBasic, ExpressionAllSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionAllWithEmptyArraySerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
- Matcher original(fromjson("{x: {$all: []}}"), ExtensionsCallbackNoop(), collator);
+ Matcher original(fromjson("{x: {$all: []}}"), ExtensionsCallbackNoop(), kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{x: {$all: []}}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
@@ -251,11 +246,10 @@ TEST(SerializeBasic, ExpressionAllWithEmptyArraySerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionAllWithRegex) {
- const CollatorInterface* collator = nullptr;
Matcher original(
- fromjson("{x: {$all: [/a.b.c/, /.d.e./]}}"), ExtensionsCallbackNoop(), collator);
+ fromjson("{x: {$all: [/a.b.c/, /.d.e./]}}"), ExtensionsCallbackNoop(), kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(),
BSON("$and" << BSON_ARRAY(BSON("x" << BSON("$regex"
@@ -272,10 +266,9 @@ TEST(SerializeBasic, ExpressionAllWithRegex) {
}
TEST(SerializeBasic, ExpressionEqSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
- Matcher original(fromjson("{x: {$eq: {a: 1}}}"), ExtensionsCallbackNoop(), collator);
+ Matcher original(fromjson("{x: {$eq: {a: 1}}}"), ExtensionsCallbackNoop(), kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{x: {$eq: {a: 1}}}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
@@ -290,10 +283,9 @@ TEST(SerializeBasic, ExpressionEqSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionNeSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
- Matcher original(fromjson("{x: {$ne: {a: 1}}}"), ExtensionsCallbackNoop(), collator);
+ Matcher original(fromjson("{x: {$ne: {a: 1}}}"), ExtensionsCallbackNoop(), kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{$nor: [{x: {$eq: {a: 1}}}]}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
@@ -305,13 +297,12 @@ TEST(SerializeBasic, ExpressionNeSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionNeWithRegexObjectSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
Matcher original(BSON("x" << BSON("$ne" << BSON("$regex"
<< "abc"))),
ExtensionsCallbackNoop(),
- collator);
+ kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(),
BSON("$nor" << BSON_ARRAY(BSON("x" << BSON("$eq" << BSON("$regex"
<< "abc"))))));
@@ -325,10 +316,9 @@ TEST(SerializeBasic, ExpressionNeWithRegexObjectSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionLtSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
- Matcher original(fromjson("{x: {$lt: 3}}"), ExtensionsCallbackNoop(), collator);
+ Matcher original(fromjson("{x: {$lt: 3}}"), ExtensionsCallbackNoop(), kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{x: {$lt: 3}}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
@@ -340,10 +330,9 @@ TEST(SerializeBasic, ExpressionLtSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionGtSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
- Matcher original(fromjson("{x: {$gt: 3}}"), ExtensionsCallbackNoop(), collator);
+ Matcher original(fromjson("{x: {$gt: 3}}"), ExtensionsCallbackNoop(), kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{x: {$gt: 3}}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
@@ -355,10 +344,9 @@ TEST(SerializeBasic, ExpressionGtSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionGteSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
- Matcher original(fromjson("{x: {$gte: 3}}"), ExtensionsCallbackNoop(), collator);
+ Matcher original(fromjson("{x: {$gte: 3}}"), ExtensionsCallbackNoop(), kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{x: {$gte: 3}}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
@@ -370,10 +358,9 @@ TEST(SerializeBasic, ExpressionGteSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionLteSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
- Matcher original(fromjson("{x: {$lte: 3}}"), ExtensionsCallbackNoop(), collator);
+ Matcher original(fromjson("{x: {$lte: 3}}"), ExtensionsCallbackNoop(), kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{x: {$lte: 3}}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
@@ -385,10 +372,9 @@ TEST(SerializeBasic, ExpressionLteSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionRegexWithObjSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
- Matcher original(fromjson("{x: {$regex: 'a.b'}}"), ExtensionsCallbackNoop(), collator);
+ Matcher original(fromjson("{x: {$regex: 'a.b'}}"), ExtensionsCallbackNoop(), kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(),
BSON("x" << BSON("$regex"
<< "a.b")));
@@ -402,10 +388,9 @@ TEST(SerializeBasic, ExpressionRegexWithObjSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionRegexWithValueAndOptionsSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
- Matcher original(fromjson("{x: /a.b/i}"), ExtensionsCallbackNoop(), collator);
+ Matcher original(fromjson("{x: /a.b/i}"), ExtensionsCallbackNoop(), kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(),
BSON("x" << BSON("$regex"
<< "a.b"
@@ -421,10 +406,9 @@ TEST(SerializeBasic, ExpressionRegexWithValueAndOptionsSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionRegexWithValueSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
- Matcher original(fromjson("{x: /a.b/}"), ExtensionsCallbackNoop(), collator);
+ Matcher original(fromjson("{x: /a.b/}"), ExtensionsCallbackNoop(), kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(),
BSON("x" << BSON("$regex"
<< "a.b")));
@@ -438,10 +422,10 @@ TEST(SerializeBasic, ExpressionRegexWithValueSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionRegexWithEqObjSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
- Matcher original(fromjson("{x: {$eq: {$regex: 'a.b'}}}"), ExtensionsCallbackNoop(), collator);
+ Matcher original(
+ fromjson("{x: {$eq: {$regex: 'a.b'}}}"), ExtensionsCallbackNoop(), kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{x: {$eq: {$regex: 'a.b'}}}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
@@ -456,10 +440,9 @@ TEST(SerializeBasic, ExpressionRegexWithEqObjSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionModSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
- Matcher original(fromjson("{x: {$mod: [2, 1]}}"), ExtensionsCallbackNoop(), collator);
+ Matcher original(fromjson("{x: {$mod: [2, 1]}}"), ExtensionsCallbackNoop(), kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{x: {$mod: [2, 1]}}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
@@ -471,10 +454,9 @@ TEST(SerializeBasic, ExpressionModSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionExistsTrueSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
- Matcher original(fromjson("{x: {$exists: true}}"), ExtensionsCallbackNoop(), collator);
+ Matcher original(fromjson("{x: {$exists: true}}"), ExtensionsCallbackNoop(), kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{x: {$exists: true}}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
@@ -486,10 +468,9 @@ TEST(SerializeBasic, ExpressionExistsTrueSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionExistsFalseSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
- Matcher original(fromjson("{x: {$exists: false}}"), ExtensionsCallbackNoop(), collator);
+ Matcher original(fromjson("{x: {$exists: false}}"), ExtensionsCallbackNoop(), kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{$nor: [{x: {$exists: true}}]}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
@@ -501,10 +482,9 @@ TEST(SerializeBasic, ExpressionExistsFalseSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionInSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
- Matcher original(fromjson("{x: {$in: [1, 2, 3]}}"), ExtensionsCallbackNoop(), collator);
+ Matcher original(fromjson("{x: {$in: [1, 2, 3]}}"), ExtensionsCallbackNoop(), kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{x: {$in: [1, 2, 3]}}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
@@ -519,10 +499,9 @@ TEST(SerializeBasic, ExpressionInSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionInWithEmptyArraySerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
- Matcher original(fromjson("{x: {$in: []}}"), ExtensionsCallbackNoop(), collator);
+ Matcher original(fromjson("{x: {$in: []}}"), ExtensionsCallbackNoop(), kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{x: {$in: []}}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
@@ -531,10 +510,10 @@ TEST(SerializeBasic, ExpressionInWithEmptyArraySerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionInWithRegexSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
- Matcher original(fromjson("{x: {$in: [/\\d+/, /\\w+/]}}"), ExtensionsCallbackNoop(), collator);
+ Matcher original(
+ fromjson("{x: {$in: [/\\d+/, /\\w+/]}}"), ExtensionsCallbackNoop(), kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{x: {$in: [/\\d+/, /\\w+/]}}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
@@ -549,10 +528,9 @@ TEST(SerializeBasic, ExpressionInWithRegexSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionNinSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
- Matcher original(fromjson("{x: {$nin: [1, 2, 3]}}"), ExtensionsCallbackNoop(), collator);
+ Matcher original(fromjson("{x: {$nin: [1, 2, 3]}}"), ExtensionsCallbackNoop(), kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{$nor: [{x: {$in: [1, 2, 3]}}]}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
@@ -567,11 +545,10 @@ TEST(SerializeBasic, ExpressionNinSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionNinWithRegexValueSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
Matcher original(
- fromjson("{x: {$nin: [/abc/, /def/, /xyz/]}}"), ExtensionsCallbackNoop(), collator);
+ fromjson("{x: {$nin: [/abc/, /def/, /xyz/]}}"), ExtensionsCallbackNoop(), kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(),
fromjson("{$nor: [{x: {$in: [/abc/, /def/, /xyz/]}}]}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
@@ -586,10 +563,10 @@ TEST(SerializeBasic, ExpressionNinWithRegexValueSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionBitsAllSetSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
- Matcher original(fromjson("{x: {$bitsAllSet: [1, 3]}}"), ExtensionsCallbackNoop(), collator);
+ Matcher original(
+ fromjson("{x: {$bitsAllSet: [1, 3]}}"), ExtensionsCallbackNoop(), kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{x: {$bitsAllSet: [1, 3]}}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
@@ -601,10 +578,10 @@ TEST(SerializeBasic, ExpressionBitsAllSetSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionBitsAllClearSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
- Matcher original(fromjson("{x: {$bitsAllClear: [1, 3]}}"), ExtensionsCallbackNoop(), collator);
+ Matcher original(
+ fromjson("{x: {$bitsAllClear: [1, 3]}}"), ExtensionsCallbackNoop(), kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{x: {$bitsAllClear: [1, 3]}}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
@@ -616,10 +593,10 @@ TEST(SerializeBasic, ExpressionBitsAllClearSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionBitsAnySetSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
- Matcher original(fromjson("{x: {$bitsAnySet: [1, 3]}}"), ExtensionsCallbackNoop(), collator);
+ Matcher original(
+ fromjson("{x: {$bitsAnySet: [1, 3]}}"), ExtensionsCallbackNoop(), kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{x: {$bitsAnySet: [1, 3]}}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
@@ -631,10 +608,10 @@ TEST(SerializeBasic, ExpressionBitsAnySetSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionBitsAnyClearSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
- Matcher original(fromjson("{x: {$bitsAnyClear: [1, 3]}}"), ExtensionsCallbackNoop(), collator);
+ Matcher original(
+ fromjson("{x: {$bitsAnyClear: [1, 3]}}"), ExtensionsCallbackNoop(), kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{x: {$bitsAnyClear: [1, 3]}}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
@@ -649,10 +626,9 @@ TEST(SerializeBasic, ExpressionBitsAnyClearSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionNotSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
- Matcher original(fromjson("{x: {$not: {$eq: 3}}}"), ExtensionsCallbackNoop(), collator);
+ Matcher original(fromjson("{x: {$not: {$eq: 3}}}"), ExtensionsCallbackNoop(), kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{$nor: [{$and: [{x: {$eq: 3}}]}]}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
@@ -664,10 +640,10 @@ TEST(SerializeBasic, ExpressionNotSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionNotWithMultipleChildrenSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
- Matcher original(fromjson("{x: {$not: {$lt: 1, $gt: 3}}}"), ExtensionsCallbackNoop(), collator);
+ Matcher original(
+ fromjson("{x: {$not: {$lt: 1, $gt: 3}}}"), ExtensionsCallbackNoop(), kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(),
fromjson("{$nor: [{$and: [{x: {$lt: 1}}, {x: {$gt: 3}}]}]}}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
@@ -680,11 +656,10 @@ TEST(SerializeBasic, ExpressionNotWithMultipleChildrenSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionNotWithBitTestSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
Matcher original(
- fromjson("{x: {$not: {$bitsAnySet: [1, 3]}}}"), ExtensionsCallbackNoop(), collator);
+ fromjson("{x: {$not: {$bitsAnySet: [1, 3]}}}"), ExtensionsCallbackNoop(), kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(),
fromjson("{$nor: [{$and: [{x: {$bitsAnySet: [1, 3]}}]}]}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
@@ -697,10 +672,10 @@ TEST(SerializeBasic, ExpressionNotWithBitTestSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionNotWithRegexObjSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
- Matcher original(fromjson("{x: {$not: {$regex: 'a.b'}}}"), ExtensionsCallbackNoop(), collator);
+ Matcher original(
+ fromjson("{x: {$not: {$regex: 'a.b'}}}"), ExtensionsCallbackNoop(), kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(),
BSON("$nor" << BSON_ARRAY(BSON("x" << BSON("$regex"
<< "a.b")))));
@@ -714,10 +689,9 @@ TEST(SerializeBasic, ExpressionNotWithRegexObjSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionNotWithRegexValueSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
- Matcher original(fromjson("{x: {$not: /a.b/}}"), ExtensionsCallbackNoop(), collator);
+ Matcher original(fromjson("{x: {$not: /a.b/}}"), ExtensionsCallbackNoop(), kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(),
BSON("$nor" << BSON_ARRAY(BSON("x" << BSON("$regex"
<< "a.b")))));
@@ -731,10 +705,9 @@ TEST(SerializeBasic, ExpressionNotWithRegexValueSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionNotWithRegexValueAndOptionsSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
- Matcher original(fromjson("{x: {$not: /a.b/i}}"), ExtensionsCallbackNoop(), collator);
+ Matcher original(fromjson("{x: {$not: /a.b/i}}"), ExtensionsCallbackNoop(), kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(),
BSON("$nor" << BSON_ARRAY(BSON("x" << BSON("$regex"
<< "a.b"
@@ -750,14 +723,13 @@ TEST(SerializeBasic, ExpressionNotWithRegexValueAndOptionsSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionNotWithGeoSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
Matcher original(fromjson("{x: {$not: {$geoIntersects: {$geometry: {type: 'Polygon', "
"coordinates: [[[0,0], [5,0], "
"[5, 5], [0, 5], [0, 0]]]}}}}}"),
ExtensionsCallbackNoop(),
- collator);
+ kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(
*reserialized.getQuery(),
fromjson("{$nor: [{$and: [{x: {$geoIntersects: {$geometry: {type: 'Polygon', coordinates: "
@@ -780,11 +752,10 @@ TEST(SerializeBasic, ExpressionNotWithGeoSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionNorSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
Matcher original(
- fromjson("{$nor: [{x: 3}, {x: {$lt: 1}}]}"), ExtensionsCallbackNoop(), collator);
+ fromjson("{$nor: [{x: 3}, {x: {$lt: 1}}]}"), ExtensionsCallbackNoop(), kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{$nor: [{x: {$eq: 3}}, {x: {$lt: 1}}]}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
@@ -799,10 +770,9 @@ TEST(SerializeBasic, ExpressionNorSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionTypeSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
- Matcher original(fromjson("{x: {$type: 2}}"), ExtensionsCallbackNoop(), collator);
+ Matcher original(fromjson("{x: {$type: 2}}"), ExtensionsCallbackNoop(), kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{x: {$type: 2}}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
@@ -814,10 +784,9 @@ TEST(SerializeBasic, ExpressionTypeSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionTypeWithNumberSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
- Matcher original(fromjson("{x: {$type: 'number'}}"), ExtensionsCallbackNoop(), collator);
+ Matcher original(fromjson("{x: {$type: 'number'}}"), ExtensionsCallbackNoop(), kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{x: {$type: 'number'}}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
@@ -829,10 +798,9 @@ TEST(SerializeBasic, ExpressionTypeWithNumberSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionEmptySerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
- Matcher original(fromjson("{}"), ExtensionsCallbackNoop(), collator);
+ Matcher original(fromjson("{}"), ExtensionsCallbackNoop(), kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
@@ -841,10 +809,10 @@ TEST(SerializeBasic, ExpressionEmptySerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionWhereSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
- Matcher original(fromjson("{$where: 'this.a == this.b'}"), ExtensionsCallbackNoop(), collator);
+ Matcher original(
+ fromjson("{$where: 'this.a == this.b'}"), ExtensionsCallbackNoop(), kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(
*reserialized.getQuery(),
BSONObjBuilder().appendCodeWScope("$where", "this.a == this.b", BSONObj()).obj());
@@ -852,22 +820,20 @@ TEST(SerializeBasic, ExpressionWhereSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionWhereWithScopeSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
Matcher original(BSON("$where" << BSONCodeWScope("this.a == this.b", BSON("x" << 3))),
ExtensionsCallbackNoop(),
- collator);
+ kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(),
BSON("$where" << BSONCodeWScope("this.a == this.b", BSON("x" << 3))));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
}
TEST(SerializeBasic, ExpressionCommentSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
- Matcher original(fromjson("{$comment: 'Hello'}"), ExtensionsCallbackNoop(), collator);
+ Matcher original(fromjson("{$comment: 'Hello'}"), ExtensionsCallbackNoop(), kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
@@ -879,15 +845,14 @@ TEST(SerializeBasic, ExpressionCommentSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionGeoWithinSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
Matcher original(
fromjson(
"{x: {$geoWithin: {$geometry: "
"{type: 'Polygon', coordinates: [[[0, 0], [10, 0], [10, 10], [0, 10], [0, 0]]]}}}}"),
ExtensionsCallbackNoop(),
- collator);
+ kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(
*reserialized.getQuery(),
fromjson("{x: {$geoWithin: {$geometry: {type: 'Polygon', coordinates: [[[0,0], [10,0], "
@@ -902,15 +867,14 @@ TEST(SerializeBasic, ExpressionGeoWithinSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionGeoIntersectsSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
Matcher original(
fromjson(
"{x: {$geoIntersects: {$geometry: {type: 'Polygon', coordinates: [[[0,0], [5,0], [5, "
"5], [0, 5], [0, 0]]]}}}}"),
ExtensionsCallbackNoop(),
- collator);
+ kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(
*reserialized.getQuery(),
fromjson("{x: {$geoIntersects: {$geometry: {type: 'Polygon', coordinates: [[[0,0], [5,0], "
@@ -932,14 +896,13 @@ TEST(SerializeBasic, ExpressionGeoIntersectsSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionNearSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
Matcher original(
fromjson("{x: {$near: {$geometry: {type: 'Point', coordinates: [0, 0]}, $maxDistance: 10, "
"$minDistance: 1}}}"),
ExtensionsCallbackNoop(),
- collator);
+ kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(
*reserialized.getQuery(),
fromjson("{x: {$near: {$geometry: {type: 'Point', coordinates: [0, 0]}, $maxDistance: 10, "
@@ -948,15 +911,14 @@ TEST(SerializeBasic, ExpressionNearSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionNearSphereSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
Matcher original(
fromjson(
"{x: {$nearSphere: {$geometry: {type: 'Point', coordinates: [0, 0]}, $maxDistance: 10, "
"$minDistance: 1}}}"),
ExtensionsCallbackNoop(),
- collator);
+ kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(
*reserialized.getQuery(),
fromjson("{x: {$nearSphere: {$geometry: {type: 'Point', coordinates: [0, 0]}, "
@@ -965,12 +927,11 @@ TEST(SerializeBasic, ExpressionNearSphereSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionTextSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
Matcher original(fromjson("{$text: {$search: 'a', $language: 'en', $caseSensitive: true}}"),
ExtensionsCallbackNoop(),
- collator);
+ kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(),
fromjson("{$text: {$search: 'a', $language: 'en', $caseSensitive: true, "
"$diacriticSensitive: false}}"));
@@ -978,12 +939,11 @@ TEST(SerializeBasic, ExpressionTextSerializesCorrectly) {
}
TEST(SerializeBasic, ExpressionTextWithDefaultLanguageSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
Matcher original(fromjson("{$text: {$search: 'a', $caseSensitive: false}}"),
ExtensionsCallbackNoop(),
- collator);
+ kSimpleCollator);
Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), collator);
+ serialize(original.getMatchExpression()), ExtensionsCallbackNoop(), kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(),
fromjson("{$text: {$search: 'a', $language: '', $caseSensitive: false, "
"$diacriticSensitive: false}}"));
@@ -991,26 +951,37 @@ TEST(SerializeBasic, ExpressionTextWithDefaultLanguageSerializesCorrectly) {
}
TEST(SerializeInternalSchema, ExpressionInternalSchemaMinItemsSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
Matcher original(fromjson("{x: {$_internalSchemaMinItems: 1}}"),
ExtensionsCallbackDisallowExtensions(),
- collator);
- Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackDisallowExtensions(), collator);
+ kSimpleCollator);
+ Matcher reserialized(serialize(original.getMatchExpression()),
+ ExtensionsCallbackDisallowExtensions(),
+ kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{x: {$_internalSchemaMinItems: 1}}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
}
TEST(SerializeInternalSchema, ExpressionInternalSchemaMaxItemsSerializesCorrectly) {
- const CollatorInterface* collator = nullptr;
Matcher original(fromjson("{x: {$_internalSchemaMaxItems: 1}}"),
ExtensionsCallbackDisallowExtensions(),
- collator);
- Matcher reserialized(
- serialize(original.getMatchExpression()), ExtensionsCallbackDisallowExtensions(), collator);
+ kSimpleCollator);
+ Matcher reserialized(serialize(original.getMatchExpression()),
+ ExtensionsCallbackDisallowExtensions(),
+ kSimpleCollator);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{x: {$_internalSchemaMaxItems: 1}}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
}
+TEST(SerializeInternalSchema, ExpressionInternalSchemaUniqueItemsSerializesCorrectly) {
+ Matcher original(fromjson("{x: {$_internalSchemaUniqueItems: true}}"),
+ ExtensionsCallbackDisallowExtensions(),
+ kSimpleCollator);
+ Matcher reserialized(serialize(original.getMatchExpression()),
+ ExtensionsCallbackDisallowExtensions(),
+ kSimpleCollator);
+ ASSERT_BSONOBJ_EQ(*reserialized.getQuery(),
+ fromjson("{x: {$_internalSchemaUniqueItems: true}}"));
+ ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
+}
} // namespace
} // namespace mongo
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
new file mode 100644
index 00000000000..f35d14eb69b
--- /dev/null
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_unique_items.cpp
@@ -0,0 +1,74 @@
+/**
+ * Copyright (C) 2017 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/matcher/schema/expression_internal_schema_unique_items.h"
+
+namespace mongo {
+constexpr StringData InternalSchemaUniqueItemsMatchExpression::kName;
+
+void InternalSchemaUniqueItemsMatchExpression::debugString(StringBuilder& debug, int level) const {
+ _debugAddSpace(debug, level);
+
+ BSONObjBuilder builder;
+ serialize(&builder);
+ debug << builder.obj().toString() << "\n";
+
+ const auto* tag = getTag();
+ if (tag) {
+ debug << " ";
+ tag->debugString(&debug);
+ }
+ debug << "\n";
+}
+
+bool InternalSchemaUniqueItemsMatchExpression::equivalent(const MatchExpression* expr) const {
+ if (matchType() != expr->matchType()) {
+ return false;
+ }
+
+ const auto* other = static_cast<const InternalSchemaUniqueItemsMatchExpression*>(expr);
+ return path() == other->path();
+}
+
+void InternalSchemaUniqueItemsMatchExpression::serialize(BSONObjBuilder* builder) const {
+ BSONObjBuilder subobj(builder->subobjStart(path()));
+ subobj.append(kName, true);
+ subobj.doneFast();
+}
+
+std::unique_ptr<MatchExpression> InternalSchemaUniqueItemsMatchExpression::shallowClone() const {
+ auto clone = stdx::make_unique<InternalSchemaUniqueItemsMatchExpression>();
+ invariantOK(clone->init(path()));
+ if (getTag()) {
+ clone->setTag(getTag()->clone());
+ }
+ return std::move(clone);
+}
+} // namespace mongo
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
new file mode 100644
index 00000000000..c1bfc208a91
--- /dev/null
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_unique_items.h
@@ -0,0 +1,74 @@
+/**
+ * Copyright (C) 2017 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#pragma once
+
+#include <utility>
+
+#include "mongo/bson/bsonelement_comparator.h"
+#include "mongo/db/matcher/expression_array.h"
+
+namespace mongo {
+
+/**
+ * Matches arrays whose elements are all unique.
+ */
+class InternalSchemaUniqueItemsMatchExpression final : public ArrayMatchingMatchExpression {
+public:
+ static constexpr StringData kName = "$_internalSchemaUniqueItems"_sd;
+
+ InternalSchemaUniqueItemsMatchExpression()
+ : ArrayMatchingMatchExpression(MatchExpression::INTERNAL_SCHEMA_UNIQUE_ITEMS) {}
+
+ Status init(StringData path) {
+ return setPath(path);
+ }
+
+ bool matchesArray(const BSONObj& array, MatchDetails*) const final {
+ auto set = _comparator.makeBSONEltSet();
+ for (auto&& elem : array) {
+ if (!std::get<bool>(set.insert(elem))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ void debugString(StringBuilder& builder, int level) const final;
+
+ bool equivalent(const MatchExpression* other) const final;
+
+ void serialize(BSONObjBuilder* builder) const final;
+
+ std::unique_ptr<MatchExpression> shallowClone() const final;
+
+private:
+ // The comparator to use when comparing BSONElements, which will never use a collation.
+ BSONElementComparator _comparator{BSONElementComparator::FieldNamesMode::kIgnore, nullptr};
+};
+} // namespace mongo
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_unique_items_test.cpp b/src/mongo/db/matcher/schema/expression_internal_schema_unique_items_test.cpp
new file mode 100644
index 00000000000..16628681ae1
--- /dev/null
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_unique_items_test.cpp
@@ -0,0 +1,111 @@
+/**
+ * Copyright (C) 2017 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/bson/json.h"
+#include "mongo/db/matcher/schema/expression_internal_schema_unique_items.h"
+#include "mongo/db/query/collation/collator_interface_mock.h"
+#include "mongo/unittest/unittest.h"
+
+namespace mongo {
+namespace {
+TEST(InternalSchemaUniqueItemsMatchExpression, RejectsNonArrays) {
+ InternalSchemaUniqueItemsMatchExpression uniqueItems;
+ ASSERT_OK(uniqueItems.init("foo"));
+ ASSERT_FALSE(uniqueItems.matchesBSON(BSON("foo" << 1)));
+ ASSERT_FALSE(uniqueItems.matchesBSON(BSON("foo" << BSONObj())));
+ ASSERT_FALSE(uniqueItems.matchesBSON(BSON("foo"
+ << "string")));
+}
+
+TEST(InternalSchemaUniqueItemsMatchExpression, MatchesEmptyArray) {
+ InternalSchemaUniqueItemsMatchExpression uniqueItems;
+ ASSERT_OK(uniqueItems.init("foo"));
+ ASSERT_TRUE(uniqueItems.matchesBSON(BSON("foo" << BSONArray())));
+}
+
+TEST(InternalSchemaUniqueItemsMatchExpression, MatchesOneElementArray) {
+ InternalSchemaUniqueItemsMatchExpression uniqueItems;
+ ASSERT_OK(uniqueItems.init("foo"));
+ ASSERT_TRUE(uniqueItems.matchesBSON(BSON("foo" << BSON_ARRAY(1))));
+ ASSERT_TRUE(uniqueItems.matchesBSON(BSON("foo" << BSON_ARRAY(BSONObj()))));
+ ASSERT_TRUE(uniqueItems.matchesBSON(BSON("foo" << BSON_ARRAY(BSON_ARRAY(9 << "bar")))));
+}
+
+TEST(InternalSchemaUniqueItemsMatchExpression, MatchesArrayOfUniqueItems) {
+ InternalSchemaUniqueItemsMatchExpression uniqueItems;
+ ASSERT_OK(uniqueItems.init("foo"));
+ ASSERT_TRUE(uniqueItems.matchesBSON(fromjson("{foo: [1, 'bar', {}, [], null]}")));
+ ASSERT_TRUE(uniqueItems.matchesBSON(fromjson("{foo: [{x: 1}, {x: 2}, {x: 2, y: 3}]}")));
+ ASSERT_TRUE(uniqueItems.matchesBSON(fromjson("{foo: [[1], [1, 2], 1]}")));
+}
+
+TEST(InternalSchemaUniqueItemsMatchExpression, MatchesNestedArrayOfUniqueItems) {
+ InternalSchemaUniqueItemsMatchExpression uniqueItems;
+ ASSERT_OK(uniqueItems.init("foo.bar"));
+ ASSERT_TRUE(uniqueItems.matchesBSON(fromjson("{foo: {bar: [1, 'bar', {}, [], null]}}")));
+ ASSERT_TRUE(uniqueItems.matchesBSON(fromjson("{foo: {bar: [{x: 1}, {x: 2}, {x: 2, y: 3}]}}")));
+ ASSERT_TRUE(uniqueItems.matchesBSON(fromjson("{foo: {bar: [[1], [1, 2], 1]}}")));
+}
+
+TEST(InternalSchemaUniqueItemsMatchExpression, RejectsArrayWithDuplicates) {
+ InternalSchemaUniqueItemsMatchExpression uniqueItems;
+ ASSERT_OK(uniqueItems.init("foo"));
+ ASSERT_FALSE(uniqueItems.matchesBSON(fromjson("{foo: [1, 1, 1]}")));
+ ASSERT_FALSE(uniqueItems.matchesBSON(fromjson("{foo: [['bar'], ['bar']]}")));
+}
+
+TEST(InternalSchemaUniqueItemsMatchExpression, RejectsNestedArrayWithDuplicates) {
+ InternalSchemaUniqueItemsMatchExpression uniqueItems;
+ ASSERT_OK(uniqueItems.init("foo"));
+ ASSERT_FALSE(uniqueItems.matchesBSON(fromjson("{foo: {bar: [1, 1, 1]}}")));
+ ASSERT_FALSE(uniqueItems.matchesBSON(fromjson("{foo: {bar: [['baz'], ['baz']]}}")));
+}
+
+TEST(InternalSchemaUniqueItemsMatchExpression, FieldNameSignificantWhenComparingNestedObjects) {
+ InternalSchemaUniqueItemsMatchExpression uniqueItems;
+ ASSERT_OK(uniqueItems.init("foo"));
+ ASSERT_TRUE(uniqueItems.matchesBSON(fromjson("{foo: [{x: 7}, {y: 7}]}")));
+ ASSERT_TRUE(uniqueItems.matchesBSON(fromjson("{foo: [{a: 'bar'}, {b: 'bar'}]}")));
+ ASSERT_FALSE(uniqueItems.matchesBSON(fromjson("{foo: [{a: 'bar'}, {a: 'bar'}]}")));
+ ASSERT_TRUE(uniqueItems.matchesBSON(fromjson("{foo: [[1, 2, 3], [1, 3, 2]]}")));
+}
+
+TEST(InternalSchemaUniqueItemsMatchExpression, AlwaysUsesBinaryComparisonRegardlessOfCollator) {
+ InternalSchemaUniqueItemsMatchExpression uniqueItems;
+ ASSERT_OK(uniqueItems.init("foo"));
+ CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual);
+ uniqueItems.setCollator(&collator);
+
+ ASSERT_TRUE(uniqueItems.matchesBSON(fromjson("{foo: ['one', 'OnE', 'ONE']}")));
+ ASSERT_TRUE(uniqueItems.matchesBSON(fromjson("{foo: [{x: 'two'}, {y: 'two'}]}")));
+ ASSERT_TRUE(uniqueItems.matchesBSON(fromjson("{foo: [{a: 'three'}, {a: 'THREE'}]}")));
+}
+} // namespace
+} // namespace mongo
diff --git a/src/mongo/db/matcher/schema/expression_parser_schema_test.cpp b/src/mongo/db/matcher/schema/expression_parser_schema_test.cpp
index 877bc6fad3b..fadd978c6e5 100644
--- a/src/mongo/db/matcher/schema/expression_parser_schema_test.cpp
+++ b/src/mongo/db/matcher/schema/expression_parser_schema_test.cpp
@@ -28,25 +28,28 @@
#include "mongo/platform/basic.h"
+#include "mongo/bson/json.h"
#include "mongo/db/matcher/expression.h"
#include "mongo/db/matcher/expression_parser.h"
#include "mongo/db/matcher/extensions_callback_disallow_extensions.h"
#include "mongo/db/matcher/schema/expression_internal_schema_max_items.h"
#include "mongo/db/matcher/schema/expression_internal_schema_min_items.h"
+#include "mongo/db/matcher/schema/expression_internal_schema_unique_items.h"
#include "mongo/unittest/unittest.h"
namespace mongo {
namespace {
+constexpr CollatorInterface* kSimpleCollator = nullptr;
+
//
// Tests for parsing the $_internalSchemaMinItems expression.
//
TEST(MatchExpressionParserInternalSchemaMinItemsTest, CorrectlyParsesIntegerArgument) {
BSONObj query = BSON("x" << BSON("$_internalSchemaMinItems" << 2));
- const CollatorInterface* collator = nullptr;
- StatusWithMatchExpression result =
- MatchExpressionParser::parse(query, ExtensionsCallbackDisallowExtensions(), collator);
+ StatusWithMatchExpression result = MatchExpressionParser::parse(
+ query, ExtensionsCallbackDisallowExtensions(), kSimpleCollator);
ASSERT_TRUE(result.isOK());
ASSERT(!result.getValue()->matchesBSON(BSON("x" << 1)));
@@ -57,9 +60,8 @@ TEST(MatchExpressionParserInternalSchemaMinItemsTest, CorrectlyParsesIntegerArgu
TEST(MatchExpressionParserInternalSchemaMinItemsTest, CorrectlyParsesLongArgument) {
BSONObj query = BSON("x" << BSON("$_internalSchemaMinItems" << 2LL));
- const CollatorInterface* collator = nullptr;
- StatusWithMatchExpression result =
- MatchExpressionParser::parse(query, ExtensionsCallbackDisallowExtensions(), collator);
+ StatusWithMatchExpression result = MatchExpressionParser::parse(
+ query, ExtensionsCallbackDisallowExtensions(), kSimpleCollator);
ASSERT_TRUE(result.isOK());
ASSERT(!result.getValue()->matchesBSON(BSON("x" << 1)));
@@ -71,9 +73,8 @@ TEST(MatchExpressionParserInternalSchemaMinItemsTest, CorrectlyParsesLongArgumen
TEST(MatchExpressionParserInternalSchemaMinItemsTest, CorrectlyParsesDoubleArgumentAsInteger) {
BSONObj query = BSON("x" << BSON("$_internalSchemaMinItems" << 2.0));
- const CollatorInterface* collator = nullptr;
- StatusWithMatchExpression result =
- MatchExpressionParser::parse(query, ExtensionsCallbackDisallowExtensions(), collator);
+ StatusWithMatchExpression result = MatchExpressionParser::parse(
+ query, ExtensionsCallbackDisallowExtensions(), kSimpleCollator);
ASSERT_TRUE(result.isOK());
ASSERT(!result.getValue()->matchesBSON(BSON("x" << 1)));
@@ -84,9 +85,8 @@ TEST(MatchExpressionParserInternalSchemaMinItemsTest, CorrectlyParsesDoubleArgum
TEST(MatchExpressionParserInternalSchemaMinItemsTest, CorrectlyParsesDecimalArgumentAsInteger) {
BSONObj query = BSON("x" << BSON("$_internalSchemaMinItems" << Decimal128("2")));
- const CollatorInterface* collator = nullptr;
- StatusWithMatchExpression result =
- MatchExpressionParser::parse(query, ExtensionsCallbackDisallowExtensions(), collator);
+ StatusWithMatchExpression result = MatchExpressionParser::parse(
+ query, ExtensionsCallbackDisallowExtensions(), kSimpleCollator);
ASSERT_TRUE(result.isOK());
ASSERT(!result.getValue()->matchesBSON(BSON("x" << 1)));
@@ -100,9 +100,8 @@ TEST(MatchExpressionParserInternalSchemaMinItemsTest, CorrectlyParsesDecimalArgu
//
TEST(MatchExpressionParserInternalSchemaMaxItemsTest, CorrectlyParsesIntegerArgument) {
BSONObj query = BSON("x" << BSON("$_internalSchemaMaxItems" << 2));
- const CollatorInterface* collator = nullptr;
- StatusWithMatchExpression result =
- MatchExpressionParser::parse(query, ExtensionsCallbackDisallowExtensions(), collator);
+ StatusWithMatchExpression result = MatchExpressionParser::parse(
+ query, ExtensionsCallbackDisallowExtensions(), kSimpleCollator);
ASSERT_TRUE(result.isOK());
ASSERT(!result.getValue()->matchesBSON(BSON("x" << 1)));
@@ -113,9 +112,8 @@ TEST(MatchExpressionParserInternalSchemaMaxItemsTest, CorrectlyParsesIntegerArgu
TEST(MatchExpressionParserInternalSchemaMaxItemsTest, CorrectlyParsesLongArgument) {
BSONObj query = BSON("x" << BSON("$_internalSchemaMaxItems" << 2LL));
- const CollatorInterface* collator = nullptr;
- StatusWithMatchExpression result =
- MatchExpressionParser::parse(query, ExtensionsCallbackDisallowExtensions(), collator);
+ StatusWithMatchExpression result = MatchExpressionParser::parse(
+ query, ExtensionsCallbackDisallowExtensions(), kSimpleCollator);
ASSERT_TRUE(result.isOK());
ASSERT(!result.getValue()->matchesBSON(BSON("x" << 1)));
@@ -127,9 +125,8 @@ TEST(MatchExpressionParserInternalSchemaMaxItemsTest, CorrectlyParsesLongArgumen
TEST(MatchExpressionParserInternalSchemaMaxItemsTest, CorrectlyParsesDoubleArgumentAsInteger) {
BSONObj query = BSON("x" << BSON("$_internalSchemaMaxItems" << 2.0));
- const CollatorInterface* collator = nullptr;
- StatusWithMatchExpression result =
- MatchExpressionParser::parse(query, ExtensionsCallbackDisallowExtensions(), collator);
+ StatusWithMatchExpression result = MatchExpressionParser::parse(
+ query, ExtensionsCallbackDisallowExtensions(), kSimpleCollator);
ASSERT_TRUE(result.isOK());
ASSERT(!result.getValue()->matchesBSON(BSON("x" << 1)));
@@ -140,9 +137,8 @@ TEST(MatchExpressionParserInternalSchemaMaxItemsTest, CorrectlyParsesDoubleArgum
TEST(MatchExpressionParserInternalSchemaMaxItemsTest, CorrectlyParsesDecimalArgumentAsInteger) {
BSONObj query = BSON("x" << BSON("$_internalSchemaMaxItems" << Decimal128("2")));
- const CollatorInterface* collator = nullptr;
- StatusWithMatchExpression result =
- MatchExpressionParser::parse(query, ExtensionsCallbackDisallowExtensions(), collator);
+ StatusWithMatchExpression result = MatchExpressionParser::parse(
+ query, ExtensionsCallbackDisallowExtensions(), kSimpleCollator);
ASSERT_TRUE(result.isOK());
ASSERT(!result.getValue()->matchesBSON(BSON("x" << 1)));
@@ -151,5 +147,42 @@ TEST(MatchExpressionParserInternalSchemaMaxItemsTest, CorrectlyParsesDecimalArgu
ASSERT(!result.getValue()->matchesBSON(BSON("x" << BSON_ARRAY(1 << 2 << 3))));
}
+TEST(MatchExpressionParserInternalSchemaUniqueItemsTest, FailsToParseNonTrueArguments) {
+ auto queryIntArgument = BSON("x" << BSON("$_internalSchemaUniqueItems" << 0));
+ auto expr = MatchExpressionParser::parse(
+ queryIntArgument, ExtensionsCallbackDisallowExtensions(), kSimpleCollator);
+ ASSERT_NOT_OK(expr.getStatus());
+
+ auto queryStringArgument = BSON("x" << BSON("$_internalSchemaUniqueItems"
+ << ""));
+ expr = MatchExpressionParser::parse(
+ queryStringArgument, ExtensionsCallbackDisallowExtensions(), kSimpleCollator);
+ ASSERT_NOT_OK(expr.getStatus());
+
+ auto queryDoubleArgument = BSON("x" << BSON("$_internalSchemaUniqueItems" << 1.0));
+ expr = MatchExpressionParser::parse(
+ queryDoubleArgument, ExtensionsCallbackDisallowExtensions(), kSimpleCollator);
+ ASSERT_NOT_OK(expr.getStatus());
+
+ auto queryFalseArgument = BSON("x" << BSON("$_internalSchemaUniqueItems" << false));
+ expr = MatchExpressionParser::parse(
+ queryFalseArgument, ExtensionsCallbackDisallowExtensions(), kSimpleCollator);
+ ASSERT_NOT_OK(expr.getStatus());
+}
+
+TEST(MatchExpressionParserInternalSchemaUniqueItemsTest, ParsesTrueBooleanArgument) {
+ auto query = BSON("x" << BSON("$_internalSchemaUniqueItems" << true));
+ auto expr = MatchExpressionParser::parse(
+ query, ExtensionsCallbackDisallowExtensions(), kSimpleCollator);
+ ASSERT_OK(expr.getStatus());
+
+ ASSERT_FALSE(expr.getValue()->matchesBSON(fromjson("{x: 1}")));
+ ASSERT_FALSE(expr.getValue()->matchesBSON(fromjson("{x: 'blah'}")));
+ ASSERT_TRUE(expr.getValue()->matchesBSON(fromjson("{x: []}")));
+ ASSERT_TRUE(expr.getValue()->matchesBSON(fromjson("{x: [0]}")));
+ ASSERT_TRUE(expr.getValue()->matchesBSON(fromjson("{x: ['7', null, [], {}, 7]}")));
+ ASSERT_FALSE(expr.getValue()->matchesBSON(fromjson("{x: ['dup', 'dup', 7]}")));
+ ASSERT_FALSE(expr.getValue()->matchesBSON(fromjson("{x: [{x: 1}, {x: 1}]}")));
+}
} // namespace
} // namespace mongo
diff --git a/src/mongo/db/pipeline/document_source_match.cpp b/src/mongo/db/pipeline/document_source_match.cpp
index 1ce429bb6be..db34c1f746a 100644
--- a/src/mongo/db/pipeline/document_source_match.cpp
+++ b/src/mongo/db/pipeline/document_source_match.cpp
@@ -275,6 +275,7 @@ Document redactSafePortionDollarOps(BSONObj expr) {
case BSONObj::opGEO_INTERSECTS:
case BSONObj::opINTERNAL_SCHEMA_MIN_ITEMS:
case BSONObj::opINTERNAL_SCHEMA_MAX_ITEMS:
+ case BSONObj::opINTERNAL_SCHEMA_UNIQUE_ITEMS:
continue;
}
}
diff --git a/src/mongo/db/pipeline/document_source_match_test.cpp b/src/mongo/db/pipeline/document_source_match_test.cpp
index b84b472f41e..9ee753078a4 100644
--- a/src/mongo/db/pipeline/document_source_match_test.cpp
+++ b/src/mongo/db/pipeline/document_source_match_test.cpp
@@ -110,6 +110,8 @@ TEST_F(DocumentSourceMatchTest, RedactSafePortion) {
assertExpectedRedactSafePortion("{a: {$_internalSchemaMaxItems: 1}}", "{}");
+ assertExpectedRedactSafePortion("{a: {$_internalSchemaUniqueItems: true}}", "{}");
+
// Combinations
assertExpectedRedactSafePortion("{a:1, b: 'asdf'}", "{a:1, b: 'asdf'}");
diff --git a/src/mongo/db/query/plan_cache.cpp b/src/mongo/db/query/plan_cache.cpp
index 124a7d3b998..7cb5210ca0c 100644
--- a/src/mongo/db/query/plan_cache.cpp
+++ b/src/mongo/db/query/plan_cache.cpp
@@ -177,9 +177,11 @@ const char* encodeMatchType(MatchExpression::MatchType mt) {
case MatchExpression::INTERNAL_SCHEMA_MAX_ITEMS:
return "internalSchemaMaxItems";
break;
+ case MatchExpression::INTERNAL_SCHEMA_UNIQUE_ITEMS:
+ return "internalSchemaUniqueItems";
+ break;
case MatchExpression::INTERNAL_SCHEMA_XOR:
return "internalSchemaXor";
- break;
default:
MONGO_UNREACHABLE;
return "";