summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mongo/db/exec/geo_near.cpp4
-rw-r--r--src/mongo/db/matcher/expression_algo_test.cpp11
-rw-r--r--src/mongo/db/matcher/expression_array.cpp13
-rw-r--r--src/mongo/db/matcher/expression_array.h6
-rw-r--r--src/mongo/db/matcher/expression_geo.cpp14
-rw-r--r--src/mongo/db/matcher/expression_geo.h4
-rw-r--r--src/mongo/db/matcher/expression_leaf.cpp28
-rw-r--r--src/mongo/db/matcher/expression_leaf.h12
-rw-r--r--src/mongo/db/matcher/expression_parser_tree.cpp102
-rw-r--r--src/mongo/db/matcher/expression_path.h12
-rw-r--r--src/mongo/db/matcher/expression_serialization_test.cpp84
-rw-r--r--src/mongo/db/matcher/expression_text_base.h4
-rw-r--r--src/mongo/db/matcher/expression_tree.cpp74
-rw-r--r--src/mongo/db/matcher/expression_tree.h4
-rw-r--r--src/mongo/db/matcher/expression_type.h14
-rw-r--r--src/mongo/db/matcher/matcher.cpp9
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.cpp6
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.h2
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_eq.cpp6
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_eq.h2
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_fmod.cpp6
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_fmod.h2
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_match_array_index.cpp8
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_match_array_index.h2
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_num_array_items.cpp8
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_num_array_items.h2
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_object_match.cpp6
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_object_match.h2
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_str_length.cpp8
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_str_length.h2
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_unique_items.cpp8
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_unique_items.h2
-rw-r--r--src/mongo/db/matcher/schema/json_schema_parser_test.cpp968
-rw-r--r--src/mongo/db/pipeline/pipeline_test.cpp34
-rw-r--r--src/mongo/embedded/stitch_support/stitch_support_test.cpp18
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.
}