diff options
Diffstat (limited to 'src/mongo/db/matcher/expression_tree.cpp')
| -rw-r--r-- | src/mongo/db/matcher/expression_tree.cpp | 144 |
1 files changed, 119 insertions, 25 deletions
diff --git a/src/mongo/db/matcher/expression_tree.cpp b/src/mongo/db/matcher/expression_tree.cpp index a7496729144..74765949ca5 100644 --- a/src/mongo/db/matcher/expression_tree.cpp +++ b/src/mongo/db/matcher/expression_tree.cpp @@ -40,16 +40,106 @@ #include "mongo/db/matcher/expression_text_base.h" namespace mongo { +namespace { + +PathMatchExpression* getEligiblePathMatchForNotSerialization(MatchExpression* expr) { + // Returns a pointer to a PathMatchExpression if 'expr' is such a pointer, otherwise returns + // nullptr. + // + // One exception: while TextMatchExpressionBase derives from PathMatchExpression, text match + // expressions cannot be serialized in the same manner as other PathMatchExpression derivatives. + // This is because the path for a TextMatchExpression is embedded within the $text object, + // whereas for other PathMatchExpressions it is on the left-hand-side, for example {x: {$eq: + // 1}}. + // + // Rather than the following dynamic_cast, we'll do a more performant, but also more verbose + // check. + // dynamic_cast<PathMatchExpression*>(expr) && !dynamic_cast<TextMatchExpressionBase*>(expr) + // + // This version below is less obviously exhaustive, but because this is just a legibility + // optimization, and this function also gets called on the query shape stats recording hot path, + // we think it is worth it. + switch (expr->matchType()) { + // leaf types + case MatchExpression::EQ: + case MatchExpression::LTE: + case MatchExpression::LT: + case MatchExpression::GT: + case MatchExpression::GTE: + case MatchExpression::REGEX: + case MatchExpression::MOD: + case MatchExpression::EXISTS: + case MatchExpression::MATCH_IN: + case MatchExpression::BITS_ALL_SET: + case MatchExpression::BITS_ALL_CLEAR: + case MatchExpression::BITS_ANY_SET: + case MatchExpression::BITS_ANY_CLEAR: + // array types + case MatchExpression::ELEM_MATCH_OBJECT: + case MatchExpression::ELEM_MATCH_VALUE: + case MatchExpression::SIZE: + // special types + case MatchExpression::TYPE_OPERATOR: + case MatchExpression::GEO: + case MatchExpression::GEO_NEAR: + // Internal subclasses of PathMatchExpression: + case MatchExpression::INTERNAL_SCHEMA_ALL_ELEM_MATCH_FROM_INDEX: + case MatchExpression::INTERNAL_SCHEMA_BIN_DATA_ENCRYPTED_TYPE: + case MatchExpression::INTERNAL_SCHEMA_BIN_DATA_FLE2_ENCRYPTED_TYPE: + case MatchExpression::INTERNAL_SCHEMA_BIN_DATA_SUBTYPE: + case MatchExpression::INTERNAL_SCHEMA_MATCH_ARRAY_INDEX: + case MatchExpression::INTERNAL_SCHEMA_MAX_ITEMS: + case MatchExpression::INTERNAL_SCHEMA_MAX_LENGTH: + case MatchExpression::INTERNAL_SCHEMA_MAX_PROPERTIES: + case MatchExpression::INTERNAL_SCHEMA_MIN_ITEMS: + case MatchExpression::INTERNAL_SCHEMA_MIN_LENGTH: + case MatchExpression::INTERNAL_SCHEMA_TYPE: + case MatchExpression::INTERNAL_SCHEMA_UNIQUE_ITEMS: + return static_cast<PathMatchExpression*>(expr); + // purposefully skip TEXT: + case MatchExpression::TEXT: + // Any other type is not considered a PathMatchExpression. + case MatchExpression::AND: + case MatchExpression::OR: + case MatchExpression::NOT: + case MatchExpression::NOR: + case MatchExpression::WHERE: + case MatchExpression::EXPRESSION: + case MatchExpression::ALWAYS_FALSE: + case MatchExpression::ALWAYS_TRUE: + case MatchExpression::INTERNAL_2D_POINT_IN_ANNULUS: + case MatchExpression::INTERNAL_BUCKET_GEO_WITHIN: + case MatchExpression::INTERNAL_EXPR_EQ: + case MatchExpression::INTERNAL_EXPR_GT: + case MatchExpression::INTERNAL_EXPR_GTE: + case MatchExpression::INTERNAL_EXPR_LT: + case MatchExpression::INTERNAL_EXPR_LTE: + case MatchExpression::INTERNAL_SCHEMA_ALLOWED_PROPERTIES: + case MatchExpression::INTERNAL_SCHEMA_COND: + case MatchExpression::INTERNAL_SCHEMA_EQ: + case MatchExpression::INTERNAL_SCHEMA_FMOD: + case MatchExpression::INTERNAL_SCHEMA_MIN_PROPERTIES: + case MatchExpression::INTERNAL_SCHEMA_OBJECT_MATCH: + case MatchExpression::INTERNAL_SCHEMA_ROOT_DOC_EQ: + case MatchExpression::INTERNAL_SCHEMA_XOR: + return nullptr; + default: + MONGO_UNREACHABLE_TASSERT(7800300); + } +}; +} // namespace void ListOfMatchExpression::_debugList(StringBuilder& debug, int indentationLevel) const { for (unsigned i = 0; i < _expressions.size(); i++) _expressions[i]->debugString(debug, indentationLevel + 1); } -void ListOfMatchExpression::_listToBSON(BSONArrayBuilder* out, bool includePath) const { +void ListOfMatchExpression::_listToBSON(BSONArrayBuilder* out, + const SerializationOptions& opts, + bool includePath) const { for (unsigned i = 0; i < _expressions.size(); i++) { BSONObjBuilder childBob(out->subobjStart()); - _expressions[i]->serialize(&childBob, includePath); + _expressions[i]->serialize(&childBob, opts, includePath); } out->doneFast(); } @@ -354,7 +444,9 @@ void AndMatchExpression::debugString(StringBuilder& debug, int indentationLevel) _debugList(debug, indentationLevel); } -void AndMatchExpression::serialize(BSONObjBuilder* out, bool includePath) const { +void AndMatchExpression::serialize(BSONObjBuilder* out, + const SerializationOptions& opts, + bool includePath) 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. @@ -362,7 +454,7 @@ void AndMatchExpression::serialize(BSONObjBuilder* out, bool includePath) const } BSONArrayBuilder arrBob(out->subarrayStart("$and")); - _listToBSON(&arrBob, includePath); + _listToBSON(&arrBob, opts, includePath); arrBob.doneFast(); } @@ -398,7 +490,9 @@ void OrMatchExpression::debugString(StringBuilder& debug, int indentationLevel) _debugList(debug, indentationLevel); } -void OrMatchExpression::serialize(BSONObjBuilder* out, bool includePath) const { +void OrMatchExpression::serialize(BSONObjBuilder* out, + const SerializationOptions& opts, + bool includePath) const { if (!numChildren()) { // It is possible for an OrMatchExpression to have no children, resulting in the serialized // expression {$or: []}, which is not a valid query object. An empty $or is logically @@ -407,7 +501,7 @@ void OrMatchExpression::serialize(BSONObjBuilder* out, bool includePath) const { return; } BSONArrayBuilder arrBob(out->subarrayStart("$or")); - _listToBSON(&arrBob, includePath); + _listToBSON(&arrBob, opts, includePath); } bool OrMatchExpression::isTriviallyFalse() const { @@ -440,9 +534,11 @@ void NorMatchExpression::debugString(StringBuilder& debug, int indentationLevel) _debugList(debug, indentationLevel); } -void NorMatchExpression::serialize(BSONObjBuilder* out, bool includePath) const { +void NorMatchExpression::serialize(BSONObjBuilder* out, + const SerializationOptions& opts, + bool includePath) const { BSONArrayBuilder arrBob(out->subarrayStart("$nor")); - _listToBSON(&arrBob, includePath); + _listToBSON(&arrBob, opts, includePath); } // ------- @@ -455,9 +551,10 @@ void NotMatchExpression::debugString(StringBuilder& debug, int indentationLevel) void NotMatchExpression::serializeNotExpressionToNor(MatchExpression* exp, BSONObjBuilder* out, + const SerializationOptions& opts, bool includePath) { BSONObjBuilder childBob; - exp->serialize(&childBob, includePath); + exp->serialize(&childBob, opts, includePath); BSONObj tempObj = childBob.obj(); BSONArrayBuilder tBob(out->subarrayStart("$nor")); @@ -465,9 +562,11 @@ void NotMatchExpression::serializeNotExpressionToNor(MatchExpression* exp, tBob.doneFast(); } -void NotMatchExpression::serialize(BSONObjBuilder* out, bool includePath) const { +void NotMatchExpression::serialize(BSONObjBuilder* out, + const SerializationOptions& opts, + bool includePath) const { if (_exp->matchType() == MatchType::AND && _exp->numChildren() == 0) { - out->append("$alwaysFalse", 1); + opts.appendLiteral(out, "$alwaysFalse", 1); return; } @@ -478,10 +577,10 @@ void NotMatchExpression::serialize(BSONObjBuilder* out, bool includePath) const // internally, so we un-nest it here to be able to re-parse it. if (_exp->matchType() == MatchType::AND) { for (size_t x = 0; x < _exp->numChildren(); ++x) { - _exp->getChild(x)->serialize(¬Bob, includePath); + _exp->getChild(x)->serialize(¬Bob, opts, includePath); } } else { - _exp->serialize(¬Bob, includePath); + _exp->serialize(¬Bob, opts, includePath); } return; } @@ -494,20 +593,15 @@ void NotMatchExpression::serialize(BSONObjBuilder* out, bool includePath) const // It is generally easier to be correct if we just always serialize to a $nor, since this will // delegate the path serialization to lower in the tree where we have the information on-hand. // However, for legibility we preserve a $not with a single path-accepting child as a $not. - // - // One exception: while TextMatchExpressionBase derives from PathMatchExpression, text match - // expressions cannot be serialized in the same manner as other PathMatchExpression derivatives. - // This is because the path for a TextMatchExpression is embedded within the $text object, - // whereas for other PathMatchExpressions it is on the left-hand-side, for example {x: {$eq: - // 1}}. - if (auto pathMatch = dynamic_cast<PathMatchExpression*>(expressionToNegate); - pathMatch && !dynamic_cast<TextMatchExpressionBase*>(expressionToNegate)) { - const auto path = pathMatch->path(); - BSONObjBuilder pathBob(out->subobjStart(path)); - pathBob.append("$not", pathMatch->getSerializedRightHandSide()); + if (auto pathMatch = getEligiblePathMatchForNotSerialization(expressionToNegate)) { + auto append = [&](StringData path) { + BSONObjBuilder pathBob(out->subobjStart(path)); + pathBob.append("$not", pathMatch->getSerializedRightHandSide(opts)); + }; + append(opts.serializeFieldPathFromString(pathMatch->path())); return; } - return serializeNotExpressionToNor(expressionToNegate, out, includePath); + return serializeNotExpressionToNor(expressionToNegate, out, opts); } bool NotMatchExpression::equivalent(const MatchExpression* other) const { |
