diff options
author | Blake Oler <blake.oler@10gen.com> | 2017-09-11 13:26:16 -0400 |
---|---|---|
committer | Blake Oler <blake.oler@10gen.com> | 2017-09-12 13:24:44 -0400 |
commit | 33cb4a950c5de3c1c783b25ecf0fd4454cce844a (patch) | |
tree | 02220841b1d28906053395dc3c74b7e33afc11fe | |
parent | 152ade06b7c7db03d358d42f41a961b3e8787a35 (diff) | |
download | mongo-33cb4a950c5de3c1c783b25ecf0fd4454cce844a.tar.gz |
SERVER-30717 Serialize OrMatchExpression to {$alwaysFalse: 1}
7 files changed, 53 insertions, 26 deletions
diff --git a/src/mongo/db/matcher/expression_always_boolean.h b/src/mongo/db/matcher/expression_always_boolean.h index 1625dcf7394..a22b8b1e479 100644 --- a/src/mongo/db/matcher/expression_always_boolean.h +++ b/src/mongo/db/matcher/expression_always_boolean.h @@ -87,10 +87,12 @@ private: class AlwaysFalseMatchExpression final : public AlwaysBooleanMatchExpression { public: + static constexpr StringData kName = "$alwaysFalse"_sd; + AlwaysFalseMatchExpression() : AlwaysBooleanMatchExpression(MatchType::ALWAYS_FALSE, false) {} StringData name() const final { - return "$alwaysFalse"_sd; + return kName; } std::unique_ptr<MatchExpression> shallowClone() const final { @@ -100,10 +102,12 @@ public: class AlwaysTrueMatchExpression final : public AlwaysBooleanMatchExpression { public: + static constexpr StringData kName = "$alwaysTrue"_sd; + AlwaysTrueMatchExpression() : AlwaysBooleanMatchExpression(MatchType::ALWAYS_TRUE, true) {} StringData name() const final { - return "$alwaysTrue"_sd; + return kName; } std::unique_ptr<MatchExpression> shallowClone() const final { diff --git a/src/mongo/db/matcher/expression_parser.cpp b/src/mongo/db/matcher/expression_parser.cpp index ba80393ccc8..1f55da8ef0f 100644 --- a/src/mongo/db/matcher/expression_parser.cpp +++ b/src/mongo/db/matcher/expression_parser.cpp @@ -101,6 +101,8 @@ const boost::container::flat_set<StringData> topLevelOperators{"$_internalSchema namespace mongo { constexpr StringData MatchExpressionParser::kAggExpression; +constexpr StringData AlwaysFalseMatchExpression::kName; +constexpr StringData AlwaysTrueMatchExpression::kName; using std::string; using stdx::make_unique; diff --git a/src/mongo/db/matcher/expression_parser_test.cpp b/src/mongo/db/matcher/expression_parser_test.cpp index 0caafd75f92..9d9fe4d7856 100644 --- a/src/mongo/db/matcher/expression_parser_test.cpp +++ b/src/mongo/db/matcher/expression_parser_test.cpp @@ -35,6 +35,7 @@ #include "mongo/db/jsobj.h" #include "mongo/db/json.h" #include "mongo/db/matcher/expression.h" +#include "mongo/db/matcher/expression_always_boolean.h" #include "mongo/db/matcher/expression_leaf.h" #include "mongo/db/matcher/extensions_callback_noop.h" #include "mongo/db/pipeline/expression_context_for_test.h" @@ -225,26 +226,25 @@ TEST(StatusWithTest, Fib1) { } TEST(MatchExpressionParserTest, AlwaysFalseFailsToParseNonOneArguments) { - auto queryIntArgument = BSON("$alwaysFalse" << 0); + auto queryIntArgument = BSON(AlwaysFalseMatchExpression::kName << 0); auto expr = MatchExpressionParser::parse(queryIntArgument, nullptr); ASSERT_EQ(expr.getStatus(), ErrorCodes::FailedToParse); - auto queryStringArgument = BSON("$alwaysFalse" - << ""); + auto queryStringArgument = BSON(AlwaysFalseMatchExpression::kName << ""); expr = MatchExpressionParser::parse(queryStringArgument, nullptr); ASSERT_EQ(expr.getStatus(), ErrorCodes::FailedToParse); - auto queryDoubleArgument = BSON("$alwaysFalse" << 1.1); + auto queryDoubleArgument = BSON(AlwaysFalseMatchExpression::kName << 1.1); expr = MatchExpressionParser::parse(queryDoubleArgument, nullptr); ASSERT_EQ(expr.getStatus(), ErrorCodes::FailedToParse); - auto queryFalseArgument = BSON("$alwaysFalse" << true); + auto queryFalseArgument = BSON(AlwaysFalseMatchExpression::kName << true); expr = MatchExpressionParser::parse(queryFalseArgument, nullptr); ASSERT_EQ(expr.getStatus(), ErrorCodes::FailedToParse); } TEST(MatchExpressionParserTest, AlwaysFalseParsesIntegerArgument) { - auto query = BSON("$alwaysFalse" << 1); + auto query = BSON(AlwaysFalseMatchExpression::kName << 1); auto expr = MatchExpressionParser::parse(query, nullptr); ASSERT_OK(expr.getStatus()); @@ -254,26 +254,25 @@ TEST(MatchExpressionParserTest, AlwaysFalseParsesIntegerArgument) { } TEST(MatchExpressionParserTest, AlwaysTrueFailsToParseNonOneArguments) { - auto queryIntArgument = BSON("$alwaysTrue" << 0); + auto queryIntArgument = BSON(AlwaysTrueMatchExpression::kName << 0); auto expr = MatchExpressionParser::parse(queryIntArgument, nullptr); ASSERT_EQ(expr.getStatus(), ErrorCodes::FailedToParse); - auto queryStringArgument = BSON("$alwaysTrue" - << ""); + auto queryStringArgument = BSON(AlwaysTrueMatchExpression::kName << ""); expr = MatchExpressionParser::parse(queryStringArgument, nullptr); ASSERT_EQ(expr.getStatus(), ErrorCodes::FailedToParse); - auto queryDoubleArgument = BSON("$alwaysTrue" << 1.1); + auto queryDoubleArgument = BSON(AlwaysTrueMatchExpression::kName << 1.1); expr = MatchExpressionParser::parse(queryDoubleArgument, nullptr); ASSERT_EQ(expr.getStatus(), ErrorCodes::FailedToParse); - auto queryFalseArgument = BSON("$alwaysTrue" << true); + auto queryFalseArgument = BSON(AlwaysTrueMatchExpression::kName << true); expr = MatchExpressionParser::parse(queryFalseArgument, nullptr); ASSERT_EQ(expr.getStatus(), ErrorCodes::FailedToParse); } TEST(MatchExpressionParserTest, AlwaysTrueParsesIntegerArgument) { - auto query = BSON("$alwaysTrue" << 1); + auto query = BSON(AlwaysTrueMatchExpression::kName << 1); auto expr = MatchExpressionParser::parse(query, nullptr); ASSERT_OK(expr.getStatus()); diff --git a/src/mongo/db/matcher/expression_serialization_test.cpp b/src/mongo/db/matcher/expression_serialization_test.cpp index 2d2bd3127fc..4ae5a56848d 100644 --- a/src/mongo/db/matcher/expression_serialization_test.cpp +++ b/src/mongo/db/matcher/expression_serialization_test.cpp @@ -32,6 +32,7 @@ #include "mongo/db/json.h" #include "mongo/db/matcher/expression.h" +#include "mongo/db/matcher/expression_always_boolean.h" #include "mongo/db/matcher/expression_parser.h" #include "mongo/db/matcher/extensions_callback_noop.h" #include "mongo/db/matcher/matcher.h" @@ -142,6 +143,19 @@ TEST(SerializeBasic, ExpressionOr) { ASSERT_EQ(original.matches(obj), reserialized.matches(obj)); } +TEST(SerializeBasic, ExpressionOrWithNoChildrenSerializesCorrectly) { + boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + // We construct an OrMatchExpression directly rather than using the match expression + // parser, since the parser does not permit a $or with no children. + OrMatchExpression original; + Matcher reserialized(serialize(&original), + kSimpleCollator, + expCtx, + ExtensionsCallbackNoop(), + MatchExpressionParser::kAllowAllSpecialFeatures); + ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), BSON(AlwaysFalseMatchExpression::kName << 1)); +} + TEST(SerializeBasic, ExpressionElemMatchObjectSerializesCorrectly) { boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); Matcher original(fromjson("{x: {$elemMatch: {a: {$gt: 0}, b: {$gt: 0}}}}"), @@ -323,7 +337,7 @@ TEST(SerializeBasic, ExpressionAllWithEmptyArraySerializesCorrectly) { expCtx, ExtensionsCallbackNoop(), MatchExpressionParser::kAllowAllSpecialFeatures); - ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{$alwaysFalse: 1}")); + ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), BSON(AlwaysFalseMatchExpression::kName << 1)); ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); BSONObj obj = fromjson("{x: [1, 2, 3]}"); @@ -1444,7 +1458,7 @@ TEST(SerializeBasic, ExpressionTextWithDefaultLanguageSerializesCorrectly) { TEST(SerializeBasic, ExpressionAlwaysTrueSerializesCorrectly) { boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - Matcher original(fromjson("{$alwaysTrue: 1}"), + Matcher original(BSON(AlwaysTrueMatchExpression::kName << 1), kSimpleCollator, expCtx, ExtensionsCallbackNoop(), @@ -1454,13 +1468,13 @@ TEST(SerializeBasic, ExpressionAlwaysTrueSerializesCorrectly) { expCtx, ExtensionsCallbackNoop(), MatchExpressionParser::kAllowAllSpecialFeatures); - ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{$alwaysTrue: 1}")); + ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), BSON(AlwaysTrueMatchExpression::kName << 1)); ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); } TEST(SerializeBasic, ExpressionAlwaysFalseSerializesCorrectly) { boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); - Matcher original(fromjson("{$alwaysFalse: 1}"), + Matcher original(BSON(AlwaysFalseMatchExpression::kName << 1), kSimpleCollator, expCtx, ExtensionsCallbackNoop(), @@ -1470,7 +1484,7 @@ TEST(SerializeBasic, ExpressionAlwaysFalseSerializesCorrectly) { expCtx, ExtensionsCallbackNoop(), MatchExpressionParser::kAllowAllSpecialFeatures); - ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), fromjson("{$alwaysFalse: 1}")); + ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), BSON(AlwaysFalseMatchExpression::kName << 1)); ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression())); } diff --git a/src/mongo/db/matcher/expression_tree.cpp b/src/mongo/db/matcher/expression_tree.cpp index e1dd45ee0e7..370b1e675b1 100644 --- a/src/mongo/db/matcher/expression_tree.cpp +++ b/src/mongo/db/matcher/expression_tree.cpp @@ -33,6 +33,7 @@ #include "mongo/bson/bsonmisc.h" #include "mongo/bson/bsonobj.h" #include "mongo/bson/bsonobjbuilder.h" +#include "mongo/db/matcher/expression_always_boolean.h" namespace mongo { @@ -147,6 +148,10 @@ void OrMatchExpression::debugString(StringBuilder& debug, int level) const { } void OrMatchExpression::serialize(BSONObjBuilder* out) const { + if (!numChildren()) { + out->append(AlwaysFalseMatchExpression::kName, 1); + return; + } BSONArrayBuilder arrBob(out->subarrayStart("$or")); _listToBSON(&arrBob); } diff --git a/src/mongo/db/matcher/expression_with_placeholder_test.cpp b/src/mongo/db/matcher/expression_with_placeholder_test.cpp index 3c7702e2703..1d717bb1f19 100644 --- a/src/mongo/db/matcher/expression_with_placeholder_test.cpp +++ b/src/mongo/db/matcher/expression_with_placeholder_test.cpp @@ -29,6 +29,7 @@ #include "mongo/platform/basic.h" #include "mongo/db/json.h" +#include "mongo/db/matcher/expression_always_boolean.h" #include "mongo/db/matcher/expression_with_placeholder.h" #include "mongo/db/query/collation/collator_interface_mock.h" #include "mongo/unittest/unittest.h" @@ -153,14 +154,14 @@ TEST(ExpressionWithPlaceholderTest, SuccessfullyParsesExpressionsWithTypeOther) TEST(ExpressionWithPlaceholderTest, SuccessfullyParsesAlwaysTrue) { constexpr CollatorInterface* collator = nullptr; - auto rawFilter = fromjson("{$alwaysTrue: 1}"); + auto rawFilter = BSON(AlwaysTrueMatchExpression::kName << 1); auto result = assertGet(ExpressionWithPlaceholder::parse(rawFilter, collator)); ASSERT_FALSE(result->getPlaceholder()); } TEST(ExpressionWithPlaceholderTest, SuccessfullyParsesAlwaysFalse) { constexpr CollatorInterface* collator = nullptr; - auto rawFilter = fromjson("{$alwaysFalse: 1}"); + auto rawFilter = BSON(AlwaysFalseMatchExpression::kName << 1); auto result = assertGet(ExpressionWithPlaceholder::parse(rawFilter, collator)); ASSERT_FALSE(result->getPlaceholder()); } @@ -299,11 +300,11 @@ TEST(ExpressionWithPlaceholderTest, SameObjectMatchesAreEquivalent) { TEST(ExpressionWithPlaceholderTest, AlwaysTruesAreEquivalent) { constexpr auto collator = nullptr; - auto rawFilter1 = fromjson("{$alwaysTrue: 1}"); + auto rawFilter1 = BSON(AlwaysTrueMatchExpression::kName << 1); auto expressionWithPlaceholder1 = ExpressionWithPlaceholder::parse(rawFilter1, collator); ASSERT_OK(expressionWithPlaceholder1.getStatus()); - auto rawFilter2 = fromjson("{$alwaysTrue: 1}"); + auto rawFilter2 = BSON(AlwaysTrueMatchExpression::kName << 1); auto expressionWithPlaceholder2 = ExpressionWithPlaceholder::parse(rawFilter2, collator); ASSERT_OK(expressionWithPlaceholder2.getStatus()); ASSERT(expressionWithPlaceholder1.getValue()->equivalent( 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 48f24f70012..1e6808e006e 100644 --- a/src/mongo/db/matcher/schema/json_schema_parser_test.cpp +++ b/src/mongo/db/matcher/schema/json_schema_parser_test.cpp @@ -29,6 +29,8 @@ #include "mongo/platform/basic.h" #include "mongo/bson/json.h" +#include "mongo/db/bson/bson_helper.h" +#include "mongo/db/matcher/expression_always_boolean.h" #include "mongo/db/matcher/schema/json_schema_parser.h" #include "mongo/unittest/unittest.h" @@ -139,7 +141,7 @@ TEST(JSONSchemaParserTest, TopLevelNonObjectTypeTranslatesCorrectly) { BSONObj schema = fromjson("{type: 'string'}"); auto result = JSONSchemaParser::parse(schema); ASSERT_OK(result.getStatus()); - ASSERT_SERIALIZES_TO(result.getValue().get(), fromjson("{$alwaysFalse: 1}")); + ASSERT_SERIALIZES_TO(result.getValue().get(), BSON(AlwaysFalseMatchExpression::kName << 1)); } TEST(JSONSchemaParserTest, TypeNumberTranslatesCorrectly) { @@ -1361,13 +1363,13 @@ TEST(JSONSchemaParserTest, FailsToParseIfBsonTypeArrayContainsUnknownAlias) { TEST(JSONSchemaParserTest, CanTranslateTopLevelTypeArrayWithoutObject) { BSONObj schema = fromjson("{type: ['number', 'string']}}}"); auto result = JSONSchemaParser::parse(schema); - ASSERT_SERIALIZES_TO(result.getValue().get(), fromjson("{$alwaysFalse: 1}")); + ASSERT_SERIALIZES_TO(result.getValue().get(), BSON(AlwaysFalseMatchExpression::kName << 1)); } TEST(JSONSchemaParserTest, CanTranslateTopLevelBsonTypeArrayWithoutObject) { BSONObj schema = fromjson("{bsonType: ['number', 'string']}}}"); auto result = JSONSchemaParser::parse(schema); - ASSERT_SERIALIZES_TO(result.getValue().get(), fromjson("{$alwaysFalse: 1}")); + ASSERT_SERIALIZES_TO(result.getValue().get(), BSON(AlwaysFalseMatchExpression::kName << 1)); } TEST(JSONSchemaParserTest, CanTranslateTopLevelTypeArrayWithObject) { |