summaryrefslogtreecommitdiff
path: root/src/mongo/db/matcher
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/matcher')
-rw-r--r--src/mongo/db/matcher/SConscript2
-rw-r--r--src/mongo/db/matcher/doc_validation_error.cpp12
-rw-r--r--src/mongo/db/matcher/expression.h9
-rw-r--r--src/mongo/db/matcher/expression_internal_expr_comparison.h205
-rw-r--r--src/mongo/db/matcher/expression_internal_expr_comparison_test.cpp279
-rw-r--r--src/mongo/db/matcher/expression_internal_expr_eq.cpp69
-rw-r--r--src/mongo/db/matcher/expression_internal_expr_eq.h82
-rw-r--r--src/mongo/db/matcher/expression_internal_expr_eq_test.cpp2
-rw-r--r--src/mongo/db/matcher/expression_leaf.h13
-rw-r--r--src/mongo/db/matcher/expression_parser.cpp55
-rw-r--r--src/mongo/db/matcher/expression_parser.h4
-rw-r--r--src/mongo/db/matcher/expression_visitor.h10
-rw-r--r--src/mongo/db/matcher/rewrite_expr.cpp80
13 files changed, 657 insertions, 165 deletions
diff --git a/src/mongo/db/matcher/SConscript b/src/mongo/db/matcher/SConscript
index 1e9e2a7d199..eef4a2f9be7 100644
--- a/src/mongo/db/matcher/SConscript
+++ b/src/mongo/db/matcher/SConscript
@@ -27,7 +27,6 @@ env.Library(
'expression_array.cpp',
'expression_expr.cpp',
'expression_geo.cpp',
- 'expression_internal_expr_eq.cpp',
'expression_leaf.cpp',
'expression_parser.cpp',
'expression_text_base.cpp',
@@ -111,6 +110,7 @@ env.CppUnitTest(
'expression_array_test.cpp',
'expression_expr_test.cpp',
'expression_geo_test.cpp',
+ 'expression_internal_expr_comparison_test.cpp',
'expression_internal_expr_eq_test.cpp',
'expression_leaf_test.cpp',
'expression_optimize_test.cpp',
diff --git a/src/mongo/db/matcher/doc_validation_error.cpp b/src/mongo/db/matcher/doc_validation_error.cpp
index 8d0c73ab824..164bb3d16c2 100644
--- a/src/mongo/db/matcher/doc_validation_error.cpp
+++ b/src/mongo/db/matcher/doc_validation_error.cpp
@@ -806,6 +806,10 @@ public:
generatePathError(*expr, kNormalReason, kInvertedReason);
}
void visit(const InternalExprEqMatchExpression* expr) final {}
+ void visit(const InternalExprGTMatchExpression* expr) final {}
+ void visit(const InternalExprGTEMatchExpression* expr) final {}
+ void visit(const InternalExprLTMatchExpression* expr) final {}
+ void visit(const InternalExprLTEMatchExpression* expr) final {}
void visit(const InternalSchemaAllElemMatchFromIndexMatchExpression* expr) final {
switch (toItemsKeywordType(*expr)) {
case ItemsKeywordType::kItems: {
@@ -1794,6 +1798,10 @@ public:
}
void visit(const InMatchExpression* expr) final {}
void visit(const InternalExprEqMatchExpression* expr) final {}
+ void visit(const InternalExprGTMatchExpression* expr) final {}
+ void visit(const InternalExprGTEMatchExpression* expr) final {}
+ void visit(const InternalExprLTMatchExpression* expr) final {}
+ void visit(const InternalExprLTEMatchExpression* expr) final {}
void visit(const InternalSchemaAllElemMatchFromIndexMatchExpression* expr) final {}
void visit(const InternalSchemaAllowedPropertiesMatchExpression* expr) final {
if (_context->shouldGenerateError(*expr)) {
@@ -2002,6 +2010,10 @@ public:
_context->finishCurrentError(expr);
}
void visit(const InternalExprEqMatchExpression* expr) final {}
+ void visit(const InternalExprGTMatchExpression* expr) final {}
+ void visit(const InternalExprGTEMatchExpression* expr) final {}
+ void visit(const InternalExprLTMatchExpression* expr) final {}
+ void visit(const InternalExprLTEMatchExpression* expr) final {}
void visit(const InternalSchemaAllElemMatchFromIndexMatchExpression* expr) final {
switch (toItemsKeywordType(*expr)) {
case ItemsKeywordType::kItems:
diff --git a/src/mongo/db/matcher/expression.h b/src/mongo/db/matcher/expression.h
index 71c781fed09..3c3077ea5df 100644
--- a/src/mongo/db/matcher/expression.h
+++ b/src/mongo/db/matcher/expression.h
@@ -108,9 +108,14 @@ public:
// Expressions that are only created internally
INTERNAL_2D_POINT_IN_ANNULUS,
- // Used to represent an expression language equality in a match expression tree, since $eq
- // in the expression language has different semantics than the equality match expression.
+ // Used to represent expression language comparisons in a match expression tree, since $eq,
+ // $gt, $gte, $lt and $lte in the expression language has different semantics than their
+ // respective match expressions.
INTERNAL_EXPR_EQ,
+ INTERNAL_EXPR_GT,
+ INTERNAL_EXPR_GTE,
+ INTERNAL_EXPR_LT,
+ INTERNAL_EXPR_LTE,
// JSON Schema expressions.
INTERNAL_SCHEMA_ALLOWED_PROPERTIES,
diff --git a/src/mongo/db/matcher/expression_internal_expr_comparison.h b/src/mongo/db/matcher/expression_internal_expr_comparison.h
new file mode 100644
index 00000000000..123813d1b80
--- /dev/null
+++ b/src/mongo/db/matcher/expression_internal_expr_comparison.h
@@ -0,0 +1,205 @@
+/**
+ * Copyright (C) 2021-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * 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
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * 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 Server Side 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.
+ */
+
+#pragma once
+
+#include "mongo/db/matcher/expression_leaf.h"
+
+namespace mongo {
+
+/**
+ * InternalExprComparisonMatchExpression consists of comparison expressions with similar semantics
+ * to the correlating $eq, $gt, $gte, $lt, and $lte aggregation expression. They differ from the
+ * regular comparison match expressions in the following ways:
+ *
+ * - There will be no type bracketing. For instance, null is considered less than 2, and objects
+ * are considered greater than 2.
+ *
+ * - The document will match if there is an array anywhere along the path. By always returning true
+ * in such cases, we match a superset of documents that the related aggregation expression would
+ * match. This sidesteps us having to implement field path expression evaluation as part of this
+ * match expression.
+ *
+ * - Comparison to NaN will consider NaN as the less than all numbers.
+ *
+ * - Comparison to an array is illegal. It is invalid usage to construct a
+ * InternalExprComparisonMatchExpression node which compares to an array.
+ */
+template <typename T>
+class InternalExprComparisonMatchExpression : public ComparisonMatchExpressionBase {
+public:
+ InternalExprComparisonMatchExpression(MatchType type, StringData path, BSONElement value)
+ : ComparisonMatchExpressionBase(type,
+ path,
+ Value(value),
+ ElementPath::LeafArrayBehavior::kNoTraversal,
+ ElementPath::NonLeafArrayBehavior::kMatchSubpath) {
+ invariant(_rhs.type() != BSONType::Undefined);
+ invariant(_rhs.type() != BSONType::Array);
+ }
+
+ virtual ~InternalExprComparisonMatchExpression() = default;
+
+ bool matchesSingleElement(const BSONElement& elem, MatchDetails* details) const final {
+ // We use NonLeafArrayBehavior::kMatchSubpath traversal in
+ // InternalExprComparisonMatchExpression. This means matchesSinglElement() will be called
+ // when an array is found anywhere along the patch we are matching against. When this
+ // occurs, we return 'true' and depend on the corresponding ExprMatchExpression node to
+ // filter properly.
+ if (elem.type() == BSONType::Array) {
+ return true;
+ }
+
+ auto comp = elem.woCompare(_rhs, BSONElement::ComparisonRulesSet(0), _collator);
+
+ switch (matchType()) {
+ case INTERNAL_EXPR_GT:
+ return comp > 0;
+ case INTERNAL_EXPR_GTE:
+ return comp >= 0;
+ case INTERNAL_EXPR_LT:
+ return comp < 0;
+ case INTERNAL_EXPR_LTE:
+ return comp <= 0;
+ case INTERNAL_EXPR_EQ:
+ return comp == 0;
+ default:
+ // This is a comparison match expression, so it must be either a $eq, $lt, $lte, $gt
+ // or $gte expression.
+ MONGO_UNREACHABLE_TASSERT(3994308);
+ }
+ };
+
+ std::unique_ptr<MatchExpression> shallowClone() const final {
+ auto clone = std::make_unique<T>(path(), _rhs);
+ clone->setCollator(_collator);
+ if (getTag()) {
+ clone->setTag(getTag()->clone());
+ }
+ return clone;
+ }
+
+ StringData name() const final {
+ return T::kName;
+ };
+};
+
+
+class InternalExprEqMatchExpression final
+ : public InternalExprComparisonMatchExpression<InternalExprEqMatchExpression> {
+public:
+ static constexpr StringData kName = "$_internalExprEq"_sd;
+
+ InternalExprEqMatchExpression(StringData path, BSONElement value)
+ : InternalExprComparisonMatchExpression<InternalExprEqMatchExpression>(
+ MatchType::INTERNAL_EXPR_EQ, path, value) {}
+
+ void acceptVisitor(MatchExpressionMutableVisitor* visitor) final {
+ visitor->visit(this);
+ }
+
+ void acceptVisitor(MatchExpressionConstVisitor* visitor) const final {
+ visitor->visit(this);
+ }
+};
+
+class InternalExprGTMatchExpression final
+ : public InternalExprComparisonMatchExpression<InternalExprGTMatchExpression> {
+public:
+ static constexpr StringData kName = "$_internalExprGt"_sd;
+
+ InternalExprGTMatchExpression(StringData path, BSONElement value)
+ : InternalExprComparisonMatchExpression<InternalExprGTMatchExpression>(
+ MatchType::INTERNAL_EXPR_GT, path, value) {}
+
+
+ void acceptVisitor(MatchExpressionMutableVisitor* visitor) final {
+ visitor->visit(this);
+ }
+
+ void acceptVisitor(MatchExpressionConstVisitor* visitor) const final {
+ visitor->visit(this);
+ }
+};
+
+class InternalExprGTEMatchExpression final
+ : public InternalExprComparisonMatchExpression<InternalExprGTEMatchExpression> {
+public:
+ static constexpr StringData kName = "$_internalExprGte"_sd;
+
+ InternalExprGTEMatchExpression(StringData path, BSONElement value)
+ : InternalExprComparisonMatchExpression<InternalExprGTEMatchExpression>(
+ MatchType::INTERNAL_EXPR_GTE, path, value) {}
+
+ void acceptVisitor(MatchExpressionMutableVisitor* visitor) final {
+ visitor->visit(this);
+ }
+
+ void acceptVisitor(MatchExpressionConstVisitor* visitor) const final {
+ visitor->visit(this);
+ }
+};
+
+class InternalExprLTMatchExpression final
+ : public InternalExprComparisonMatchExpression<InternalExprLTMatchExpression> {
+public:
+ static constexpr StringData kName = "$_internalExprLt"_sd;
+
+ InternalExprLTMatchExpression(StringData path, BSONElement value)
+ : InternalExprComparisonMatchExpression<InternalExprLTMatchExpression>(
+ MatchType::INTERNAL_EXPR_LT, path, value) {}
+
+ void acceptVisitor(MatchExpressionMutableVisitor* visitor) final {
+ visitor->visit(this);
+ }
+
+ void acceptVisitor(MatchExpressionConstVisitor* visitor) const final {
+ visitor->visit(this);
+ }
+};
+
+class InternalExprLTEMatchExpression final
+ : public InternalExprComparisonMatchExpression<InternalExprLTEMatchExpression> {
+public:
+ static constexpr StringData kName = "$_internalExprLte"_sd;
+
+ InternalExprLTEMatchExpression(StringData path, BSONElement value)
+ : InternalExprComparisonMatchExpression<InternalExprLTEMatchExpression>(
+ MatchType::INTERNAL_EXPR_LTE, path, value) {}
+
+ void acceptVisitor(MatchExpressionMutableVisitor* visitor) final {
+ visitor->visit(this);
+ }
+
+ void acceptVisitor(MatchExpressionConstVisitor* visitor) const final {
+ visitor->visit(this);
+ }
+};
+
+} // namespace mongo
diff --git a/src/mongo/db/matcher/expression_internal_expr_comparison_test.cpp b/src/mongo/db/matcher/expression_internal_expr_comparison_test.cpp
new file mode 100644
index 00000000000..9085008ad75
--- /dev/null
+++ b/src/mongo/db/matcher/expression_internal_expr_comparison_test.cpp
@@ -0,0 +1,279 @@
+/**
+ * Copyright (C) 2021-present MongoDB, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the Server Side Public License, version 1,
+ * as published by MongoDB, Inc.
+ *
+ * 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
+ * Server Side Public License for more details.
+ *
+ * You should have received a copy of the Server Side Public License
+ * along with this program. If not, see
+ * <http://www.mongodb.com/licensing/server-side-public-license>.
+ *
+ * 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 Server Side 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/matcher/expression_internal_expr_comparison.h"
+#include "mongo/db/matcher/matcher.h"
+#include "mongo/db/pipeline/expression_context_for_test.h"
+#include "mongo/db/query/collation/collator_interface_mock.h"
+#include "mongo/db/query/index_tag.h"
+#include "mongo/unittest/death_test.h"
+#include "mongo/unittest/unittest.h"
+
+namespace mongo {
+namespace {
+
+const double kNaN = std::numeric_limits<double>::quiet_NaN();
+
+TEST(InternalExprComparisonMatchExpression, DoesNotPerformTypeBracketing) {
+ BSONObj operand = BSON("x" << 2);
+ {
+ InternalExprGTMatchExpression gt(operand.firstElement().fieldNameStringData(),
+ operand.firstElement());
+ ASSERT_FALSE(gt.matchesBSON(BSON("x" << MINKEY)));
+ ASSERT_FALSE(gt.matchesBSON(BSON("y" << 0)));
+ ASSERT_FALSE(gt.matchesBSON(BSON("x" << BSONNULL)));
+ ASSERT_FALSE(gt.matchesBSON(BSON("x" << BSONUndefined)));
+ ASSERT_FALSE(gt.matchesBSON(BSON("x" << 1)));
+ ASSERT_FALSE(gt.matchesBSON(BSON("x" << 2)));
+ ASSERT_TRUE(gt.matchesBSON(BSON("x" << 3.5)));
+ ASSERT_TRUE(gt.matchesBSON(BSON("x"
+ << "string")));
+ ASSERT_TRUE(gt.matchesBSON(BSON("x" << BSON("a" << 1))));
+ ASSERT_TRUE(gt.matchesBSON(BSON("x" << BSON_ARRAY(1 << 2 << 3))));
+ ASSERT_TRUE(gt.matchesBSON(BSON("x" << OID())));
+ ASSERT_TRUE(gt.matchesBSON(BSON("x" << DATENOW)));
+ ASSERT_TRUE(gt.matchesBSON(BSON("x" << Timestamp(0, 3))));
+ ASSERT_TRUE(gt.matchesBSON(BSON("x"
+ << "/^m/")));
+ ASSERT_TRUE(gt.matchesBSON(BSON("x" << MAXKEY)));
+ }
+ {
+ InternalExprGTEMatchExpression gte(operand.firstElement().fieldNameStringData(),
+ operand.firstElement());
+ ASSERT_FALSE(gte.matchesBSON(BSON("x" << MINKEY)));
+ ASSERT_FALSE(gte.matchesBSON(BSON("y" << 0)));
+ ASSERT_FALSE(gte.matchesBSON(BSON("x" << BSONNULL)));
+ ASSERT_FALSE(gte.matchesBSON(BSON("x" << BSONUndefined)));
+ ASSERT_FALSE(gte.matchesBSON(BSON("x" << 1)));
+ ASSERT_TRUE(gte.matchesBSON(BSON("x" << 2)));
+ ASSERT_TRUE(gte.matchesBSON(BSON("x" << 3.5)));
+ ASSERT_TRUE(gte.matchesBSON(BSON("x"
+ << "string")));
+ ASSERT_TRUE(gte.matchesBSON(BSON("x" << BSON("a" << 1))));
+ ASSERT_TRUE(gte.matchesBSON(BSON("x" << BSON_ARRAY(1 << 2 << 3))));
+ ASSERT_TRUE(gte.matchesBSON(BSON("x" << OID())));
+ ASSERT_TRUE(gte.matchesBSON(BSON("x" << DATENOW)));
+ ASSERT_TRUE(gte.matchesBSON(BSON("x" << Timestamp(0, 3))));
+ ASSERT_TRUE(gte.matchesBSON(BSON("x"
+ << "/^m/")));
+ ASSERT_TRUE(gte.matchesBSON(BSON("x" << MAXKEY)));
+ }
+ {
+ InternalExprLTMatchExpression lt(operand.firstElement().fieldNameStringData(),
+ operand.firstElement());
+ ASSERT_TRUE(lt.matchesBSON(BSON("x" << MINKEY)));
+ ASSERT_TRUE(lt.matchesBSON(BSON("y" << 0)));
+ ASSERT_TRUE(lt.matchesBSON(BSON("x" << BSONNULL)));
+ ASSERT_TRUE(lt.matchesBSON(BSON("x" << BSONUndefined)));
+ ASSERT_TRUE(lt.matchesBSON(BSON("x" << 1)));
+ ASSERT_FALSE(lt.matchesBSON(BSON("x" << 2)));
+ ASSERT_FALSE(lt.matchesBSON(BSON("x" << 3.5)));
+ ASSERT_FALSE(lt.matchesBSON(BSON("x"
+ << "string")));
+ ASSERT_FALSE(lt.matchesBSON(BSON("x" << BSON("a" << 1))));
+ ASSERT_TRUE(lt.matchesBSON(BSON(
+ "x" << BSON_ARRAY(1 << 2 << 3)))); // Always returns true if path contains an array.
+ ASSERT_FALSE(lt.matchesBSON(BSON("x" << OID())));
+ ASSERT_FALSE(lt.matchesBSON(BSON("x" << DATENOW)));
+ ASSERT_FALSE(lt.matchesBSON(BSON("x" << Timestamp(0, 3))));
+ ASSERT_FALSE(lt.matchesBSON(BSON("x"
+ << "/^m/")));
+ ASSERT_FALSE(lt.matchesBSON(BSON("x" << MAXKEY)));
+ }
+ {
+ InternalExprLTEMatchExpression lte(operand.firstElement().fieldNameStringData(),
+ operand.firstElement());
+ ASSERT_TRUE(lte.matchesBSON(BSON("x" << MINKEY)));
+ ASSERT_TRUE(lte.matchesBSON(BSON("y" << 0)));
+ ASSERT_TRUE(lte.matchesBSON(BSON("x" << BSONNULL)));
+ ASSERT_TRUE(lte.matchesBSON(BSON("x" << BSONUndefined)));
+ ASSERT_TRUE(lte.matchesBSON(BSON("x" << 1)));
+ ASSERT_TRUE(lte.matchesBSON(BSON("x" << 2)));
+ ASSERT_FALSE(lte.matchesBSON(BSON("x" << 3.5)));
+ ASSERT_FALSE(lte.matchesBSON(BSON("x"
+ << "string")));
+ ASSERT_FALSE(lte.matchesBSON(BSON("x" << BSON("a" << 1))));
+ ASSERT_TRUE(lte.matchesBSON(BSON(
+ "x" << BSON_ARRAY(1 << 2 << 3)))); // Always returns true if path contains an array.
+ ASSERT_FALSE(lte.matchesBSON(BSON("x" << OID())));
+ ASSERT_FALSE(lte.matchesBSON(BSON("x" << DATENOW)));
+ ASSERT_FALSE(lte.matchesBSON(BSON("x" << Timestamp(0, 3))));
+ ASSERT_FALSE(lte.matchesBSON(BSON("x"
+ << "/^m/")));
+ ASSERT_FALSE(lte.matchesBSON(BSON("x" << MAXKEY)));
+ }
+}
+
+TEST(InternalExprComparisonMatchExpression, CorrectlyComparesNaN) {
+ BSONObj operand = BSON("x" << kNaN);
+ // This behavior differs from how regular comparison MatchExpressions treat NaN, and places NaN
+ // within the total order of values as less than all numbers.
+ {
+ InternalExprGTMatchExpression gt(operand.firstElement().fieldNameStringData(),
+ operand.firstElement());
+ ASSERT_FALSE(gt.matchesBSON(BSON("x" << MINKEY)));
+ ASSERT_FALSE(gt.matchesBSON(BSON("x" << BSONNULL)));
+ ASSERT_FALSE(gt.matchesBSON(BSON("x" << kNaN)));
+ ASSERT_TRUE(gt.matchesBSON(BSON("x" << Decimal128::kNegativeInfinity)));
+ ASSERT_TRUE(gt.matchesBSON(BSON("x" << 2)));
+ ASSERT_TRUE(gt.matchesBSON(BSON("x" << 3.5)));
+ ASSERT_TRUE(gt.matchesBSON(BSON("x" << MAXKEY)));
+ }
+ {
+ InternalExprGTEMatchExpression gte(operand.firstElement().fieldNameStringData(),
+ operand.firstElement());
+ ASSERT_FALSE(gte.matchesBSON(BSON("x" << MINKEY)));
+ ASSERT_FALSE(gte.matchesBSON(BSON("x" << BSONNULL)));
+ ASSERT_TRUE(gte.matchesBSON(BSON("x" << kNaN)));
+ ASSERT_TRUE(gte.matchesBSON(BSON("x" << Decimal128::kNegativeInfinity)));
+ ASSERT_TRUE(gte.matchesBSON(BSON("x" << 2)));
+ ASSERT_TRUE(gte.matchesBSON(BSON("x" << 3.5)));
+ ASSERT_TRUE(gte.matchesBSON(BSON("x" << MAXKEY)));
+ }
+ {
+ InternalExprLTMatchExpression lt(operand.firstElement().fieldNameStringData(),
+ operand.firstElement());
+ ASSERT_TRUE(lt.matchesBSON(BSON("x" << MINKEY)));
+ ASSERT_TRUE(lt.matchesBSON(BSON("x" << BSONNULL)));
+ ASSERT_FALSE(lt.matchesBSON(BSON("x" << kNaN)));
+ ASSERT_FALSE(lt.matchesBSON(BSON("x" << Decimal128::kNegativeInfinity)));
+ ASSERT_FALSE(lt.matchesBSON(BSON("x" << 2)));
+ ASSERT_FALSE(lt.matchesBSON(BSON("x" << 3.5)));
+ ASSERT_FALSE(lt.matchesBSON(BSON("x" << MAXKEY)));
+ }
+ {
+ InternalExprLTEMatchExpression lte(operand.firstElement().fieldNameStringData(),
+ operand.firstElement());
+ ASSERT_TRUE(lte.matchesBSON(BSON("x" << MINKEY)));
+ ASSERT_TRUE(lte.matchesBSON(BSON("x" << BSONNULL)));
+ ASSERT_TRUE(lte.matchesBSON(BSON("x" << kNaN)));
+ ASSERT_FALSE(lte.matchesBSON(BSON("x" << Decimal128::kNegativeInfinity)));
+ ASSERT_FALSE(lte.matchesBSON(BSON("x" << 2)));
+ ASSERT_FALSE(lte.matchesBSON(BSON("x" << 3.5)));
+ ASSERT_FALSE(lte.matchesBSON(BSON("x" << MAXKEY)));
+ }
+}
+
+TEST(InternalExprComparisonMatchExpression, AlwaysReturnsTrueWithLeafArrays) {
+ BSONObj operand = BSON("x" << 2);
+ {
+ InternalExprGTMatchExpression gt(operand.firstElement().fieldNameStringData(),
+ operand.firstElement());
+ ASSERT_TRUE(gt.matchesBSON(BSON("x" << BSON_ARRAY(BSONNULL))));
+ ASSERT_TRUE(gt.matchesBSON(BSON("x" << BSON_ARRAY(0))));
+ ASSERT_TRUE(gt.matchesBSON(BSON("x" << BSON_ARRAY(1 << 2 << 3))));
+ ASSERT_FALSE(gt.matchesBSON(BSON("x" << 1)));
+ }
+ {
+ InternalExprGTEMatchExpression gte(operand.firstElement().fieldNameStringData(),
+ operand.firstElement());
+ ASSERT_TRUE(gte.matchesBSON(BSON("x" << BSON_ARRAY(BSONNULL))));
+ ASSERT_TRUE(gte.matchesBSON(BSON("x" << BSON_ARRAY(0))));
+ ASSERT_TRUE(gte.matchesBSON(BSON("x" << BSON_ARRAY(1 << 2 << 3))));
+ ASSERT_FALSE(gte.matchesBSON(BSON("x" << 1)));
+ }
+ {
+ InternalExprLTMatchExpression lt(operand.firstElement().fieldNameStringData(),
+ operand.firstElement());
+ ASSERT_TRUE(lt.matchesBSON(BSON("x" << BSON_ARRAY(BSONNULL))));
+ ASSERT_TRUE(lt.matchesBSON(BSON("x" << BSON_ARRAY(0))));
+ ASSERT_TRUE(lt.matchesBSON(BSON("x" << BSON_ARRAY(1 << 2 << 3))));
+ ASSERT_FALSE(lt.matchesBSON(BSON("x" << 3)));
+ }
+ {
+ InternalExprLTEMatchExpression lte(operand.firstElement().fieldNameStringData(),
+ operand.firstElement());
+ ASSERT_TRUE(lte.matchesBSON(BSON("x" << BSON_ARRAY(BSONNULL))));
+ ASSERT_TRUE(lte.matchesBSON(BSON("x" << BSON_ARRAY(0))));
+ ASSERT_TRUE(lte.matchesBSON(BSON("x" << BSON_ARRAY(1 << 2 << 3))));
+ ASSERT_FALSE(lte.matchesBSON(BSON("x" << 3)));
+ }
+}
+
+TEST(InternalExprComparisonMatchExpression, AlwaysReturnsTrueWithNonLeafArrays) {
+ BSONObj operand = BSON("x.y" << 2);
+ {
+ InternalExprGTMatchExpression gt(operand.firstElement().fieldNameStringData(),
+ operand.firstElement());
+ ASSERT_TRUE(gt.matchesBSON(BSON("x" << BSON_ARRAY(BSONNULL))));
+ ASSERT_TRUE(gt.matchesBSON(BSON("x" << BSON_ARRAY(1 << 2 << 3))));
+ ASSERT_TRUE(gt.matchesBSON(BSON("x" << BSON_ARRAY(BSON("y" << 1)))));
+ ASSERT_TRUE(gt.matchesBSON(BSON("x" << BSON_ARRAY(BSON("y" << 2) << BSON("y" << 3)))));
+ ASSERT_FALSE(gt.matchesBSON(BSON("x" << BSON("y" << 1))));
+ }
+ {
+ InternalExprGTEMatchExpression gte(operand.firstElement().fieldNameStringData(),
+ operand.firstElement());
+ ASSERT_TRUE(gte.matchesBSON(BSON("x" << BSON_ARRAY(BSONNULL))));
+ ASSERT_TRUE(gte.matchesBSON(BSON("x" << BSON_ARRAY(1 << 2 << 3))));
+ ASSERT_TRUE(gte.matchesBSON(BSON("x" << BSON_ARRAY(BSON("y" << 1)))));
+ ASSERT_TRUE(gte.matchesBSON(BSON("x" << BSON_ARRAY(BSON("y" << 2) << BSON("y" << 3)))));
+ ASSERT_FALSE(gte.matchesBSON(BSON("x" << BSON("y" << 1))));
+ }
+ {
+ InternalExprLTMatchExpression lt(operand.firstElement().fieldNameStringData(),
+ operand.firstElement());
+ ASSERT_TRUE(lt.matchesBSON(BSON("x" << BSON_ARRAY(BSONNULL))));
+ ASSERT_TRUE(lt.matchesBSON(BSON("x" << BSON_ARRAY(1 << 2 << 3))));
+ ASSERT_TRUE(lt.matchesBSON(BSON("x" << BSON_ARRAY(BSON("y" << 3)))));
+ ASSERT_TRUE(lt.matchesBSON(BSON("x" << BSON_ARRAY(BSON("y" << 1) << BSON("y" << 2)))));
+ ASSERT_FALSE(lt.matchesBSON(BSON("x" << BSON("y" << 3))));
+ }
+ {
+ InternalExprLTEMatchExpression lte(operand.firstElement().fieldNameStringData(),
+ operand.firstElement());
+ ASSERT_TRUE(lte.matchesBSON(BSON("x" << BSON_ARRAY(BSONNULL))));
+ ASSERT_TRUE(lte.matchesBSON(BSON("x" << BSON_ARRAY(1 << 2 << 3))));
+ ASSERT_TRUE(lte.matchesBSON(BSON("x" << BSON_ARRAY(BSON("y" << 3)))));
+ ASSERT_TRUE(lte.matchesBSON(BSON("x" << BSON_ARRAY(BSON("y" << 1) << BSON("y" << 2)))));
+ ASSERT_FALSE(lte.matchesBSON(BSON("x" << BSON("y" << 3))));
+ }
+}
+
+DEATH_TEST_REGEX(InternalExprComparisonMatchExpression,
+ CannotCompareToArray,
+ R"#(Invariant failure.*_rhs.type\(\) != BSONType::Array)#") {
+ BSONObj operand = BSON("x" << BSON_ARRAY(1 << 2));
+ InternalExprGTMatchExpression gt(operand.firstElement().fieldNameStringData(),
+ operand.firstElement());
+}
+
+DEATH_TEST_REGEX(InternalExprComparisonMatchExpression,
+ CannotCompareToUndefined,
+ R"#(Invariant failure.*_rhs.type\(\) != BSONType::Undefined)#") {
+ BSONObj operand = BSON("x" << BSONUndefined);
+ InternalExprGTMatchExpression gt(operand.firstElement().fieldNameStringData(),
+ operand.firstElement());
+}
+
+
+} // namespace
+} // namespace mongo
diff --git a/src/mongo/db/matcher/expression_internal_expr_eq.cpp b/src/mongo/db/matcher/expression_internal_expr_eq.cpp
deleted file mode 100644
index 83d84254c32..00000000000
--- a/src/mongo/db/matcher/expression_internal_expr_eq.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-/**
- * Copyright (C) 2018-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * 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
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * 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 Server Side 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/matcher/expression_internal_expr_eq.h"
-
-#include "mongo/bson/bsonobj.h"
-#include "mongo/bson/bsonobjbuilder.h"
-
-namespace mongo {
-
-constexpr StringData InternalExprEqMatchExpression::kName;
-
-bool InternalExprEqMatchExpression::matchesSingleElement(const BSONElement& elem,
- MatchDetails* details) const {
- // We use NonLeafArrayBehavior::kMatchSubpath traversal in InternalExprEqMatchExpression. This
- // means matchesSinglElement() will be called when an array is found anywhere along the patch we
- // are matching against. When this occurs, we return 'true' and depend on the corresponding
- // ExprMatchExpression node to filter properly.
- if (elem.type() == BSONType::Array) {
- return true;
- }
-
- if (elem.canonicalType() != _rhs.canonicalType()) {
- return false;
- }
-
- auto comp = BSONElement::compareElements(
- elem, _rhs, BSONElement::ComparisonRules::kConsiderFieldName, _collator);
- return comp == 0;
-}
-
-std::unique_ptr<MatchExpression> InternalExprEqMatchExpression::shallowClone() const {
- auto clone = std::make_unique<InternalExprEqMatchExpression>(path(), _rhs);
- clone->setCollator(_collator);
- if (getTag()) {
- clone->setTag(getTag()->clone());
- }
- return clone;
-}
-
-} // namespace mongo
diff --git a/src/mongo/db/matcher/expression_internal_expr_eq.h b/src/mongo/db/matcher/expression_internal_expr_eq.h
deleted file mode 100644
index 2c52eb4e67d..00000000000
--- a/src/mongo/db/matcher/expression_internal_expr_eq.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/**
- * Copyright (C) 2018-present MongoDB, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the Server Side Public License, version 1,
- * as published by MongoDB, Inc.
- *
- * 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
- * Server Side Public License for more details.
- *
- * You should have received a copy of the Server Side Public License
- * along with this program. If not, see
- * <http://www.mongodb.com/licensing/server-side-public-license>.
- *
- * 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 Server Side 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.
- */
-
-#pragma once
-
-#include "mongo/db/matcher/expression_leaf.h"
-
-namespace mongo {
-
-/**
- * An InternalExprEqMatchExpression is an equality expression with similar semantics to the $eq
- * expression. It differs from the regular equality match expression in the following ways:
- *
- * - The document will match if there is an array anywhere along the path. By always returning true
- * in such cases, we match a superset of documents that the related aggregation expression would
- * match. This sidesteps us having to implement field path expression evaluation as part of this
- * match expression.
- *
- * - Equality to null matches literal nulls, but not documents in which the field path is missing or
- * undefined.
- *
- * - Equality to an array is illegal. It is invalid usage to construct a
- * InternalExprEqMatchExpression node which compares to an array.
- */
-class InternalExprEqMatchExpression final : public ComparisonMatchExpressionBase {
-public:
- static constexpr StringData kName = "$_internalExprEq"_sd;
-
- InternalExprEqMatchExpression(StringData path, BSONElement value)
- : ComparisonMatchExpressionBase(MatchType::INTERNAL_EXPR_EQ,
- path,
- Value(value),
- ElementPath::LeafArrayBehavior::kNoTraversal,
- ElementPath::NonLeafArrayBehavior::kMatchSubpath) {
- invariant(_rhs.type() != BSONType::Undefined);
- invariant(_rhs.type() != BSONType::Array);
- }
-
- StringData name() const final {
- return kName;
- }
-
- bool matchesSingleElement(const BSONElement&, MatchDetails*) const final;
-
- std::unique_ptr<MatchExpression> shallowClone() const final;
-
- void acceptVisitor(MatchExpressionMutableVisitor* visitor) final {
- visitor->visit(this);
- }
-
- void acceptVisitor(MatchExpressionConstVisitor* visitor) const final {
- visitor->visit(this);
- }
-};
-
-} // namespace mongo
diff --git a/src/mongo/db/matcher/expression_internal_expr_eq_test.cpp b/src/mongo/db/matcher/expression_internal_expr_eq_test.cpp
index bd5ad2d878a..0de1fbec00b 100644
--- a/src/mongo/db/matcher/expression_internal_expr_eq_test.cpp
+++ b/src/mongo/db/matcher/expression_internal_expr_eq_test.cpp
@@ -30,7 +30,7 @@
#include "mongo/platform/basic.h"
#include "mongo/bson/json.h"
-#include "mongo/db/matcher/expression_internal_expr_eq.h"
+#include "mongo/db/matcher/expression_internal_expr_comparison.h"
#include "mongo/db/matcher/matcher.h"
#include "mongo/db/pipeline/expression_context_for_test.h"
#include "mongo/db/query/collation/collator_interface_mock.h"
diff --git a/src/mongo/db/matcher/expression_leaf.h b/src/mongo/db/matcher/expression_leaf.h
index d9a4d6192dc..e042e6d99fb 100644
--- a/src/mongo/db/matcher/expression_leaf.h
+++ b/src/mongo/db/matcher/expression_leaf.h
@@ -104,6 +104,19 @@ public:
}
}
+ static bool isInternalExprComparison(MatchType matchType) {
+ switch (matchType) {
+ 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:
+ return true;
+ default:
+ return false;
+ }
+ }
+
ComparisonMatchExpressionBase(MatchType type,
StringData path,
Value rhs,
diff --git a/src/mongo/db/matcher/expression_parser.cpp b/src/mongo/db/matcher/expression_parser.cpp
index 197cc481e3c..7ef37bc2e02 100644
--- a/src/mongo/db/matcher/expression_parser.cpp
+++ b/src/mongo/db/matcher/expression_parser.cpp
@@ -44,7 +44,7 @@
#include "mongo/db/matcher/expression_array.h"
#include "mongo/db/matcher/expression_expr.h"
#include "mongo/db/matcher/expression_geo.h"
-#include "mongo/db/matcher/expression_internal_expr_eq.h"
+#include "mongo/db/matcher/expression_internal_expr_comparison.h"
#include "mongo/db/matcher/expression_leaf.h"
#include "mongo/db/matcher/expression_tree.h"
#include "mongo/db/matcher/expression_type.h"
@@ -1692,6 +1692,55 @@ StatusWithMatchExpression parseSubField(const BSONObj& context,
return {std::move(exprEqExpr)};
}
+ case PathAcceptingKeyword::INTERNAL_EXPR_GT: {
+ if (e.type() == BSONType::Undefined || e.type() == BSONType::Array) {
+ return {Status(ErrorCodes::BadValue,
+ str::stream() << InternalExprGTMatchExpression::kName
+ << " cannot be used to compare to type: "
+ << typeName(e.type()))};
+ }
+
+ auto exprGtExpr = std::make_unique<InternalExprGTMatchExpression>(name, e);
+ exprGtExpr->setCollator(expCtx->getCollator());
+ return {std::move(exprGtExpr)};
+ }
+ case PathAcceptingKeyword::INTERNAL_EXPR_GTE: {
+ if (e.type() == BSONType::Undefined || e.type() == BSONType::Array) {
+ return {Status(ErrorCodes::BadValue,
+ str::stream() << InternalExprGTEMatchExpression::kName
+ << " cannot be used to compare to type: "
+ << typeName(e.type()))};
+ }
+
+ auto exprGteExpr = std::make_unique<InternalExprGTEMatchExpression>(name, e);
+ exprGteExpr->setCollator(expCtx->getCollator());
+ return {std::move(exprGteExpr)};
+ }
+ case PathAcceptingKeyword::INTERNAL_EXPR_LT: {
+ if (e.type() == BSONType::Undefined || e.type() == BSONType::Array) {
+ return {Status(ErrorCodes::BadValue,
+ str::stream() << InternalExprLTMatchExpression::kName
+ << " cannot be used to compare to type: "
+ << typeName(e.type()))};
+ }
+
+ auto exprLtExpr = std::make_unique<InternalExprLTMatchExpression>(name, e);
+ exprLtExpr->setCollator(expCtx->getCollator());
+ return {std::move(exprLtExpr)};
+ }
+ case PathAcceptingKeyword::INTERNAL_EXPR_LTE: {
+ if (e.type() == BSONType::Undefined || e.type() == BSONType::Array) {
+ return {Status(ErrorCodes::BadValue,
+ str::stream() << InternalExprLTEMatchExpression::kName
+ << " cannot be used to compare to type: "
+ << typeName(e.type()))};
+ }
+
+ auto exprLteExpr = std::make_unique<InternalExprLTEMatchExpression>(name, e);
+ exprLteExpr->setCollator(expCtx->getCollator());
+ return {std::move(exprLteExpr)};
+ }
+
// Handles bitwise query operators.
case PathAcceptingKeyword::BITS_ALL_SET: {
return parseBitTest<BitsAllSetMatchExpression>(name, e, expCtx);
@@ -1990,6 +2039,10 @@ MONGO_INITIALIZER(MatchExpressionParser)(InitializerContext* context) {
queryOperatorMap =
std::make_unique<StringMap<PathAcceptingKeyword>>(StringMap<PathAcceptingKeyword>{
{"_internalExprEq", PathAcceptingKeyword::INTERNAL_EXPR_EQ},
+ {"_internalExprGt", PathAcceptingKeyword::INTERNAL_EXPR_GT},
+ {"_internalExprGte", PathAcceptingKeyword::INTERNAL_EXPR_GTE},
+ {"_internalExprLt", PathAcceptingKeyword::INTERNAL_EXPR_LT},
+ {"_internalExprLte", PathAcceptingKeyword::INTERNAL_EXPR_LTE},
{"_internalSchemaAllElemMatchFromIndex",
PathAcceptingKeyword::INTERNAL_SCHEMA_ALL_ELEM_MATCH_FROM_INDEX},
{"_internalSchemaBinDataEncryptedType",
diff --git a/src/mongo/db/matcher/expression_parser.h b/src/mongo/db/matcher/expression_parser.h
index 98ed368d95e..101a7e10332 100644
--- a/src/mongo/db/matcher/expression_parser.h
+++ b/src/mongo/db/matcher/expression_parser.h
@@ -62,6 +62,10 @@ enum class PathAcceptingKeyword {
GREATER_THAN,
GREATER_THAN_OR_EQUAL,
INTERNAL_EXPR_EQ,
+ INTERNAL_EXPR_GT,
+ INTERNAL_EXPR_GTE,
+ INTERNAL_EXPR_LT,
+ INTERNAL_EXPR_LTE,
INTERNAL_SCHEMA_ALL_ELEM_MATCH_FROM_INDEX,
INTERNAL_SCHEMA_BIN_DATA_ENCRYPTED_TYPE,
INTERNAL_SCHEMA_BIN_DATA_SUBTYPE,
diff --git a/src/mongo/db/matcher/expression_visitor.h b/src/mongo/db/matcher/expression_visitor.h
index 237263cd4ba..59ec095ea9a 100644
--- a/src/mongo/db/matcher/expression_visitor.h
+++ b/src/mongo/db/matcher/expression_visitor.h
@@ -50,6 +50,10 @@ class GeoMatchExpression;
class GeoNearMatchExpression;
class InMatchExpression;
class InternalExprEqMatchExpression;
+class InternalExprGTMatchExpression;
+class InternalExprGTEMatchExpression;
+class InternalExprLTMatchExpression;
+class InternalExprLTEMatchExpression;
class InternalSchemaAllElemMatchFromIndexMatchExpression;
class InternalSchemaAllowedPropertiesMatchExpression;
class InternalSchemaBinDataEncryptedTypeExpression;
@@ -115,6 +119,12 @@ public:
virtual void visit(tree_walker::MaybeConstPtr<IsConst, GeoNearMatchExpression> expr) = 0;
virtual void visit(tree_walker::MaybeConstPtr<IsConst, InMatchExpression> expr) = 0;
virtual void visit(tree_walker::MaybeConstPtr<IsConst, InternalExprEqMatchExpression> expr) = 0;
+ virtual void visit(tree_walker::MaybeConstPtr<IsConst, InternalExprGTMatchExpression> expr) = 0;
+ virtual void visit(
+ tree_walker::MaybeConstPtr<IsConst, InternalExprGTEMatchExpression> expr) = 0;
+ virtual void visit(tree_walker::MaybeConstPtr<IsConst, InternalExprLTMatchExpression> expr) = 0;
+ virtual void visit(
+ tree_walker::MaybeConstPtr<IsConst, InternalExprLTEMatchExpression> expr) = 0;
virtual void visit(
tree_walker::MaybeConstPtr<IsConst, InternalSchemaAllElemMatchFromIndexMatchExpression>
expr) = 0;
diff --git a/src/mongo/db/matcher/rewrite_expr.cpp b/src/mongo/db/matcher/rewrite_expr.cpp
index b5b12278d17..27e19d4c5fe 100644
--- a/src/mongo/db/matcher/rewrite_expr.cpp
+++ b/src/mongo/db/matcher/rewrite_expr.cpp
@@ -33,7 +33,7 @@
#include "mongo/db/matcher/rewrite_expr.h"
-#include "mongo/db/matcher/expression_internal_expr_eq.h"
+#include "mongo/db/matcher/expression_internal_expr_comparison.h"
#include "mongo/db/matcher/expression_leaf.h"
#include "mongo/db/matcher/expression_tree.h"
#include "mongo/logv2/log.h"
@@ -141,6 +141,32 @@ std::unique_ptr<MatchExpression> RewriteExpr::_rewriteComparisonExpression(
lhs = dynamic_cast<ExpressionFieldPath*>(operandList[1].get());
rhs = dynamic_cast<ExpressionConstant*>(operandList[0].get());
invariant(lhs && rhs);
+
+ // The MatchExpression is normalized so that the field path expression is on the left. For
+ // cases like {$gt: [1, "$x"]} where the order of the child expressions matter, we also
+ // change the comparison operator.
+ switch (cmpOperator) {
+ case ExpressionCompare::GT: {
+ cmpOperator = ExpressionCompare::LT;
+ break;
+ }
+ case ExpressionCompare::GTE: {
+ cmpOperator = ExpressionCompare::LTE;
+ break;
+ }
+ case ExpressionCompare::LT: {
+ cmpOperator = ExpressionCompare::GT;
+ break;
+ }
+ case ExpressionCompare::LTE: {
+ cmpOperator = ExpressionCompare::GTE;
+ break;
+ }
+ case ExpressionCompare::EQ:
+ break;
+ default:
+ MONGO_UNREACHABLE_TASSERT(3994306);
+ }
}
// Build argument for ComparisonMatchExpression.
@@ -155,20 +181,56 @@ std::unique_ptr<MatchExpression> RewriteExpr::_rewriteComparisonExpression(
std::unique_ptr<MatchExpression> RewriteExpr::_buildComparisonMatchExpression(
ExpressionCompare::CmpOp comparisonOp, BSONElement fieldAndValue) {
- invariant(comparisonOp == ExpressionCompare::EQ);
-
- auto eqMatchExpr =
- std::make_unique<InternalExprEqMatchExpression>(fieldAndValue.fieldName(), fieldAndValue);
- eqMatchExpr->setCollator(_collator);
+ tassert(3994301,
+ "comparisonOp must be one of the following: $eq, $gt, $gte, $lt, $lte",
+ comparisonOp == ExpressionCompare::EQ || comparisonOp == ExpressionCompare::GT ||
+ comparisonOp == ExpressionCompare::GTE || comparisonOp == ExpressionCompare::LT ||
+ comparisonOp == ExpressionCompare::LTE);
+
+ std::unique_ptr<MatchExpression> matchExpr;
+
+ switch (comparisonOp) {
+ case ExpressionCompare::EQ: {
+ matchExpr = std::make_unique<InternalExprEqMatchExpression>(fieldAndValue.fieldName(),
+ fieldAndValue);
+ break;
+ }
+ case ExpressionCompare::GT: {
+ matchExpr = std::make_unique<InternalExprGTMatchExpression>(fieldAndValue.fieldName(),
+ fieldAndValue);
+ break;
+ }
+ case ExpressionCompare::GTE: {
+ matchExpr = std::make_unique<InternalExprGTEMatchExpression>(fieldAndValue.fieldName(),
+ fieldAndValue);
+ break;
+ }
+ case ExpressionCompare::LT: {
+ matchExpr = std::make_unique<InternalExprLTMatchExpression>(fieldAndValue.fieldName(),
+ fieldAndValue);
+ break;
+ }
+ case ExpressionCompare::LTE: {
+ matchExpr = std::make_unique<InternalExprLTEMatchExpression>(fieldAndValue.fieldName(),
+ fieldAndValue);
+ break;
+ }
+ default:
+ MONGO_UNREACHABLE_TASSERT(3994307);
+ }
+ matchExpr->setCollator(_collator);
- return eqMatchExpr;
+ return matchExpr;
}
bool RewriteExpr::_canRewriteComparison(
const boost::intrusive_ptr<ExpressionCompare>& expression) const {
- // Currently we only rewrite $eq expressions.
- if (expression->getOp() != ExpressionCompare::EQ) {
+ // Currently we only rewrite $eq, $gt, $gte, $lt and $lte expressions.
+ auto op = expression->getOp();
+ if (op != ExpressionCompare::EQ && op != ExpressionCompare::GT &&
+ op != ExpressionCompare::GTE && op != ExpressionCompare::LT &&
+ op != ExpressionCompare::LTE) {
return false;
}