summaryrefslogtreecommitdiff
path: root/src/mongo
diff options
context:
space:
mode:
authorJames Wahlin <james@mongodb.com>2017-06-29 13:41:44 -0400
committerJames Wahlin <james@mongodb.com>2017-07-21 11:18:54 -0400
commita7164c0527ac1f231d12a889bf6d16b264af338e (patch)
tree47994590d41924ca1c6cff09f6f1aafe25e8792c /src/mongo
parente6032315026a446d1c8eef647b0bd6f2e82f2edc (diff)
downloadmongo-a7164c0527ac1f231d12a889bf6d16b264af338e.tar.gz
SERVER-29814 Move BSONObj::MatchType/BSONElement::getGtLtOp() to matcher
Diffstat (limited to 'src/mongo')
-rw-r--r--src/mongo/bson/bsonelement.cpp50
-rw-r--r--src/mongo/bson/bsonelement.h3
-rw-r--r--src/mongo/bson/bsonmisc.cpp8
-rw-r--r--src/mongo/bson/bsonmisc.h2
-rw-r--r--src/mongo/bson/bsonobj.h32
-rw-r--r--src/mongo/db/fts/SConscript65
-rw-r--r--src/mongo/db/fts/fts_spec.cpp4
-rw-r--r--src/mongo/db/index/SConscript1
-rw-r--r--src/mongo/db/matcher/SConscript1
-rw-r--r--src/mongo/db/matcher/expression_geo.cpp12
-rw-r--r--src/mongo/db/matcher/expression_parser.cpp171
-rw-r--r--src/mongo/db/matcher/expression_parser.h44
-rw-r--r--src/mongo/db/matcher/path_accepting_keyword_test.cpp140
-rw-r--r--src/mongo/db/ops/modifier_pull.cpp4
-rw-r--r--src/mongo/db/pipeline/document_source_match.cpp62
-rw-r--r--src/mongo/db/update/pull_node.cpp3
16 files changed, 377 insertions, 225 deletions
diff --git a/src/mongo/bson/bsonelement.cpp b/src/mongo/bson/bsonelement.cpp
index ecb1f932eef..de5d1e6d303 100644
--- a/src/mongo/bson/bsonelement.cpp
+++ b/src/mongo/bson/bsonelement.cpp
@@ -306,42 +306,6 @@ string BSONElement::jsonString(JsonStringFormat format, bool includeFieldNames,
namespace {
-// Map from query operator string name to operator MatchType. Used in BSONElement::getGtLtOp().
-const StringMap<BSONObj::MatchType> queryOperatorMap{
- // TODO: SERVER-19565 Add $eq after auditing callers.
- {"lt", BSONObj::LT},
- {"lte", BSONObj::LTE},
- {"gte", BSONObj::GTE},
- {"gt", BSONObj::GT},
- {"in", BSONObj::opIN},
- {"ne", BSONObj::NE},
- {"size", BSONObj::opSIZE},
- {"all", BSONObj::opALL},
- {"nin", BSONObj::NIN},
- {"exists", BSONObj::opEXISTS},
- {"mod", BSONObj::opMOD},
- {"type", BSONObj::opTYPE},
- {"regex", BSONObj::opREGEX},
- {"options", BSONObj::opOPTIONS},
- {"elemMatch", BSONObj::opELEM_MATCH},
- {"near", BSONObj::opNEAR},
- {"nearSphere", BSONObj::opNEAR},
- {"geoNear", BSONObj::opNEAR},
- {"within", BSONObj::opWITHIN},
- {"geoWithin", BSONObj::opWITHIN},
- {"geoIntersects", BSONObj::opGEO_INTERSECTS},
- {"bitsAllSet", BSONObj::opBITS_ALL_SET},
- {"bitsAllClear", BSONObj::opBITS_ALL_CLEAR},
- {"bitsAnySet", BSONObj::opBITS_ANY_SET},
- {"bitsAnyClear", BSONObj::opBITS_ANY_CLEAR},
- {"_internalSchemaMinItems", BSONObj::opINTERNAL_SCHEMA_MIN_ITEMS},
- {"_internalSchemaMaxItems", BSONObj::opINTERNAL_SCHEMA_MAX_ITEMS},
- {"_internalSchemaUniqueItems", BSONObj::opINTERNAL_SCHEMA_UNIQUE_ITEMS},
- {"_internalSchemaObjectMatch", BSONObj::opINTERNAL_SCHEMA_OBJECT_MATCH},
- {"_internalSchemaMinLength", BSONObj::opINTERNAL_SCHEMA_MIN_LENGTH},
- {"_internalSchemaMaxLength", BSONObj::opINTERNAL_SCHEMA_MAX_LENGTH},
-};
-
// Compares two string elements using a simple binary compare.
int compareElementStringValues(const BSONElement& leftStr, const BSONElement& rightStr) {
// we use memcmp as we allow zeros in UTF8 strings
@@ -357,20 +321,6 @@ int compareElementStringValues(const BSONElement& leftStr, const BSONElement& ri
} // namespace
-int BSONElement::getGtLtOp(int def) const {
- const char* fn = fieldName();
- if (fn[0] == '$' && fn[1]) {
- StringData opName = fieldNameStringData().substr(1);
-
- StringMap<BSONObj::MatchType>::const_iterator queryOp = queryOperatorMap.find(opName);
- if (queryOp == queryOperatorMap.end()) {
- return def;
- }
- return queryOp->second;
- }
- return def;
-}
-
/** transform a BSON array into a vector of BSONElements.
we match array # positions with their vector position, and ignore
any fields with non-numeric field names.
diff --git a/src/mongo/bson/bsonelement.h b/src/mongo/bson/bsonelement.h
index 060e9c2c491..add268f1a1d 100644
--- a/src/mongo/bson/bsonelement.h
+++ b/src/mongo/bson/bsonelement.h
@@ -557,9 +557,6 @@ public:
return data;
}
- /** 0 == Equality, just not defined yet */
- int getGtLtOp(int def = 0) const;
-
/** Constructs an empty element */
BSONElement();
diff --git a/src/mongo/bson/bsonmisc.cpp b/src/mongo/bson/bsonmisc.cpp
index 28e88690b29..4aab5b6265d 100644
--- a/src/mongo/bson/bsonmisc.cpp
+++ b/src/mongo/bson/bsonmisc.cpp
@@ -31,14 +31,6 @@
namespace mongo {
-int getGtLtOp(const BSONElement& e) {
- if (e.type() != Object)
- return BSONObj::Equality;
-
- BSONElement fe = e.embeddedObject().firstElement();
- return fe.getGtLtOp();
-}
-
bool fieldsMatch(const BSONObj& lhs, const BSONObj& rhs) {
BSONObjIterator l(lhs);
BSONObjIterator r(rhs);
diff --git a/src/mongo/bson/bsonmisc.h b/src/mongo/bson/bsonmisc.h
index 2ca9d39c805..cd8e973687f 100644
--- a/src/mongo/bson/bsonmisc.h
+++ b/src/mongo/bson/bsonmisc.h
@@ -34,8 +34,6 @@
namespace mongo {
-int getGtLtOp(const BSONElement& e);
-
class BSONElementCmpWithoutField {
public:
/**
diff --git a/src/mongo/bson/bsonobj.h b/src/mongo/bson/bsonobj.h
index 8a6e41f88f9..4344995b23c 100644
--- a/src/mongo/bson/bsonobj.h
+++ b/src/mongo/bson/bsonobj.h
@@ -512,38 +512,6 @@ public:
*/
bool valid(BSONVersion version) const;
- enum MatchType {
- Equality = 0,
- LT = 0x1,
- LTE = 0x3,
- GTE = 0x6,
- GT = 0x4,
- opIN = 0x8, // { x : { $in : [1,2,3] } }
- NE = 0x9,
- opSIZE = 0x0A,
- opALL = 0x0B,
- NIN = 0x0C,
- opEXISTS = 0x0D,
- opMOD = 0x0E,
- opTYPE = 0x0F,
- opREGEX = 0x10,
- opOPTIONS = 0x11,
- opELEM_MATCH = 0x12,
- opNEAR = 0x13,
- opWITHIN = 0x14,
- opGEO_INTERSECTS = 0x16,
- opBITS_ALL_SET = 0x17,
- opBITS_ALL_CLEAR = 0x18,
- opBITS_ANY_SET = 0x19,
- opBITS_ANY_CLEAR = 0x1A,
- opINTERNAL_SCHEMA_MIN_ITEMS = 0x1B,
- opINTERNAL_SCHEMA_MAX_ITEMS = 0x1C,
- opINTERNAL_SCHEMA_UNIQUE_ITEMS = 0x1D,
- opINTERNAL_SCHEMA_OBJECT_MATCH = 0x1E,
- opINTERNAL_SCHEMA_MIN_LENGTH = 0x1F,
- opINTERNAL_SCHEMA_MAX_LENGTH = 0x20,
- };
-
/** add all elements of the object to the specified vector */
void elems(std::vector<BSONElement>&) const;
/** add all elements of the object to the specified list */
diff --git a/src/mongo/db/fts/SConscript b/src/mongo/db/fts/SConscript
index 6111a897f0c..8b9a7c1c103 100644
--- a/src/mongo/db/fts/SConscript
+++ b/src/mongo/db/fts/SConscript
@@ -60,6 +60,7 @@ baseEnv.Library('base', [
"$BUILD_DIR/mongo/db/bson/dotted_path_support",
"$BUILD_DIR/mongo/db/common",
"$BUILD_DIR/mongo/db/fts/unicode/unicode",
+ "$BUILD_DIR/mongo/db/matcher/expressions",
"$BUILD_DIR/mongo/util/md5",
"$BUILD_DIR/third_party/shim_stemmer",
])
@@ -78,44 +79,26 @@ env.Library('ftsmongod', [
'ftsmongod.cpp',
], LIBDEPS=["base","$BUILD_DIR/mongo/base"])
-env.CppUnitTest( "fts_basic_phrase_matcher_test", "fts_basic_phrase_matcher_test.cpp",
- LIBDEPS=["base"] )
-
-env.CppUnitTest( "fts_basic_tokenizer_test", "fts_basic_tokenizer_test.cpp",
- LIBDEPS=["base"] )
-
-env.CppUnitTest( "fts_element_iterator_test", "fts_element_iterator_test.cpp",
- LIBDEPS=["base"] )
-
-env.CppUnitTest( "fts_index_format_test", "fts_index_format_test.cpp",
- LIBDEPS=["base"] )
-
-env.CppUnitTest( "fts_language_test", "fts_language_test.cpp",
- LIBDEPS=["base"] )
-
-env.CppUnitTest( "fts_matcher_test", "fts_matcher_test.cpp",
- LIBDEPS=["base"] )
-
-env.CppUnitTest( "fts_query_impl_test", "fts_query_impl_test.cpp",
- LIBDEPS=["base"] )
-
-env.CppUnitTest( "fts_query_noop_test", "fts_query_noop_test.cpp",
- LIBDEPS=["fts_query_noop"] )
-
-env.CppUnitTest( "fts_spec_test", "fts_spec_test.cpp",
- LIBDEPS=["base"] )
-
-env.CppUnitTest( "fts_stemmer_test", "stemmer_test.cpp",
- LIBDEPS=["base"] )
-
-env.CppUnitTest( "fts_stop_words_test", "stop_words_test.cpp",
- LIBDEPS=["base"] )
-
-env.CppUnitTest( "fts_tokenizer_test", "tokenizer_test.cpp",
- LIBDEPS=["base"] )
-
-env.CppUnitTest( "fts_unicode_phrase_matcher_test", "fts_unicode_phrase_matcher_test.cpp",
- LIBDEPS=["base"] )
-
-env.CppUnitTest( "fts_unicode_tokenizer_test", "fts_unicode_tokenizer_test.cpp",
- LIBDEPS=["base"] )
+env.CppUnitTest(target='fts_test',
+ source=[
+ "fts_basic_phrase_matcher_test.cpp",
+ "fts_basic_tokenizer_test.cpp",
+ "fts_element_iterator_test.cpp",
+ "fts_index_format_test.cpp",
+ "fts_language_test.cpp",
+ "fts_matcher_test.cpp",
+ "fts_query_impl_test.cpp",
+ "fts_query_noop_test.cpp",
+ "fts_spec_test.cpp",
+ "fts_unicode_phrase_matcher_test.cpp",
+ "fts_unicode_tokenizer_test.cpp",
+ "stemmer_test.cpp",
+ "stop_words_test.cpp",
+ "tokenizer_test.cpp",
+ ],
+ LIBDEPS=[
+ "$BUILD_DIR/mongo/db/fts/fts_query_noop",
+ "$BUILD_DIR/mongo/db/matcher/expressions",
+ "base",
+ ],
+) \ No newline at end of file
diff --git a/src/mongo/db/fts/fts_spec.cpp b/src/mongo/db/fts/fts_spec.cpp
index c9216b0bb45..9022b72bb0b 100644
--- a/src/mongo/db/fts/fts_spec.cpp
+++ b/src/mongo/db/fts/fts_spec.cpp
@@ -36,6 +36,7 @@
#include "mongo/db/fts/fts_element_iterator.h"
#include "mongo/db/fts/fts_tokenizer.h"
#include "mongo/db/fts/fts_util.h"
+#include "mongo/db/matcher/expression_parser.h"
#include "mongo/util/mongoutils/str.h"
#include "mongo/util/stringutils.h"
@@ -249,7 +250,8 @@ Status FTSSpec::getIndexPrefix(const BSONObj& query, BSONObj* out) const {
return Status(ErrorCodes::BadValue,
str::stream() << "need have an equality filter on: " << extraBefore(i));
- if (e.isABSONObj() && e.Obj().firstElement().getGtLtOp(-1) != -1)
+ if (e.isABSONObj() &&
+ MatchExpressionParser::parsePathAcceptingKeyword(e.Obj().firstElement()))
return Status(ErrorCodes::BadValue,
str::stream() << "need have an equality filter on: " << extraBefore(i));
diff --git a/src/mongo/db/index/SConscript b/src/mongo/db/index/SConscript
index 92edf813f18..63c69e1763d 100644
--- a/src/mongo/db/index/SConscript
+++ b/src/mongo/db/index/SConscript
@@ -73,6 +73,7 @@ env.CppUnitTest(
],
LIBDEPS=[
'key_generator',
+ "$BUILD_DIR/mongo/db/matcher/expressions",
'$BUILD_DIR/mongo/db/mongohasher',
'$BUILD_DIR/mongo/db/query/collation/collator_interface_mock',
],
diff --git a/src/mongo/db/matcher/SConscript b/src/mongo/db/matcher/SConscript
index 9585af33806..47868a55921 100644
--- a/src/mongo/db/matcher/SConscript
+++ b/src/mongo/db/matcher/SConscript
@@ -75,6 +75,7 @@ env.CppUnitTest(
'expression_parser_geo_test.cpp',
'expression_test.cpp',
'expression_tree_test.cpp',
+ 'path_accepting_keyword_test.cpp',
'schema/expression_internal_schema_max_items_test.cpp',
'schema/expression_internal_schema_max_length_test.cpp',
'schema/expression_internal_schema_min_items_test.cpp',
diff --git a/src/mongo/db/matcher/expression_geo.cpp b/src/mongo/db/matcher/expression_geo.cpp
index 104eefec513..c15d1f564a7 100644
--- a/src/mongo/db/matcher/expression_geo.cpp
+++ b/src/mongo/db/matcher/expression_geo.cpp
@@ -31,8 +31,10 @@
#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kDefault
#include "mongo/db/matcher/expression_geo.h"
+
#include "mongo/bson/simple_bsonobj_comparator.h"
#include "mongo/db/geo/geoparser.h"
+#include "mongo/db/matcher/expression_parser.h"
#include "mongo/platform/basic.h"
#include "mongo/util/log.h"
#include "mongo/util/mongoutils/str.h"
@@ -59,10 +61,10 @@ Status GeoExpression::parseQuery(const BSONObj& obj) {
str::stream() << "can't parse extra field: " << outerIt.next());
}
- BSONObj::MatchType matchType = static_cast<BSONObj::MatchType>(queryElt.getGtLtOp());
- if (BSONObj::opGEO_INTERSECTS == matchType) {
+ auto keyword = MatchExpressionParser::parsePathAcceptingKeyword(queryElt);
+ if (PathAcceptingKeyword::GEO_INTERSECTS == keyword) {
predicate = GeoExpression::INTERSECT;
- } else if (BSONObj::opWITHIN == matchType) {
+ } else if (PathAcceptingKeyword::WITHIN == keyword) {
predicate = GeoExpression::WITHIN;
} else {
// eoo() or unknown query predicate.
@@ -230,8 +232,8 @@ Status GeoNearExpression::parseNewQuery(const BSONObj& obj) {
if (!e.isABSONObj()) {
return Status(ErrorCodes::BadValue, "geo near query argument is not an object");
}
- BSONObj::MatchType matchType = static_cast<BSONObj::MatchType>(e.getGtLtOp());
- if (BSONObj::opNEAR != matchType) {
+
+ if (PathAcceptingKeyword::GEO_NEAR != MatchExpressionParser::parsePathAcceptingKeyword(e)) {
return Status(ErrorCodes::BadValue,
mongoutils::str::stream() << "invalid geo near query operator: "
<< e.fieldName());
diff --git a/src/mongo/db/matcher/expression_parser.cpp b/src/mongo/db/matcher/expression_parser.cpp
index 9263862e16c..92639890e08 100644
--- a/src/mongo/db/matcher/expression_parser.cpp
+++ b/src/mongo/db/matcher/expression_parser.cpp
@@ -30,6 +30,7 @@
#include "mongo/db/matcher/expression_parser.h"
+#include "mongo/base/init.h"
#include "mongo/bson/bsonmisc.h"
#include "mongo/bson/bsonobj.h"
#include "mongo/bson/bsonobjbuilder.h"
@@ -48,6 +49,7 @@
#include "mongo/db/namespace_string.h"
#include "mongo/stdx/memory.h"
#include "mongo/util/mongoutils/str.h"
+#include "mongo/util/string_map.h"
namespace {
@@ -107,8 +109,6 @@ StatusWithMatchExpression MatchExpressionParser::_parseSubField(const BSONObj& c
const BSONElement& e,
const CollatorInterface* collator,
bool topLevel) {
- // TODO: these should move to getGtLtOp, or its replacement
-
if (mongoutils::str::equals("$eq", e.fieldName()))
return _parseComparison(name, new EqualityMatchExpression(), e, collator);
@@ -116,25 +116,27 @@ StatusWithMatchExpression MatchExpressionParser::_parseSubField(const BSONObj& c
return _parseNot(name, e, collator, topLevel);
}
- int x = e.getGtLtOp(-1);
- switch (x) {
- case -1:
- // $where cannot be a sub-expression because it works on top-level documents only.
- if (mongoutils::str::equals("$where", e.fieldName())) {
- return {Status(ErrorCodes::BadValue, "$where cannot be applied to a field")};
- }
+ auto parseExpMatchType = MatchExpressionParser::parsePathAcceptingKeyword(e);
+ if (!parseExpMatchType) {
+ // $where cannot be a sub-expression because it works on top-level documents only.
+ if (mongoutils::str::equals("$where", e.fieldName())) {
+ return {Status(ErrorCodes::BadValue, "$where cannot be applied to a field")};
+ }
- return {Status(ErrorCodes::BadValue,
- mongoutils::str::stream() << "unknown operator: " << e.fieldName())};
- case BSONObj::LT:
+ return {Status(ErrorCodes::BadValue,
+ mongoutils::str::stream() << "unknown operator: " << e.fieldName())};
+ }
+
+ switch (*parseExpMatchType) {
+ case PathAcceptingKeyword::LESS_THAN:
return _parseComparison(name, new LTMatchExpression(), e, collator);
- case BSONObj::LTE:
+ case PathAcceptingKeyword::LESS_THAN_OR_EQUAL:
return _parseComparison(name, new LTEMatchExpression(), e, collator);
- case BSONObj::GT:
+ case PathAcceptingKeyword::GREATER_THAN:
return _parseComparison(name, new GTMatchExpression(), e, collator);
- case BSONObj::GTE:
+ case PathAcceptingKeyword::GREATER_THAN_OR_EQUAL:
return _parseComparison(name, new GTEMatchExpression(), e, collator);
- case BSONObj::NE: {
+ case PathAcceptingKeyword::NOT_EQUAL: {
if (RegEx == e.type()) {
// Just because $ne can be rewritten as the negation of an
// equality does not mean that $ne of a regex is allowed. See SERVER-1705.
@@ -150,10 +152,10 @@ StatusWithMatchExpression MatchExpressionParser::_parseSubField(const BSONObj& c
return s2;
return {std::move(n)};
}
- case BSONObj::Equality:
+ case PathAcceptingKeyword::EQUALITY:
return _parseComparison(name, new EqualityMatchExpression(), e, collator);
- case BSONObj::opIN: {
+ case PathAcceptingKeyword::IN_EXPR: {
if (e.type() != Array)
return {Status(ErrorCodes::BadValue, "$in needs an array")};
std::unique_ptr<InMatchExpression> temp = stdx::make_unique<InMatchExpression>();
@@ -166,7 +168,7 @@ StatusWithMatchExpression MatchExpressionParser::_parseSubField(const BSONObj& c
return {std::move(temp)};
}
- case BSONObj::NIN: {
+ case PathAcceptingKeyword::NOT_IN: {
if (e.type() != Array)
return {Status(ErrorCodes::BadValue, "$nin needs an array")};
std::unique_ptr<InMatchExpression> temp = stdx::make_unique<InMatchExpression>();
@@ -185,7 +187,7 @@ StatusWithMatchExpression MatchExpressionParser::_parseSubField(const BSONObj& c
return {std::move(temp2)};
}
- case BSONObj::opSIZE: {
+ case PathAcceptingKeyword::SIZE: {
int size = 0;
if (e.type() == NumberInt) {
size = e.numberInt();
@@ -216,7 +218,7 @@ StatusWithMatchExpression MatchExpressionParser::_parseSubField(const BSONObj& c
return {std::move(temp)};
}
- case BSONObj::opEXISTS: {
+ case PathAcceptingKeyword::EXISTS: {
if (e.eoo())
return {Status(ErrorCodes::BadValue, "$exists can't be eoo")};
std::unique_ptr<ExistsMatchExpression> temp =
@@ -233,73 +235,74 @@ StatusWithMatchExpression MatchExpressionParser::_parseSubField(const BSONObj& c
return {std::move(temp2)};
}
- case BSONObj::opTYPE:
+ case PathAcceptingKeyword::TYPE:
return _parseType(name, e);
- case BSONObj::opMOD:
+ case PathAcceptingKeyword::MOD:
return _parseMOD(name, e);
- case BSONObj::opOPTIONS: {
+ case PathAcceptingKeyword::OPTIONS: {
// TODO: try to optimize this
// we have to do this since $options can be before or after a $regex
// but we validate here
BSONObjIterator i(context);
while (i.more()) {
BSONElement temp = i.next();
- if (temp.getGtLtOp(-1) == BSONObj::opREGEX)
+ if (MatchExpressionParser::parsePathAcceptingKeyword(temp) ==
+ PathAcceptingKeyword::REGEX)
return {nullptr};
}
return {Status(ErrorCodes::BadValue, "$options needs a $regex")};
}
- case BSONObj::opREGEX: {
+ case PathAcceptingKeyword::REGEX: {
return _parseRegexDocument(name, context);
}
- case BSONObj::opELEM_MATCH:
+ case PathAcceptingKeyword::ELEM_MATCH:
return _parseElemMatch(name, e, collator, topLevel);
- case BSONObj::opALL:
+ case PathAcceptingKeyword::ALL:
return _parseAll(name, e, collator, topLevel);
- case BSONObj::opWITHIN:
- case BSONObj::opGEO_INTERSECTS:
- return _parseGeo(name, x, context);
+ case PathAcceptingKeyword::WITHIN:
+ case PathAcceptingKeyword::GEO_INTERSECTS:
+ return _parseGeo(name, *parseExpMatchType, context);
- case BSONObj::opNEAR:
+ case PathAcceptingKeyword::GEO_NEAR:
return {Status(ErrorCodes::BadValue,
mongoutils::str::stream() << "near must be first in: " << context)};
// Handles bitwise query operators.
- case BSONObj::opBITS_ALL_SET: {
+ case PathAcceptingKeyword::BITS_ALL_SET: {
return _parseBitTest<BitsAllSetMatchExpression>(name, e);
}
- case BSONObj::opBITS_ALL_CLEAR: {
+ case PathAcceptingKeyword::BITS_ALL_CLEAR: {
return _parseBitTest<BitsAllClearMatchExpression>(name, e);
}
- case BSONObj::opBITS_ANY_SET: {
+ case PathAcceptingKeyword::BITS_ANY_SET: {
return _parseBitTest<BitsAnySetMatchExpression>(name, e);
}
- case BSONObj::opBITS_ANY_CLEAR: {
+ case PathAcceptingKeyword::BITS_ANY_CLEAR: {
return _parseBitTest<BitsAnyClearMatchExpression>(name, e);
}
- case BSONObj::opINTERNAL_SCHEMA_MIN_ITEMS: {
+ case PathAcceptingKeyword::INTERNAL_SCHEMA_MIN_ITEMS: {
return _parseInternalSchemaSingleIntegerArgument<InternalSchemaMinItemsMatchExpression>(
name, e);
}
- case BSONObj::opINTERNAL_SCHEMA_MAX_ITEMS: {
+ case PathAcceptingKeyword::INTERNAL_SCHEMA_MAX_ITEMS: {
return _parseInternalSchemaSingleIntegerArgument<InternalSchemaMaxItemsMatchExpression>(
name, e);
}
- case BSONObj::opINTERNAL_SCHEMA_OBJECT_MATCH: {
+ case PathAcceptingKeyword::INTERNAL_SCHEMA_OBJECT_MATCH: {
if (e.type() != BSONType::Object) {
return Status(ErrorCodes::FailedToParse,
str::stream() << "$_internalSchemaObjectMatch must be an object");
@@ -318,7 +321,7 @@ StatusWithMatchExpression MatchExpressionParser::_parseSubField(const BSONObj& c
return {std::move(expr)};
}
- case BSONObj::opINTERNAL_SCHEMA_UNIQUE_ITEMS: {
+ case PathAcceptingKeyword::INTERNAL_SCHEMA_UNIQUE_ITEMS: {
if (!e.isBoolean() || !e.boolean()) {
return {ErrorCodes::FailedToParse,
str::stream() << name << " must be a boolean of value true"};
@@ -332,12 +335,12 @@ StatusWithMatchExpression MatchExpressionParser::_parseSubField(const BSONObj& c
return {std::move(expr)};
}
- case BSONObj::opINTERNAL_SCHEMA_MIN_LENGTH: {
+ case PathAcceptingKeyword::INTERNAL_SCHEMA_MIN_LENGTH: {
return _parseInternalSchemaSingleIntegerArgument<
InternalSchemaMinLengthMatchExpression>(name, e);
}
- case BSONObj::opINTERNAL_SCHEMA_MAX_LENGTH: {
+ case PathAcceptingKeyword::INTERNAL_SCHEMA_MAX_LENGTH: {
return _parseInternalSchemaSingleIntegerArgument<
InternalSchemaMaxLengthMatchExpression>(name, e);
}
@@ -497,7 +500,11 @@ Status MatchExpressionParser::_parseSub(const char* name,
if (mongoutils::str::equals(fieldName, "$near") ||
mongoutils::str::equals(fieldName, "$nearSphere") ||
mongoutils::str::equals(fieldName, "$geoNear")) {
- StatusWithMatchExpression s = _parseGeo(name, firstElt.getGtLtOp(), sub);
+ StatusWithMatchExpression s =
+ _parseGeo(name,
+ *MatchExpressionParser::parsePathAcceptingKeyword(
+ firstElt, PathAcceptingKeyword::EQUALITY),
+ sub);
if (s.isOK()) {
root->add(s.getValue().release());
}
@@ -633,8 +640,13 @@ StatusWithMatchExpression MatchExpressionParser::_parseRegexDocument(const char*
BSONObjIterator i(doc);
while (i.more()) {
BSONElement e = i.next();
- switch (e.getGtLtOp()) {
- case BSONObj::opREGEX:
+ auto matchType = MatchExpressionParser::parsePathAcceptingKeyword(e);
+ if (!matchType) {
+ continue;
+ }
+
+ switch (*matchType) {
+ case PathAcceptingKeyword::REGEX:
if (e.type() == String) {
regex = e.String();
} else if (e.type() == RegEx) {
@@ -645,7 +657,7 @@ StatusWithMatchExpression MatchExpressionParser::_parseRegexDocument(const char*
}
break;
- case BSONObj::opOPTIONS:
+ case PathAcceptingKeyword::OPTIONS:
if (e.type() != String)
return {Status(ErrorCodes::BadValue, "$options has to be a string")};
regexOptions = e.String();
@@ -885,7 +897,8 @@ StatusWithMatchExpression MatchExpressionParser::_parseAll(const char* name,
if (!s.isOK())
return s;
myAnd->add(r.release());
- } else if (e.type() == Object && e.Obj().firstElement().getGtLtOp(-1) != -1) {
+ } else if (e.type() == Object &&
+ MatchExpressionParser::parsePathAcceptingKeyword(e.Obj().firstElement())) {
return {Status(ErrorCodes::BadValue, "no $ expressions in $all")};
} else {
std::unique_ptr<EqualityMatchExpression> x =
@@ -1096,9 +1109,9 @@ StatusWithMatchExpression MatchExpressionParser::_parseInternalSchemaSingleInteg
}
StatusWithMatchExpression MatchExpressionParser::_parseGeo(const char* name,
- int type,
+ PathAcceptingKeyword type,
const BSONObj& section) {
- if (BSONObj::opWITHIN == type || BSONObj::opGEO_INTERSECTS == type) {
+ if (PathAcceptingKeyword::WITHIN == type || PathAcceptingKeyword::GEO_INTERSECTS == type) {
std::unique_ptr<GeoExpression> gq = stdx::make_unique<GeoExpression>(name);
Status parseStatus = gq->parseFrom(section);
@@ -1112,7 +1125,7 @@ StatusWithMatchExpression MatchExpressionParser::_parseGeo(const char* name,
return StatusWithMatchExpression(s);
return {std::move(e)};
} else {
- invariant(BSONObj::opNEAR == type);
+ invariant(PathAcceptingKeyword::GEO_NEAR == type);
std::unique_ptr<GeoNearExpression> nq = stdx::make_unique<GeoNearExpression>(name);
Status s = nq->parseFrom(section);
if (!s.isOK()) {
@@ -1125,4 +1138,62 @@ StatusWithMatchExpression MatchExpressionParser::_parseGeo(const char* name,
return {std::move(e)};
}
}
+
+namespace {
+// Maps from query operator string name to operator PathAcceptingKeyword.
+std::unique_ptr<StringMap<PathAcceptingKeyword>> queryOperatorMap;
+
+MONGO_INITIALIZER(MatchExpressionParser)(InitializerContext* context) {
+ queryOperatorMap =
+ stdx::make_unique<StringMap<PathAcceptingKeyword>>(StringMap<PathAcceptingKeyword>{
+ // TODO: SERVER-19565 Add $eq after auditing callers.
+ {"lt", PathAcceptingKeyword::LESS_THAN},
+ {"lte", PathAcceptingKeyword::LESS_THAN_OR_EQUAL},
+ {"gte", PathAcceptingKeyword::GREATER_THAN_OR_EQUAL},
+ {"gt", PathAcceptingKeyword::GREATER_THAN},
+ {"in", PathAcceptingKeyword::IN_EXPR},
+ {"ne", PathAcceptingKeyword::NOT_EQUAL},
+ {"size", PathAcceptingKeyword::SIZE},
+ {"all", PathAcceptingKeyword::ALL},
+ {"nin", PathAcceptingKeyword::NOT_IN},
+ {"exists", PathAcceptingKeyword::EXISTS},
+ {"mod", PathAcceptingKeyword::MOD},
+ {"type", PathAcceptingKeyword::TYPE},
+ {"regex", PathAcceptingKeyword::REGEX},
+ {"options", PathAcceptingKeyword::OPTIONS},
+ {"elemMatch", PathAcceptingKeyword::ELEM_MATCH},
+ {"near", PathAcceptingKeyword::GEO_NEAR},
+ {"nearSphere", PathAcceptingKeyword::GEO_NEAR},
+ {"geoNear", PathAcceptingKeyword::GEO_NEAR},
+ {"within", PathAcceptingKeyword::WITHIN},
+ {"geoWithin", PathAcceptingKeyword::WITHIN},
+ {"geoIntersects", PathAcceptingKeyword::GEO_INTERSECTS},
+ {"bitsAllSet", PathAcceptingKeyword::BITS_ALL_SET},
+ {"bitsAllClear", PathAcceptingKeyword::BITS_ALL_CLEAR},
+ {"bitsAnySet", PathAcceptingKeyword::BITS_ANY_SET},
+ {"bitsAnyClear", PathAcceptingKeyword::BITS_ANY_CLEAR},
+ {"_internalSchemaMinItems", PathAcceptingKeyword::INTERNAL_SCHEMA_MIN_ITEMS},
+ {"_internalSchemaMaxItems", PathAcceptingKeyword::INTERNAL_SCHEMA_MAX_ITEMS},
+ {"_internalSchemaUniqueItems", PathAcceptingKeyword::INTERNAL_SCHEMA_UNIQUE_ITEMS},
+ {"_internalSchemaObjectMatch", PathAcceptingKeyword::INTERNAL_SCHEMA_OBJECT_MATCH},
+ {"_internalSchemaMinLength", PathAcceptingKeyword::INTERNAL_SCHEMA_MIN_LENGTH},
+ {"_internalSchemaMaxLength", PathAcceptingKeyword::INTERNAL_SCHEMA_MAX_LENGTH}});
+ return Status::OK();
+}
+} // anonymous namespace
+
+boost::optional<PathAcceptingKeyword> MatchExpressionParser::parsePathAcceptingKeyword(
+ BSONElement typeElem, boost::optional<PathAcceptingKeyword> defaultKeyword) {
+ auto fieldName = typeElem.fieldName();
+ if (fieldName[0] == '$' && fieldName[1]) {
+ auto opName = typeElem.fieldNameStringData().substr(1);
+ auto queryOp = queryOperatorMap->find(opName);
+
+ if (queryOp == queryOperatorMap->end()) {
+ return defaultKeyword;
+ }
+ return queryOp->second;
+ }
+ return defaultKeyword;
+}
} // namespace mongo
diff --git a/src/mongo/db/matcher/expression_parser.h b/src/mongo/db/matcher/expression_parser.h
index c41e93ae5b1..b661693efa8 100644
--- a/src/mongo/db/matcher/expression_parser.h
+++ b/src/mongo/db/matcher/expression_parser.h
@@ -43,6 +43,38 @@ namespace mongo {
class CollatorInterface;
class OperationContext;
+enum class PathAcceptingKeyword {
+ EQUALITY,
+ LESS_THAN,
+ LESS_THAN_OR_EQUAL,
+ GREATER_THAN_OR_EQUAL,
+ GREATER_THAN,
+ IN_EXPR,
+ NOT_EQUAL,
+ SIZE,
+ ALL,
+ NOT_IN,
+ EXISTS,
+ MOD,
+ TYPE,
+ REGEX,
+ OPTIONS,
+ ELEM_MATCH,
+ GEO_NEAR,
+ WITHIN,
+ GEO_INTERSECTS,
+ BITS_ALL_SET,
+ BITS_ALL_CLEAR,
+ BITS_ANY_SET,
+ BITS_ANY_CLEAR,
+ INTERNAL_SCHEMA_MIN_ITEMS,
+ INTERNAL_SCHEMA_MAX_ITEMS,
+ INTERNAL_SCHEMA_UNIQUE_ITEMS,
+ INTERNAL_SCHEMA_OBJECT_MATCH,
+ INTERNAL_SCHEMA_MIN_LENGTH,
+ INTERNAL_SCHEMA_MAX_LENGTH
+};
+
class MatchExpressionParser {
public:
/**
@@ -51,6 +83,14 @@ public:
static const double kLongLongMaxPlusOneAsDouble;
/**
+ * Parses PathAcceptingKeyword from 'typeElem'. Returns 'defaultKeyword' if 'typeElem'
+ * doesn't represent a known type, or represents PathAcceptingKeyword::EQUALITY which is not
+ * handled by this parser (see SERVER-19565).
+ */
+ static boost::optional<PathAcceptingKeyword> parsePathAcceptingKeyword(
+ BSONElement typeElem, boost::optional<PathAcceptingKeyword> defaultKeyword = boost::none);
+
+ /**
* caller has to maintain ownership obj
* the tree has views (BSONElement) into obj
*/
@@ -167,7 +207,9 @@ private:
StatusWithMatchExpression _parseType(const char* name, const BSONElement& elt);
- StatusWithMatchExpression _parseGeo(const char* name, int type, const BSONObj& section);
+ StatusWithMatchExpression _parseGeo(const char* name,
+ PathAcceptingKeyword type,
+ const BSONObj& section);
// arrays
diff --git a/src/mongo/db/matcher/path_accepting_keyword_test.cpp b/src/mongo/db/matcher/path_accepting_keyword_test.cpp
new file mode 100644
index 00000000000..4830dd77505
--- /dev/null
+++ b/src/mongo/db/matcher/path_accepting_keyword_test.cpp
@@ -0,0 +1,140 @@
+/**
+ * Copyright (C) 2017 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.
+ */
+
+#include "mongo/platform/basic.h"
+
+#include "mongo/db/jsobj.h"
+#include "mongo/db/matcher/expression_parser.h"
+#include "mongo/unittest/unittest.h"
+
+namespace mongo {
+namespace {
+
+TEST(PathAcceptingKeyword, CanParseKnownMatchTypes) {
+ ASSERT_TRUE(PathAcceptingKeyword::LESS_THAN ==
+ MatchExpressionParser::parsePathAcceptingKeyword(BSON("$lt" << 1).firstElement()));
+ ASSERT_TRUE(PathAcceptingKeyword::LESS_THAN_OR_EQUAL ==
+ MatchExpressionParser::parsePathAcceptingKeyword(BSON("$lte" << 1).firstElement()));
+ ASSERT_TRUE(PathAcceptingKeyword::GREATER_THAN_OR_EQUAL ==
+ MatchExpressionParser::parsePathAcceptingKeyword(BSON("$gte" << 1).firstElement()));
+ ASSERT_TRUE(PathAcceptingKeyword::GREATER_THAN ==
+ MatchExpressionParser::parsePathAcceptingKeyword(BSON("$gt" << 1).firstElement()));
+ ASSERT_TRUE(PathAcceptingKeyword::IN_EXPR ==
+ MatchExpressionParser::parsePathAcceptingKeyword(BSON("$in" << 1).firstElement()));
+ ASSERT_TRUE(PathAcceptingKeyword::NOT_EQUAL ==
+ MatchExpressionParser::parsePathAcceptingKeyword(BSON("$ne" << 1).firstElement()));
+ ASSERT_TRUE(PathAcceptingKeyword::SIZE == MatchExpressionParser::parsePathAcceptingKeyword(
+ BSON("$size" << 1).firstElement()));
+ ASSERT_TRUE(PathAcceptingKeyword::ALL ==
+ MatchExpressionParser::parsePathAcceptingKeyword(BSON("$all" << 1).firstElement()));
+ ASSERT_TRUE(PathAcceptingKeyword::NOT_IN ==
+ MatchExpressionParser::parsePathAcceptingKeyword(BSON("$nin" << 1).firstElement()));
+ ASSERT_TRUE(PathAcceptingKeyword::EXISTS == MatchExpressionParser::parsePathAcceptingKeyword(
+ BSON("$exists" << 1).firstElement()));
+ ASSERT_TRUE(PathAcceptingKeyword::MOD ==
+ MatchExpressionParser::parsePathAcceptingKeyword(BSON("$mod" << 1).firstElement()));
+ ASSERT_TRUE(PathAcceptingKeyword::TYPE == MatchExpressionParser::parsePathAcceptingKeyword(
+ BSON("$type" << 1).firstElement()));
+ ASSERT_TRUE(PathAcceptingKeyword::REGEX == MatchExpressionParser::parsePathAcceptingKeyword(
+ BSON("$regex" << 1).firstElement()));
+ ASSERT_TRUE(PathAcceptingKeyword::OPTIONS == MatchExpressionParser::parsePathAcceptingKeyword(
+ BSON("$options" << 1).firstElement()));
+ ASSERT_TRUE(
+ PathAcceptingKeyword::ELEM_MATCH ==
+ MatchExpressionParser::parsePathAcceptingKeyword(BSON("$elemMatch" << 1).firstElement()));
+ ASSERT_TRUE(PathAcceptingKeyword::GEO_NEAR == MatchExpressionParser::parsePathAcceptingKeyword(
+ BSON("$near" << 1).firstElement()));
+ ASSERT_TRUE(PathAcceptingKeyword::GEO_NEAR == MatchExpressionParser::parsePathAcceptingKeyword(
+ BSON("$geoNear" << 1).firstElement()));
+ ASSERT_TRUE(PathAcceptingKeyword::WITHIN == MatchExpressionParser::parsePathAcceptingKeyword(
+ BSON("$within" << 1).firstElement()));
+ ASSERT_TRUE(PathAcceptingKeyword::WITHIN == MatchExpressionParser::parsePathAcceptingKeyword(
+ BSON("$geoWithin" << 1).firstElement()));
+ ASSERT_TRUE(PathAcceptingKeyword::GEO_INTERSECTS ==
+ MatchExpressionParser::parsePathAcceptingKeyword(
+ BSON("$geoIntersects" << 1).firstElement()));
+ ASSERT_TRUE(
+ PathAcceptingKeyword::BITS_ALL_SET ==
+ MatchExpressionParser::parsePathAcceptingKeyword(BSON("$bitsAllSet" << 1).firstElement()));
+ ASSERT_TRUE(PathAcceptingKeyword::BITS_ALL_CLEAR ==
+ MatchExpressionParser::parsePathAcceptingKeyword(
+ BSON("$bitsAllClear" << 1).firstElement()));
+ ASSERT_TRUE(
+ PathAcceptingKeyword::BITS_ANY_SET ==
+ MatchExpressionParser::parsePathAcceptingKeyword(BSON("$bitsAnySet" << 1).firstElement()));
+ ASSERT_TRUE(PathAcceptingKeyword::BITS_ANY_CLEAR ==
+ MatchExpressionParser::parsePathAcceptingKeyword(
+ BSON("$bitsAnyClear" << 1).firstElement()));
+ ASSERT_TRUE(PathAcceptingKeyword::INTERNAL_SCHEMA_MIN_ITEMS ==
+ MatchExpressionParser::parsePathAcceptingKeyword(
+ BSON("$_internalSchemaMinItems" << 1).firstElement()));
+ ASSERT_TRUE(PathAcceptingKeyword::INTERNAL_SCHEMA_MAX_ITEMS ==
+ MatchExpressionParser::parsePathAcceptingKeyword(
+ BSON("$_internalSchemaMaxItems" << 1).firstElement()));
+ ASSERT_TRUE(PathAcceptingKeyword::INTERNAL_SCHEMA_UNIQUE_ITEMS ==
+ MatchExpressionParser::parsePathAcceptingKeyword(
+ BSON("$_internalSchemaUniqueItems" << 1).firstElement()));
+ ASSERT_TRUE(PathAcceptingKeyword::INTERNAL_SCHEMA_OBJECT_MATCH ==
+ MatchExpressionParser::parsePathAcceptingKeyword(
+ BSON("$_internalSchemaObjectMatch" << 1).firstElement()));
+ ASSERT_TRUE(PathAcceptingKeyword::INTERNAL_SCHEMA_MIN_LENGTH ==
+ MatchExpressionParser::parsePathAcceptingKeyword(
+ BSON("$_internalSchemaMinLength" << 1).firstElement()));
+ ASSERT_TRUE(PathAcceptingKeyword::INTERNAL_SCHEMA_MAX_LENGTH ==
+ MatchExpressionParser::parsePathAcceptingKeyword(
+ BSON("$_internalSchemaMaxLength" << 1).firstElement()));
+}
+
+TEST(PathAcceptingKeyword, EqualityMatchReturnsDefault) {
+ // 'boost::none' is the default when none specified.
+ ASSERT_TRUE(boost::none ==
+ MatchExpressionParser::parsePathAcceptingKeyword(BSON("$eq" << 1).firstElement()));
+ // Should return default specified by caller.
+ ASSERT_TRUE(PathAcceptingKeyword::GEO_NEAR ==
+ MatchExpressionParser::parsePathAcceptingKeyword(BSON("$eq" << 1).firstElement(),
+ PathAcceptingKeyword::GEO_NEAR));
+}
+
+TEST(PathAcceptingKeyword, UnknownExpressionReturnsDefault) {
+ // Non-existent expression starting with '$'.
+ ASSERT_TRUE(PathAcceptingKeyword::GEO_INTERSECTS ==
+ MatchExpressionParser::parsePathAcceptingKeyword(
+ BSON("$foo" << 1).firstElement(), PathAcceptingKeyword::GEO_INTERSECTS));
+ // Existing expression but missing leading '$'.
+ ASSERT_TRUE(PathAcceptingKeyword::NOT_IN ==
+ MatchExpressionParser::parsePathAcceptingKeyword(BSON("size" << 1).firstElement(),
+ PathAcceptingKeyword::NOT_IN));
+}
+
+TEST(PathAcceptingKeyword, EmptyBSONElemReturnsDefault) {
+ BSONElement emptyElem;
+ ASSERT_TRUE(boost::none == MatchExpressionParser::parsePathAcceptingKeyword(emptyElem));
+}
+
+} // namespace
+} // namespace mongo
diff --git a/src/mongo/db/ops/modifier_pull.cpp b/src/mongo/db/ops/modifier_pull.cpp
index 70d5442a716..f3adc0e3538 100644
--- a/src/mongo/db/ops/modifier_pull.cpp
+++ b/src/mongo/db/ops/modifier_pull.cpp
@@ -109,7 +109,9 @@ Status ModifierPull::init(const BSONElement& modExpr, const Options& opts, bool*
_exprObj = _exprElt.embeddedObject();
// If not is not a query operator, then it is a primitive.
- _matcherOnPrimitive = (_exprObj.firstElement().getGtLtOp() != 0);
+ _matcherOnPrimitive = (MatchExpressionParser::parsePathAcceptingKeyword(
+ _exprObj.firstElement(), PathAcceptingKeyword::EQUALITY) !=
+ PathAcceptingKeyword::EQUALITY);
// If the object is primitive then wrap it up into an object.
if (_matcherOnPrimitive)
diff --git a/src/mongo/db/pipeline/document_source_match.cpp b/src/mongo/db/pipeline/document_source_match.cpp
index 8cef470c453..e34d9f2d1a5 100644
--- a/src/mongo/db/pipeline/document_source_match.cpp
+++ b/src/mongo/db/pipeline/document_source_match.cpp
@@ -34,6 +34,7 @@
#include "mongo/db/matcher/expression_algo.h"
#include "mongo/db/matcher/expression_array.h"
#include "mongo/db/matcher/expression_leaf.h"
+#include "mongo/db/matcher/expression_parser.h"
#include "mongo/db/matcher/extensions_callback_noop.h"
#include "mongo/db/pipeline/document.h"
#include "mongo/db/pipeline/expression.h"
@@ -196,30 +197,31 @@ Document redactSafePortionDollarOps(BSONObj expr) {
continue;
}
- switch (BSONObj::MatchType(field.getGtLtOp(BSONObj::Equality))) {
+ switch (*MatchExpressionParser::parsePathAcceptingKeyword(field,
+ PathAcceptingKeyword::EQUALITY)) {
// These are always ok
- case BSONObj::opTYPE:
- case BSONObj::opREGEX:
- case BSONObj::opOPTIONS:
- case BSONObj::opMOD:
- case BSONObj::opBITS_ALL_SET:
- case BSONObj::opBITS_ALL_CLEAR:
- case BSONObj::opBITS_ANY_SET:
- case BSONObj::opBITS_ANY_CLEAR:
+ case PathAcceptingKeyword::TYPE:
+ case PathAcceptingKeyword::REGEX:
+ case PathAcceptingKeyword::OPTIONS:
+ case PathAcceptingKeyword::MOD:
+ case PathAcceptingKeyword::BITS_ALL_SET:
+ case PathAcceptingKeyword::BITS_ALL_CLEAR:
+ case PathAcceptingKeyword::BITS_ANY_SET:
+ case PathAcceptingKeyword::BITS_ANY_CLEAR:
output[field.fieldNameStringData()] = Value(field);
break;
// These are ok if the type of the rhs is allowed in comparisons
- case BSONObj::LTE:
- case BSONObj::GTE:
- case BSONObj::LT:
- case BSONObj::GT:
+ case PathAcceptingKeyword::LESS_THAN_OR_EQUAL:
+ case PathAcceptingKeyword::GREATER_THAN_OR_EQUAL:
+ case PathAcceptingKeyword::LESS_THAN:
+ case PathAcceptingKeyword::GREATER_THAN:
if (isTypeRedactSafeInComparison(field.type()))
output[field.fieldNameStringData()] = Value(field);
break;
// $in must be all-or-nothing (like $or). Can't include subset of elements.
- case BSONObj::opIN: {
+ case PathAcceptingKeyword::IN_EXPR: {
bool allOk = true;
BSONForEach(elem, field.Obj()) {
if (!isTypeRedactSafeInComparison(elem.type())) {
@@ -234,7 +236,7 @@ Document redactSafePortionDollarOps(BSONObj expr) {
break;
}
- case BSONObj::opALL: {
+ case PathAcceptingKeyword::ALL: {
// $all can include subset of elements (like $and).
vector<Value> matches;
BSONForEach(elem, field.Obj()) {
@@ -249,7 +251,7 @@ Document redactSafePortionDollarOps(BSONObj expr) {
break;
}
- case BSONObj::opELEM_MATCH: {
+ case PathAcceptingKeyword::ELEM_MATCH: {
BSONObj subIn = field.Obj();
Document subOut;
if (subIn.firstElementFieldName()[0] == '$') {
@@ -265,20 +267,20 @@ Document redactSafePortionDollarOps(BSONObj expr) {
}
// These are never allowed
- case BSONObj::Equality: // This actually means unknown
- case BSONObj::opNEAR:
- case BSONObj::NE:
- case BSONObj::opSIZE:
- case BSONObj::NIN:
- case BSONObj::opEXISTS:
- case BSONObj::opWITHIN:
- case BSONObj::opGEO_INTERSECTS:
- case BSONObj::opINTERNAL_SCHEMA_MIN_ITEMS:
- case BSONObj::opINTERNAL_SCHEMA_MAX_ITEMS:
- case BSONObj::opINTERNAL_SCHEMA_UNIQUE_ITEMS:
- case BSONObj::opINTERNAL_SCHEMA_OBJECT_MATCH:
- case BSONObj::opINTERNAL_SCHEMA_MIN_LENGTH:
- case BSONObj::opINTERNAL_SCHEMA_MAX_LENGTH:
+ case PathAcceptingKeyword::EQUALITY: // This actually means unknown
+ case PathAcceptingKeyword::GEO_NEAR:
+ case PathAcceptingKeyword::NOT_EQUAL:
+ case PathAcceptingKeyword::SIZE:
+ case PathAcceptingKeyword::NOT_IN:
+ case PathAcceptingKeyword::EXISTS:
+ case PathAcceptingKeyword::WITHIN:
+ case PathAcceptingKeyword::GEO_INTERSECTS:
+ case PathAcceptingKeyword::INTERNAL_SCHEMA_MIN_ITEMS:
+ case PathAcceptingKeyword::INTERNAL_SCHEMA_MAX_ITEMS:
+ case PathAcceptingKeyword::INTERNAL_SCHEMA_UNIQUE_ITEMS:
+ case PathAcceptingKeyword::INTERNAL_SCHEMA_OBJECT_MATCH:
+ case PathAcceptingKeyword::INTERNAL_SCHEMA_MIN_LENGTH:
+ case PathAcceptingKeyword::INTERNAL_SCHEMA_MAX_LENGTH:
continue;
}
}
diff --git a/src/mongo/db/update/pull_node.cpp b/src/mongo/db/update/pull_node.cpp
index 45052c0c0f9..4d9c9c2fa09 100644
--- a/src/mongo/db/update/pull_node.cpp
+++ b/src/mongo/db/update/pull_node.cpp
@@ -129,7 +129,8 @@ Status PullNode::init(BSONElement modExpr, const CollatorInterface* collator) {
try {
if (modExpr.type() == mongo::Object &&
- modExpr.embeddedObject().firstElement().getGtLtOp(-1) == -1) {
+ !MatchExpressionParser::parsePathAcceptingKeyword(
+ modExpr.embeddedObject().firstElement())) {
_matcher = stdx::make_unique<ObjectMatcher>(modExpr.embeddedObject(), collator);
} else if (modExpr.type() == mongo::Object || modExpr.type() == mongo::RegEx) {
_matcher = stdx::make_unique<WrappedObjectMatcher>(modExpr, collator);