diff options
35 files changed, 644 insertions, 843 deletions
diff --git a/src/mongo/db/exec/geo_near.cpp b/src/mongo/db/exec/geo_near.cpp index 2e54cab9822..168552ef471 100644 --- a/src/mongo/db/exec/geo_near.cpp +++ b/src/mongo/db/exec/geo_near.cpp @@ -516,6 +516,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 b9c59595520..372f9e2694f 100644 --- a/src/mongo/db/matcher/expression_algo_test.cpp +++ b/src/mongo/db/matcher/expression_algo_test.cpp @@ -868,7 +868,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); } @@ -912,13 +912,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 0b3c53aa893..42e29934623 100644 --- a/src/mongo/db/matcher/expression_array.cpp +++ b/src/mongo/db/matcher/expression_array.cpp @@ -100,10 +100,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 { @@ -175,7 +175,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++) { @@ -184,7 +184,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 { @@ -223,8 +224,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 6aff784d77a..bab018abfc2 100644 --- a/src/mongo/db/matcher/expression_array.h +++ b/src/mongo/db/matcher/expression_array.h @@ -86,7 +86,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; @@ -141,7 +141,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; @@ -192,7 +192,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 a56422dfbd2..0530686631f 100644 --- a/src/mongo/db/matcher/expression_geo.cpp +++ b/src/mongo/db/matcher/expression_geo.cpp @@ -386,10 +386,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 { @@ -444,10 +444,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 572607f5010..c23e87d9271 100644 --- a/src/mongo/db/matcher/expression_geo.h +++ b/src/mongo/db/matcher/expression_geo.h @@ -88,7 +88,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; @@ -179,7 +179,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 08f4214ef6d..52d19d188c9 100644 --- a/src/mongo/db/matcher/expression_leaf.cpp +++ b/src/mongo/db/matcher/expression_leaf.cpp @@ -86,8 +86,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, @@ -289,15 +289,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 { @@ -332,8 +332,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 { @@ -366,8 +366,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 { @@ -439,8 +439,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); @@ -451,7 +451,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 { @@ -778,7 +778,7 @@ void BitTestMatchExpression::debugString(StringBuilder& debug, int level) const } } -void BitTestMatchExpression::serialize(BSONObjBuilder* out) const { +BSONObj BitTestMatchExpression::getSerializedRightHandSide() const { std::string opString = ""; switch (matchType()) { @@ -804,7 +804,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 50bfd3c4860..66c25dcf1c8 100644 --- a/src/mongo/db/matcher/expression_leaf.h +++ b/src/mongo/db/matcher/expression_leaf.h @@ -106,7 +106,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; @@ -303,7 +303,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; @@ -347,7 +347,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; @@ -383,7 +383,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; @@ -406,7 +406,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; @@ -495,7 +495,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_tree.cpp b/src/mongo/db/matcher/expression_parser_tree.cpp deleted file mode 100644 index bb47a469918..00000000000 --- a/src/mongo/db/matcher/expression_parser_tree.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/** - * 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 0eccdeb56fd..274aa16d57c 100644 --- a/src/mongo/db/matcher/expression_path.h +++ b/src/mongo/db/matcher/expression_path.h @@ -118,6 +118,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 80d3fa9e39f..d428850e8a5 100644 --- a/src/mongo/db/matcher/expression_serialization_test.cpp +++ b/src/mongo/db/matcher/expression_serialization_test.cpp @@ -259,6 +259,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}}"), @@ -374,7 +415,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}}"); @@ -396,8 +437,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}}"); @@ -628,7 +669,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}"); @@ -711,7 +752,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}"); @@ -735,7 +776,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'}"); @@ -840,7 +881,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}"); @@ -860,8 +901,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}"); @@ -881,8 +921,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}"); @@ -903,8 +942,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'}"); @@ -925,8 +964,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'}"); @@ -947,10 +986,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'}"); @@ -972,10 +1011,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 17fa880e386..507bf4d6acc 100644 --- a/src/mongo/db/matcher/expression_text_base.h +++ b/src/mongo/db/matcher/expression_text_base.h @@ -60,6 +60,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 f569b0f8f47..c7c2d3ccb27 100644 --- a/src/mongo/db/matcher/expression_tree.cpp +++ b/src/mongo/db/matcher/expression_tree.cpp @@ -33,6 +33,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 { @@ -317,19 +318,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 d9b30aa4e19..35f9e8bc343 100644 --- a/src/mongo/db/matcher/expression_tree.h +++ b/src/mongo/db/matcher/expression_tree.h @@ -240,6 +240,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 d13352afd37..d3e66580092 100644 --- a/src/mongo/db/matcher/expression_type.h +++ b/src/mongo/db/matcher/expression_type.h @@ -77,12 +77,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 { @@ -197,10 +197,10 @@ public: debug << "\n"; } - void serialize(BSONObjBuilder* out) const final { - BSONObjBuilder subBuilder(out->subobjStart(path())); - subBuilder.append(name(), _binDataSubType); - subBuilder.doneFast(); + BSONObj getSerializedRightHandSide() const final { + BSONObjBuilder bob; + bob.append(name(), _binDataSubType); + return bob.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 28107d43a69..d51ed4b2061 100644 --- a/src/mongo/db/matcher/matcher.cpp +++ b/src/mongo/db/matcher/matcher.cpp @@ -45,13 +45,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 d36f6cbb2ee..2b8d2a7b76d 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 @@ -73,8 +73,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); { @@ -83,7 +83,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 bb4aa65e393..c56884b96bb 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 @@ -63,7 +63,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 c611ba7e298..9f7db2244dd 100644 --- a/src/mongo/db/matcher/schema/expression_internal_schema_eq.cpp +++ b/src/mongo/db/matcher/schema/expression_internal_schema_eq.cpp @@ -66,10 +66,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 bc9266aa6b9..7d6406da36c 100644 --- a/src/mongo/db/matcher/schema/expression_internal_schema_eq.h +++ b/src/mongo/db/matcher/schema/expression_internal_schema_eq.h @@ -54,7 +54,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 4c11200de89..71e954a930f 100644 --- a/src/mongo/db/matcher/schema/expression_internal_schema_fmod.cpp +++ b/src/mongo/db/matcher/schema/expression_internal_schema_fmod.cpp @@ -73,13 +73,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 59ace4f87fa..76fb8670c17 100644 --- a/src/mongo/db/matcher/schema/expression_internal_schema_fmod.h +++ b/src/mongo/db/matcher/schema/expression_internal_schema_fmod.h @@ -54,7 +54,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 ae3a14c2360..7c929a08910 100644 --- a/src/mongo/db/matcher/schema/expression_internal_schema_match_array_index.cpp +++ b/src/mongo/db/matcher/schema/expression_internal_schema_match_array_index.cpp @@ -68,10 +68,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("")); { @@ -81,7 +81,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 634d77d9212..89958d3fb82 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 @@ -68,7 +68,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 6ec88dab4b9..dd4281890d7 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 @@ -50,10 +50,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 974d3b34f41..930369c6241 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 @@ -48,7 +48,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 e3e0da0db4b..ab36f871ef3 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 @@ -57,12 +57,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 bde29919db8..86d80870a97 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 @@ -45,7 +45,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 c7be28624c7..0bf8e45caf7 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 @@ -53,10 +53,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 19599a10877..3fbdd83c1dd 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 @@ -60,7 +60,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 1ca21a24cf5..2854d58c5d5 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 @@ -58,10 +58,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 7193f066838..f9e9527e6f1 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 @@ -75,7 +75,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 cd7221f9b64..5a7ad265bdb 100644 --- a/src/mongo/db/matcher/schema/json_schema_parser_test.cpp +++ b/src/mongo/db/matcher/schema/json_schema_parser_test.cpp @@ -114,26 +114,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) { @@ -149,12 +134,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) { @@ -162,17 +144,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) { @@ -181,17 +155,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) { @@ -199,12 +165,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) { @@ -213,11 +176,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) { @@ -244,16 +207,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) { @@ -270,16 +227,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) { @@ -289,18 +240,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) { @@ -310,16 +253,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) { @@ -328,25 +265,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) { @@ -357,16 +292,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) { @@ -377,16 +306,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) { @@ -409,16 +332,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) { @@ -429,16 +346,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) { @@ -479,16 +390,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) { @@ -498,16 +403,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) { @@ -528,9 +427,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")) @@ -563,16 +462,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) { @@ -600,26 +497,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); } @@ -629,11 +515,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) { @@ -660,12 +544,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) { @@ -674,11 +557,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) { @@ -705,26 +586,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) { @@ -733,11 +603,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) { @@ -758,11 +626,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) { @@ -771,15 +637,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) { @@ -807,12 +667,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) { @@ -821,16 +680,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) { @@ -839,11 +692,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) { @@ -871,12 +722,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) { @@ -885,16 +735,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) { @@ -903,11 +747,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) { @@ -962,21 +804,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) { @@ -985,12 +820,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) { @@ -1001,31 +835,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); } @@ -1149,15 +976,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); } @@ -1169,15 +994,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); } @@ -1267,11 +1090,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) { @@ -1280,11 +1101,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) { @@ -1329,17 +1148,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) { @@ -1368,31 +1181,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) { @@ -1402,21 +1213,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); } @@ -1605,23 +1414,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, @@ -1693,12 +1499,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) { @@ -1707,16 +1512,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) { @@ -1811,46 +1610,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); } @@ -1861,19 +1653,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) { @@ -1921,11 +1705,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); @@ -1935,11 +1718,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); } @@ -1986,23 +1768,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); @@ -2012,23 +1788,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); } @@ -2061,13 +1831,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) { @@ -2093,15 +1862,11 @@ TEST(JSONSchemaParserTest, EncryptTranslatesCorrectly) { auto optimizedResult = MatchExpression::optimize(std::move(result.getValue())); ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"( { - $or: [ - {$nor: [{foo: {$exists: true}}]}, - {$and: [ - {foo: {$_internalSchemaBinDataSubType: 6}}, - {foo: {$_internalSchemaType: [5]}} - ] - } - ] - } + $or: + [{foo: {$not: {$exists: true}}}, { + $and: + [ {foo: {$_internalSchemaBinDataSubType: 6}}, {foo: {$_internalSchemaType: [5]}} ] + }] })")); } @@ -2121,24 +1886,27 @@ TEST(JSONSchemaParserTest, NestedEncryptTranslatesCorrectly) { 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}}]}, - {$and: [ - {b: {$_internalSchemaBinDataSubType: 6}}, - {b: {$_internalSchemaType: [5]}} - ] - } + $or: + [{a: {$not: {$exists: true}}}, { + $and: [ + { + a: { + $_internalSchemaObjectMatch: { + $or: [ + {b: {$not : {$exists: true}}}, + { + $and: [ + {b: {$_internalSchemaBinDataSubType: 6}}, + {b: {$_internalSchemaType: [5]}} ] - } - } - }, - {a: {$_internalSchemaType: [3]}} - ] - } - ] + } + ] + } + } + }, + {a: {$_internalSchemaType: [3]}} + ] + }] })")); } @@ -2149,25 +1917,25 @@ TEST(JSONSchemaParserTest, NestedEncryptInArrayTranslatesCorrectly) { auto optimizedResult = MatchExpression::optimize(std::move(result.getValue())); ASSERT_SERIALIZES_TO(optimizedResult, fromjson(R"( { - $or: [ - {$nor: [{a: {$exists: true}}]}, - { + $or: + [{a: {$not: {$exists: true}}}, { $and: [ { - a: { - $_internalSchemaAllElemMatchFromIndex: [ - 0, - {$and: [ + a: { + $_internalSchemaAllElemMatchFromIndex : [ + 0, + { + $and: [ {i: {$_internalSchemaBinDataSubType: 6}}, - {i: {$_internalSchemaType: [5]}}] - } - ] - } + {i: {$_internalSchemaType: [5]}} + ] + } + ] + } }, {a: {$_internalSchemaType: [4]}} ] - } - ] + }] })")); } diff --git a/src/mongo/db/pipeline/pipeline_test.cpp b/src/mongo/db/pipeline/pipeline_test.cpp index 0c3d87617c1..adbf542e2e3 100644 --- a/src/mongo/db/pipeline/pipeline_test.cpp +++ b/src/mongo/db/pipeline/pipeline_test.cpp @@ -733,10 +733,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) { @@ -994,10 +999,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) { @@ -1012,7 +1022,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) { diff --git a/src/mongo/embedded/stitch_support/stitch_support_test.cpp b/src/mongo/embedded/stitch_support/stitch_support_test.cpp index 6092100dac6..416e1920169 100644 --- a/src/mongo/embedded/stitch_support/stitch_support_test.cpp +++ b/src/mongo/embedded/stitch_support/stitch_support_test.cpp @@ -375,18 +375,16 @@ TEST_F(StitchSupportTest, CheckMatchWorksWithDefaults) { } TEST_F(StitchSupportTest, CheckMatchWorksWithStatus) { - ASSERT_EQ("bad query: BadValue: unknown operator: $bogus", - checkMatchStatus("{a: {$bogus: 1}}", "{a: 1}")); - ASSERT_EQ("bad query: BadValue: $where is not allowed in this context", + ASSERT_EQ("unknown operator: $bogus", checkMatchStatus("{a: {$bogus: 1}}", "{a: 1}")); + ASSERT_EQ("$where is not allowed in this context", checkMatchStatus("{$where: 'this.a == 1'}", "{a: 1}")); - ASSERT_EQ("bad query: BadValue: $text is not allowed in this context", + ASSERT_EQ("$text is not allowed in this context", checkMatchStatus("{$text: {$search: 'stitch'}}", "{a: 'stitch lib'}")); - ASSERT_EQ( - "bad query: BadValue: $geoNear, $near, and $nearSphere are not allowed in this context", - checkMatchStatus( - "{location: {$near: {$geometry: {type: 'Point', " - "coordinates: [ -73.9667, 40.78 ] }, $minDistance: 10, $maxDistance: 500}}}", - "{type: 'Point', 'coordinates': [100.0, 0.0]}")); + ASSERT_EQ("$geoNear, $near, and $nearSphere are not allowed in this context", + checkMatchStatus( + "{location: {$near: {$geometry: {type: 'Point', " + "coordinates: [ -73.9667, 40.78 ] }, $minDistance: 10, $maxDistance: 500}}}", + "{type: 'Point', 'coordinates': [100.0, 0.0]}")); // 'check_match' cannot actually fail so we do not test it with a status. } |