diff options
author | Benjamin Murphy <benjamin_murphy@me.com> | 2016-02-25 10:19:29 -0500 |
---|---|---|
committer | Benjamin Murphy <benjamin_murphy@me.com> | 2016-03-14 18:03:04 -0400 |
commit | 488f48f2f497a4e844aa3057e49f96d2a4be1bcb (patch) | |
tree | b73cd307dcfeb06e241d63195df243314b8fb91e | |
parent | 4b6952e97e74d8c7bd16ebfc5fe6e412ccf0f48c (diff) | |
download | mongo-488f48f2f497a4e844aa3057e49f96d2a4be1bcb.tar.gz |
SERVER-22833 Rename MatchExpression::toBSON() to serialize(), and ensure it produces parseable output.
28 files changed, 910 insertions, 91 deletions
diff --git a/src/mongo/db/exec/collection_scan.cpp b/src/mongo/db/exec/collection_scan.cpp index 696a05a71ea..e6ead6b431c 100644 --- a/src/mongo/db/exec/collection_scan.cpp +++ b/src/mongo/db/exec/collection_scan.cpp @@ -234,7 +234,7 @@ unique_ptr<PlanStageStats> CollectionScan::getStats() { // Add a BSON representation of the filter to the stats tree, if there is one. if (NULL != _filter) { BSONObjBuilder bob; - _filter->toBSON(&bob); + _filter->serialize(&bob); _commonStats.filter = bob.obj(); } diff --git a/src/mongo/db/exec/fetch.cpp b/src/mongo/db/exec/fetch.cpp index 79b70ceba83..d2309d1217b 100644 --- a/src/mongo/db/exec/fetch.cpp +++ b/src/mongo/db/exec/fetch.cpp @@ -216,7 +216,7 @@ unique_ptr<PlanStageStats> FetchStage::getStats() { // Add a BSON representation of the filter to the stats tree, if there is one. if (NULL != _filter) { BSONObjBuilder bob; - _filter->toBSON(&bob); + _filter->serialize(&bob); _commonStats.filter = bob.obj(); } diff --git a/src/mongo/db/exec/geo_near.cpp b/src/mongo/db/exec/geo_near.cpp index bfad1618b62..f15ab7ac59c 100644 --- a/src/mongo/db/exec/geo_near.cpp +++ b/src/mongo/db/exec/geo_near.cpp @@ -462,7 +462,7 @@ public: initPath(twoDPath); } - void toBSON(BSONObjBuilder* out) const final { + void serialize(BSONObjBuilder* out) const final { out->append("TwoDPtInAnnulusExpression", true); } diff --git a/src/mongo/db/exec/index_scan.cpp b/src/mongo/db/exec/index_scan.cpp index 4e7410eeb13..de3f8d8f327 100644 --- a/src/mongo/db/exec/index_scan.cpp +++ b/src/mongo/db/exec/index_scan.cpp @@ -277,7 +277,7 @@ std::unique_ptr<PlanStageStats> IndexScan::getStats() { // Add a BSON representation of the filter to the stats tree, if there is one. if (NULL != _filter) { BSONObjBuilder bob; - _filter->toBSON(&bob); + _filter->serialize(&bob); _commonStats.filter = bob.obj(); } diff --git a/src/mongo/db/exec/or.cpp b/src/mongo/db/exec/or.cpp index 327ec1b31fd..f9d51880c2f 100644 --- a/src/mongo/db/exec/or.cpp +++ b/src/mongo/db/exec/or.cpp @@ -143,7 +143,7 @@ unique_ptr<PlanStageStats> OrStage::getStats() { // Add a BSON representation of the filter to the stats tree, if there is one. if (NULL != _filter) { BSONObjBuilder bob; - _filter->toBSON(&bob); + _filter->serialize(&bob); _commonStats.filter = bob.obj(); } diff --git a/src/mongo/db/exec/text_or.cpp b/src/mongo/db/exec/text_or.cpp index 23011388369..195af913a99 100644 --- a/src/mongo/db/exec/text_or.cpp +++ b/src/mongo/db/exec/text_or.cpp @@ -115,7 +115,7 @@ std::unique_ptr<PlanStageStats> TextOrStage::getStats() { if (_filter) { BSONObjBuilder bob; - _filter->toBSON(&bob); + _filter->serialize(&bob); _commonStats.filter = bob.obj(); } diff --git a/src/mongo/db/matcher/SConscript b/src/mongo/db/matcher/SConscript index e9353127805..cb267e448d2 100644 --- a/src/mongo/db/matcher/SConscript +++ b/src/mongo/db/matcher/SConscript @@ -141,3 +141,14 @@ env.Library( 'incomplete', ], ) + +env.CppUnitTest( + target='expression_serialization_test', + source=[ + 'expression_serialization_test.cpp', + ], + LIBDEPS=[ + 'expressions', + 'expressions_geo', + ], +) diff --git a/src/mongo/db/matcher/expression.cpp b/src/mongo/db/matcher/expression.cpp index 989e075d1de..187050c5eb5 100644 --- a/src/mongo/db/matcher/expression.cpp +++ b/src/mongo/db/matcher/expression.cpp @@ -57,10 +57,15 @@ bool MatchExpression::matchesBSON(const BSONObj& doc, MatchDetails* details) con void FalseMatchExpression::debugString(StringBuilder& debug, int level) const { _debugAddSpace(debug, level); - debug << "$false\n"; + debug << "$all: []\n"; } -void FalseMatchExpression::toBSON(BSONObjBuilder* out) const { - out->append("$false", 1); +void FalseMatchExpression::serialize(BSONObjBuilder* out) const { + // Our query language has no "always false" operator aside from a $all with no children, so use + // that as a proxy here. + BSONObjBuilder child(out->subobjStart(_path)); + BSONArrayBuilder allChild(child.subarrayStart("$all")); + allChild.doneFast(); + child.doneFast(); } } diff --git a/src/mongo/db/matcher/expression.h b/src/mongo/db/matcher/expression.h index 43433346fa7..ad10d17621c 100644 --- a/src/mongo/db/matcher/expression.h +++ b/src/mongo/db/matcher/expression.h @@ -233,12 +233,18 @@ public: } } + /** + * Serialize the MatchExpression to BSON, appending to 'out'. Output of this method is expected + * to be a valid query object, that, when parsed, produces a logically equivalent + * MatchExpression. + */ + virtual void serialize(BSONObjBuilder* out) const = 0; + // // Debug information // virtual std::string toString() const; virtual void debugString(StringBuilder& debug, int level = 0) const = 0; - virtual void toBSON(BSONObjBuilder* out) const = 0; protected: void _debugAddSpace(StringBuilder& debug, int level) const; @@ -250,7 +256,9 @@ private: class FalseMatchExpression : public MatchExpression { public: - FalseMatchExpression() : MatchExpression(ALWAYS_FALSE) {} + FalseMatchExpression(StringData path) : MatchExpression(ALWAYS_FALSE) { + _path = path; + } virtual bool matches(const MatchableDocument* doc, MatchDetails* details = 0) const { return false; @@ -261,15 +269,18 @@ public: } virtual std::unique_ptr<MatchExpression> shallowClone() const { - return stdx::make_unique<FalseMatchExpression>(); + return stdx::make_unique<FalseMatchExpression>(_path); } virtual void debugString(StringBuilder& debug, int level = 0) const; - virtual void toBSON(BSONObjBuilder* out) const; + virtual void serialize(BSONObjBuilder* out) const; virtual bool equivalent(const MatchExpression* other) const { return other->matchType() == ALWAYS_FALSE; } + +private: + StringData _path; }; } diff --git a/src/mongo/db/matcher/expression_array.cpp b/src/mongo/db/matcher/expression_array.cpp index 4ca68653e3d..9f0b11f1424 100644 --- a/src/mongo/db/matcher/expression_array.cpp +++ b/src/mongo/db/matcher/expression_array.cpp @@ -128,14 +128,10 @@ void ElemMatchObjectMatchExpression::debugString(StringBuilder& debug, int level _sub->debugString(debug, level + 1); } -void ElemMatchObjectMatchExpression::toBSON(BSONObjBuilder* out) const { +void ElemMatchObjectMatchExpression::serialize(BSONObjBuilder* out) const { BSONObjBuilder subBob; - _sub->toBSON(&subBob); - if (path().empty()) { - out->append("$elemMatch", subBob.obj()); - } else { - out->append(path(), BSON("$elemMatch" << subBob.obj())); - } + _sub->serialize(&subBob); + out->append(path(), BSON("$elemMatch" << subBob.obj())); } @@ -202,16 +198,16 @@ void ElemMatchValueMatchExpression::debugString(StringBuilder& debug, int level) } } -void ElemMatchValueMatchExpression::toBSON(BSONObjBuilder* out) const { +void ElemMatchValueMatchExpression::serialize(BSONObjBuilder* out) const { BSONObjBuilder emBob; + for (unsigned i = 0; i < _subs.size(); i++) { - _subs[i]->toBSON(&emBob); - } - if (path().empty()) { - out->append("$elemMatch", emBob.obj()); - } else { - out->append(path(), BSON("$elemMatch" << emBob.obj())); + BSONObjBuilder predicate; + _subs[i]->serialize(&predicate); + BSONObj predObj = predicate.obj(); + emBob.appendElements(predObj.firstElement().embeddedObject()); } + out->append(path(), BSON("$elemMatch" << emBob.obj())); } @@ -239,7 +235,7 @@ void SizeMatchExpression::debugString(StringBuilder& debug, int level) const { } } -void SizeMatchExpression::toBSON(BSONObjBuilder* out) const { +void SizeMatchExpression::serialize(BSONObjBuilder* out) const { out->append(path(), BSON("$size" << _size)); } diff --git a/src/mongo/db/matcher/expression_array.h b/src/mongo/db/matcher/expression_array.h index 0776bd5a3cf..76459c93b8c 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 toBSON(BSONObjBuilder* out) const; + virtual void serialize(BSONObjBuilder* out) const; virtual size_t numChildren() const { return 1; @@ -126,7 +126,7 @@ public: virtual void debugString(StringBuilder& debug, int level) const; - virtual void toBSON(BSONObjBuilder* out) const; + virtual void serialize(BSONObjBuilder* out) const; virtual std::vector<MatchExpression*>* getChildVector() { return &_subs; @@ -164,7 +164,7 @@ public: virtual void debugString(StringBuilder& debug, int level) const; - virtual void toBSON(BSONObjBuilder* out) const; + virtual void serialize(BSONObjBuilder* out) const; 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 ed5c78f4fb6..15a538609b4 100644 --- a/src/mongo/db/matcher/expression_geo.cpp +++ b/src/mongo/db/matcher/expression_geo.cpp @@ -369,7 +369,7 @@ void GeoMatchExpression::debugString(StringBuilder& debug, int level) const { debug << "\n"; } -void GeoMatchExpression::toBSON(BSONObjBuilder* out) const { +void GeoMatchExpression::serialize(BSONObjBuilder* out) const { out->appendElements(_rawObj); } @@ -426,7 +426,7 @@ void GeoNearMatchExpression::debugString(StringBuilder& debug, int level) const debug << "\n"; } -void GeoNearMatchExpression::toBSON(BSONObjBuilder* out) const { +void GeoNearMatchExpression::serialize(BSONObjBuilder* out) const { out->appendElements(_rawObj); } diff --git a/src/mongo/db/matcher/expression_geo.h b/src/mongo/db/matcher/expression_geo.h index d10ec5954d2..856b31045f8 100644 --- a/src/mongo/db/matcher/expression_geo.h +++ b/src/mongo/db/matcher/expression_geo.h @@ -91,7 +91,7 @@ public: virtual void debugString(StringBuilder& debug, int level = 0) const; - virtual void toBSON(BSONObjBuilder* out) const; + virtual void serialize(BSONObjBuilder* out) const; virtual bool equivalent(const MatchExpression* other) const; @@ -174,7 +174,7 @@ public: virtual void debugString(StringBuilder& debug, int level = 0) const; - virtual void toBSON(BSONObjBuilder* out) const; + virtual void serialize(BSONObjBuilder* out) const; 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 1389fd8f98a..042e4169609 100644 --- a/src/mongo/db/matcher/expression_leaf.cpp +++ b/src/mongo/db/matcher/expression_leaf.cpp @@ -185,8 +185,7 @@ void ComparisonMatchExpression::debugString(StringBuilder& debug, int level) con debug << "$gte"; break; default: - debug << " UNKNOWN - should be impossible"; - break; + invariant(false); } debug << " " << _rhs.toString(false); @@ -199,7 +198,7 @@ void ComparisonMatchExpression::debugString(StringBuilder& debug, int level) con debug << "\n"; } -void ComparisonMatchExpression::toBSON(BSONObjBuilder* out) const { +void ComparisonMatchExpression::serialize(BSONObjBuilder* out) const { string opString = ""; switch (matchType()) { case LT: @@ -218,8 +217,7 @@ void ComparisonMatchExpression::toBSON(BSONObjBuilder* out) const { opString = "$gte"; break; default: - opString = " UNKNOWN - should be impossible"; - break; + invariant(false); } out->append(path(), BSON(opString << _rhs)); @@ -307,7 +305,7 @@ void RegexMatchExpression::debugString(StringBuilder& debug, int level) const { debug << "\n"; } -void RegexMatchExpression::toBSON(BSONObjBuilder* out) const { +void RegexMatchExpression::serialize(BSONObjBuilder* out) const { out->appendRegex(path(), _regex, _flags); } @@ -342,7 +340,7 @@ void ModMatchExpression::debugString(StringBuilder& debug, int level) const { debug << "\n"; } -void ModMatchExpression::toBSON(BSONObjBuilder* out) const { +void ModMatchExpression::serialize(BSONObjBuilder* out) const { out->append(path(), BSON("$mod" << BSON_ARRAY(_divisor << _remainder))); } @@ -377,7 +375,7 @@ void ExistsMatchExpression::debugString(StringBuilder& debug, int level) const { debug << "\n"; } -void ExistsMatchExpression::toBSON(BSONObjBuilder* out) const { +void ExistsMatchExpression::serialize(BSONObjBuilder* out) const { out->append(path(), BSON("$exists" << true)); } @@ -494,7 +492,7 @@ void TypeMatchExpression::debugString(StringBuilder& debug, int level) const { debug << "\n"; } -void TypeMatchExpression::toBSON(BSONObjBuilder* out) const { +void TypeMatchExpression::serialize(BSONObjBuilder* out) const { if (matchesAllNumbers()) { out->append(path(), BSON("$type" << kMatchesAllNumbersAlias)); } else { @@ -591,13 +589,13 @@ void ArrayFilterEntries::debugString(StringBuilder& debug) const { debug << "]"; } -void ArrayFilterEntries::toBSON(BSONArrayBuilder* out) const { +void ArrayFilterEntries::serialize(BSONArrayBuilder* out) const { for (BSONElementSet::const_iterator it = _equalities.begin(); it != _equalities.end(); ++it) { out->append(*it); } for (size_t i = 0; i < _regexes.size(); ++i) { BSONObjBuilder regexBob; - _regexes[i]->toBSON(®exBob); + _regexes[i]->serialize(®exBob); out->append(regexBob.obj().firstElement()); } out->doneFast(); @@ -654,10 +652,10 @@ void InMatchExpression::debugString(StringBuilder& debug, int level) const { debug << "\n"; } -void InMatchExpression::toBSON(BSONObjBuilder* out) const { +void InMatchExpression::serialize(BSONObjBuilder* out) const { BSONObjBuilder inBob(out->subobjStart(path())); BSONArrayBuilder arrBob(inBob.subarrayStart("$in")); - _arrayEntries.toBSON(&arrBob); + _arrayEntries.serialize(&arrBob); inBob.doneFast(); } @@ -878,7 +876,7 @@ void BitTestMatchExpression::debugString(StringBuilder& debug, int level) const } } -void BitTestMatchExpression::toBSON(BSONObjBuilder* out) const { +void BitTestMatchExpression::serialize(BSONObjBuilder* out) const { string opString = ""; switch (matchType()) { diff --git a/src/mongo/db/matcher/expression_leaf.h b/src/mongo/db/matcher/expression_leaf.h index 2b382768788..1f1b305e141 100644 --- a/src/mongo/db/matcher/expression_leaf.h +++ b/src/mongo/db/matcher/expression_leaf.h @@ -97,7 +97,7 @@ public: virtual void debugString(StringBuilder& debug, int level = 0) const; - virtual void toBSON(BSONObjBuilder* out) const; + virtual void serialize(BSONObjBuilder* out) const; virtual bool equivalent(const MatchExpression* other) const; @@ -210,7 +210,7 @@ public: virtual void debugString(StringBuilder& debug, int level) const; - virtual void toBSON(BSONObjBuilder* out) const; + virtual void serialize(BSONObjBuilder* out) const; void shortDebugString(StringBuilder& debug) const; @@ -248,7 +248,7 @@ public: virtual void debugString(StringBuilder& debug, int level) const; - virtual void toBSON(BSONObjBuilder* out) const; + virtual void serialize(BSONObjBuilder* out) const; virtual bool equivalent(const MatchExpression* other) const; @@ -283,7 +283,7 @@ public: virtual void debugString(StringBuilder& debug, int level) const; - virtual void toBSON(BSONObjBuilder* out) const; + virtual void serialize(BSONObjBuilder* out) const; virtual bool equivalent(const MatchExpression* other) const; }; @@ -337,7 +337,7 @@ public: void debugString(StringBuilder& debug) const; - void toBSON(BSONArrayBuilder* out) const; + void serialize(BSONArrayBuilder* out) const; private: bool _hasNull; // if _equalities has a jstNULL element in it @@ -364,7 +364,7 @@ public: virtual void debugString(StringBuilder& debug, int level) const; - virtual void toBSON(BSONObjBuilder* out) const; + virtual void serialize(BSONObjBuilder* out) const; virtual bool equivalent(const MatchExpression* other) const; @@ -425,7 +425,7 @@ public: virtual void debugString(StringBuilder& debug, int level) const; - virtual void toBSON(BSONObjBuilder* out) const; + virtual void serialize(BSONObjBuilder* out) const; virtual bool equivalent(const MatchExpression* other) const; @@ -481,7 +481,7 @@ public: virtual void debugString(StringBuilder& debug, int level) const; - virtual void toBSON(BSONObjBuilder* out) const; + virtual void serialize(BSONObjBuilder* out) const; virtual bool equivalent(const MatchExpression* other) const; diff --git a/src/mongo/db/matcher/expression_leaf_test.cpp b/src/mongo/db/matcher/expression_leaf_test.cpp index 43fdd47e380..b2946f5f546 100644 --- a/src/mongo/db/matcher/expression_leaf_test.cpp +++ b/src/mongo/db/matcher/expression_leaf_test.cpp @@ -1373,17 +1373,6 @@ TEST(TypeMatchExpression, MatchesElementNumber) { ASSERT_FALSE(type.matchesSingleElement(notMatch["a"])); } -TEST(TypeMatchExpression, ElementNumberToBSONIsFormattedCorrectly) { - TypeMatchExpression type; - ASSERT_OK(type.initAsMatchingAllNumbers("a")); - ASSERT_EQ("a", type.path()); - - BSONObjBuilder objBuilder; - type.toBSON(&objBuilder); - ASSERT_EQ(objBuilder.obj(), - BSON("a" << BSON("$type" << TypeMatchExpression::kMatchesAllNumbersAlias))); -} - TEST(TypeMatchExpression, InvalidTypeMatchExpressionTypeCode) { TypeMatchExpression type; ASSERT_NOT_OK(type.initWithBSONType("", JSTypeMax + 1)); diff --git a/src/mongo/db/matcher/expression_parser.cpp b/src/mongo/db/matcher/expression_parser.cpp index c249f28a4be..fd6b8802524 100644 --- a/src/mongo/db/matcher/expression_parser.cpp +++ b/src/mongo/db/matcher/expression_parser.cpp @@ -804,7 +804,7 @@ StatusWithMatchExpression MatchExpressionParser::_parseAll(const char* name, } if (myAnd->numChildren() == 0) { - return {stdx::make_unique<FalseMatchExpression>()}; + return {stdx::make_unique<FalseMatchExpression>(name)}; } return {std::move(myAnd)}; diff --git a/src/mongo/db/matcher/expression_serialization_test.cpp b/src/mongo/db/matcher/expression_serialization_test.cpp new file mode 100644 index 00000000000..39a472adef6 --- /dev/null +++ b/src/mongo/db/matcher/expression_serialization_test.cpp @@ -0,0 +1,791 @@ +/** + * Copyright (C) 2016 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +// Unit tests for MatchExpression::serialize serialization. + +#include "mongo/platform/basic.h" + +#include "mongo/db/json.h" +#include "mongo/db/matcher/expression.h" +#include "mongo/db/matcher/expression_parser.h" +#include "mongo/db/matcher/extensions_callback_noop.h" +#include "mongo/db/matcher/matcher.h" +#include "mongo/unittest/unittest.h" + +namespace mongo { +namespace { + +using std::pair; +using std::string; +using std::unique_ptr; + +BSONObj serialize(MatchExpression* match) { + BSONObjBuilder bob; + match->serialize(&bob); + return bob.obj(); +} + +TEST(SerializeBasic, AndExpressionWithOneChildSerializesCorrectly) { + Matcher original(fromjson("{$and: [{x: 0}]}"), ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), fromjson("{$and: [{x: {$eq: 0}}]}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + + BSONObj obj = fromjson("{x: 1}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{x: 0}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, AndExpressionWithTwoChildrenSerializesCorrectly) { + Matcher original(fromjson("{$and: [{x: 1}, {x: 2}]}"), ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), fromjson("{$and: [{x: {$eq: 1}}, {x: {$eq: 2}}]}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + + BSONObj obj = fromjson("{x: 1}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{x: [1, 2]}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, AndExpressionWithTwoIdenticalChildrenSerializesCorrectly) { + Matcher original(fromjson("{$and: [{x: 1}, {x: 1}]}"), ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), fromjson("{$and: [{x: {$eq: 1}}, {x: {$eq: 1}}]}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + + BSONObj obj = fromjson("{x: 1}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{x: -1}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, ExpressionOr) { + Matcher original(fromjson("{$or: [{x: 'A'}, {x: 'B'}]}"), ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), fromjson("{$or: [{x: {$eq: 'A'}}, {x: {$eq: 'B'}}]}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + + BSONObj obj = fromjson("{x: 'A'}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{x: 'a'}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, ExpressionElemMatchObjectSerializesCorrectly) { + Matcher original(fromjson("{x: {$elemMatch: {a: {$gt: 0}, b: {$gt: 0}}}}"), + ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), + fromjson("{x: {$elemMatch: {$and: [{a: {$gt: 0}}, {b: {$gt: 0}}]}}}")); + ASSERT_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)); +} + +TEST(SerializeBasic, ExpressionElemMatchObjectWithEmptyStringSerializesCorrectly) { + Matcher original(fromjson("{'': {$elemMatch: {a: {$gt: 0}, b: {$gt: 0}}}}"), + ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), + fromjson("{'': {$elemMatch: {$and: [{a: {$gt: 0}}, {b: {$gt: 0}}]}}}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + + BSONObj obj = fromjson("{'': [{a: 1, b: -1}, {a: -1, b: 1}]}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{'': [{a: 1, b: 1}, {a: 0, b: 0}]}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, ExpressionElemMatchValueSerializesCorrectly) { + Matcher original(fromjson("{x: {$elemMatch: {$lt: 1, $gt: -1}}}"), ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), fromjson("{x: {$elemMatch: {$lt: 1, $gt: -1}}}")); + ASSERT_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, ExpressionElemMatchValueWithEmptyStringSerializesCorrectly) { + Matcher original(fromjson("{x: {$elemMatch: {$lt: 1, $gt: -1}}}"), ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), fromjson("{x: {$elemMatch: {$lt: 1, $gt: -1}}}")); + ASSERT_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, ExpressionSizeSerializesCorrectly) { + Matcher original(fromjson("{x: {$size: 2}}"), ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), fromjson("{x: {$size: 2}}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + + BSONObj obj = fromjson("{x: [1, 2, 3]}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{x: [1, 2]}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, ExpressionAllSerializesCorrectly) { + Matcher original(fromjson("{x: {$all: [1, 2]}}"), ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), fromjson("{$and: [{x: {$eq: 1}}, {x: {$eq: 2}}]}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + + BSONObj obj = fromjson("{x: [1, 2, 3]}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{x: [1, 3]}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, ExpressionAllWithEmptyArraySerializesCorrectly) { + Matcher original(fromjson("{x: {$all: []}}"), ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), fromjson("{x: {$all: []}}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + + BSONObj obj = fromjson("{x: [1, 2, 3]}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, ExpressionAllWithRegex) { + Matcher original(fromjson("{x: {$all: [/a.b.c/, /.d.e./]}}"), ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), fromjson("{$and: [{x: /a.b.c/}, {x: /.d.e./}]}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + + BSONObj obj = fromjson("{x: 'abcde'}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{x: 'adbec'}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, ExpressionEqSerializesCorrectly) { + Matcher original(fromjson("{x: {$eq: {a: 1}}}"), ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), fromjson("{x: {$eq: {a: 1}}}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + + BSONObj obj = fromjson("{x: {a: 1}}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{x: {a: [1, 2]}}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{x: {a: 2}}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, ExpressionNeSerializesCorrectly) { + Matcher original(fromjson("{x: {$ne: {a: 1}}}"), ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), fromjson("{$nor: [{x: {$eq: {a: 1}}}]}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + + BSONObj obj = fromjson("{x: {a: 1}}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{x: {a: [1, 2]}}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, ExpressionLtSerializesCorrectly) { + Matcher original(fromjson("{x: {$lt: 3}}"), ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), fromjson("{x: {$lt: 3}}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + + BSONObj obj = fromjson("{x: 3}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{x: 2.9}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, ExpressionGtSerializesCorrectly) { + Matcher original(fromjson("{x: {$gt: 3}}"), ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), fromjson("{x: {$gt: 3}}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + + BSONObj obj = fromjson("{x: 3}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{x: 3.1}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, ExpressionGteSerializesCorrectly) { + Matcher original(fromjson("{x: {$gte: 3}}"), ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), fromjson("{x: {$gte: 3}}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + + BSONObj obj = fromjson("{x: 3}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{x: 2}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, ExpressionLteSerializesCorrectly) { + Matcher original(fromjson("{x: {$lte: 3}}"), ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), fromjson("{x: {$lte: 3}}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + + BSONObj obj = fromjson("{x: 3}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{x: 4}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, ExpressionRegexWithObjSerializesCorrectly) { + Matcher original(fromjson("{x: {$regex: 'a.b'}}"), ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), fromjson("{x: {$regex: 'a.b'}}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + + BSONObj obj = fromjson("{x: 'abc'}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{x: 'acb'}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, ExpressionRegexWithValueSerializesCorrectly) { + Matcher original(fromjson("{x: /a.b/i}"), ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), fromjson("{x: {$regex: 'a.b', $options: 'i'}}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + + BSONObj obj = fromjson("{x: 'abc'}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{x: 'acb'}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, ExpressionRegexWithValueAndOptionsSerializesCorrectly) { + Matcher original(fromjson("{x: /a.b/}"), ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), fromjson("{x: {$regex: 'a.b'}}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + + BSONObj obj = fromjson("{x: 'abc'}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{x: 'acb'}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, ExpressionRegexWithEqObjSerializesCorrectly) { + Matcher original(fromjson("{x: {$eq: {$regex: 'a.b'}}}"), ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), fromjson("{x: {$eq: {$regex: 'a.b'}}}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + + BSONObj obj = fromjson("{x: 'abc'}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{x: 'acb'}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{x: /a.b.c/}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, ExpressionModSerializesCorrectly) { + Matcher original(fromjson("{x: {$mod: [2, 1]}}"), ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), fromjson("{x: {$mod: [2, 1]}}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + + BSONObj obj = fromjson("{x: 1}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{x: 2}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, ExpressionExistsTrueSerializesCorrectly) { + Matcher original(fromjson("{x: {$exists: true}}"), ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), fromjson("{x: {$exists: true}}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + + BSONObj obj = fromjson("{x: 1}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{a: 1}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, ExpressionExistsFalseSerializesCorrectly) { + Matcher original(fromjson("{x: {$exists: false}}"), ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), fromjson("{$nor: [{x: {$exists: true}}]}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + + BSONObj obj = fromjson("{x: 1}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{a: 1}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, ExpressionInSerializesCorrectly) { + Matcher original(fromjson("{x: {$in: [1, 2, 3]}}"), ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), fromjson("{x: {$in: [1, 2, 3]}}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + + BSONObj obj = fromjson("{x: 1}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{x: 4}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{x: [1, 2]}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, ExpressionInWithEmptyArraySerializesCorrectly) { + Matcher original(fromjson("{x: {$in: []}}"), ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), fromjson("{x: {$in: []}}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + + BSONObj obj = fromjson("{x: 1}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, ExpressionInWithRegexSerializesCorrectly) { + Matcher original(fromjson("{x: {$in: [/\\d+/, /\\w+/]}}"), ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), fromjson("{x: {$in: [/\\d+/, /\\w+/]}}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + + BSONObj obj = fromjson("{x: '1234'}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{x: 'abcd'}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{x: '1a2b'}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, ExpressionNinSerializesCorrectly) { + Matcher original(fromjson("{x: {$nin: [1, 2, 3]}}"), ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), fromjson("{$nor: [{x: {$in: [1, 2, 3]}}]}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + + BSONObj obj = fromjson("{x: 1}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{x: 4}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{x: [1, 2]}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, ExpressionBitsAllSetSerializesCorrectly) { + Matcher original(fromjson("{x: {$bitsAllSet: [1, 3]}}"), ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), fromjson("{x: {$bitsAllSet: [1, 3]}}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + + BSONObj obj = fromjson("{x: 2}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{x: 10}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, ExpressionBitsAllClearSerializesCorrectly) { + Matcher original(fromjson("{x: {$bitsAllClear: [1, 3]}}"), ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), fromjson("{x: {$bitsAllClear: [1, 3]}}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + + BSONObj obj = fromjson("{x: 2}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{x: 1}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, ExpressionBitsAnySetSerializesCorrectly) { + Matcher original(fromjson("{x: {$bitsAnySet: [1, 3]}}"), ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), fromjson("{x: {$bitsAnySet: [1, 3]}}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + + BSONObj obj = fromjson("{x: 2}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{x: 4}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, ExpressionBitsAnyClearSerializesCorrectly) { + Matcher original(fromjson("{x: {$bitsAnyClear: [1, 3]}}"), ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), fromjson("{x: {$bitsAnyClear: [1, 3]}}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + + BSONObj obj = fromjson("{x: 2}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{x: 1}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{x: 10}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, ExpressionNotSerializesCorrectly) { + Matcher original(fromjson("{x: {$not: {$eq: 3}}}"), ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), fromjson("{$nor: [{$and: [{x: {$eq: 3}}]}]}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + + BSONObj obj = fromjson("{x: 3}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{x: 4}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, ExpressionNotWithMultipleChildrenSerializesCorrectly) { + Matcher original(fromjson("{x: {$not: {$lt: 1, $gt: 3}}}"), ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), + fromjson("{$nor: [{$and: [{x: {$lt: 1}}, {x: {$gt: 3}}]}]}}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + + BSONObj obj = fromjson("{x: 2}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{x: 4}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, ExpressionNotWithBitTestSerializesCorrectly) { + Matcher original(fromjson("{x: {$not: {$bitsAnySet: [1, 3]}}}"), ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), fromjson("{$nor: [{$and: [{x: {$bitsAnySet: [1, 3]}}]}]}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + + BSONObj obj = fromjson("{x: 2}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{x: 4}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, ExpressionNotWithRegexObjSerializesCorrectly) { + Matcher original(fromjson("{x: {$not: {$regex: 'a.b'}}}"), ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), fromjson("{$nor: [{x: /a.b/}]}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + + BSONObj obj = fromjson("{x: 'abc'}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{x: 'acb'}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, ExpressionNotWithRegexValueSerializesCorrectly) { + Matcher original(fromjson("{x: {$not: /a.b/}}"), ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), fromjson("{$nor: [{x: /a.b/}]}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + + BSONObj obj = fromjson("{x: 'abc'}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{x: 'acb'}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, ExpressionNotWithRegexValueAndOptionsSerializesCorrectly) { + Matcher original(fromjson("{x: {$not: /a.b/i}}"), ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), fromjson("{$nor: [{x: /a.b/i}]}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + + BSONObj obj = fromjson("{x: 'abc'}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{x: 'acb'}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, ExpressionNotWithGeoSerializesCorrectly) { + Matcher original(fromjson( + "{x: {$not: {$geoIntersects: {$geometry: {type: 'Polygon', " + "coordinates: [[[0,0], [5,0], " + "[5, 5], [0, 5], [0, 0]]]}}}}}"), + ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), + fromjson( + "{$nor: [{$and: [{x: {$geoIntersects: {$geometry: {type: 'Polygon', coordinates: " + "[[[0,0], " + "[5,0], [5, 5], [0, 5], [0, 0]]]}}}}]}]}")); + + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + BSONObj obj = + fromjson("{x: {type: 'Polygon', coordinates: [[4, 4], [4, 6], [6, 6], [6, 4], [4, 4]]}}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson( + "{x: {type: 'Polygon', coordinates: [[4, 4], [4, 4.5], [4.5, 4.5], [4.5, 4], [4, 4]]}}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson( + "{x: {type: 'Polygon', coordinates: [[5.5, 5.5], [5.5, 6], [6, 6], [6, 5.5], [5.5, " + "5.5]]}}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, ExpressionNorSerializesCorrectly) { + Matcher original(fromjson("{$nor: [{x: 3}, {x: {$lt: 1}}]}"), ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), fromjson("{$nor: [{x: {$eq: 3}}, {x: {$lt: 1}}]}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + + BSONObj obj = fromjson("{x: 3}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{x: 0}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{x: 2}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, ExpressionTypeSerializesCorrectly) { + Matcher original(fromjson("{x: {$type: 2}}"), ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), fromjson("{x: {$type: 2}}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + + BSONObj obj = fromjson("{x: 3}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{x: '3'}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, ExpressionTypeWithNumberSerializesCorrectly) { + Matcher original(fromjson("{x: {$type: 'number'}}"), ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), fromjson("{x: {$type: 'number'}}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + + BSONObj obj = fromjson("{x: 3}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{x: '3'}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, ExpressionEmptySerializesCorrectly) { + Matcher original(fromjson("{}"), ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), fromjson("{}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + + BSONObj obj = fromjson("{x: 3}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, ExpressionWhereSerializesCorrectly) { + Matcher original(fromjson("{$where: 'this.a == this.b'}"), ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), + BSONObjBuilder().appendCodeWScope("$where", "this.a == this.b", BSONObj()).obj()); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); +} + +TEST(SerializeBasic, ExpressionWhereWithScopeSerializesCorrectly) { + Matcher original(BSON("$where" << BSONCodeWScope("this.a == this.b", BSON("x" << 3))), + ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), + BSON("$where" << BSONCodeWScope("this.a == this.b", BSON("x" << 3)))); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); +} + +TEST(SerializeBasic, ExpressionCommentSerializesCorrectly) { + Matcher original(fromjson("{$comment: 'Hello'}"), ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), fromjson("{}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + + BSONObj obj = fromjson("{a: 1, b: 2}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{a: 'z', b: 'z'}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, ExpressionGeoWithinSerializesCorrectly) { + Matcher original( + fromjson( + "{x: {$geoWithin: {$geometry: " + "{type: 'Polygon', coordinates: [[[0, 0], [10, 0], [10, 10], [0, 10], [0, 0]]]}}}}"), + ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), + fromjson( + "{x: {$geoWithin: {$geometry: {type: 'Polygon', coordinates: [[[0,0], [10,0], " + "[10, 10], [0, 10], [0, 0]]]}}}}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + + BSONObj obj = fromjson("{x: {type: 'Point', coordinates: [5, 5]}}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson("{x: {type: 'Point', coordinates: [50, 50]}}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, ExpressionGeoIntersectsSerializesCorrectly) { + Matcher original( + fromjson( + "{x: {$geoIntersects: {$geometry: {type: 'Polygon', coordinates: [[[0,0], [5,0], [5, " + "5], [0, 5], [0, 0]]]}}}}"), + ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), + fromjson( + "{x: {$geoIntersects: {$geometry: {type: 'Polygon', coordinates: [[[0,0], [5,0], " + "[5, 5], [0, 5], [0, 0]]]}}}}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); + + BSONObj obj = + fromjson("{x: {type: 'Polygon', coordinates: [[4, 4], [4, 6], [6, 6], [6, 4], [4, 4]]}}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson( + "{x: {type: 'Polygon', coordinates: [[4, 4], [4, 4.5], [4.5, 4.5], [4.5, 4], [4, 4]]}}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); + + obj = fromjson( + "{x: {type: 'Polygon', coordinates: [[5.5, 5.5], [5.5, 6], [6, 6], [6, 5.5], [5.5, " + "5.5]]}}"); + ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); +} + +TEST(SerializeBasic, ExpressionNearSerializesCorrectly) { + Matcher original( + fromjson( + "{x: {$near: {$geometry: {type: 'Point', coordinates: [0, 0]}, $maxDistance: 10, " + "$minDistance: 1}}}"), + ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), + fromjson( + "{x: {$near: {$geometry: {type: 'Point', coordinates: [0, 0]}, $maxDistance: 10, " + "$minDistance: 1}}}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); +} + +TEST(SerializeBasic, ExpressionNearSphereSerializesCorrectly) { + Matcher original( + fromjson( + "{x: {$nearSphere: {$geometry: {type: 'Point', coordinates: [0, 0]}, $maxDistance: 10, " + "$minDistance: 1}}}"), + ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), + fromjson( + "{x: {$nearSphere: {$geometry: {type: 'Point', coordinates: [0, 0]}, " + "$maxDistance: 10, $minDistance: 1}}}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); +} + +TEST(SerializeBasic, ExpressionTextSerializesCorrectly) { + Matcher original(fromjson("{$text: {$search: 'a', $language: 'en', $caseSensitive: true}}"), + ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), + fromjson( + "{$text: {$search: 'a', $language: 'en', $caseSensitive: true, " + "$diacriticSensitive: false}}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); +} + +TEST(SerializeBasic, ExpressionTextWithDefaultLanguageSerializesCorrectly) { + Matcher original(fromjson("{$text: {$search: 'a', $caseSensitive: false}}"), + ExtensionsCallbackNoop()); + Matcher reserialized(serialize(original.getMatchExpression()), ExtensionsCallbackNoop()); + ASSERT_EQ(*reserialized.getQuery(), + fromjson( + "{$text: {$search: 'a', $language: '', $caseSensitive: false, " + "$diacriticSensitive: false}}")); + ASSERT_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); +} + +} // namespace +} // namespace mongo diff --git a/src/mongo/db/matcher/expression_text_base.cpp b/src/mongo/db/matcher/expression_text_base.cpp index 82fa7f9fd3e..afed1a33e6f 100644 --- a/src/mongo/db/matcher/expression_text_base.cpp +++ b/src/mongo/db/matcher/expression_text_base.cpp @@ -54,7 +54,7 @@ void TextMatchExpressionBase::debugString(StringBuilder& debug, int level) const debug << "\n"; } -void TextMatchExpressionBase::toBSON(BSONObjBuilder* out) const { +void TextMatchExpressionBase::serialize(BSONObjBuilder* out) const { const fts::FTSQuery& ftsQuery = getFTSQuery(); out->append("$text", BSON("$search" << ftsQuery.getQuery() << "$language" << ftsQuery.getLanguage() diff --git a/src/mongo/db/matcher/expression_text_base.h b/src/mongo/db/matcher/expression_text_base.h index 187a91bb2d4..06a23a010bf 100644 --- a/src/mongo/db/matcher/expression_text_base.h +++ b/src/mongo/db/matcher/expression_text_base.h @@ -79,7 +79,7 @@ public: void debugString(StringBuilder& debug, int level = 0) const final; - void toBSON(BSONObjBuilder* out) const final; + void serialize(BSONObjBuilder* out) const final; bool equivalent(const MatchExpression* other) const final; }; diff --git a/src/mongo/db/matcher/expression_tree.cpp b/src/mongo/db/matcher/expression_tree.cpp index bc07445d0cc..02da8991465 100644 --- a/src/mongo/db/matcher/expression_tree.cpp +++ b/src/mongo/db/matcher/expression_tree.cpp @@ -56,7 +56,7 @@ void ListOfMatchExpression::_debugList(StringBuilder& debug, int level) const { void ListOfMatchExpression::_listToBSON(BSONArrayBuilder* out) const { for (unsigned i = 0; i < _expressions.size(); i++) { BSONObjBuilder childBob(out->subobjStart()); - _expressions[i]->toBSON(&childBob); + _expressions[i]->serialize(&childBob); } out->doneFast(); } @@ -107,9 +107,16 @@ void AndMatchExpression::debugString(StringBuilder& debug, int level) const { _debugList(debug, level); } -void AndMatchExpression::toBSON(BSONObjBuilder* out) const { +void AndMatchExpression::serialize(BSONObjBuilder* out) const { + if (!numChildren()) { + // It is possible for an AndMatchExpression to have no children, resulting in the serialized + // expression {$and: []}, which is not a valid query object. + return; + } + BSONArrayBuilder arrBob(out->subarrayStart("$and")); _listToBSON(&arrBob); + arrBob.doneFast(); } // ----- @@ -139,7 +146,7 @@ void OrMatchExpression::debugString(StringBuilder& debug, int level) const { _debugList(debug, level); } -void OrMatchExpression::toBSON(BSONObjBuilder* out) const { +void OrMatchExpression::serialize(BSONObjBuilder* out) const { BSONArrayBuilder arrBob(out->subarrayStart("$or")); _listToBSON(&arrBob); } @@ -170,7 +177,7 @@ void NorMatchExpression::debugString(StringBuilder& debug, int level) const { _debugList(debug, level); } -void NorMatchExpression::toBSON(BSONObjBuilder* out) const { +void NorMatchExpression::serialize(BSONObjBuilder* out) const { BSONArrayBuilder arrBob(out->subarrayStart("$nor")); _listToBSON(&arrBob); } @@ -183,10 +190,17 @@ void NotMatchExpression::debugString(StringBuilder& debug, int level) const { _exp->debugString(debug, level + 1); } -void NotMatchExpression::toBSON(BSONObjBuilder* out) const { - BSONObjBuilder childBob(out->subobjStart("$not")); - _exp->toBSON(&childBob); - childBob.doneFast(); +void NotMatchExpression::serialize(BSONObjBuilder* out) const { + 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(); } bool NotMatchExpression::equivalent(const MatchExpression* other) const { diff --git a/src/mongo/db/matcher/expression_tree.h b/src/mongo/db/matcher/expression_tree.h index 0f4b8018523..2b65bc9d2f6 100644 --- a/src/mongo/db/matcher/expression_tree.h +++ b/src/mongo/db/matcher/expression_tree.h @@ -101,7 +101,7 @@ public: virtual void debugString(StringBuilder& debug, int level = 0) const; - virtual void toBSON(BSONObjBuilder* out) const; + virtual void serialize(BSONObjBuilder* out) const; }; class OrMatchExpression : public ListOfMatchExpression { @@ -125,7 +125,7 @@ public: virtual void debugString(StringBuilder& debug, int level = 0) const; - virtual void toBSON(BSONObjBuilder* out) const; + virtual void serialize(BSONObjBuilder* out) const; }; class NorMatchExpression : public ListOfMatchExpression { @@ -149,7 +149,7 @@ public: virtual void debugString(StringBuilder& debug, int level = 0) const; - virtual void toBSON(BSONObjBuilder* out) const; + virtual void serialize(BSONObjBuilder* out) const; }; class NotMatchExpression : public MatchExpression { @@ -183,7 +183,7 @@ public: virtual void debugString(StringBuilder& debug, int level = 0) const; - virtual void toBSON(BSONObjBuilder* out) const; + virtual void serialize(BSONObjBuilder* out) const; bool equivalent(const MatchExpression* other) const; diff --git a/src/mongo/db/matcher/expression_where_base.cpp b/src/mongo/db/matcher/expression_where_base.cpp index 1bd7c2bde22..21b746e13ab 100644 --- a/src/mongo/db/matcher/expression_where_base.cpp +++ b/src/mongo/db/matcher/expression_where_base.cpp @@ -46,7 +46,7 @@ void WhereMatchExpressionBase::debugString(StringBuilder& debug, int level) cons debug << "scope: " << getScope() << "\n"; } -void WhereMatchExpressionBase::toBSON(BSONObjBuilder* out) const { +void WhereMatchExpressionBase::serialize(BSONObjBuilder* out) const { out->appendCodeWScope("$where", getCode(), getScope()); } diff --git a/src/mongo/db/matcher/expression_where_base.h b/src/mongo/db/matcher/expression_where_base.h index 8aabbed669d..ac845559bb2 100644 --- a/src/mongo/db/matcher/expression_where_base.h +++ b/src/mongo/db/matcher/expression_where_base.h @@ -54,7 +54,7 @@ public: void debugString(StringBuilder& debug, int level = 0) const final; - void toBSON(BSONObjBuilder* out) const final; + void serialize(BSONObjBuilder* out) const final; bool equivalent(const MatchExpression* other) const final; diff --git a/src/mongo/db/matcher/matcher.h b/src/mongo/db/matcher/matcher.h index 7de034a654c..99b54b37a8a 100644 --- a/src/mongo/db/matcher/matcher.h +++ b/src/mongo/db/matcher/matcher.h @@ -60,6 +60,10 @@ public: return _pattern.toString(); } + MatchExpression* getMatchExpression() { + return _expression.get(); + } + private: BSONObj _pattern; diff --git a/src/mongo/db/query/explain.cpp b/src/mongo/db/query/explain.cpp index 1dd784ffd04..890d50e9021 100644 --- a/src/mongo/db/query/explain.cpp +++ b/src/mongo/db/query/explain.cpp @@ -552,7 +552,7 @@ void Explain::generatePlannerInfo(PlanExecutor* exec, // does not canonicalize for idhack updates). In these cases, 'query' is NULL. if (NULL != query) { BSONObjBuilder parsedQueryBob(plannerBob.subobjStart("parsedQuery")); - query->root()->toBSON(&parsedQueryBob); + query->root()->serialize(&parsedQueryBob); parsedQueryBob.doneFast(); } diff --git a/src/mongo/db/query/parsed_projection_test.cpp b/src/mongo/db/query/parsed_projection_test.cpp index 6f4066c406b..8d88c628639 100644 --- a/src/mongo/db/query/parsed_projection_test.cpp +++ b/src/mongo/db/query/parsed_projection_test.cpp @@ -181,7 +181,7 @@ TEST(ParsedProjectionTest, ValidPositionalOperatorProjections) { // to achieve the same effect. // Projection parser should handle this the same way as an empty path. TEST(ParsedProjectionTest, InvalidPositionalProjectionDefaultPathMatchExpression) { - unique_ptr<MatchExpression> queryMatchExpr(new FalseMatchExpression()); + unique_ptr<MatchExpression> queryMatchExpr(new FalseMatchExpression("")); ASSERT(NULL == queryMatchExpr->path().rawData()); ParsedProjection* out = NULL; diff --git a/src/mongo/dbtests/extensions_callback_real_test.cpp b/src/mongo/dbtests/extensions_callback_real_test.cpp index 56465cb2c7e..d1284f7aea4 100644 --- a/src/mongo/dbtests/extensions_callback_real_test.cpp +++ b/src/mongo/dbtests/extensions_callback_real_test.cpp @@ -247,13 +247,13 @@ TEST_F(ExtensionsCallbackRealTest, WhereExpressionsWithSameScopeHaveSameBSONRepr auto expr1 = unittest::assertGet(ExtensionsCallbackReal(&_txn, &_nss).parseWhere(query1.firstElement())); BSONObjBuilder builder1; - expr1->toBSON(&builder1); + expr1->serialize(&builder1); BSONObj query2 = BSON("$where" << BSONCodeWScope(code, BSON("a" << true))); auto expr2 = unittest::assertGet(ExtensionsCallbackReal(&_txn, &_nss).parseWhere(query2.firstElement())); BSONObjBuilder builder2; - expr2->toBSON(&builder2); + expr2->serialize(&builder2); ASSERT_EQ(builder1.obj(), builder2.obj()); } @@ -266,13 +266,13 @@ TEST_F(ExtensionsCallbackRealTest, auto expr1 = unittest::assertGet(ExtensionsCallbackReal(&_txn, &_nss).parseWhere(query1.firstElement())); BSONObjBuilder builder1; - expr1->toBSON(&builder1); + expr1->serialize(&builder1); BSONObj query2 = BSON("$where" << BSONCodeWScope(code, BSON("a" << false))); auto expr2 = unittest::assertGet(ExtensionsCallbackReal(&_txn, &_nss).parseWhere(query2.firstElement())); BSONObjBuilder builder2; - expr2->toBSON(&builder2); + expr2->serialize(&builder2); ASSERT_NE(builder1.obj(), builder2.obj()); } |