summaryrefslogtreecommitdiff
path: root/src/mongo/db/matcher/expression_tree.cpp
diff options
context:
space:
mode:
authorLucas de Castro Borges <lucas@gnuabordo.com.br>2025-02-18 17:02:53 -0300
committerLucas de Castro Borges <lucas@gnuabordo.com.br>2025-02-18 17:02:53 -0300
commit959575a5ca598bf5f37fb5cebe7ed1d80d3d71f7 (patch)
treeacc8d60aedb12b70048e676e8a7349deb0010db8 /src/mongo/db/matcher/expression_tree.cpp
parent76588293975fc059cf076779e4283e6ffaf8afff (diff)
New upstream version 6.0.20upstream
Diffstat (limited to 'src/mongo/db/matcher/expression_tree.cpp')
-rw-r--r--src/mongo/db/matcher/expression_tree.cpp144
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(&notBob, includePath);
+ _exp->getChild(x)->serialize(&notBob, opts, includePath);
}
} else {
- _exp->serialize(&notBob, includePath);
+ _exp->serialize(&notBob, 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 {