summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Wahlin <james@mongodb.com>2019-01-15 16:56:21 -0500
committerJames Wahlin <james@mongodb.com>2019-02-19 09:18:55 -0500
commita973d4f651a4e0f5a19c2edb2de2e6e53ee5e212 (patch)
tree32dce10e07b74559acc1a224f71cd8abb8ea5118
parent1f11c8beeb5ebdbc315085c1a72f0b81cd5522d5 (diff)
downloadmongo-a973d4f651a4e0f5a19c2edb2de2e6e53ee5e212.tar.gz
SERVER-39019 Fix incorrect $elemMatch $ne serialization
(cherry picked from commit 4677879eb75934ee3cd76c9e9807f6892ac17400) SERVER-13779 Allow $not to be applied to $regex (cherry picked from commit a2c0f15d6dc9fcda389b18b54287c4fcb5be44cd)
-rw-r--r--jstests/core/not2.js81
-rw-r--r--src/mongo/db/exec/geo_near.cpp4
-rw-r--r--src/mongo/db/matcher/expression_algo_test.cpp11
-rw-r--r--src/mongo/db/matcher/expression_array.cpp13
-rw-r--r--src/mongo/db/matcher/expression_array.h6
-rw-r--r--src/mongo/db/matcher/expression_geo.cpp14
-rw-r--r--src/mongo/db/matcher/expression_geo.h4
-rw-r--r--src/mongo/db/matcher/expression_leaf.cpp28
-rw-r--r--src/mongo/db/matcher/expression_leaf.h12
-rw-r--r--src/mongo/db/matcher/expression_parser.cpp6
-rw-r--r--src/mongo/db/matcher/expression_parser_tree.cpp105
-rw-r--r--src/mongo/db/matcher/expression_path.h12
-rw-r--r--src/mongo/db/matcher/expression_serialization_test.cpp84
-rw-r--r--src/mongo/db/matcher/expression_text_base.h4
-rw-r--r--src/mongo/db/matcher/expression_tree.cpp74
-rw-r--r--src/mongo/db/matcher/expression_tree.h4
-rw-r--r--src/mongo/db/matcher/expression_type.h6
-rw-r--r--src/mongo/db/matcher/matcher.cpp9
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.cpp6
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.h2
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_eq.cpp6
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_eq.h2
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_fmod.cpp6
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_fmod.h2
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_match_array_index.cpp8
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_match_array_index.h2
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_num_array_items.cpp8
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_num_array_items.h2
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_object_match.cpp6
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_object_match.h2
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_str_length.cpp8
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_str_length.h2
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_unique_items.cpp8
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_unique_items.h2
-rw-r--r--src/mongo/db/matcher/schema/json_schema_parser_test.cpp891
-rw-r--r--src/mongo/db/pipeline/pipeline_test.cpp34
36 files changed, 635 insertions, 839 deletions
diff --git a/jstests/core/not2.js b/jstests/core/not2.js
index 21e8d9aa9c4..610d79c4d8f 100644
--- a/jstests/core/not2.js
+++ b/jstests/core/not2.js
@@ -6,14 +6,12 @@
const coll = db.jstests_not2;
coll.drop();
- function check(query, expected, size) {
- if (size === undefined) {
- size = 1;
- }
- assert.eq(size, coll.find(query).itcount(), tojson(query));
- if (size > 0) {
- const cursor = coll.find(query).sort({i: 1});
- assert.eq(expected, cursor.toArray()[0].i, tojson(query));
+ function check(query, expected) {
+ const resultList = coll.find(query).sort({i: 1}).toArray();
+ assert.eq(expected.length, resultList.length, query);
+
+ for (let x = 0; x < expected.length; ++x) {
+ assert.eq(expected[x], resultList[x].i, query);
}
}
@@ -30,53 +28,56 @@
// TODO SERVER-12735: We currently do not handle double negatives during query
// canonicalization.
fail({i: {$not: {$not: "a"}}});
- check({i: {$not: {$not: {$gt: "a"}}}}, "b");
+ check({i: {$not: {$not: {$gt: "a"}}}}, ["b"]);
fail({i: {$not: "a"}});
fail({i: {$not: {$ref: "foo"}}});
fail({i: {$not: {}}});
- check({i: {$gt: "a"}}, "b");
- check({i: {$not: {$gt: "a"}}}, "a");
- check({i: {$not: {$ne: "a"}}}, "a");
- check({i: {$not: {$gte: "b"}}}, "a");
- check({i: {$exists: true}}, "a", 2);
- check({i: {$not: {$exists: true}}}, "", 0);
- check({j: {$not: {$exists: false}}}, "", 0);
- check({j: {$not: {$exists: true}}}, "a", 2);
- check({i: {$not: {$in: ["a"]}}}, "b");
- check({i: {$not: {$in: ["a", "b"]}}}, "", 0);
- check({i: {$not: {$in: ["g"]}}}, "a", 2);
- check({i: {$not: {$nin: ["a"]}}}, "a");
- check({i: {$not: /a/}}, "b");
- check({i: {$not: /(a|b)/}}, "", 0);
- check({i: {$not: /a/, $regex: "a"}}, "", 0);
- check({i: {$not: /aa/}}, "a", 2);
- fail({i: {$not: {$regex: "a"}}});
+ check({i: {$gt: "a"}}, ["b"]);
+ check({i: {$not: {$gt: "a"}}}, ["a"]);
+ check({i: {$not: {$ne: "a"}}}, ["a"]);
+ check({i: {$not: {$gte: "b"}}}, ["a"]);
+ check({i: {$exists: true}}, ["a", "b"]);
+ check({i: {$not: {$exists: true}}}, []);
+ check({j: {$not: {$exists: false}}}, []);
+ check({j: {$not: {$exists: true}}}, ["a", "b"]);
+ check({i: {$not: {$in: ["a"]}}}, ["b"]);
+ check({i: {$not: {$in: ["a", "b"]}}}, []);
+ check({i: {$not: {$in: ["g"]}}}, ["a", "b"]);
+ check({i: {$not: {$nin: ["a"]}}}, ["a"]);
+ check({i: {$not: /a/}}, ["b"]);
+ check({i: {$not: /(a|b)/}}, []);
+ check({i: {$not: /a/, $regex: "a"}}, []);
+ check({i: {$not: /aa/}}, ["a", "b"]);
+ check({i: {$not: {$regex: "a"}}}, ["b"]);
+ check({i: {$not: {$regex: "A", $options: "i"}}}, ["b"]);
+ check({i: {$not: {$regex: "[ab]"}}}, []);
+ check({i: {$not: {$regex: "^foo"}}}, ["a", "b"]);
fail({i: {$not: {$options: "a"}}});
- check({i: {$type: 2}}, "a", 2);
- check({i: {$not: {$type: 1}}}, "a", 2);
- check({i: {$not: {$type: 2}}}, "", 0);
+ check({i: {$type: 2}}, ["a", "b"]);
+ check({i: {$not: {$type: 1}}}, ["a", "b"]);
+ check({i: {$not: {$type: 2}}}, []);
assert.writeOK(coll.remove({}));
assert.writeOK(coll.insert({i: 1}));
- check({i: {$not: {$mod: [5, 1]}}}, null, 0);
- check({i: {$mod: [5, 2]}}, null, 0);
- check({i: {$not: {$mod: [5, 2]}}}, 1, 1);
+ check({i: {$not: {$mod: [5, 1]}}}, []);
+ check({i: {$mod: [5, 2]}}, []);
+ check({i: {$not: {$mod: [5, 2]}}}, [1]);
assert.writeOK(coll.remove({}));
assert.writeOK(coll.insert({i: ["a", "b"]}));
- check({i: {$not: {$size: 2}}}, null, 0);
- check({i: {$not: {$size: 3}}}, ["a", "b"]);
- check({i: {$not: {$gt: "a"}}}, null, 0);
- check({i: {$not: {$gt: "c"}}}, ["a", "b"]);
- check({i: {$not: {$all: ["a", "b"]}}}, null, 0);
- check({i: {$not: {$all: ["c"]}}}, ["a", "b"]);
+ check({i: {$not: {$size: 2}}}, []);
+ check({i: {$not: {$size: 3}}}, [["a", "b"]]);
+ check({i: {$not: {$gt: "a"}}}, []);
+ check({i: {$not: {$gt: "c"}}}, [["a", "b"]]);
+ check({i: {$not: {$all: ["a", "b"]}}}, []);
+ check({i: {$not: {$all: ["c"]}}}, [["a", "b"]]);
assert.writeOK(coll.remove({}));
assert.writeOK(coll.insert({i: [{j: "a"}]}));
assert.writeOK(coll.insert({i: [{j: "b"}]}));
- check({i: {$not: {$elemMatch: {j: "a"}}}}, [{j: "b"}]);
- check({i: {$not: {$elemMatch: {j: "f"}}}}, [{j: "a"}], 2);
+ check({i: {$not: {$elemMatch: {j: "a"}}}}, [[{j: "b"}]]);
+ check({i: {$not: {$elemMatch: {j: "f"}}}}, [[{j: "a"}], [{j: "b"}]]);
}
// Run the test without any index.
diff --git a/src/mongo/db/exec/geo_near.cpp b/src/mongo/db/exec/geo_near.cpp
index e9f3ab1a370..bb7fb19e3a5 100644
--- a/src/mongo/db/exec/geo_near.cpp
+++ b/src/mongo/db/exec/geo_near.cpp
@@ -528,6 +528,10 @@ public:
// These won't be called.
//
+ BSONObj getSerializedRightHandSide() const final {
+ MONGO_UNREACHABLE;
+ }
+
void debugString(StringBuilder& debug, int level = 0) const final {
MONGO_UNREACHABLE;
}
diff --git a/src/mongo/db/matcher/expression_algo_test.cpp b/src/mongo/db/matcher/expression_algo_test.cpp
index 80e235118d8..b99301fc95b 100644
--- a/src/mongo/db/matcher/expression_algo_test.cpp
+++ b/src/mongo/db/matcher/expression_algo_test.cpp
@@ -871,7 +871,7 @@ TEST(SplitMatchExpression, NotWithIndependentChildIsSplittable) {
BSONObjBuilder firstBob;
splitExpr.first->serialize(&firstBob);
- ASSERT_BSONOBJ_EQ(firstBob.obj(), fromjson("{$nor: [{$and: [{x: {$gt: 4}}]}]}"));
+ ASSERT_BSONOBJ_EQ(firstBob.obj(), fromjson("{x: {$not: {$gt: 4}}}"));
ASSERT_FALSE(splitExpr.second);
}
@@ -915,13 +915,12 @@ TEST(SplitMatchExpression, ComplexMatchExpressionSplitsCorrectly) {
splitExpr.second->serialize(&secondBob);
ASSERT_BSONOBJ_EQ(firstBob.obj(), fromjson("{$or: [{'a.b': {$eq: 3}}, {'a.b.c': {$eq: 4}}]}"));
- ASSERT_BSONOBJ_EQ(
- secondBob.obj(),
- fromjson("{$and: [{$nor: [{$and: [{x: {$size: 2}}]}]}, {$nor: [{x: {$gt: 4}}, {$and: "
- "[{$nor: [{$and: [{x: "
- "{$eq: 1}}]}]}, {y: {$eq: 3}}]}]}]}"));
+ ASSERT_BSONOBJ_EQ(secondBob.obj(),
+ fromjson("{$and: [{x: {$not: {$size: 2}}}, {$nor: [{x: {$gt: 4}}, {$and: "
+ "[{x: {$not: {$eq: 1}}}, {y: {$eq: 3}}]}]}]}"));
}
+
TEST(SplitMatchExpression, ShouldNotExtractPrefixOfDottedPathAsIndependent) {
BSONObj matchPredicate = fromjson("{$and: [{a: 1}, {'a.b': 1}, {'a.c': 1}]}");
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
diff --git a/src/mongo/db/matcher/expression_array.cpp b/src/mongo/db/matcher/expression_array.cpp
index 22bc85edd29..16e144c89c0 100644
--- a/src/mongo/db/matcher/expression_array.cpp
+++ b/src/mongo/db/matcher/expression_array.cpp
@@ -103,10 +103,10 @@ void ElemMatchObjectMatchExpression::debugString(StringBuilder& debug, int level
_sub->debugString(debug, level + 1);
}
-void ElemMatchObjectMatchExpression::serialize(BSONObjBuilder* out) const {
+BSONObj ElemMatchObjectMatchExpression::getSerializedRightHandSide() const {
BSONObjBuilder subBob;
_sub->serialize(&subBob);
- out->append(path(), BSON("$elemMatch" << subBob.obj()));
+ return BSON("$elemMatch" << subBob.obj());
}
MatchExpression::ExpressionOptimizerFunc ElemMatchObjectMatchExpression::getOptimizer() const {
@@ -178,7 +178,7 @@ void ElemMatchValueMatchExpression::debugString(StringBuilder& debug, int level)
}
}
-void ElemMatchValueMatchExpression::serialize(BSONObjBuilder* out) const {
+BSONObj ElemMatchValueMatchExpression::getSerializedRightHandSide() const {
BSONObjBuilder emBob;
for (unsigned i = 0; i < _subs.size(); i++) {
@@ -187,7 +187,8 @@ void ElemMatchValueMatchExpression::serialize(BSONObjBuilder* out) const {
BSONObj predObj = predicate.obj();
emBob.appendElements(predObj.firstElement().embeddedObject());
}
- out->append(path(), BSON("$elemMatch" << emBob.obj()));
+
+ return BSON("$elemMatch" << emBob.obj());
}
MatchExpression::ExpressionOptimizerFunc ElemMatchValueMatchExpression::getOptimizer() const {
@@ -226,8 +227,8 @@ void SizeMatchExpression::debugString(StringBuilder& debug, int level) const {
}
}
-void SizeMatchExpression::serialize(BSONObjBuilder* out) const {
- out->append(path(), BSON("$size" << _size));
+BSONObj SizeMatchExpression::getSerializedRightHandSide() const {
+ return BSON("$size" << _size);
}
bool SizeMatchExpression::equivalent(const MatchExpression* other) const {
diff --git a/src/mongo/db/matcher/expression_array.h b/src/mongo/db/matcher/expression_array.h
index fdaee8fda5d..4e572a2d9eb 100644
--- a/src/mongo/db/matcher/expression_array.h
+++ b/src/mongo/db/matcher/expression_array.h
@@ -89,7 +89,7 @@ public:
virtual void debugString(StringBuilder& debug, int level) const;
- virtual void serialize(BSONObjBuilder* out) const;
+ BSONObj getSerializedRightHandSide() const final;
std::vector<MatchExpression*>* getChildVector() final {
return nullptr;
@@ -144,7 +144,7 @@ public:
virtual void debugString(StringBuilder& debug, int level) const;
- virtual void serialize(BSONObjBuilder* out) const;
+ BSONObj getSerializedRightHandSide() const final;
virtual std::vector<MatchExpression*>* getChildVector() {
return &_subs;
@@ -195,7 +195,7 @@ public:
virtual void debugString(StringBuilder& debug, int level) const;
- virtual void serialize(BSONObjBuilder* out) const;
+ BSONObj getSerializedRightHandSide() const final;
virtual bool equivalent(const MatchExpression* other) const;
diff --git a/src/mongo/db/matcher/expression_geo.cpp b/src/mongo/db/matcher/expression_geo.cpp
index 298f025464c..a1e19abe2f3 100644
--- a/src/mongo/db/matcher/expression_geo.cpp
+++ b/src/mongo/db/matcher/expression_geo.cpp
@@ -389,10 +389,10 @@ void GeoMatchExpression::debugString(StringBuilder& debug, int level) const {
debug << "\n";
}
-void GeoMatchExpression::serialize(BSONObjBuilder* out) const {
- BSONObjBuilder subobj(out->subobjStart(path()));
+BSONObj GeoMatchExpression::getSerializedRightHandSide() const {
+ BSONObjBuilder subobj;
subobj.appendElements(_rawObj);
- subobj.doneFast();
+ return subobj.obj();
}
bool GeoMatchExpression::equivalent(const MatchExpression* other) const {
@@ -447,10 +447,10 @@ void GeoNearMatchExpression::debugString(StringBuilder& debug, int level) const
debug << "\n";
}
-void GeoNearMatchExpression::serialize(BSONObjBuilder* out) const {
- BSONObjBuilder subobj(out->subobjStart(path()));
- subobj.appendElements(_rawObj);
- subobj.doneFast();
+BSONObj GeoNearMatchExpression::getSerializedRightHandSide() const {
+ BSONObjBuilder objBuilder;
+ objBuilder.appendElements(_rawObj);
+ return objBuilder.obj();
}
bool GeoNearMatchExpression::equivalent(const MatchExpression* other) const {
diff --git a/src/mongo/db/matcher/expression_geo.h b/src/mongo/db/matcher/expression_geo.h
index 03ebc49df7d..9f8d4d4b285 100644
--- a/src/mongo/db/matcher/expression_geo.h
+++ b/src/mongo/db/matcher/expression_geo.h
@@ -91,7 +91,7 @@ public:
virtual void debugString(StringBuilder& debug, int level = 0) const;
- virtual void serialize(BSONObjBuilder* out) const;
+ BSONObj getSerializedRightHandSide() const final;
virtual bool equivalent(const MatchExpression* other) const;
@@ -182,7 +182,7 @@ public:
virtual void debugString(StringBuilder& debug, int level = 0) const;
- virtual void serialize(BSONObjBuilder* out) const;
+ BSONObj getSerializedRightHandSide() const final;
virtual bool equivalent(const MatchExpression* other) const;
diff --git a/src/mongo/db/matcher/expression_leaf.cpp b/src/mongo/db/matcher/expression_leaf.cpp
index b9a53b72d3e..2bbcd54450d 100644
--- a/src/mongo/db/matcher/expression_leaf.cpp
+++ b/src/mongo/db/matcher/expression_leaf.cpp
@@ -87,8 +87,8 @@ void ComparisonMatchExpressionBase::debugString(StringBuilder& debug, int level)
debug << "\n";
}
-void ComparisonMatchExpressionBase::serialize(BSONObjBuilder* out) const {
- out->append(path(), BSON(name() << _rhs));
+BSONObj ComparisonMatchExpressionBase::getSerializedRightHandSide() const {
+ return BSON(name() << _rhs);
}
ComparisonMatchExpression::ComparisonMatchExpression(MatchType type,
@@ -271,15 +271,15 @@ void RegexMatchExpression::debugString(StringBuilder& debug, int level) const {
debug << "\n";
}
-void RegexMatchExpression::serialize(BSONObjBuilder* out) const {
- BSONObjBuilder regexBuilder(out->subobjStart(path()));
+BSONObj RegexMatchExpression::getSerializedRightHandSide() const {
+ BSONObjBuilder regexBuilder;
regexBuilder.append("$regex", _regex);
if (!_flags.empty()) {
regexBuilder.append("$options", _flags);
}
- regexBuilder.doneFast();
+ return regexBuilder.obj();
}
void RegexMatchExpression::serializeToBSONTypeRegex(BSONObjBuilder* out) const {
@@ -314,8 +314,8 @@ void ModMatchExpression::debugString(StringBuilder& debug, int level) const {
debug << "\n";
}
-void ModMatchExpression::serialize(BSONObjBuilder* out) const {
- out->append(path(), BSON("$mod" << BSON_ARRAY(_divisor << _remainder)));
+BSONObj ModMatchExpression::getSerializedRightHandSide() const {
+ return BSON("$mod" << BSON_ARRAY(_divisor << _remainder));
}
bool ModMatchExpression::equivalent(const MatchExpression* other) const {
@@ -348,8 +348,8 @@ void ExistsMatchExpression::debugString(StringBuilder& debug, int level) const {
debug << "\n";
}
-void ExistsMatchExpression::serialize(BSONObjBuilder* out) const {
- out->append(path(), BSON("$exists" << true));
+BSONObj ExistsMatchExpression::getSerializedRightHandSide() const {
+ return BSON("$exists" << true);
}
bool ExistsMatchExpression::equivalent(const MatchExpression* other) const {
@@ -421,8 +421,8 @@ void InMatchExpression::debugString(StringBuilder& debug, int level) const {
debug << "\n";
}
-void InMatchExpression::serialize(BSONObjBuilder* out) const {
- BSONObjBuilder inBob(out->subobjStart(path()));
+BSONObj InMatchExpression::getSerializedRightHandSide() const {
+ BSONObjBuilder inBob;
BSONArrayBuilder arrBob(inBob.subarrayStart("$in"));
for (auto&& _equality : _equalitySet) {
arrBob.append(_equality);
@@ -433,7 +433,7 @@ void InMatchExpression::serialize(BSONObjBuilder* out) const {
arrBob.append(regexBob.obj().firstElement());
}
arrBob.doneFast();
- inBob.doneFast();
+ return inBob.obj();
}
bool InMatchExpression::equivalent(const MatchExpression* other) const {
@@ -754,7 +754,7 @@ void BitTestMatchExpression::debugString(StringBuilder& debug, int level) const
}
}
-void BitTestMatchExpression::serialize(BSONObjBuilder* out) const {
+BSONObj BitTestMatchExpression::getSerializedRightHandSide() const {
std::string opString = "";
switch (matchType()) {
@@ -780,7 +780,7 @@ void BitTestMatchExpression::serialize(BSONObjBuilder* out) const {
}
arrBob.doneFast();
- out->append(path(), BSON(opString << arrBob.arr()));
+ return BSON(opString << arrBob.arr());
}
bool BitTestMatchExpression::equivalent(const MatchExpression* other) const {
diff --git a/src/mongo/db/matcher/expression_leaf.h b/src/mongo/db/matcher/expression_leaf.h
index c34b5b60343..1c77d23afb1 100644
--- a/src/mongo/db/matcher/expression_leaf.h
+++ b/src/mongo/db/matcher/expression_leaf.h
@@ -107,7 +107,7 @@ public:
virtual void debugString(StringBuilder& debug, int level = 0) const;
- virtual void serialize(BSONObjBuilder* out) const;
+ BSONObj getSerializedRightHandSide() const final;
virtual bool equivalent(const MatchExpression* other) const;
@@ -304,7 +304,7 @@ public:
virtual void debugString(StringBuilder& debug, int level) const;
- virtual void serialize(BSONObjBuilder* out) const;
+ BSONObj getSerializedRightHandSide() const final;
void serializeToBSONTypeRegex(BSONObjBuilder* out) const;
@@ -348,7 +348,7 @@ public:
virtual void debugString(StringBuilder& debug, int level) const;
- virtual void serialize(BSONObjBuilder* out) const;
+ BSONObj getSerializedRightHandSide() const final;
virtual bool equivalent(const MatchExpression* other) const;
@@ -384,7 +384,7 @@ public:
virtual void debugString(StringBuilder& debug, int level) const;
- virtual void serialize(BSONObjBuilder* out) const;
+ BSONObj getSerializedRightHandSide() const final;
virtual bool equivalent(const MatchExpression* other) const;
@@ -407,7 +407,7 @@ public:
virtual void debugString(StringBuilder& debug, int level) const;
- virtual void serialize(BSONObjBuilder* out) const;
+ BSONObj getSerializedRightHandSide() const final;
virtual bool equivalent(const MatchExpression* other) const;
@@ -496,7 +496,7 @@ public:
virtual void debugString(StringBuilder& debug, int level) const;
- virtual void serialize(BSONObjBuilder* out) const;
+ BSONObj getSerializedRightHandSide() const final;
virtual bool equivalent(const MatchExpression* other) const;
diff --git a/src/mongo/db/matcher/expression_parser.cpp b/src/mongo/db/matcher/expression_parser.cpp
index 7435ab97bec..fa24e92eeac 100644
--- a/src/mongo/db/matcher/expression_parser.cpp
+++ b/src/mongo/db/matcher/expression_parser.cpp
@@ -1375,12 +1375,6 @@ StatusWithMatchExpression parseNot(StringData name,
return parseStatus;
}
- for (size_t i = 0; i < theAnd->numChildren(); i++) {
- if (theAnd->getChild(i)->matchType() == MatchExpression::REGEX) {
- return {ErrorCodes::BadValue, "$not cannot have a regex"};
- }
- }
-
return {stdx::make_unique<NotMatchExpression>(theAnd.release())};
}
diff --git a/src/mongo/db/matcher/expression_parser_tree.cpp b/src/mongo/db/matcher/expression_parser_tree.cpp
deleted file mode 100644
index ea064e4236b..00000000000
--- a/src/mongo/db/matcher/expression_parser_tree.cpp
+++ /dev/null
@@ -1,105 +0,0 @@
-// expression_parser_tree.cpp
-
-
-/**
- * Copyright (C) 2018-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * 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
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * 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 Server Side 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/db/matcher/expression_parser.h"
-
-#include "mongo/bson/bsonobj.h"
-#include "mongo/bson/bsonobjbuilder.h"
-#include "mongo/db/matcher/expression_array.h"
-#include "mongo/db/matcher/expression_leaf.h"
-#include "mongo/db/matcher/expression_tree.h"
-#include "mongo/stdx/memory.h"
-#include "mongo/util/mongoutils/str.h"
-
-namespace mongo {
-Status MatchExpressionParser::_parseTreeList(const BSONObj& arr,
- ListOfMatchExpression* out,
- const boost::intrusive_ptr<ExpressionContext>& expCtx,
- AllowedFeatureSet allowedFeatures,
- DocumentParseLevel currentLevel) {
- if (arr.isEmpty())
- return Status(ErrorCodes::BadValue, "$and/$or/$nor must be a nonempty array");
-
- BSONObjIterator i(arr);
- while (i.more()) {
- BSONElement e = i.next();
-
- if (e.type() != Object)
- return Status(ErrorCodes::BadValue, "$or/$and/$nor entries need to be full objects");
-
- StatusWithMatchExpression sub = _parse(e.Obj(), expCtx, allowedFeatures, currentLevel);
- if (!sub.isOK())
- return sub.getStatus();
-
- out->add(sub.getValue().release());
- }
- return Status::OK();
-}
-
-StatusWithMatchExpression MatchExpressionParser::_parseNot(
- const char* name,
- const BSONElement& e,
- const boost::intrusive_ptr<ExpressionContext>& expCtx,
- AllowedFeatureSet allowedFeatures,
- DocumentParseLevel currentLevel) {
- if (e.type() == RegEx) {
- StatusWithMatchExpression s = _parseRegexElement(name, e);
- if (!s.isOK())
- return s;
- std::unique_ptr<NotMatchExpression> n =
- stdx::make_unique<NotMatchExpression>(s.getValue().release());
- return {std::move(n)};
- }
-
- uassert(ErrorCodes::BadValue, "$not needs a regex or a document", e.type() == Object);
-
- BSONObj notObject = e.Obj();
-
- uassert(ErrorCodes::BadValue, "$not cannot be empty", !notObject.isEmpty());
-
- std::unique_ptr<AndMatchExpression> theAnd = stdx::make_unique<AndMatchExpression>();
- Status s = _parseSub(name, notObject, theAnd.get(), expCtx, allowedFeatures, currentLevel);
- if (!s.isOK())
- return StatusWithMatchExpression(s);
-
- // TODO: this seems arbitrary?
- // tested in jstests/not2.js
- for (unsigned i = 0; i < theAnd->numChildren(); i++)
- if (theAnd->getChild(i)->matchType() == MatchExpression::REGEX)
- return StatusWithMatchExpression(ErrorCodes::BadValue, "$not cannot have a regex");
-
- std::unique_ptr<NotMatchExpression> theNot =
- stdx::make_unique<NotMatchExpression>(theAnd.release());
-
- return {std::move(theNot)};
-}
-}
diff --git a/src/mongo/db/matcher/expression_path.h b/src/mongo/db/matcher/expression_path.h
index b6da3387720..748b6f862b4 100644
--- a/src/mongo/db/matcher/expression_path.h
+++ b/src/mongo/db/matcher/expression_path.h
@@ -119,6 +119,18 @@ public:
}
}
+ void serialize(BSONObjBuilder* out) const override {
+ out->append(path(), getSerializedRightHandSide());
+ }
+
+ /**
+ * Returns a BSONObj that represents the right-hand-side of a PathMatchExpression. Used for
+ * serialization of PathMatchExpression in cases where we do not want to serialize the path in
+ * line with the expression. For example {x: {$not: {$eq: 1}}}, where $eq is the
+ * PathMatchExpression.
+ */
+ virtual BSONObj getSerializedRightHandSide() const = 0;
+
protected:
void _doAddDependencies(DepsTracker* deps) const final {
if (!_path.empty()) {
diff --git a/src/mongo/db/matcher/expression_serialization_test.cpp b/src/mongo/db/matcher/expression_serialization_test.cpp
index 5b0cd3beff3..63f238593ae 100644
--- a/src/mongo/db/matcher/expression_serialization_test.cpp
+++ b/src/mongo/db/matcher/expression_serialization_test.cpp
@@ -260,6 +260,47 @@ TEST(SerializeBasic, ExpressionElemMatchValueWithEmptyStringSerializesCorrectly)
ASSERT_EQ(original.matches(obj), reserialized.matches(obj));
}
+TEST(SerializeBasic, ExpressionElemMatchValueWithNotEqualSerializesCorrectly) {
+ boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ Matcher original(fromjson("{x: {$elemMatch: {$ne: 1}}}"),
+ expCtx,
+ ExtensionsCallbackNoop(),
+ MatchExpressionParser::kAllowAllSpecialFeatures);
+ Matcher reserialized(serialize(original.getMatchExpression()),
+ expCtx,
+ ExtensionsCallbackNoop(),
+ MatchExpressionParser::kAllowAllSpecialFeatures);
+ ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{x: {$elemMatch: {$not: {$eq: 1}}}}"));
+ ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
+
+ BSONObj obj = fromjson("{x: [{a: 1, b: -1}, {a: -1, b: 1}]}");
+ ASSERT_EQ(original.matches(obj), reserialized.matches(obj));
+
+ obj = fromjson("{x: [{a: 1, b: 1}, {a: 0, b: 0}]}");
+ ASSERT_EQ(original.matches(obj), reserialized.matches(obj));
+
+ obj = fromjson("{x: [1, 0]}");
+ ASSERT_EQ(original.matches(obj), reserialized.matches(obj));
+}
+
+TEST(SerializeBasic, ExpressionElemMatchValueWithNotLessThanGreaterThanSerializesCorrectly) {
+ boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ Matcher original(fromjson("{x: {$elemMatch: {$not: {$lt: 10, $gt: 5}}}}"),
+ expCtx,
+ ExtensionsCallbackNoop(),
+ MatchExpressionParser::kAllowAllSpecialFeatures);
+ Matcher reserialized(serialize(original.getMatchExpression()),
+ expCtx,
+ ExtensionsCallbackNoop(),
+ MatchExpressionParser::kAllowAllSpecialFeatures);
+ ASSERT_BSONOBJ_EQ(*reserialized.getQuery(),
+ fromjson("{x: {$elemMatch: {$not: {$lt: 10, $gt: 5}}}}"));
+ ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
+
+ auto obj = fromjson("{x: [5]}");
+ ASSERT_EQ(original.matches(obj), reserialized.matches(obj));
+}
+
TEST(SerializeBasic, ExpressionSizeSerializesCorrectly) {
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
Matcher original(fromjson("{x: {$size: 2}}"),
@@ -375,7 +416,7 @@ TEST(SerializeBasic, ExpressionNeSerializesCorrectly) {
expCtx,
ExtensionsCallbackNoop(),
MatchExpressionParser::kAllowAllSpecialFeatures);
- ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{$nor: [{x: {$eq: {a: 1}}}]}"));
+ ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{x: {$not: {$eq: {a: 1}}}}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
BSONObj obj = fromjson("{x: {a: 1}}");
@@ -397,8 +438,8 @@ TEST(SerializeBasic, ExpressionNeWithRegexObjectSerializesCorrectly) {
ExtensionsCallbackNoop(),
MatchExpressionParser::kAllowAllSpecialFeatures);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(),
- BSON("$nor" << BSON_ARRAY(BSON("x" << BSON("$eq" << BSON("$regex"
- << "abc"))))));
+ BSON("x" << BSON("$not" << BSON("$eq" << BSON("$regex"
+ << "abc")))));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
BSONObj obj = fromjson("{x: {a: 1}}");
@@ -629,7 +670,7 @@ TEST(SerializeBasic, ExpressionExistsFalseSerializesCorrectly) {
expCtx,
ExtensionsCallbackNoop(),
MatchExpressionParser::kAllowAllSpecialFeatures);
- ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{$nor: [{x: {$exists: true}}]}"));
+ ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{x: {$not: {$exists: true}}}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
BSONObj obj = fromjson("{x: 1}");
@@ -712,7 +753,7 @@ TEST(SerializeBasic, ExpressionNinSerializesCorrectly) {
expCtx,
ExtensionsCallbackNoop(),
MatchExpressionParser::kAllowAllSpecialFeatures);
- ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{$nor: [{x: {$in: [1, 2, 3]}}]}"));
+ ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{x: {$not: {$in: [1, 2, 3]}}}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
BSONObj obj = fromjson("{x: 1}");
@@ -736,7 +777,7 @@ TEST(SerializeBasic, ExpressionNinWithRegexValueSerializesCorrectly) {
ExtensionsCallbackNoop(),
MatchExpressionParser::kAllowAllSpecialFeatures);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(),
- fromjson("{$nor: [{x: {$in: [/abc/, /def/, /xyz/]}}]}"));
+ fromjson("{x: {$not: {$in: [/abc/, /def/, /xyz/]}}}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
BSONObj obj = fromjson("{x: 'abc'}");
@@ -841,7 +882,7 @@ TEST(SerializeBasic, ExpressionNotSerializesCorrectly) {
expCtx,
ExtensionsCallbackNoop(),
MatchExpressionParser::kAllowAllSpecialFeatures);
- ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{$nor: [{$and: [{x: {$eq: 3}}]}]}"));
+ ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{x: {$not: {$eq: 3}}}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
BSONObj obj = fromjson("{x: 3}");
@@ -861,8 +902,7 @@ TEST(SerializeBasic, ExpressionNotWithMultipleChildrenSerializesCorrectly) {
expCtx,
ExtensionsCallbackNoop(),
MatchExpressionParser::kAllowAllSpecialFeatures);
- ASSERT_BSONOBJ_EQ(*reserialized.getQuery(),
- fromjson("{$nor: [{$and: [{x: {$lt: 1}}, {x: {$gt: 3}}]}]}}"));
+ ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{x: {$not: {$lt: 1, $gt: 3}}}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
BSONObj obj = fromjson("{x: 2}");
@@ -882,8 +922,7 @@ TEST(SerializeBasic, ExpressionNotWithBitTestSerializesCorrectly) {
expCtx,
ExtensionsCallbackNoop(),
MatchExpressionParser::kAllowAllSpecialFeatures);
- ASSERT_BSONOBJ_EQ(*reserialized.getQuery(),
- fromjson("{$nor: [{$and: [{x: {$bitsAnySet: [1, 3]}}]}]}"));
+ ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{x: {$not: {$bitsAnySet: [1, 3]}}}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
BSONObj obj = fromjson("{x: 2}");
@@ -904,8 +943,8 @@ TEST(SerializeBasic, ExpressionNotWithRegexObjSerializesCorrectly) {
ExtensionsCallbackNoop(),
MatchExpressionParser::kAllowAllSpecialFeatures);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(),
- BSON("$nor" << BSON_ARRAY(BSON("x" << BSON("$regex"
- << "a.b")))));
+ BSON("x" << BSON("$not" << BSON("$regex"
+ << "a.b"))));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
BSONObj obj = fromjson("{x: 'abc'}");
@@ -926,8 +965,8 @@ TEST(SerializeBasic, ExpressionNotWithRegexValueSerializesCorrectly) {
ExtensionsCallbackNoop(),
MatchExpressionParser::kAllowAllSpecialFeatures);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(),
- BSON("$nor" << BSON_ARRAY(BSON("x" << BSON("$regex"
- << "a.b")))));
+ BSON("x" << BSON("$not" << BSON("$regex"
+ << "a.b"))));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
BSONObj obj = fromjson("{x: 'abc'}");
@@ -948,10 +987,10 @@ TEST(SerializeBasic, ExpressionNotWithRegexValueAndOptionsSerializesCorrectly) {
ExtensionsCallbackNoop(),
MatchExpressionParser::kAllowAllSpecialFeatures);
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(),
- BSON("$nor" << BSON_ARRAY(BSON("x" << BSON("$regex"
- << "a.b"
- << "$options"
- << "i")))));
+ BSON("x" << BSON("$not" << BSON("$regex"
+ << "a.b"
+ << "$options"
+ << "i"))));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
BSONObj obj = fromjson("{x: 'abc'}");
@@ -973,10 +1012,9 @@ TEST(SerializeBasic, ExpressionNotWithGeoSerializesCorrectly) {
expCtx,
ExtensionsCallbackNoop(),
MatchExpressionParser::kAllowAllSpecialFeatures);
- ASSERT_BSONOBJ_EQ(
- *reserialized.getQuery(),
- fromjson("{$nor: [{$and: [{x: {$geoIntersects: {$geometry: {type: 'Polygon', coordinates: "
- "[[[ 0, 0 ], [5, 0], [5, 5], [0, 5], [0, 0]]]}}}}]}]}"));
+ ASSERT_BSONOBJ_EQ(*reserialized.getQuery(),
+ fromjson("{x: {$not: {$geoIntersects: {$geometry: {type: 'Polygon', "
+ "coordinates: [[[0, 0], [5, 0], [5, 5], [0, 5], [0, 0]]]}}}}}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
BSONObj obj =
diff --git a/src/mongo/db/matcher/expression_text_base.h b/src/mongo/db/matcher/expression_text_base.h
index a31640595f3..422f2686648 100644
--- a/src/mongo/db/matcher/expression_text_base.h
+++ b/src/mongo/db/matcher/expression_text_base.h
@@ -61,6 +61,10 @@ public:
*/
virtual const fts::FTSQuery& getFTSQuery() const = 0;
+ BSONObj getSerializedRightHandSide() const final {
+ MONGO_UNREACHABLE;
+ }
+
//
// Methods inherited from MatchExpression.
//
diff --git a/src/mongo/db/matcher/expression_tree.cpp b/src/mongo/db/matcher/expression_tree.cpp
index 306229765f2..c24749ea7b3 100644
--- a/src/mongo/db/matcher/expression_tree.cpp
+++ b/src/mongo/db/matcher/expression_tree.cpp
@@ -36,6 +36,7 @@
#include "mongo/bson/bsonobj.h"
#include "mongo/bson/bsonobjbuilder.h"
#include "mongo/db/matcher/expression_always_boolean.h"
+#include "mongo/db/matcher/expression_path.h"
namespace mongo {
@@ -320,19 +321,82 @@ void NotMatchExpression::debugString(StringBuilder& debug, int level) const {
_exp->debugString(debug, level + 1);
}
-void NotMatchExpression::serialize(BSONObjBuilder* out) const {
- BSONObjBuilder childBob;
- _exp->serialize(&childBob);
+boost::optional<StringData> NotMatchExpression::getPathIfNotWithSinglePathMatchExpressionTree(
+ MatchExpression* exp) {
+ if (auto pathMatch = dynamic_cast<PathMatchExpression*>(exp)) {
+ return pathMatch->path();
+ }
+
+ if (exp->matchType() == MatchExpression::MatchType::AND && exp->numChildren() > 0) {
+ boost::optional<StringData> path;
+ for (size_t i = 0; i < exp->numChildren(); ++i) {
+ auto pathMatchChild = dynamic_cast<PathMatchExpression*>(exp->getChild(i));
+ if (!pathMatchChild) {
+ return boost::none;
+ }
+
+ if (path && path != pathMatchChild->path()) {
+ return boost::none;
+ } else if (!path) {
+ path = pathMatchChild->path();
+ }
+ }
+
+ invariant(path);
+ return path;
+ }
+
+ return boost::none;
+}
+void NotMatchExpression::serializeNotExpressionToNor(MatchExpression* exp, BSONObjBuilder* out) {
+ BSONObjBuilder childBob;
+ exp->serialize(&childBob);
BSONObj tempObj = childBob.obj();
- // We don't know what the inner object is, and thus whether serializing to $not will result in a
- // parseable MatchExpression. As a fix, we change it to $nor, which is always parseable.
BSONArrayBuilder tBob(out->subarrayStart("$nor"));
tBob.append(tempObj);
tBob.doneFast();
}
+void NotMatchExpression::serialize(BSONObjBuilder* out) const {
+ if (_exp->matchType() == MatchType::AND && _exp->numChildren() == 0) {
+ out->append("$alwaysFalse", 1);
+ return;
+ }
+
+ // When a $not contains an expression that is not a PathMatchExpression tree representing a
+ // single path, we transform to a $nor.
+ // There are trees constructed to represent JSONSchema that require a nor representation to
+ // be valid. Here is an example:
+ // JSONSchema:
+ // {properties: {foo: {type: "string", not: {maxLength: 4}}}}
+ // MatchExpression tree generated:
+ // {foo: {$not: {$or: [{$not: {$_internalSchemaType: [ 2 ]}},
+ // {$_internalSchemaMaxLength: 4}]}}}
+ boost::optional<StringData> path = getPathIfNotWithSinglePathMatchExpressionTree(_exp.get());
+ if (!path) {
+ return serializeNotExpressionToNor(_exp.get(), out);
+ }
+
+ BSONObjBuilder pathBob(out->subobjStart(*path));
+
+ if (_exp->matchType() == MatchType::AND) {
+ BSONObjBuilder notBob(pathBob.subobjStart("$not"));
+ for (size_t x = 0; x < _exp->numChildren(); ++x) {
+ auto* pathMatchExpression = dynamic_cast<PathMatchExpression*>(_exp->getChild(x));
+ invariant(pathMatchExpression);
+ notBob.appendElements(pathMatchExpression->getSerializedRightHandSide());
+ }
+ notBob.doneFast();
+ } else {
+ auto* pathMatchExpression = dynamic_cast<PathMatchExpression*>(_exp.get());
+ invariant(pathMatchExpression);
+ pathBob.append("$not", pathMatchExpression->getSerializedRightHandSide());
+ }
+ pathBob.doneFast();
+}
+
bool NotMatchExpression::equivalent(const MatchExpression* other) const {
if (matchType() != other->matchType())
return false;
diff --git a/src/mongo/db/matcher/expression_tree.h b/src/mongo/db/matcher/expression_tree.h
index d109b7d87a4..c3e4070f275 100644
--- a/src/mongo/db/matcher/expression_tree.h
+++ b/src/mongo/db/matcher/expression_tree.h
@@ -243,6 +243,10 @@ public:
}
private:
+ static boost::optional<StringData> getPathIfNotWithSinglePathMatchExpressionTree(
+ MatchExpression* exp);
+ static void serializeNotExpressionToNor(MatchExpression* exp, BSONObjBuilder* out);
+
ExpressionOptimizerFunc getOptimizer() const final;
std::unique_ptr<MatchExpression> _exp;
diff --git a/src/mongo/db/matcher/expression_type.h b/src/mongo/db/matcher/expression_type.h
index 48d21ed177f..1ee12f85700 100644
--- a/src/mongo/db/matcher/expression_type.h
+++ b/src/mongo/db/matcher/expression_type.h
@@ -78,12 +78,12 @@ public:
debug << "\n";
}
- void serialize(BSONObjBuilder* out) const final {
- BSONObjBuilder subBuilder(out->subobjStart(path()));
+ BSONObj getSerializedRightHandSide() const final {
+ BSONObjBuilder subBuilder;
BSONArrayBuilder arrBuilder(subBuilder.subarrayStart(name()));
_typeSet.toBSONArray(&arrBuilder);
arrBuilder.doneFast();
- subBuilder.doneFast();
+ return subBuilder.obj();
}
bool equivalent(const MatchExpression* other) const final {
diff --git a/src/mongo/db/matcher/matcher.cpp b/src/mongo/db/matcher/matcher.cpp
index 5a24c854cc7..eb558cdab1a 100644
--- a/src/mongo/db/matcher/matcher.cpp
+++ b/src/mongo/db/matcher/matcher.cpp
@@ -48,13 +48,8 @@ Matcher::Matcher(const BSONObj& pattern,
const ExtensionsCallback& extensionsCallback,
const MatchExpressionParser::AllowedFeatureSet allowedFeatures)
: _pattern(pattern) {
- StatusWithMatchExpression statusWithMatcher =
- MatchExpressionParser::parse(pattern, expCtx, extensionsCallback, allowedFeatures);
- uassert(16810,
- mongoutils::str::stream() << "bad query: " << statusWithMatcher.getStatus().toString(),
- statusWithMatcher.isOK());
-
- _expression = std::move(statusWithMatcher.getValue());
+ _expression = uassertStatusOK(
+ MatchExpressionParser::parse(pattern, expCtx, extensionsCallback, allowedFeatures));
}
bool Matcher::matches(const BSONObj& doc, MatchDetails* details) const {
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 86a0f62ee04..d055638f0a7 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
@@ -74,8 +74,8 @@ void InternalSchemaAllElemMatchFromIndexMatchExpression::debugString(StringBuild
_expression->getFilter()->debugString(debug, level + 1);
}
-void InternalSchemaAllElemMatchFromIndexMatchExpression::serialize(BSONObjBuilder* out) const {
- BSONObjBuilder allElemMatchBob(out->subobjStart(path()));
+BSONObj InternalSchemaAllElemMatchFromIndexMatchExpression::getSerializedRightHandSide() const {
+ BSONObjBuilder allElemMatchBob;
BSONArrayBuilder subArray(allElemMatchBob.subarrayStart(kName));
subArray.append(_index);
{
@@ -84,7 +84,7 @@ void InternalSchemaAllElemMatchFromIndexMatchExpression::serialize(BSONObjBuilde
eBuilder.doneFast();
}
subArray.doneFast();
- allElemMatchBob.doneFast();
+ return allElemMatchBob.obj();
}
MatchExpression::ExpressionOptimizerFunc
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.h b/src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.h
index f8ae9e83899..d96a4d58e6a 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.h
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.h
@@ -64,7 +64,7 @@ public:
void debugString(StringBuilder& debug, int level) const final;
- void serialize(BSONObjBuilder* out) const final;
+ BSONObj getSerializedRightHandSide() const final;
bool equivalent(const MatchExpression* other) const final;
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_eq.cpp b/src/mongo/db/matcher/schema/expression_internal_schema_eq.cpp
index 52856a2dddc..414d0167156 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_eq.cpp
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_eq.cpp
@@ -67,10 +67,10 @@ void InternalSchemaEqMatchExpression::debugString(StringBuilder& debug, int leve
debug << "\n";
}
-void InternalSchemaEqMatchExpression::serialize(BSONObjBuilder* out) const {
- BSONObjBuilder eqObj(out->subobjStart(path()));
+BSONObj InternalSchemaEqMatchExpression::getSerializedRightHandSide() const {
+ BSONObjBuilder eqObj;
eqObj.appendAs(_rhsElem, kName);
- eqObj.doneFast();
+ return eqObj.obj();
}
bool InternalSchemaEqMatchExpression::equivalent(const MatchExpression* other) const {
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_eq.h b/src/mongo/db/matcher/schema/expression_internal_schema_eq.h
index 3914e063b26..0c1b73b1089 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_eq.h
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_eq.h
@@ -55,7 +55,7 @@ public:
void debugString(StringBuilder& debug, int level) const final;
- void serialize(BSONObjBuilder* out) const final;
+ BSONObj getSerializedRightHandSide() const final;
bool equivalent(const MatchExpression* other) const final;
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_fmod.cpp b/src/mongo/db/matcher/schema/expression_internal_schema_fmod.cpp
index d427b7a82c8..1ae2e2e5880 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_fmod.cpp
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_fmod.cpp
@@ -74,13 +74,13 @@ void InternalSchemaFmodMatchExpression::debugString(StringBuilder& debug, int le
debug << "\n";
}
-void InternalSchemaFmodMatchExpression::serialize(BSONObjBuilder* out) const {
- BSONObjBuilder objMatchBob(out->subobjStart(path()));
+BSONObj InternalSchemaFmodMatchExpression::getSerializedRightHandSide() const {
+ BSONObjBuilder objMatchBob;
BSONArrayBuilder arrBuilder(objMatchBob.subarrayStart("$_internalSchemaFmod"));
arrBuilder.append(_divisor);
arrBuilder.append(_remainder);
arrBuilder.doneFast();
- objMatchBob.doneFast();
+ return objMatchBob.obj();
}
bool InternalSchemaFmodMatchExpression::equivalent(const MatchExpression* other) const {
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_fmod.h b/src/mongo/db/matcher/schema/expression_internal_schema_fmod.h
index 598381f4d61..44fbcfa1036 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_fmod.h
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_fmod.h
@@ -55,7 +55,7 @@ public:
void debugString(StringBuilder& debug, int level) const final;
- void serialize(BSONObjBuilder* out) const final;
+ BSONObj getSerializedRightHandSide() const final;
bool equivalent(const MatchExpression* other) const final;
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_match_array_index.cpp b/src/mongo/db/matcher/schema/expression_internal_schema_match_array_index.cpp
index 5c3dc40c2bb..bd3fb8d47fa 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
@@ -69,10 +69,10 @@ bool InternalSchemaMatchArrayIndexMatchExpression::equivalent(const MatchExpress
_expression->equivalent(other->_expression.get());
}
-void InternalSchemaMatchArrayIndexMatchExpression::serialize(BSONObjBuilder* builder) const {
- BSONObjBuilder pathSubobj(builder->subobjStart(path()));
+BSONObj InternalSchemaMatchArrayIndexMatchExpression::getSerializedRightHandSide() const {
+ BSONObjBuilder objBuilder;
{
- BSONObjBuilder matchArrayElemSubobj(pathSubobj.subobjStart(kName));
+ BSONObjBuilder matchArrayElemSubobj(objBuilder.subobjStart(kName));
matchArrayElemSubobj.append("index", _index);
matchArrayElemSubobj.append("namePlaceholder", _expression->getPlaceholder().value_or(""));
{
@@ -82,7 +82,7 @@ void InternalSchemaMatchArrayIndexMatchExpression::serialize(BSONObjBuilder* bui
}
matchArrayElemSubobj.doneFast();
}
- pathSubobj.doneFast();
+ return objBuilder.obj();
}
std::unique_ptr<MatchExpression> InternalSchemaMatchArrayIndexMatchExpression::shallowClone()
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_match_array_index.h b/src/mongo/db/matcher/schema/expression_internal_schema_match_array_index.h
index c24b23fd3ff..ce934c3303c 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_match_array_index.h
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_match_array_index.h
@@ -69,7 +69,7 @@ public:
return _expression->matchesBSONElement(element, details);
}
- void serialize(BSONObjBuilder* builder) const final;
+ BSONObj getSerializedRightHandSide() const final;
std::unique_ptr<MatchExpression> shallowClone() const final;
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_num_array_items.cpp b/src/mongo/db/matcher/schema/expression_internal_schema_num_array_items.cpp
index c6411dc3a62..d9f75a28ca1 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
@@ -51,10 +51,10 @@ void InternalSchemaNumArrayItemsMatchExpression::debugString(StringBuilder& debu
debug << "\n";
}
-void InternalSchemaNumArrayItemsMatchExpression::serialize(BSONObjBuilder* out) const {
- BSONObjBuilder subBob(out->subobjStart(path()));
- subBob.append(_name, _numItems);
- subBob.doneFast();
+BSONObj InternalSchemaNumArrayItemsMatchExpression::getSerializedRightHandSide() const {
+ BSONObjBuilder objBuilder;
+ objBuilder.append(_name, _numItems);
+ return objBuilder.obj();
}
bool InternalSchemaNumArrayItemsMatchExpression::equivalent(const MatchExpression* other) const {
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_num_array_items.h b/src/mongo/db/matcher/schema/expression_internal_schema_num_array_items.h
index 3c86f03cf33..2a0b77501bd 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_num_array_items.h
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_num_array_items.h
@@ -49,7 +49,7 @@ public:
void debugString(StringBuilder& debug, int level) const final;
- void serialize(BSONObjBuilder* out) const final;
+ BSONObj getSerializedRightHandSide() const final;
bool equivalent(const MatchExpression* other) const final;
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_object_match.cpp b/src/mongo/db/matcher/schema/expression_internal_schema_object_match.cpp
index 3f31b131526..4efe226106c 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
@@ -58,12 +58,12 @@ void InternalSchemaObjectMatchExpression::debugString(StringBuilder& debug, int
_sub->debugString(debug, level + 1);
}
-void InternalSchemaObjectMatchExpression::serialize(BSONObjBuilder* out) const {
- BSONObjBuilder objMatchBob(out->subobjStart(path()));
+BSONObj InternalSchemaObjectMatchExpression::getSerializedRightHandSide() const {
+ BSONObjBuilder objMatchBob;
BSONObjBuilder subBob(objMatchBob.subobjStart(kName));
_sub->serialize(&subBob);
subBob.doneFast();
- objMatchBob.doneFast();
+ return objMatchBob.obj();
}
bool InternalSchemaObjectMatchExpression::equivalent(const MatchExpression* other) const {
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_object_match.h b/src/mongo/db/matcher/schema/expression_internal_schema_object_match.h
index e74be16e58c..32954cf091e 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_object_match.h
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_object_match.h
@@ -46,7 +46,7 @@ public:
void debugString(StringBuilder& debug, int level = 0) const final;
- void serialize(BSONObjBuilder* out) const final;
+ BSONObj getSerializedRightHandSide() const final;
bool equivalent(const MatchExpression* other) const final;
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_str_length.cpp b/src/mongo/db/matcher/schema/expression_internal_schema_str_length.cpp
index ae657a2ed85..311fa7ff7a8 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
@@ -54,10 +54,10 @@ void InternalSchemaStrLengthMatchExpression::debugString(StringBuilder& debug, i
debug << "\n";
}
-void InternalSchemaStrLengthMatchExpression::serialize(BSONObjBuilder* out) const {
- BSONObjBuilder subBob(out->subobjStart(path()));
- subBob.append(_name, _strLen);
- subBob.doneFast();
+BSONObj InternalSchemaStrLengthMatchExpression::getSerializedRightHandSide() const {
+ BSONObjBuilder objBuilder;
+ objBuilder.append(_name, _strLen);
+ return objBuilder.obj();
}
bool InternalSchemaStrLengthMatchExpression::equivalent(const MatchExpression* other) const {
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_str_length.h b/src/mongo/db/matcher/schema/expression_internal_schema_str_length.h
index 9477cde4d3b..ce6237e66f7 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_str_length.h
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_str_length.h
@@ -61,7 +61,7 @@ public:
void debugString(StringBuilder& debug, int level) const final;
- void serialize(BSONObjBuilder* out) const final;
+ BSONObj getSerializedRightHandSide() const final;
bool equivalent(const MatchExpression* other) const final;
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_unique_items.cpp b/src/mongo/db/matcher/schema/expression_internal_schema_unique_items.cpp
index cda5a0f59e3..4558cc6dafa 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
@@ -59,10 +59,10 @@ bool InternalSchemaUniqueItemsMatchExpression::equivalent(const MatchExpression*
return path() == other->path();
}
-void InternalSchemaUniqueItemsMatchExpression::serialize(BSONObjBuilder* builder) const {
- BSONObjBuilder subobj(builder->subobjStart(path()));
- subobj.append(kName, true);
- subobj.doneFast();
+BSONObj InternalSchemaUniqueItemsMatchExpression::getSerializedRightHandSide() const {
+ BSONObjBuilder bob;
+ bob.append(kName, true);
+ return bob.obj();
}
std::unique_ptr<MatchExpression> InternalSchemaUniqueItemsMatchExpression::shallowClone() const {
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_unique_items.h b/src/mongo/db/matcher/schema/expression_internal_schema_unique_items.h
index df0dddccd8d..a655589d26d 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_unique_items.h
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_unique_items.h
@@ -76,7 +76,7 @@ public:
bool equivalent(const MatchExpression* other) const final;
- void serialize(BSONObjBuilder* builder) const final;
+ BSONObj getSerializedRightHandSide() const final;
std::unique_ptr<MatchExpression> shallowClone() const final;
diff --git a/src/mongo/db/matcher/schema/json_schema_parser_test.cpp b/src/mongo/db/matcher/schema/json_schema_parser_test.cpp
index 8528d88bce1..546b661dcd7 100644
--- a/src/mongo/db/matcher/schema/json_schema_parser_test.cpp
+++ b/src/mongo/db/matcher/schema/json_schema_parser_test.cpp
@@ -115,26 +115,11 @@ TEST(JSONSchemaParserTest, NestedTypeObjectTranslatesCorrectly) {
auto result = JSONSchemaParser::parse(schema);
ASSERT_OK(result.getStatus());
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
- ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"({
- $or: [
- {$nor: [{a: {$exists: true}}]},
- {
- $and: [
- {
- a: {
- $_internalSchemaObjectMatch: {
- $or: [
- {$nor: [{b: {$exists: true}}]},
- {b: {$_internalSchemaType: [2]}}
- ]
- }
- }
- },
- {a: {$_internalSchemaType: [3]}}
- ]
- }
- ]
- })"));
+ ASSERT_SERIALIZES_TO(
+ optimizedResult,
+ fromjson("{$or: [{a: {$not: {$exists: true }}}, {$and: [{a: {$_internalSchemaObjectMatch: "
+ "{$or: [{b: {$not: {$exists: true}}}, {b: {$_internalSchemaType: [2]}}]}}}, {a: "
+ "{$_internalSchemaType: [3]}}]}]}"));
}
TEST(JSONSchemaParserTest, TopLevelNonObjectTypeTranslatesCorrectly) {
@@ -150,12 +135,9 @@ TEST(JSONSchemaParserTest, TypeNumberTranslatesCorrectly) {
auto result = JSONSchemaParser::parse(schema);
ASSERT_OK(result.getStatus());
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
- ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"({
- $or: [
- {$nor: [{num: {$exists: true}}]},
- {num: {$_internalSchemaType: ['number']}}
- ]
- })"));
+ ASSERT_SERIALIZES_TO(optimizedResult,
+ fromjson("{$or: [{num: {$not: {$exists: true }}}, {num: "
+ "{ $_internalSchemaType: [ 'number' ]}}]}"));
}
TEST(JSONSchemaParserTest, MaximumTranslatesCorrectlyWithTypeNumber) {
@@ -163,17 +145,9 @@ TEST(JSONSchemaParserTest, MaximumTranslatesCorrectlyWithTypeNumber) {
auto result = JSONSchemaParser::parse(schema);
ASSERT_OK(result.getStatus());
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
- ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"({
- $or: [
- {$nor: [{num: {$exists: true}}]},
- {
- $and: [
- {num: {$lte: 0 }},
- {num: {$_internalSchemaType: ['number']}}
- ]
- }
- ]
- })"));
+ ASSERT_SERIALIZES_TO(optimizedResult,
+ fromjson("{$or: [{num: {$not: {$exists: true}}}, {$and: [{num: {$lte: "
+ "0}}, {num: {$_internalSchemaType: ['number']}}]}]}"));
}
TEST(JSONSchemaParserTest, MaximumTranslatesCorrectlyWithBsonTypeLong) {
@@ -182,17 +156,9 @@ TEST(JSONSchemaParserTest, MaximumTranslatesCorrectlyWithBsonTypeLong) {
auto result = JSONSchemaParser::parse(schema);
ASSERT_OK(result.getStatus());
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
- ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"({
- $or: [
- {$nor: [{num: {$exists: true}}]},
- {
- $and: [
- {num: {$lte: 0}},
- {num: {$_internalSchemaType: [18]}}
- ]
- }
- ]
- })"));
+ ASSERT_SERIALIZES_TO(optimizedResult,
+ fromjson("{$or: [{num: {$not: {$exists: true}}}, {$and: [{num: {$lte: "
+ "0}}, {num: {$_internalSchemaType: [18]}}]}]}"));
}
TEST(JSONSchemaParserTest, MaximumTranslatesCorrectlyWithTypeString) {
@@ -200,12 +166,9 @@ TEST(JSONSchemaParserTest, MaximumTranslatesCorrectlyWithTypeString) {
auto result = JSONSchemaParser::parse(schema);
ASSERT_OK(result.getStatus());
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
- ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"({
- $or: [
- {$nor: [{num: {$exists: true}}]},
- {num: {$_internalSchemaType: [2]}}
- ]
- })"));
+ ASSERT_SERIALIZES_TO(
+ optimizedResult,
+ fromjson("{$or: [{num: {$not: {$exists: true }}}, {num: {$_internalSchemaType: [2]}}]}"));
}
TEST(JSONSchemaParserTest, MaximumTranslatesCorrectlyWithNoType) {
@@ -214,11 +177,11 @@ TEST(JSONSchemaParserTest, MaximumTranslatesCorrectlyWithNoType) {
ASSERT_OK(result.getStatus());
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"({
- $or: [
- {$nor: [{num: {$exists: true}}]},
- {$nor: [{ num: {$_internalSchemaType: ['number']}}]},
- {num: {$lte: 0}}]
- })"));
+ $or:
+ [{num: {$not: {$exists : true}}},
+ {num: {$not: {$_internalSchemaType: ["number"]}}},
+ {num: {$lte: 0}}]
+ })"));
}
TEST(JSONSchemaParserTest, FailsToParseIfMaximumIsNotANumber) {
@@ -245,16 +208,10 @@ TEST(JSONSchemaParserTest, MinimumTranslatesCorrectlyWithTypeNumber) {
ASSERT_OK(result.getStatus());
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"({
- $or: [
- {$nor: [{num: {$exists: true}}]},
- {
- $and: [
- {num: {$gte: 0}},
- {num: {$_internalSchemaType: ['number']}}
- ]
- }
- ]
- })"));
+ $or:
+ [{num: {$not: {$exists : true}}},
+ {$and: [ {num: {$gte: 0}}, {num: {$_internalSchemaType: ["number"]}}]}]
+ })"));
}
TEST(JSONSchemaParserTest, FailsToParseIfMaxLengthIsNonIntegralDouble) {
@@ -271,16 +228,10 @@ TEST(JSONSchemaParserTest, MaxLengthTranslatesCorrectlyWithIntegralDouble) {
ASSERT_OK(result.getStatus());
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"({
- $or: [
- {$nor: [{foo: {$exists: true}}]},
- {
- $and: [
- {foo: {$_internalSchemaMaxLength: 5}},
- {foo: {$_internalSchemaType: [2]}}
- ]
- }
- ]
- })"));
+ $or:
+ [{foo: {$not: {$exists: true}}},
+ {$and: [ {foo: {$_internalSchemaMaxLength: 5}}, {foo: {$_internalSchemaType: [2]}}]}]
+ })"));
}
TEST(JSONSchemaParserTest, MaxLengthTranslatesCorrectlyWithTypeString) {
@@ -290,18 +241,10 @@ TEST(JSONSchemaParserTest, MaxLengthTranslatesCorrectlyWithTypeString) {
ASSERT_OK(result.getStatus());
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"({
- $or: [
- {
- $nor: [{foo: {$exists: true}}]
- },
- {
- $and: [
- {foo: {$_internalSchemaMaxLength: 5}},
- {foo: { $_internalSchemaType: [2]}}
- ]
- }
- ]
- })"));
+ $or:
+ [{foo: {$not: {$exists : true}}},
+ {$and: [ {foo: {$_internalSchemaMaxLength: 5}}, {foo: {$_internalSchemaType: [2]}}]}]
+ })"));
}
TEST(JSONSchemaParserTest, MinimumTranslatesCorrectlyWithBsonTypeLong) {
@@ -311,16 +254,10 @@ TEST(JSONSchemaParserTest, MinimumTranslatesCorrectlyWithBsonTypeLong) {
ASSERT_OK(result.getStatus());
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"({
- $or: [
- {$nor: [{num: {$exists: true}}]},
- {
- $and: [
- {num: {$gte: 0}},
- { num: { $_internalSchemaType: [18]}}
- ]
- }
- ]
- })"));
+ $or:
+ [{num: {$not: {$exists: true}}},
+ {$and: [ {num: {$gte: 0}}, {num: {$_internalSchemaType: [18]}}]}]
+ })"));
}
TEST(JSONSchemaParserTest, MinimumTranslatesCorrectlyWithTypeString) {
@@ -329,25 +266,23 @@ TEST(JSONSchemaParserTest, MinimumTranslatesCorrectlyWithTypeString) {
ASSERT_OK(result.getStatus());
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"({
- $or: [
- {$nor: [{num: {$exists: true}}]},
- {num: {$_internalSchemaType: [2]}}
- ]
- })"));
+ $or:
+ [{num: {$not: {$exists: true}}}, {num: {$_internalSchemaType: [2]}}]
+ })"));
}
+
TEST(JSONSchemaParserTest, MinimumTranslatesCorrectlyWithNoType) {
BSONObj schema = fromjson("{properties: {num: {minimum: 0}}}");
auto result = JSONSchemaParser::parse(schema);
ASSERT_OK(result.getStatus());
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"({
- $or: [
- {$nor: [{num: {$exists: true}}]},
- {$nor: [{num: {$_internalSchemaType: ['number']}}]},
- {num: {$gte: 0}}
- ]
- })"));
+ $or:
+ [{num: {$not: {$exists: true}}},
+ {num: {$not: {$_internalSchemaType: ["number"]}}},
+ {num: {$gte: 0}}]
+ })"));
}
TEST(JSONSchemaParserTest, MaximumTranslatesCorrectlyWithExclusiveMaximumTrue) {
@@ -358,16 +293,10 @@ TEST(JSONSchemaParserTest, MaximumTranslatesCorrectlyWithExclusiveMaximumTrue) {
ASSERT_OK(result.getStatus());
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"({
- $or: [
- {$nor: [{num: {$exists: true}}]},
- {
- $and: [
- {num: {$lt: 0}},
- {num: {$_internalSchemaType: [18]}}
- ]
- }
- ]
- })"));
+ $or:
+ [{num: {$not: {$exists: true}}},
+ {$and: [ {num: {$lt: 0}}, {num: {$_internalSchemaType: [18]}}]}]
+ })"));
}
TEST(JSONSchemaParserTest, MaximumTranslatesCorrectlyWithExclusiveMaximumFalse) {
@@ -378,16 +307,10 @@ TEST(JSONSchemaParserTest, MaximumTranslatesCorrectlyWithExclusiveMaximumFalse)
ASSERT_OK(result.getStatus());
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"({
- $or: [
- {$nor: [{num: {$exists: true}}]},
- {
- $and: [
- {num: {$lte: 0}},
- {num: {$_internalSchemaType: [18]}}
- ]
- }
- ]
- })"));
+ $or:
+ [{num: {$not: {$exists: true}}},
+ {$and: [ {num: {$lte: 0}}, {num: {$_internalSchemaType: [18]}}]}]
+ })"));
}
TEST(JSONSchemaParserTest, FailsToParseIfExclusiveMaximumIsPresentButMaximumIsNot) {
@@ -410,16 +333,10 @@ TEST(JSONSchemaParserTest, MinimumTranslatesCorrectlyWithExclusiveMinimumTrue) {
ASSERT_OK(result.getStatus());
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"({
- $or: [
- {$nor: [{num: {$exists: true}}]},
- {
- $and: [
- {num: {$gt: 0}},
- {num: {$_internalSchemaType: [18]}}
- ]
- }
- ]
- })"));
+ $or:
+ [{num: {$not: {$exists: true}}},
+ {$and: [ {num: {$gt: 0}}, {num: {$_internalSchemaType: [18]}}]}]
+ })"));
}
TEST(JSONSchemaParserTest, MinimumTranslatesCorrectlyWithExclusiveMinimumFalse) {
@@ -430,16 +347,10 @@ TEST(JSONSchemaParserTest, MinimumTranslatesCorrectlyWithExclusiveMinimumFalse)
ASSERT_OK(result.getStatus());
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"({
- $or: [
- {$nor: [{num: {$exists: true}}]},
- {
- $and: [
- {num: {$gte: 0}},
- {num: {$_internalSchemaType: [18]}}
- ]
- }
- ]
- })"));
+ $or:
+ [{num: {$not: {$exists: true}}},
+ {$and: [ {num: {$gte: 0}}, {num: {$_internalSchemaType: [18]}}]}]
+ })"));
}
TEST(JSONSchemaParserTest, FailsToParseIfExclusiveMinimumIsPresentButMinimumIsNot) {
@@ -480,16 +391,10 @@ TEST(JSONSchemaParserTest, MinLengthTranslatesCorrectlyWithTypeString) {
ASSERT_OK(result.getStatus());
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"({
- $or: [
- {$nor: [{foo: {$exists: true}}]},
- {
- $and: [
- {foo: {$_internalSchemaMinLength: 5}},
- {foo: {$_internalSchemaType: [2]}}
- ]
- }
- ]
- })"));
+ $or:
+ [{foo: {$not: {$exists: true}}},
+ {$and: [ {foo: {$_internalSchemaMinLength: 5}}, {foo: {$_internalSchemaType: [2]}}]}]
+ })"));
}
TEST(JSONSchemaParserTest, MinLengthTranslatesCorrectlyWithIntegralDouble) {
@@ -499,16 +404,10 @@ TEST(JSONSchemaParserTest, MinLengthTranslatesCorrectlyWithIntegralDouble) {
ASSERT_OK(result.getStatus());
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"({
- $or: [
- {$nor: [{foo: {$exists: true}}]},
- {
- $and: [
- {foo: {$_internalSchemaMinLength: 5}},
- {foo: {$_internalSchemaType: [2]}}
- ]
- }
- ]
- })"));
+ $or:
+ [{foo: {$not: {$exists: true}}},
+ {$and: [ {foo: {$_internalSchemaMinLength: 5}}, {foo: {$_internalSchemaType: [2]}}]}]
+ })"));
}
TEST(JSONSchemaParserTest, FailsToParseIfMinimumIsNotANumber) {
@@ -529,9 +428,9 @@ TEST(JSONSchemaParserTest, PatternTranslatesCorrectlyWithString) {
auto result = JSONSchemaParser::parse(schema);
ASSERT_OK(result.getStatus());
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
- BSONObj expected =
+ auto expected =
BSON("$or" << BSON_ARRAY(
- BSON("$nor" << BSON_ARRAY(BSON("foo" << BSON("$exists" << true))))
+ BSON("foo" << BSON("$not" << BSON("$exists" << true)))
<< BSON("$and" << BSON_ARRAY(
BSON("foo" << BSON("$regex"
<< "abc"))
@@ -564,16 +463,14 @@ TEST(JSONSchemaParserTest, MultipleOfTranslatesCorrectlyWithTypeNumber) {
ASSERT_OK(result.getStatus());
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"({
- $or: [
- {$nor: [{foo: {$exists: true}}]},
- {
- $and: [
- {foo: {$_internalSchemaFmod: [NumberDecimal('5.3'), 0]}},
- {foo: {$_internalSchemaType: ['number']}}
- ]
- }
- ]
- })"));
+ $or:
+ [{foo: {$not: {$exists: true}}}, {
+ $and: [
+ {foo: {$_internalSchemaFmod: [ NumberDecimal('5.3'), 0]}},
+ {foo: {$_internalSchemaType: ["number"]}}
+ ]
+ }]
+ })"));
}
TEST(JSONSchemaParserTest, FailsToParseIfAllOfIsNotAnArray) {
@@ -601,26 +498,15 @@ TEST(JSONSchemaParserTest, AllOfTranslatesCorrectly) {
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
auto expectedResult = fromjson(
R"({
- $or: [
- {$nor: [{foo: {$exists: true}}]},
- {
- $and: [
- {
- $or: [
- {$nor: [{foo:{ $_internalSchemaType: ['number']}}]},
- {foo: {$gte: 0}}
- ]
- },
- {
- $or: [
- {$nor: [{foo: {$_internalSchemaType: ['number']}}]},
- {foo: {$lte: 10}}
- ]
- }
- ]
- }
- ]
- })");
+ $or:
+ [{foo: {$not: {$exists: true}}},
+ {
+ $and : [
+ {$or: [ {foo: {$not: {$_internalSchemaType: ["number"]}}}, {foo: {$gte: 0}}]},
+ {$or: [ {foo: {$not: {$_internalSchemaType: ["number"]}}}, {foo: {$lte: 10}}]}
+ ]
+ }]
+ })");
ASSERT_SERIALIZES_TO(optimizedResult, expectedResult);
}
@@ -630,11 +516,9 @@ TEST(JSONSchemaParserTest, TopLevelAllOfTranslatesCorrectly) {
ASSERT_OK(result.getStatus());
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"({
- $or: [
- {$nor: [{foo: {$exists: true}}]},
- {foo: {$_internalSchemaType: [2]}}
- ]
- })"));
+ $or:
+ [{foo: {$not: {$exists: true}}}, {foo: {$_internalSchemaType: [2]}}]
+ })"));
}
TEST(JSONSchemaParserTest, FailsToParseIfAnyOfIsNotAnArray) {
@@ -661,12 +545,11 @@ TEST(JSONSchemaParserTest, AnyOfTranslatesCorrectly) {
ASSERT_OK(result.getStatus());
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"({
- $or: [
- {$nor: [{foo: {$exists: true}}]},
- {foo: {$_internalSchemaType: ['number']}},
- {foo: {$_internalSchemaType: [2]}}
- ]
- })"));
+ $or:
+ [{foo: {$not: {$exists: true}}},
+ {foo: {$_internalSchemaType: ["number"]}},
+ {foo: {$_internalSchemaType: [2]}}]
+ })"));
}
TEST(JSONSchemaParserTest, TopLevelAnyOfTranslatesCorrectly) {
@@ -675,11 +558,9 @@ TEST(JSONSchemaParserTest, TopLevelAnyOfTranslatesCorrectly) {
ASSERT_OK(result.getStatus());
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"({
- $or: [
- {$nor: [{foo: {$exists: true}}]},
- {foo: {$_internalSchemaType: [2]}}
- ]
- })"));
+ $or:
+ [{foo: {$not: {$exists: true}}}, {foo: {$_internalSchemaType: [2]}}]
+ })"));
}
TEST(JSONSchemaParserTest, FailsToParseIfOneOfIsNotAnArray) {
@@ -706,26 +587,15 @@ TEST(JSONSchemaParserTest, OneOfTranslatesCorrectly) {
ASSERT_OK(result.getStatus());
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"({
- $or: [
- {$nor: [{foo: {$exists: true}}]},
- {
- $_internalSchemaXor: [
- {
- $or: [
- {$nor: [{foo: {$_internalSchemaType: ['number']}}]},
- {foo: {$gte: 0}}
- ]
- },
- {
- $or: [
- {$nor: [{foo: {$_internalSchemaType: ['number']}}]},
- {foo: {$lte: 10}}
- ]
- }
- ]
- }
- ]
- })"));
+ $or:
+ [{foo: {$not: {$exists: true}}},
+ {
+ $_internalSchemaXor : [
+ {$or: [ {foo: {$not: {$_internalSchemaType: ["number"]}}}, {foo: {$gte : 0}}]},
+ {$or: [ {foo: {$not: {$_internalSchemaType: ["number"]}}}, {foo: {$lte : 10}}]}
+ ]
+ }]
+ })"));
}
TEST(JSONSchemaParserTest, TopLevelOneOfTranslatesCorrectly) {
@@ -734,11 +604,9 @@ TEST(JSONSchemaParserTest, TopLevelOneOfTranslatesCorrectly) {
ASSERT_OK(result.getStatus());
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"({
- $or: [
- {$nor: [{foo: {$exists: true}}]},
- {foo: {$_internalSchemaType: [2]}}
- ]
- })"));
+ $or:
+ [{foo: {$not: {$exists: true}}}, {foo: {$_internalSchemaType: [2]}}]
+ })"));
}
TEST(JSONSchemaParserTest, FailsToParseIfNotIsNotAnObject) {
@@ -759,11 +627,9 @@ TEST(JSONSchemaParserTest, NotTranslatesCorrectly) {
ASSERT_OK(result.getStatus());
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"({
- $or: [
- {$nor: [{foo: {$exists: true}}]},
- {$nor: [{ foo: {$_internalSchemaType: ['number']}}]}
- ]
- })"));
+ $or:
+ [{foo: {$not: {$exists: true}}}, {foo: {$not: {$_internalSchemaType: ['number']}}}]
+ })"));
}
TEST(JSONSchemaParserTest, TopLevelNotTranslatesCorrectly) {
@@ -772,15 +638,9 @@ TEST(JSONSchemaParserTest, TopLevelNotTranslatesCorrectly) {
ASSERT_OK(result.getStatus());
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"({
- $nor: [
- {
- $or: [
- {$nor: [{foo: {$exists: true}}]},
- {foo: {$_internalSchemaType: [2]}}
- ]
- }
- ]
- })"));
+ $nor:
+ [{$or: [ {foo: {$not: {$exists: true}}}, {foo: {$_internalSchemaType: [2]}}]}]
+ })"));
}
TEST(JSONSchemaParserTest, FailsToParseIfMinItemsIsNotANumber) {
@@ -808,12 +668,11 @@ TEST(JSONSchemaParserTest, MinItemsTranslatesCorrectlyWithNoType) {
ASSERT_OK(result.getStatus());
optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"({
- $or: [
- {$nor: [{a: {$exists: true}}]},
- {$nor: [{a: {$_internalSchemaType: [4]}}]},
- {a: {$_internalSchemaMinItems: 1}}
- ]
- })"));
+ $or:
+ [{a: {$not: {$exists: true}}},
+ {a: {$not: {$_internalSchemaType: [4]}}},
+ {a: {$_internalSchemaMinItems: 1}}]
+ })"));
}
TEST(JSONSchemaParserTest, MinItemsTranslatesCorrectlyWithArrayType) {
@@ -822,16 +681,10 @@ TEST(JSONSchemaParserTest, MinItemsTranslatesCorrectlyWithArrayType) {
ASSERT_OK(result.getStatus());
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"({
- $or: [
- {$nor: [{a: {$exists: true}}]},
- {
- $and: [
- {a: {$_internalSchemaMinItems: 1}},
- {a: {$_internalSchemaType: [4]}}
- ]
- }
- ]
- })"));
+ $or:
+ [{a: {$not: {$exists: true}}},
+ {$and: [ {a: {$_internalSchemaMinItems: 1}}, {a: {$_internalSchemaType: [4]}}]}]
+ })"));
}
TEST(JSONSchemaParserTest, MinItemsTranslatesCorrectlyWithNonArrayType) {
@@ -840,11 +693,9 @@ TEST(JSONSchemaParserTest, MinItemsTranslatesCorrectlyWithNonArrayType) {
ASSERT_OK(result.getStatus());
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"({
- $or: [
- {$nor: [{a: {$exists: true}}]},
- {a: {$_internalSchemaType: ['number']}}
- ]
- })"));
+ $or:
+ [{a: {$not: {$exists: true}}}, {a: {$_internalSchemaType: ["number"]}}]
+ })"));
}
TEST(JSONSchemaParserTest, FailsToParseIfMaxItemsIsNotANumber) {
@@ -872,12 +723,11 @@ TEST(JSONSchemaParserTest, MaxItemsTranslatesCorrectlyWithNoType) {
ASSERT_OK(result.getStatus());
optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"({
- $or: [
- {$nor: [{a: {$exists: true}}]},
- {$nor: [{a: {$_internalSchemaType: [4]}}]},
- {a: {$_internalSchemaMaxItems: 1}}
- ]
- })"));
+ $or:
+ [{a: {$not: {$exists: true}}},
+ {a: {$not: {$_internalSchemaType: [4]}}},
+ {a: {$_internalSchemaMaxItems: 1}}]
+ })"));
}
TEST(JSONSchemaParserTest, MaxItemsTranslatesCorrectlyWithArrayType) {
@@ -886,16 +736,10 @@ TEST(JSONSchemaParserTest, MaxItemsTranslatesCorrectlyWithArrayType) {
ASSERT_OK(result.getStatus());
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"({
- $or: [
- {$nor: [{a: {$exists: true}}]},
- {
- $and: [
- {a: {$_internalSchemaMaxItems: 1}},
- {a: {$_internalSchemaType: [4]}}
- ]
- }
- ]
- })"));
+ $or:
+ [{a: {$not: {$exists: true}}},
+ {$and: [ {a: {$_internalSchemaMaxItems: 1}}, {a: {$_internalSchemaType: [4]}}]}]
+ })"));
}
TEST(JSONSchemaParserTest, MaxItemsTranslatesCorrectlyWithNonArrayType) {
@@ -904,11 +748,9 @@ TEST(JSONSchemaParserTest, MaxItemsTranslatesCorrectlyWithNonArrayType) {
ASSERT_OK(result.getStatus());
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"({
- $or: [
- {$nor: [{a: {$exists: true}}]},
- {a: {$_internalSchemaType: [2]}}
- ]
- })"));
+ $or:
+ [{a: {$not: {$exists : true}}}, {a: {$_internalSchemaType: [2]}}]
+ })"));
}
TEST(JSONSchemaParserTest, RequiredFailsToParseIfNotAnArray) {
@@ -963,21 +805,14 @@ TEST(JSONSchemaParserTest, RequiredTranslatesCorrectlyWithMultipleElements) {
ASSERT_OK(result.getStatus());
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"({
- $or: [
- {$nor: [{x: {$exists: true}}]},
- {$nor: [{x: {$_internalSchemaType: [3]}}]},
- {
- x: {
- $_internalSchemaObjectMatch: {
- $and: [
- {y: {$exists: true}},
- {z: {$exists: true}}
- ]
- }
- }
- }
- ]
- })"));
+ $or:
+ [{x: {$not: {$exists: true}}}, {x: {$not: {$_internalSchemaType: [3]}}}, {
+ x: {
+ $_internalSchemaObjectMatch:
+ {$and: [ {y: {$exists: true}}, {z: {$exists: true}}]}
+ }
+ }]
+ })"));
}
TEST(JSONSchemaParserTest, RequiredTranslatesCorrectlyInsideProperties) {
@@ -986,12 +821,11 @@ TEST(JSONSchemaParserTest, RequiredTranslatesCorrectlyInsideProperties) {
ASSERT_OK(result.getStatus());
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"({
- $or: [
- {$nor: [{x: {$exists: true}}]},
- {$nor: [{x: {$_internalSchemaType: [3]}}]},
- {x: {$_internalSchemaObjectMatch: {y: {$exists: true }}}}
- ]
- })"));
+ $or:
+ [{x: {$not: {$exists: true}}},
+ {x: {$not: {$_internalSchemaType: [3]}}},
+ {x: {$_internalSchemaObjectMatch: {y: {$exists: true}}}}]
+ })"));
}
TEST(JSONSchemaParserTest, RequiredTranslatesCorrectlyInsidePropertiesWithSiblingProperties) {
@@ -1002,31 +836,24 @@ TEST(JSONSchemaParserTest, RequiredTranslatesCorrectlyInsidePropertiesWithSiblin
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
auto expectedResult = fromjson(
R"({
- $or: [
- {$nor: [{x: {$exists: true}}]},
- {
- $and: [
- {
- $or: [
- {$nor: [{x: {$_internalSchemaType: [3]}}]},
- {
- x: {
- $_internalSchemaObjectMatch: {
- y: {$_internalSchemaType: ['number']}
- }
- }
- }
- ]
- },
- {
- $or: [
- {$nor: [{x: {$_internalSchemaType: [3]}}]},
- {x: {$_internalSchemaObjectMatch: {y: {$exists: true}}}}
- ]
- }
- ]
- }
- ]
+ $or:
+ [{x: {$not: {$exists: true}}},
+ {
+ $and: [
+ {
+ $or: [
+ {x: {$not: {$_internalSchemaType: [3]}}},
+ {x: {$_internalSchemaObjectMatch: {y : {$_internalSchemaType: ["number"]}}}}
+ ]
+ },
+ {
+ $or: [
+ {x: {$not: {$_internalSchemaType: [3]}}},
+ {x: {$_internalSchemaObjectMatch: {y: {$exists: true}}}}
+ ]
+ }
+ ]
+ }]
})");
ASSERT_SERIALIZES_TO(optimizedResult, expectedResult);
}
@@ -1150,15 +977,13 @@ TEST(JSONSchemaParserTest, NestedMinPropertiesTranslatesCorrectlyWithoutRequired
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
auto expectedResult = fromjson(R"(
{
- $or: [
- {$nor: [{obj: {$exists: true}}]},
- {
- $and: [
- {obj: {$_internalSchemaObjectMatch: {$_internalSchemaMinProperties: 2}}},
- {obj: {$_internalSchemaType: [3]}}
- ]
- }
- ]
+ $or:
+ [{obj: {$not: {$exists: true}}}, {
+ $and: [
+ {obj: {$_internalSchemaObjectMatch: {$_internalSchemaMinProperties: 2}}},
+ {obj: {$_internalSchemaType: [3]}}
+ ]
+ }]
})");
ASSERT_SERIALIZES_TO(optimizedResult, expectedResult);
}
@@ -1170,15 +995,13 @@ TEST(JSONSchemaParserTest, NestedMaxPropertiesTranslatesCorrectlyWithoutRequired
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
auto expectedResult = fromjson(R"(
{
- $or: [
- {$nor: [{obj: {$exists: true}}]},
- {
- $and: [
- {obj: {$_internalSchemaObjectMatch: {$_internalSchemaMaxProperties: 2}}},
- {obj: {$_internalSchemaType: [3]}}
- ]
- }
- ]
+ $or:
+ [{obj: {$not: {$exists: true}}}, {
+ $and: [
+ {obj: {$_internalSchemaObjectMatch: {$_internalSchemaMaxProperties: 2}}},
+ {obj: {$_internalSchemaType: [3]}}
+ ]
+ }]
})");
ASSERT_SERIALIZES_TO(optimizedResult, expectedResult);
}
@@ -1268,11 +1091,9 @@ TEST(JSONSchemaParserTest, CanTranslateNestedTypeArray) {
ASSERT_OK(result.getStatus());
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"({
- $or: [
- {$nor: [{a: {$exists: true}}]},
- {a: {$_internalSchemaType: ['number', 3]}}
- ]
- })"));
+ $or:
+ [{a: {$not: {$exists: true}}}, {a: {$_internalSchemaType: [ "number", 3 ]}}]
+ })"));
}
TEST(JSONSchemaParserTest, CanTranslateNestedBsonTypeArray) {
@@ -1281,11 +1102,9 @@ TEST(JSONSchemaParserTest, CanTranslateNestedBsonTypeArray) {
ASSERT_OK(result.getStatus());
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"({
- $or: [
- {$nor: [{a: {$exists: true}}]},
- {a: {$_internalSchemaType: ['number', 7]}}
- ]
- })"));
+ $or:
+ [{a: {$not: {$exists: true}}}, {a: {$_internalSchemaType: [ "number", 7 ]}}]
+ })"));
}
TEST(JSONSchemaParserTest, DependenciesFailsToParseIfNotAnObject) {
@@ -1330,17 +1149,11 @@ TEST(JSONSchemaParserTest, TopLevelSchemaDependencyTranslatesCorrectly) {
ASSERT_OK(result.getStatus());
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"({
- $_internalSchemaCond: [
- {a: {$exists: true}},
- {
- $or: [
- {$nor: [{b: {$exists: true}}]},
- {b: {$_internalSchemaType: [2]}}
- ]
- },
- {$alwaysTrue: 1}
- ]
- })"));
+ $_internalSchemaCond:
+ [{a: {$exists: true}},
+ {$or: [ {b: {$not: {$exists: true}}}, {b: {$_internalSchemaType: [2]}}]},
+ {$alwaysTrue: 1}]
+ })"));
}
TEST(JSONSchemaParserTest, TopLevelPropertyDependencyTranslatesCorrectly) {
@@ -1369,31 +1182,29 @@ TEST(JSONSchemaParserTest, NestedSchemaDependencyTranslatesCorrectly) {
ASSERT_OK(result.getStatus());
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"({
- $or: [
- {$nor: [{a: {$exists: true}}]},
- {
- $_internalSchemaCond: [
- {a: {$_internalSchemaObjectMatch: {b: {$exists: true}}}},
- {
- $or: [
- {$nor: [{a: {$_internalSchemaType: [3]}}]},
- {
- a: {
- $_internalSchemaObjectMatch: {
- $or: [
- {$nor: [{c: {$exists: true}}]},
- {c: {$_internalSchemaType: [3]}}
- ]
- }
- }
- }
- ]
- },
- {$alwaysTrue: 1}
+ $or:
+ [{a: {$not: {$exists: true}}}, {
+ $_internalSchemaCond: [
+ {a: {$_internalSchemaObjectMatch : {b: {$exists: true}}}},
+ {
+ $or : [
+ {a: {$not: {$_internalSchemaType: [3]}}},
+ {
+ a: {
+ $_internalSchemaObjectMatch : {
+ $or : [
+ {c: {$not: {$exists: true}}},
+ {c: {$_internalSchemaType: [3]}}
]
}
- ]
- })"));
+ }
+ }
+ ]
+ },
+ {$alwaysTrue : 1}
+ ]
+ }]
+ })"));
}
TEST(JSONSchemaParserTest, NestedPropertyDependencyTranslatesCorrectly) {
@@ -1403,21 +1214,19 @@ TEST(JSONSchemaParserTest, NestedPropertyDependencyTranslatesCorrectly) {
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
auto expectedResult = fromjson(R"(
{
- $or: [
- {$nor: [{a: {$exists: true}}]},
- {
- $_internalSchemaCond: [
- {a: {$_internalSchemaObjectMatch: {b: {$exists: true}}}},
- {
- $and: [
- {a: {$_internalSchemaObjectMatch: {c: {$exists: true}}}},
- {a: {$_internalSchemaObjectMatch: {d: {$exists: true}}}}
- ]
- },
- {$alwaysTrue: 1}
- ]
- }
- ]
+ $or:
+ [{a: {$not: {$exists: true}}}, {
+ $_internalSchemaCond: [
+ {a: {$_internalSchemaObjectMatch : {b : {$exists : true}}}},
+ {
+ $and: [
+ {a: {$_internalSchemaObjectMatch: {c: {$exists: true}}}},
+ {a: {$_internalSchemaObjectMatch: {d: {$exists: true}}}}
+ ]
+ },
+ {$alwaysTrue: 1}
+ ]
+ }]
})");
ASSERT_SERIALIZES_TO(optimizedResult, expectedResult);
}
@@ -1606,23 +1415,20 @@ TEST(JSONSchemaParserTest, NestedAdditionalPropertiesTranslatesCorrectly) {
ASSERT_OK(result.getStatus());
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"({
- $or: [
- {$nor: [{obj: {$exists: true}}]},
- {$nor: [{obj: {$_internalSchemaType: [3]}}]},
- {
- obj: {
- $_internalSchemaObjectMatch: {
- $_internalSchemaAllowedProperties: {
- properties: [],
- namePlaceholder: "i",
- patternProperties: [],
- otherwise: {i: {$_internalSchemaType: ['number']}}
- }
- }
- }
- }
- ]
- })"));
+ $or:
+ [{obj: {$not: {$exists: true}}}, {obj: {$not: {$_internalSchemaType: [3]}}}, {
+ obj: {
+ $_internalSchemaObjectMatch: {
+ $_internalSchemaAllowedProperties: {
+ properties: [],
+ namePlaceholder: "i",
+ patternProperties: [],
+ otherwise: {i: {$_internalSchemaType: ["number"]}}
+ }
+ }
+ }
+ }]
+ })"));
}
TEST(JSONSchemaParserTest,
@@ -1694,12 +1500,11 @@ TEST(JSONSchemaParserTest, UniqueItemsTranslatesCorrectlyWithNoType) {
ASSERT_OK(result.getStatus());
optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"({
- $or: [
- {$nor: [{a: {$exists: true}}]},
- {$nor: [{a: {$_internalSchemaType: [4]}}]},
- {a: {$_internalSchemaUniqueItems: true}}
- ]
- })"));
+ $or:
+ [{a: {$not: {$exists: true}}},
+ {a: {$not: {$_internalSchemaType: [4]}}},
+ {a: {$_internalSchemaUniqueItems: true}}]
+ })"));
}
TEST(JSONSchemaParserTest, UniqueItemsTranslatesCorrectlyWithTypeArray) {
@@ -1708,16 +1513,10 @@ TEST(JSONSchemaParserTest, UniqueItemsTranslatesCorrectlyWithTypeArray) {
ASSERT_OK(result.getStatus());
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"({
- $or: [
- {$nor: [{a: {$exists: true}}]},
- {
- $and: [
- {a: {$_internalSchemaUniqueItems: true}},
- {a: {$_internalSchemaType: [4]}}
- ]
- }
- ]
- })"));
+ $or:
+ [{a: {$not: {$exists: true}}},
+ {$and: [ {a: {$_internalSchemaUniqueItems: true}}, {a: {$_internalSchemaType: [4]}}]}]
+ })"));
}
TEST(JSONSchemaParserTest, CorrectlyIgnoresUnknownKeywordsParameterIsSet) {
@@ -1812,46 +1611,39 @@ TEST(JSONSchemaParserTest, ItemsParsesSuccessfullyAsArrayInNestedSchema) {
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
auto expectedResult = fromjson(R"(
{
- $or: [
- {$nor: [{a: {$exists: true}}]},
- {$nor: [{a: {$_internalSchemaType: [4]}}]},
- {
- $and: [
- {
- a: {
- $_internalSchemaMatchArrayIndex: {
- index: 0,
- namePlaceholder: "i",
- expression: {
- $or: [
- {$nor: [{i: {$_internalSchemaType: [2]}}]},
- {i: {$_internalSchemaMaxLength: 4}}
- ]
- }
- }
- }
- },
- {
- a: {
- $_internalSchemaMatchArrayIndex: {
- index: 1,
- namePlaceholder: "i",
- expression: {
- $or: [
- {
- $nor: [
- {i: {$_internalSchemaType: ['number']}}
- ]
- },
- {i: {$gte: 0}}
- ]
- }
- }
- }
- }
- ]
- }
- ]
+ $or:
+ [{a: {$not: {$exists: true}}}, {a: {$not: {$_internalSchemaType: [4]}}}, {
+ $and: [
+ {
+ a: {
+ $_internalSchemaMatchArrayIndex: {
+ index: 0,
+ namePlaceholder: "i",
+ expression: {
+ $or: [
+ {i: {$not: {$_internalSchemaType: [2]}}},
+ {i: {$_internalSchemaMaxLength: 4}}
+ ]
+ }
+ }
+ }
+ },
+ {
+ a: {
+ $_internalSchemaMatchArrayIndex : {
+ index: 1,
+ namePlaceholder: "i",
+ expression: {
+ $or: [
+ {i : {$not: {$_internalSchemaType: ["number"]}}},
+ {i: {$gte: 0}}
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }]
})");
ASSERT_SERIALIZES_TO(optimizedResult, expectedResult);
}
@@ -1862,19 +1654,11 @@ TEST(JSONSchemaParserTest, ItemsParsesSuccessfullyAsObjectInNestedSchema) {
ASSERT_OK(result.getStatus());
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"({
- $or: [
- {$nor: [{a: {$exists: true}}]},
- {$nor: [{a: {$_internalSchemaType: [4]}}]},
- {
- a: {
- $_internalSchemaAllElemMatchFromIndex: [
- 0,
- {i: {$_internalSchemaType: [2]}}
- ]
- }
- }
- ]
- })"));
+ $or:
+ [{a: {$not: {$exists: true}}},
+ {a: {$not: {$_internalSchemaType: [4]}}},
+ {a: {$_internalSchemaAllElemMatchFromIndex : [ 0, {i: {$_internalSchemaType: [2]}}]}}]
+ })"));
}
TEST(JSONSchemaParserTest, FailsToParseIfAdditionalItemsIsNotAnObjectOrBoolean) {
@@ -1922,11 +1706,10 @@ TEST(JSONSchemaParserTest, AdditionalItemsTranslatesSucessfullyAsBooleanInNested
auto optimizedExpr = MatchExpression::optimize(std::move(expr.getValue()));
auto expectedResult = fromjson(R"(
{
- $or: [
- {$nor: [{a: {$exists: true}}]},
- {$nor: [{a: {$_internalSchemaType: [4]}}]},
- {a: {$_internalSchemaAllElemMatchFromIndex: [0, {$alwaysTrue: 1}]}}
- ]
+ $or:
+ [{a: {$not: {$exists: true}}},
+ {a: {$not: {$_internalSchemaType: [4]}}},
+ {a: {$_internalSchemaAllElemMatchFromIndex: [0, {$alwaysTrue: 1}]}}]
})");
ASSERT_SERIALIZES_TO(optimizedExpr, expectedResult);
@@ -1936,11 +1719,10 @@ TEST(JSONSchemaParserTest, AdditionalItemsTranslatesSucessfullyAsBooleanInNested
optimizedExpr = MatchExpression::optimize(std::move(expr.getValue()));
expectedResult = fromjson(R"(
{
- $or: [
- {$nor: [{a: {$exists: true}}]},
- {$nor: [{a: {$_internalSchemaType: [4]}}]},
- {a: {$_internalSchemaAllElemMatchFromIndex: [0, {$alwaysFalse: 1}]}}
- ]
+ $or:
+ [{a: {$not: {$exists: true}}},
+ {a: {$not: {$_internalSchemaType: [4]}}},
+ {a: {$_internalSchemaAllElemMatchFromIndex: [0, {$alwaysFalse: 1}]}}]
})");
ASSERT_SERIALIZES_TO(optimizedExpr, expectedResult);
}
@@ -1987,23 +1769,17 @@ TEST(JSONSchemaParserTest, AdditionalItemsGeneratesEmptyExpressionIfItemsAnObjec
auto optimizedExpr = MatchExpression::optimize(std::move(expr.getValue()));
auto expectedResult = fromjson(R"(
{
- $or: [
- {$nor: [{a: {$exists: true}}]},
- {$nor: [{a: {$_internalSchemaType: [4]}}]},
- {
- a: {
- $_internalSchemaAllElemMatchFromIndex: [
- 0,
- {
- $or: [
- {$nor: [{i: {$_internalSchemaType: ['number']}}]},
- {i: {$gte: 7}}
- ]
- }
- ]
- }
- }
- ]
+ $or:
+ [{a: {$not: {$exists: true}}},
+ {a: {$not: {$_internalSchemaType: [4]}}},
+ {
+ a: {
+ $_internalSchemaAllElemMatchFromIndex: [
+ 0,
+ {$or: [ {i: {$not: {$_internalSchemaType: ["number"]}}}, {i: {$gte: 7}}]}
+ ]
+ }
+ }]
})");
ASSERT_SERIALIZES_TO(optimizedExpr, expectedResult);
@@ -2013,23 +1789,17 @@ TEST(JSONSchemaParserTest, AdditionalItemsGeneratesEmptyExpressionIfItemsAnObjec
optimizedExpr = MatchExpression::optimize(std::move(expr.getValue()));
expectedResult = fromjson(R"(
{
- $or: [
- {$nor: [{a: {$exists: true}}]},
- {$nor: [{a: {$_internalSchemaType: [4]}}]},
- {
- a: {
- $_internalSchemaAllElemMatchFromIndex: [
- 0,
- {
- $or: [
- {$nor: [{i: {$_internalSchemaType: ['number']}}]},
- {i: {$gte: 7}}
- ]
- }
- ]
- }
- }
- ]
+ $or:
+ [{a: {$not: {$exists: true}}},
+ {a: {$not: {$_internalSchemaType: [4]}}},
+ {
+ a: {
+ $_internalSchemaAllElemMatchFromIndex : [
+ 0,
+ {$or: [ {i: {$not: {$_internalSchemaType: ["number"]}}}, {i: {$gte: 7}}]}
+ ]
+ }
+ }]
})");
ASSERT_SERIALIZES_TO(optimizedExpr, expectedResult);
}
@@ -2062,13 +1832,12 @@ TEST(JSONSchemaParserTest, EnumTranslatesCorrectly) {
ASSERT_OK(result.getStatus());
auto optimizedResult = MatchExpression::optimize(std::move(result.getValue()));
ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"({
- $or: [
- {$nor: [{foo: {$exists: true}}]},
- {foo: {$_internalSchemaEq: 1}},
- {foo: {$_internalSchemaEq: "2"}},
- {foo: {$_internalSchemaEq: [3]}}
- ]
- })"));
+ $or:
+ [{foo: {$not: {$exists: true}}},
+ {foo: {$_internalSchemaEq: 1}},
+ {foo: {$_internalSchemaEq: "2"}},
+ {foo: {$_internalSchemaEq: [3]}}]
+ })"));
}
TEST(JSONSchemaParserTest, TopLevelEnumTranslatesCorrectly) {
diff --git a/src/mongo/db/pipeline/pipeline_test.cpp b/src/mongo/db/pipeline/pipeline_test.cpp
index 126507b455e..5dfd2825050 100644
--- a/src/mongo/db/pipeline/pipeline_test.cpp
+++ b/src/mongo/db/pipeline/pipeline_test.cpp
@@ -690,10 +690,15 @@ TEST(PipelineOptimizationTest, MatchWithNorOnlySplitsIndependentChildren) {
"[{$unwind: {path: '$a'}}, "
"{$match: {$nor: [{$and: [{a: {$eq: 1}}, {b: {$eq: 1}}]}, {b: {$eq: 2}} ]}}]";
string outputPipe =
- "[{$match: {$nor: [{b: {$eq: 2}}]}}, "
- "{$unwind: {path: '$a'}}, "
- "{$match: {$nor: [{$and: [{a: {$eq: 1}}, {b: {$eq: 1}}]}]}}]";
- assertPipelineOptimizesTo(inputPipe, outputPipe);
+ R"(
+ [{$match: {b: {$not: {$eq: 2}}}},
+ {$unwind: {path: '$a'}},
+ {$match: {$nor: [{$and: [{a: {$eq: 1}}, {b: {$eq: 1}}]}]}}])";
+ string serializedPipe = R"(
+ [{$match: {$nor: [{b: {$eq: 2}}]}},
+ {$unwind: {path: '$a'}},
+ {$match: {$nor: [{$and: [{a: {$eq: 1}}, {b: {$eq: 1}}]}]}}])";
+ assertPipelineOptimizesAndSerializesTo(inputPipe, outputPipe, serializedPipe);
}
TEST(PipelineOptimizationTest, MatchWithOrDoesNotSplit) {
@@ -951,10 +956,15 @@ TEST(PipelineOptimizationTest, NorCanSplitAcrossProjectWithRename) {
"[{$project: {_id: false, x: true, y: '$z'}},"
"{$match: {$nor: [{w: {$eq: 1}}, {y: {$eq: 1}}]}}]";
string outputPipe =
- "[{$match: {$nor: [{z: {$eq: 1}}]}},"
- "{$project: {_id: false, x: true, y: '$z'}},"
- "{$match: {$nor: [{w: {$eq: 1}}]}}]";
- assertPipelineOptimizesTo(inputPipe, outputPipe);
+ R"([{$match: {z : {$not: {$eq: 1}}}},
+ {$project: {_id: false, x: true, y: "$z"}},
+ {$match: {w: {$not: {$eq: 1}}}}])";
+ string serializedPipe = R"(
+ [{$match: {$nor: [ {z : {$eq: 1}}]}},
+ {$project: {_id: false, x: true, y: "$z"}},
+ {$match: {$nor: [ {w: {$eq: 1}}]}}]
+ )";
+ assertPipelineOptimizesAndSerializesTo(inputPipe, outputPipe, serializedPipe);
}
TEST(PipelineOptimizationTest, MatchCanMoveAcrossSeveralRenames) {
@@ -969,7 +979,13 @@ TEST(PipelineOptimizationTest, MatchCanMoveAcrossSeveralRenames) {
"{$match: {z: {$eq: 2}}},"
"{$addFields: {b: '$c'}},"
"{$project: {_id: true, z: true, a: '$b'}}]";
- assertPipelineOptimizesTo(inputPipe, outputPipe);
+ string serializedPipe = R"(
+ [{$match: {d : {$eq: 1}}},
+ {$project: {_id: false, c: "$d"}},
+ {$match: {z : {$eq: 2}}},
+ {$addFields: {b: "$c"}},
+ {$project: {_id: true, z: true, a: "$b"}}])";
+ assertPipelineOptimizesAndSerializesTo(inputPipe, outputPipe, serializedPipe);
}
TEST(PipelineOptimizationTest, RenameShouldNotBeAppliedToDependentMatch) {