diff options
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) { |