summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCharlie Swanson <charlie.swanson@mongodb.com>2023-02-10 20:24:21 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2023-02-11 03:25:32 +0000
commit36d9f82090f1d84f38d3d444319ce17393455353 (patch)
tree69a87cb94a0f7b3161c185439047427f8899bad2
parent1e55535078cb529ae36f32198086e382d0a7d4bd (diff)
downloadmongo-36d9f82090f1d84f38d3d444319ce17393455353.tar.gz
SERVER-73141 Generate query shape for expressions in expression_leaf.h
-rw-r--r--src/mongo/db/exec/bucket_unpacker.cpp4
-rw-r--r--src/mongo/db/exec/collection_scan.cpp4
-rw-r--r--src/mongo/db/exec/fetch.cpp6
-rw-r--r--src/mongo/db/exec/index_scan.cpp4
-rw-r--r--src/mongo/db/exec/or.cpp6
-rw-r--r--src/mongo/db/exec/text_or.cpp4
-rw-r--r--src/mongo/db/matcher/expression.h25
-rw-r--r--src/mongo/db/matcher/expression_algo_test.cpp803
-rw-r--r--src/mongo/db/matcher/expression_always_boolean.h3
-rw-r--r--src/mongo/db/matcher/expression_arity.h7
-rw-r--r--src/mongo/db/matcher/expression_array.cpp20
-rw-r--r--src/mongo/db/matcher/expression_array.h9
-rw-r--r--src/mongo/db/matcher/expression_expr.cpp3
-rw-r--r--src/mongo/db/matcher/expression_expr.h2
-rw-r--r--src/mongo/db/matcher/expression_expr_test.cpp9
-rw-r--r--src/mongo/db/matcher/expression_geo.cpp12
-rw-r--r--src/mongo/db/matcher/expression_geo.h12
-rw-r--r--src/mongo/db/matcher/expression_internal_bucket_geo_within.cpp5
-rw-r--r--src/mongo/db/matcher/expression_internal_bucket_geo_within.h2
-rw-r--r--src/mongo/db/matcher/expression_internal_expr_eq_test.cpp2
-rw-r--r--src/mongo/db/matcher/expression_leaf.cpp49
-rw-r--r--src/mongo/db/matcher/expression_leaf.h18
-rw-r--r--src/mongo/db/matcher/expression_optimize_test.cpp48
-rw-r--r--src/mongo/db/matcher/expression_path.h21
-rw-r--r--src/mongo/db/matcher/expression_serialization_test.cpp4
-rw-r--r--src/mongo/db/matcher/expression_text_base.cpp3
-rw-r--r--src/mongo/db/matcher/expression_text_base.h6
-rw-r--r--src/mongo/db/matcher/expression_tree.cpp35
-rw-r--r--src/mongo/db/matcher/expression_tree.h12
-rw-r--r--src/mongo/db/matcher/expression_type.h8
-rw-r--r--src/mongo/db/matcher/expression_where_base.cpp3
-rw-r--r--src/mongo/db/matcher/expression_where_base.h2
-rw-r--r--src/mongo/db/matcher/parsed_match_expression_for_test.h75
-rw-r--r--src/mongo/db/matcher/rewrite_expr_test.cpp2
-rw-r--r--src/mongo/db/matcher/schema/assert_serializes_to.h8
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.cpp6
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.h3
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_allowed_properties.cpp9
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_allowed_properties.h2
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_eq.cpp4
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_eq.h3
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_fmod.cpp4
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_fmod.h3
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_match_array_index.cpp8
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_match_array_index.h3
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_num_array_items.cpp4
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_num_array_items.h3
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_num_properties.cpp5
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_num_properties.h2
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_object_match.cpp6
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_object_match.h3
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_root_doc_eq.cpp3
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_root_doc_eq.h2
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_str_length.cpp4
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_str_length.h3
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_unique_items.cpp6
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_unique_items.h3
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_xor.cpp5
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_xor.h2
-rw-r--r--src/mongo/db/matcher/schema/object_keywords_test.cpp4
-rw-r--r--src/mongo/db/pipeline/document_source_internal_unpack_bucket.cpp5
-rw-r--r--src/mongo/db/pipeline/document_source_internal_unpack_bucket_test/create_predicates_on_bucket_level_field_test.cpp70
-rw-r--r--src/mongo/db/pipeline/document_source_lookup_test.cpp5
-rw-r--r--src/mongo/db/pipeline/document_source_match.cpp16
-rw-r--r--src/mongo/db/pipeline/document_source_match_test.cpp2
-rw-r--r--src/mongo/db/query/SConscript18
-rw-r--r--src/mongo/db/query/canonical_query.cpp4
-rw-r--r--src/mongo/db/query/canonical_query_test.cpp4
-rw-r--r--src/mongo/db/query/explain.cpp2
-rw-r--r--src/mongo/db/query/plan_explainer_sbe.cpp4
-rw-r--r--src/mongo/db/query/planner_access_test.cpp4
-rw-r--r--src/mongo/db/query/projection_ast_util.cpp2
-rw-r--r--src/mongo/db/query/query_shape.cpp39
-rw-r--r--src/mongo/db/query/query_shape.h54
-rw-r--r--src/mongo/db/query/query_shape_test.cpp135
-rw-r--r--src/mongo/db/query/serialization_options.h70
-rw-r--r--src/mongo/dbtests/extensions_callback_real_test.cpp5
77 files changed, 1055 insertions, 720 deletions
diff --git a/src/mongo/db/exec/bucket_unpacker.cpp b/src/mongo/db/exec/bucket_unpacker.cpp
index 6ba30b07503..472c1ea369b 100644
--- a/src/mongo/db/exec/bucket_unpacker.cpp
+++ b/src/mongo/db/exec/bucket_unpacker.cpp
@@ -910,9 +910,9 @@ std::pair<bool, BSONObj> BucketSpec::pushdownPredicate(
BSONObjBuilder result;
if (metaOnlyPredicate)
- metaOnlyPredicate->serialize(&result);
+ metaOnlyPredicate->serialize(&result, {});
if (bucketMetricPredicate)
- bucketMetricPredicate->serialize(&result);
+ bucketMetricPredicate->serialize(&result, {});
return std::make_pair(bucketMetricPredicate.get(), result.obj());
}
diff --git a/src/mongo/db/exec/collection_scan.cpp b/src/mongo/db/exec/collection_scan.cpp
index 6cbacb0c997..e9deeaa9069 100644
--- a/src/mongo/db/exec/collection_scan.cpp
+++ b/src/mongo/db/exec/collection_scan.cpp
@@ -471,9 +471,7 @@ void CollectionScan::doReattachToOperationContext() {
unique_ptr<PlanStageStats> CollectionScan::getStats() {
// Add a BSON representation of the filter to the stats tree, if there is one.
if (nullptr != _filter) {
- BSONObjBuilder bob;
- _filter->serialize(&bob);
- _commonStats.filter = bob.obj();
+ _commonStats.filter = _filter->serialize();
}
unique_ptr<PlanStageStats> ret = std::make_unique<PlanStageStats>(_commonStats, STAGE_COLLSCAN);
diff --git a/src/mongo/db/exec/fetch.cpp b/src/mongo/db/exec/fetch.cpp
index 2bdcb890bc8..70718070292 100644
--- a/src/mongo/db/exec/fetch.cpp
+++ b/src/mongo/db/exec/fetch.cpp
@@ -191,10 +191,8 @@ unique_ptr<PlanStageStats> FetchStage::getStats() {
_commonStats.isEOF = isEOF();
// Add a BSON representation of the filter to the stats tree, if there is one.
- if (nullptr != _filter) {
- BSONObjBuilder bob;
- _filter->serialize(&bob);
- _commonStats.filter = bob.obj();
+ if (_filter) {
+ _commonStats.filter = _filter->serialize();
}
unique_ptr<PlanStageStats> ret = std::make_unique<PlanStageStats>(_commonStats, STAGE_FETCH);
diff --git a/src/mongo/db/exec/index_scan.cpp b/src/mongo/db/exec/index_scan.cpp
index 03b29817202..1d89f2c3957 100644
--- a/src/mongo/db/exec/index_scan.cpp
+++ b/src/mongo/db/exec/index_scan.cpp
@@ -294,9 +294,7 @@ std::unique_ptr<PlanStageStats> IndexScan::getStats() {
// Add a BSON representation of the filter to the stats tree, if there is one.
if (nullptr != _filter) {
- BSONObjBuilder bob;
- _filter->serialize(&bob);
- _commonStats.filter = bob.obj();
+ _commonStats.filter = _filter->serialize();
}
// These specific stats fields never change.
diff --git a/src/mongo/db/exec/or.cpp b/src/mongo/db/exec/or.cpp
index ec0d680ac37..078765ffc84 100644
--- a/src/mongo/db/exec/or.cpp
+++ b/src/mongo/db/exec/or.cpp
@@ -122,10 +122,8 @@ unique_ptr<PlanStageStats> OrStage::getStats() {
_commonStats.isEOF = isEOF();
// Add a BSON representation of the filter to the stats tree, if there is one.
- if (nullptr != _filter) {
- BSONObjBuilder bob;
- _filter->serialize(&bob);
- _commonStats.filter = bob.obj();
+ if (_filter) {
+ _commonStats.filter = _filter->serialize();
}
unique_ptr<PlanStageStats> ret = std::make_unique<PlanStageStats>(_commonStats, STAGE_OR);
diff --git a/src/mongo/db/exec/text_or.cpp b/src/mongo/db/exec/text_or.cpp
index c7a9f3d7b17..2d477ca49e5 100644
--- a/src/mongo/db/exec/text_or.cpp
+++ b/src/mongo/db/exec/text_or.cpp
@@ -105,9 +105,7 @@ std::unique_ptr<PlanStageStats> TextOrStage::getStats() {
_commonStats.isEOF = isEOF();
if (_filter) {
- BSONObjBuilder bob;
- _filter->serialize(&bob);
- _commonStats.filter = bob.obj();
+ _commonStats.filter = _filter->serialize();
}
unique_ptr<PlanStageStats> ret = std::make_unique<PlanStageStats>(_commonStats, STAGE_TEXT_OR);
diff --git a/src/mongo/db/matcher/expression.h b/src/mongo/db/matcher/expression.h
index a56139a343c..afc50fe73f0 100644
--- a/src/mongo/db/matcher/expression.h
+++ b/src/mongo/db/matcher/expression.h
@@ -41,6 +41,7 @@
#include "mongo/db/matcher/match_details.h"
#include "mongo/db/matcher/matchable.h"
#include "mongo/db/pipeline/dependencies.h"
+#include "mongo/db/query/serialization_options.h"
#include "mongo/util/fail_point.h"
namespace mongo {
@@ -473,19 +474,27 @@ public:
void setCollator(const CollatorInterface* collator);
/**
- * Serialize the MatchExpression to BSON, appending to 'out'. Output of this method is expected
- * to be a valid query object, that, when parsed, produces a logically equivalent
- * MatchExpression. If 'includePath' is false then the serialization should assume it's in a
- * context where the path has been serialized elsewhere, such as within an $elemMatch value.
+ * Serialize the MatchExpression to BSON, appending to 'out'.
+ *
+ * See 'SerializationOptions' for some options.
+ *
+ * Generally, the output of this method is expected to be a valid query object that, when
+ * parsed, produces a logically equivalent MatchExpression. However, if special options are set,
+ * this no longer holds.
+ *
+ * If 'options.replacementForLiteralArgs' is set, the result is no longer expected to re-parse,
+ * since we will put strings in places where strings may not be accpeted syntactically (e.g. a
+ * number is always expected, as in with the $mod expression).
*/
- virtual void serialize(BSONObjBuilder* out, bool includePath = true) const = 0;
+ virtual void serialize(BSONObjBuilder* out, SerializationOptions options) const = 0;
/**
- * Convenience method which serializes this MatchExpression to a BSONObj.
+ * Convenience method which serializes this MatchExpression to a BSONObj. See the override with
+ * a BSONObjBuilder* argument for details.
*/
- BSONObj serialize(bool includePath = true) const {
+ BSONObj serialize(SerializationOptions options = {}) const {
BSONObjBuilder bob;
- serialize(&bob, includePath);
+ serialize(&bob, options);
return bob.obj();
}
diff --git a/src/mongo/db/matcher/expression_algo_test.cpp b/src/mongo/db/matcher/expression_algo_test.cpp
index c1fcb232736..fc810253af8 100644
--- a/src/mongo/db/matcher/expression_algo_test.cpp
+++ b/src/mongo/db/matcher/expression_algo_test.cpp
@@ -36,6 +36,7 @@
#include "mongo/db/matcher/expression.h"
#include "mongo/db/matcher/expression_algo.h"
#include "mongo/db/matcher/expression_parser.h"
+#include "mongo/db/matcher/parsed_match_expression_for_test.h"
#include "mongo/db/pipeline/expression_context_for_test.h"
#include "mongo/db/query/collation/collator_interface_mock.h"
#include "mongo/platform/decimal128.h"
@@ -44,36 +45,8 @@ namespace mongo {
using std::unique_ptr;
-/**
- * A MatchExpression does not hold the memory for BSONElements, so use ParsedMatchExpression to
- * ensure that the BSONObj outlives the MatchExpression.
- */
-class ParsedMatchExpression {
-public:
- ParsedMatchExpression(const std::string& str, const CollatorInterface* collator = nullptr)
- : _obj(fromjson(str)) {
- _expCtx = make_intrusive<ExpressionContextForTest>();
- _expCtx->setCollator(CollatorInterface::cloneCollator(collator));
- StatusWithMatchExpression result = MatchExpressionParser::parse(_obj, _expCtx);
- ASSERT_OK(result.getStatus());
- _expr = std::move(result.getValue());
- }
-
- const MatchExpression* get() const {
- return _expr.get();
- }
- std::unique_ptr<MatchExpression> extractExpr() {
- return std::move(_expr);
- }
-
-private:
- const BSONObj _obj;
- std::unique_ptr<MatchExpression> _expr;
- boost::intrusive_ptr<ExpressionContext> _expCtx;
-};
-
-void assertMatchesEqual(const ParsedMatchExpression& expected,
+void assertMatchesEqual(const ParsedMatchExpressionForTest& expected,
const std::unique_ptr<MatchExpression>& actual) {
if (expected.get() == nullptr) {
ASSERT(actual == nullptr);
@@ -91,29 +64,29 @@ TEST(ExpressionAlgoIsSubsetOf, NullAndOmittedField) {
ASSERT_EQUALS(ErrorCodes::BadValue,
MatchExpressionParser::parse(undefined, std::move(expCtx)).getStatus());
- ParsedMatchExpression empty("{}");
- ParsedMatchExpression null("{a: null}");
+ ParsedMatchExpressionForTest empty("{}");
+ ParsedMatchExpressionForTest null("{a: null}");
ASSERT_TRUE(expression::isSubsetOf(null.get(), empty.get()));
ASSERT_FALSE(expression::isSubsetOf(empty.get(), null.get()));
- ParsedMatchExpression b1("{b: 1}");
- ParsedMatchExpression aNullB1("{a: null, b: 1}");
+ ParsedMatchExpressionForTest b1("{b: 1}");
+ ParsedMatchExpressionForTest aNullB1("{a: null, b: 1}");
ASSERT_TRUE(expression::isSubsetOf(aNullB1.get(), b1.get()));
ASSERT_FALSE(expression::isSubsetOf(b1.get(), aNullB1.get()));
- ParsedMatchExpression a1C3("{a: 1, c: 3}");
- ParsedMatchExpression a1BNullC3("{a: 1, b: null, c: 3}");
+ ParsedMatchExpressionForTest a1C3("{a: 1, c: 3}");
+ ParsedMatchExpressionForTest a1BNullC3("{a: 1, b: null, c: 3}");
ASSERT_TRUE(expression::isSubsetOf(a1BNullC3.get(), a1C3.get()));
ASSERT_FALSE(expression::isSubsetOf(a1C3.get(), a1BNullC3.get()));
}
TEST(ExpressionAlgoIsSubsetOf, NullAndIn) {
- ParsedMatchExpression eqNull("{x: null}");
- ParsedMatchExpression inNull("{x: {$in: [null]}}");
- ParsedMatchExpression inNullOr2("{x: {$in: [null, 2]}}");
+ ParsedMatchExpressionForTest eqNull("{x: null}");
+ ParsedMatchExpressionForTest inNull("{x: {$in: [null]}}");
+ ParsedMatchExpressionForTest inNullOr2("{x: {$in: [null, 2]}}");
ASSERT_TRUE(expression::isSubsetOf(inNull.get(), eqNull.get()));
ASSERT_FALSE(expression::isSubsetOf(inNullOr2.get(), eqNull.get()));
@@ -123,19 +96,19 @@ TEST(ExpressionAlgoIsSubsetOf, NullAndIn) {
}
TEST(ExpressionAlgoIsSubsetOf, NullAndExists) {
- ParsedMatchExpression null("{x: null}");
- ParsedMatchExpression exists("{x: {$exists: true}}");
+ ParsedMatchExpressionForTest null("{x: null}");
+ ParsedMatchExpressionForTest exists("{x: {$exists: true}}");
ASSERT_FALSE(expression::isSubsetOf(null.get(), exists.get()));
ASSERT_FALSE(expression::isSubsetOf(exists.get(), null.get()));
}
TEST(ExpressionAlgoIsSubsetOf, Compare_NaN) {
- ParsedMatchExpression nan("{x: NaN}");
- ParsedMatchExpression lt("{x: {$lt: 5}}");
- ParsedMatchExpression lte("{x: {$lte: 5}}");
- ParsedMatchExpression gte("{x: {$gte: 5}}");
- ParsedMatchExpression gt("{x: {$gt: 5}}");
- ParsedMatchExpression in("{x: {$in: [5]}}");
+ ParsedMatchExpressionForTest nan("{x: NaN}");
+ ParsedMatchExpressionForTest lt("{x: {$lt: 5}}");
+ ParsedMatchExpressionForTest lte("{x: {$lte: 5}}");
+ ParsedMatchExpressionForTest gte("{x: {$gte: 5}}");
+ ParsedMatchExpressionForTest gt("{x: {$gt: 5}}");
+ ParsedMatchExpressionForTest in("{x: {$in: [5]}}");
ASSERT_TRUE(expression::isSubsetOf(nan.get(), nan.get()));
ASSERT_FALSE(expression::isSubsetOf(nan.get(), lt.get()));
@@ -149,7 +122,7 @@ TEST(ExpressionAlgoIsSubsetOf, Compare_NaN) {
ASSERT_FALSE(expression::isSubsetOf(nan.get(), in.get()));
ASSERT_FALSE(expression::isSubsetOf(in.get(), nan.get()));
- ParsedMatchExpression decNan("{x : NumberDecimal(\"NaN\") }");
+ ParsedMatchExpressionForTest decNan("{x : NumberDecimal(\"NaN\") }");
ASSERT_TRUE(expression::isSubsetOf(decNan.get(), decNan.get()));
ASSERT_TRUE(expression::isSubsetOf(nan.get(), decNan.get()));
ASSERT_TRUE(expression::isSubsetOf(decNan.get(), nan.get()));
@@ -164,9 +137,9 @@ TEST(ExpressionAlgoIsSubsetOf, Compare_NaN) {
}
TEST(ExpressionAlgoIsSubsetOf, Compare_EQ) {
- ParsedMatchExpression a5("{a: 5}");
- ParsedMatchExpression a6("{a: 6}");
- ParsedMatchExpression b5("{b: 5}");
+ ParsedMatchExpressionForTest a5("{a: 5}");
+ ParsedMatchExpressionForTest a6("{a: 6}");
+ ParsedMatchExpressionForTest b5("{b: 5}");
ASSERT_TRUE(expression::isSubsetOf(a5.get(), a5.get()));
ASSERT_FALSE(expression::isSubsetOf(a5.get(), a6.get()));
@@ -174,10 +147,10 @@ TEST(ExpressionAlgoIsSubsetOf, Compare_EQ) {
}
TEST(ExpressionAlgoIsSubsetOf, CompareAnd_EQ) {
- ParsedMatchExpression a1B2("{a: 1, b: 2}");
- ParsedMatchExpression a1B7("{a: 1, b: 7}");
- ParsedMatchExpression a1("{a: 1}");
- ParsedMatchExpression b2("{b: 2}");
+ ParsedMatchExpressionForTest a1B2("{a: 1, b: 2}");
+ ParsedMatchExpressionForTest a1B7("{a: 1, b: 7}");
+ ParsedMatchExpressionForTest a1("{a: 1}");
+ ParsedMatchExpressionForTest b2("{b: 2}");
ASSERT_TRUE(expression::isSubsetOf(a1B2.get(), a1B2.get()));
ASSERT_FALSE(expression::isSubsetOf(a1B2.get(), a1B7.get()));
@@ -188,74 +161,74 @@ TEST(ExpressionAlgoIsSubsetOf, CompareAnd_EQ) {
}
TEST(ExpressionAlgoIsSubsetOf, CompareAnd_GT) {
- ParsedMatchExpression filter("{a: {$gt: 5}, b: {$gt: 6}}");
- ParsedMatchExpression query("{a: {$gt: 5}, b: {$gt: 6}, c: {$gt: 7}}");
+ ParsedMatchExpressionForTest filter("{a: {$gt: 5}, b: {$gt: 6}}");
+ ParsedMatchExpressionForTest query("{a: {$gt: 5}, b: {$gt: 6}, c: {$gt: 7}}");
ASSERT_TRUE(expression::isSubsetOf(query.get(), filter.get()));
ASSERT_FALSE(expression::isSubsetOf(filter.get(), query.get()));
}
TEST(ExpressionAlgoIsSubsetOf, CompareAnd_SingleField) {
- ParsedMatchExpression filter("{a: {$gt: 5, $lt: 7}}");
- ParsedMatchExpression query("{a: {$gt: 5, $lt: 6}}");
+ ParsedMatchExpressionForTest filter("{a: {$gt: 5, $lt: 7}}");
+ ParsedMatchExpressionForTest query("{a: {$gt: 5, $lt: 6}}");
ASSERT_TRUE(expression::isSubsetOf(query.get(), filter.get()));
ASSERT_FALSE(expression::isSubsetOf(filter.get(), query.get()));
}
TEST(ExpressionAlgoIsSubsetOf, CompareOr_LT) {
- ParsedMatchExpression lt5("{a: {$lt: 5}}");
- ParsedMatchExpression eq2OrEq3("{$or: [{a: 2}, {a: 3}]}");
- ParsedMatchExpression eq4OrEq5("{$or: [{a: 4}, {a: 5}]}");
- ParsedMatchExpression eq4OrEq6("{$or: [{a: 4}, {a: 6}]}");
+ ParsedMatchExpressionForTest lt5("{a: {$lt: 5}}");
+ ParsedMatchExpressionForTest eq2OrEq3("{$or: [{a: 2}, {a: 3}]}");
+ ParsedMatchExpressionForTest eq4OrEq5("{$or: [{a: 4}, {a: 5}]}");
+ ParsedMatchExpressionForTest eq4OrEq6("{$or: [{a: 4}, {a: 6}]}");
ASSERT_TRUE(expression::isSubsetOf(eq2OrEq3.get(), lt5.get()));
ASSERT_FALSE(expression::isSubsetOf(eq4OrEq5.get(), lt5.get()));
ASSERT_FALSE(expression::isSubsetOf(eq4OrEq6.get(), lt5.get()));
- ParsedMatchExpression lt4OrLt5("{$or: [{a: {$lt: 4}}, {a: {$lt: 5}}]}");
+ ParsedMatchExpressionForTest lt4OrLt5("{$or: [{a: {$lt: 4}}, {a: {$lt: 5}}]}");
ASSERT_TRUE(expression::isSubsetOf(lt4OrLt5.get(), lt5.get()));
ASSERT_TRUE(expression::isSubsetOf(lt5.get(), lt4OrLt5.get()));
- ParsedMatchExpression lt7OrLt8("{$or: [{a: {$lt: 7}}, {a: {$lt: 8}}]}");
+ ParsedMatchExpressionForTest lt7OrLt8("{$or: [{a: {$lt: 7}}, {a: {$lt: 8}}]}");
ASSERT_FALSE(expression::isSubsetOf(lt7OrLt8.get(), lt5.get()));
ASSERT_TRUE(expression::isSubsetOf(lt5.get(), lt7OrLt8.get()));
}
TEST(ExpressionAlgoIsSubsetOf, CompareOr_GTE) {
- ParsedMatchExpression gte5("{a: {$gte: 5}}");
- ParsedMatchExpression eq4OrEq6("{$or: [{a: 4}, {a: 6}]}");
- ParsedMatchExpression eq5OrEq6("{$or: [{a: 5}, {a: 6}]}");
- ParsedMatchExpression eq7OrEq8("{$or: [{a: 7}, {a: 8}]}");
+ ParsedMatchExpressionForTest gte5("{a: {$gte: 5}}");
+ ParsedMatchExpressionForTest eq4OrEq6("{$or: [{a: 4}, {a: 6}]}");
+ ParsedMatchExpressionForTest eq5OrEq6("{$or: [{a: 5}, {a: 6}]}");
+ ParsedMatchExpressionForTest eq7OrEq8("{$or: [{a: 7}, {a: 8}]}");
ASSERT_FALSE(expression::isSubsetOf(eq4OrEq6.get(), gte5.get()));
ASSERT_TRUE(expression::isSubsetOf(eq5OrEq6.get(), gte5.get()));
ASSERT_TRUE(expression::isSubsetOf(eq7OrEq8.get(), gte5.get()));
- ParsedMatchExpression gte5OrGte6("{$or: [{a: {$gte: 5}}, {a: {$gte: 6}}]}");
+ ParsedMatchExpressionForTest gte5OrGte6("{$or: [{a: {$gte: 5}}, {a: {$gte: 6}}]}");
ASSERT_TRUE(expression::isSubsetOf(gte5OrGte6.get(), gte5.get()));
ASSERT_TRUE(expression::isSubsetOf(gte5.get(), gte5OrGte6.get()));
- ParsedMatchExpression gte3OrGte4("{$or: [{a: {$gte: 3}}, {a: {$gte: 4}}]}");
+ ParsedMatchExpressionForTest gte3OrGte4("{$or: [{a: {$gte: 3}}, {a: {$gte: 4}}]}");
ASSERT_FALSE(expression::isSubsetOf(gte3OrGte4.get(), gte5.get()));
ASSERT_TRUE(expression::isSubsetOf(gte5.get(), gte3OrGte4.get()));
}
TEST(ExpressionAlgoIsSubsetOf, DifferentCanonicalTypes) {
- ParsedMatchExpression number("{x: {$gt: 1}}");
- ParsedMatchExpression string("{x: {$gt: 'a'}}");
+ ParsedMatchExpressionForTest number("{x: {$gt: 1}}");
+ ParsedMatchExpressionForTest string("{x: {$gt: 'a'}}");
ASSERT_FALSE(expression::isSubsetOf(number.get(), string.get()));
ASSERT_FALSE(expression::isSubsetOf(string.get(), number.get()));
}
TEST(ExpressionAlgoIsSubsetOf, DifferentNumberTypes) {
- ParsedMatchExpression numberDouble("{x: 5.0}");
- ParsedMatchExpression numberInt("{x: NumberInt(5)}");
- ParsedMatchExpression numberLong("{x: NumberLong(5)}");
+ ParsedMatchExpressionForTest numberDouble("{x: 5.0}");
+ ParsedMatchExpressionForTest numberInt("{x: NumberInt(5)}");
+ ParsedMatchExpressionForTest numberLong("{x: NumberLong(5)}");
ASSERT_TRUE(expression::isSubsetOf(numberDouble.get(), numberInt.get()));
ASSERT_TRUE(expression::isSubsetOf(numberDouble.get(), numberLong.get()));
@@ -266,15 +239,15 @@ TEST(ExpressionAlgoIsSubsetOf, DifferentNumberTypes) {
}
TEST(ExpressionAlgoIsSubsetOf, PointInUnboundedRange) {
- ParsedMatchExpression a4("{a: 4}");
- ParsedMatchExpression a5("{a: 5}");
- ParsedMatchExpression a6("{a: 6}");
- ParsedMatchExpression b5("{b: 5}");
+ ParsedMatchExpressionForTest a4("{a: 4}");
+ ParsedMatchExpressionForTest a5("{a: 5}");
+ ParsedMatchExpressionForTest a6("{a: 6}");
+ ParsedMatchExpressionForTest b5("{b: 5}");
- ParsedMatchExpression lt5("{a: {$lt: 5}}");
- ParsedMatchExpression lte5("{a: {$lte: 5}}");
- ParsedMatchExpression gte5("{a: {$gte: 5}}");
- ParsedMatchExpression gt5("{a: {$gt: 5}}");
+ ParsedMatchExpressionForTest lt5("{a: {$lt: 5}}");
+ ParsedMatchExpressionForTest lte5("{a: {$lte: 5}}");
+ ParsedMatchExpressionForTest gte5("{a: {$gte: 5}}");
+ ParsedMatchExpressionForTest gt5("{a: {$gt: 5}}");
ASSERT_TRUE(expression::isSubsetOf(a4.get(), lte5.get()));
ASSERT_TRUE(expression::isSubsetOf(a5.get(), lte5.get()));
@@ -306,26 +279,26 @@ TEST(ExpressionAlgoIsSubsetOf, PointInUnboundedRange) {
}
TEST(ExpressionAlgoIsSubsetOf, PointInBoundedRange) {
- ParsedMatchExpression filter("{a: {$gt: 5, $lt: 10}}");
- ParsedMatchExpression query("{a: 6}");
+ ParsedMatchExpressionForTest filter("{a: {$gt: 5, $lt: 10}}");
+ ParsedMatchExpressionForTest query("{a: 6}");
ASSERT_TRUE(expression::isSubsetOf(query.get(), filter.get()));
ASSERT_FALSE(expression::isSubsetOf(filter.get(), query.get()));
}
TEST(ExpressionAlgoIsSubsetOf, PointInBoundedRange_FakeAnd) {
- ParsedMatchExpression filter("{a: {$gt: 5, $lt: 10}}");
- ParsedMatchExpression query("{$and: [{a: 6}, {a: 6}]}");
+ ParsedMatchExpressionForTest filter("{a: {$gt: 5, $lt: 10}}");
+ ParsedMatchExpressionForTest query("{$and: [{a: 6}, {a: 6}]}");
ASSERT_TRUE(expression::isSubsetOf(query.get(), filter.get()));
ASSERT_FALSE(expression::isSubsetOf(filter.get(), query.get()));
}
TEST(ExpressionAlgoIsSubsetOf, MultiplePointsInBoundedRange) {
- ParsedMatchExpression filter("{a: {$gt: 5, $lt: 10}}");
- ParsedMatchExpression queryAllInside("{a: {$in: [6, 7, 8]}}");
- ParsedMatchExpression queryStraddleLower("{a: {$in: [4.9, 5.1]}}");
- ParsedMatchExpression queryStraddleUpper("{a: {$in: [9.9, 10.1]}}");
+ ParsedMatchExpressionForTest filter("{a: {$gt: 5, $lt: 10}}");
+ ParsedMatchExpressionForTest queryAllInside("{a: {$in: [6, 7, 8]}}");
+ ParsedMatchExpressionForTest queryStraddleLower("{a: {$in: [4.9, 5.1]}}");
+ ParsedMatchExpressionForTest queryStraddleUpper("{a: {$in: [9.9, 10.1]}}");
ASSERT_TRUE(expression::isSubsetOf(queryAllInside.get(), filter.get()));
ASSERT_FALSE(expression::isSubsetOf(queryStraddleLower.get(), filter.get()));
@@ -333,18 +306,18 @@ TEST(ExpressionAlgoIsSubsetOf, MultiplePointsInBoundedRange) {
}
TEST(ExpressionAlgoIsSubsetOf, PointInCompoundRange) {
- ParsedMatchExpression filter("{a: {$gt: 5}, b: {$gt: 6}, c: {$gt: 7}}");
- ParsedMatchExpression query("{a: 10, b: 10, c: 10}");
+ ParsedMatchExpressionForTest filter("{a: {$gt: 5}, b: {$gt: 6}, c: {$gt: 7}}");
+ ParsedMatchExpressionForTest query("{a: 10, b: 10, c: 10}");
ASSERT_TRUE(expression::isSubsetOf(query.get(), filter.get()));
ASSERT_FALSE(expression::isSubsetOf(filter.get(), query.get()));
}
TEST(ExpressionAlgoIsSubsetOf, Compare_LT_LTE) {
- ParsedMatchExpression lte4("{x: {$lte: 4}}");
- ParsedMatchExpression lt5("{x: {$lt: 5}}");
- ParsedMatchExpression lte5("{x: {$lte: 5}}");
- ParsedMatchExpression lt6("{x: {$lt: 6}}");
+ ParsedMatchExpressionForTest lte4("{x: {$lte: 4}}");
+ ParsedMatchExpressionForTest lt5("{x: {$lt: 5}}");
+ ParsedMatchExpressionForTest lte5("{x: {$lte: 5}}");
+ ParsedMatchExpressionForTest lt6("{x: {$lt: 6}}");
ASSERT_TRUE(expression::isSubsetOf(lte4.get(), lte5.get()));
ASSERT_TRUE(expression::isSubsetOf(lt5.get(), lte5.get()));
@@ -358,10 +331,10 @@ TEST(ExpressionAlgoIsSubsetOf, Compare_LT_LTE) {
}
TEST(ExpressionAlgoIsSubsetOf, Compare_GT_GTE) {
- ParsedMatchExpression gte6("{x: {$gte: 6}}");
- ParsedMatchExpression gt5("{x: {$gt: 5}}");
- ParsedMatchExpression gte5("{x: {$gte: 5}}");
- ParsedMatchExpression gt4("{x: {$gt: 4}}");
+ ParsedMatchExpressionForTest gte6("{x: {$gte: 6}}");
+ ParsedMatchExpressionForTest gt5("{x: {$gt: 5}}");
+ ParsedMatchExpressionForTest gte5("{x: {$gte: 5}}");
+ ParsedMatchExpressionForTest gt4("{x: {$gt: 4}}");
ASSERT_TRUE(expression::isSubsetOf(gte6.get(), gte5.get()));
ASSERT_TRUE(expression::isSubsetOf(gt5.get(), gte5.get()));
@@ -375,18 +348,19 @@ TEST(ExpressionAlgoIsSubsetOf, Compare_GT_GTE) {
}
TEST(ExpressionAlgoIsSubsetOf, BoundedRangeInUnboundedRange) {
- ParsedMatchExpression filter("{a: {$gt: 1}}");
- ParsedMatchExpression query("{a: {$gt: 5, $lt: 10}}");
+ ParsedMatchExpressionForTest filter("{a: {$gt: 1}}");
+ ParsedMatchExpressionForTest query("{a: {$gt: 5, $lt: 10}}");
ASSERT_TRUE(expression::isSubsetOf(query.get(), filter.get()));
ASSERT_FALSE(expression::isSubsetOf(filter.get(), query.get()));
}
TEST(ExpressionAlgoIsSubsetOf, MultipleRangesInUnboundedRange) {
- ParsedMatchExpression filter("{a: {$gt: 1}}");
- ParsedMatchExpression negative("{$or: [{a: {$gt: 5, $lt: 10}}, {a: {$lt: 0}}]}");
- ParsedMatchExpression unbounded("{$or: [{a: {$gt: 5, $lt: 10}}, {a: {$gt: 15}}]}");
- ParsedMatchExpression bounded("{$or: [{a: {$gt: 5, $lt: 10}}, {a: {$gt: 20, $lt: 30}}]}");
+ ParsedMatchExpressionForTest filter("{a: {$gt: 1}}");
+ ParsedMatchExpressionForTest negative("{$or: [{a: {$gt: 5, $lt: 10}}, {a: {$lt: 0}}]}");
+ ParsedMatchExpressionForTest unbounded("{$or: [{a: {$gt: 5, $lt: 10}}, {a: {$gt: 15}}]}");
+ ParsedMatchExpressionForTest bounded(
+ "{$or: [{a: {$gt: 5, $lt: 10}}, {a: {$gt: 20, $lt: 30}}]}");
ASSERT_FALSE(expression::isSubsetOf(negative.get(), filter.get()));
ASSERT_TRUE(expression::isSubsetOf(unbounded.get(), filter.get()));
@@ -394,10 +368,10 @@ TEST(ExpressionAlgoIsSubsetOf, MultipleRangesInUnboundedRange) {
}
TEST(ExpressionAlgoIsSubsetOf, MultipleFields) {
- ParsedMatchExpression filter("{a: {$gt: 5}, b: {$lt: 10}}");
- ParsedMatchExpression onlyA("{$or: [{a: 6, b: {$lt: 4}}, {a: {$gt: 11}}]}");
- ParsedMatchExpression onlyB("{$or: [{b: {$lt: 4}}, {a: {$gt: 11}, b: 9}]}");
- ParsedMatchExpression both("{$or: [{a: 6, b: {$lt: 4}}, {a: {$gt: 11}, b: 9}]}");
+ ParsedMatchExpressionForTest filter("{a: {$gt: 5}, b: {$lt: 10}}");
+ ParsedMatchExpressionForTest onlyA("{$or: [{a: 6, b: {$lt: 4}}, {a: {$gt: 11}}]}");
+ ParsedMatchExpressionForTest onlyB("{$or: [{b: {$lt: 4}}, {a: {$gt: 11}, b: 9}]}");
+ ParsedMatchExpressionForTest both("{$or: [{a: 6, b: {$lt: 4}}, {a: {$gt: 11}, b: 9}]}");
ASSERT_FALSE(expression::isSubsetOf(onlyA.get(), filter.get()));
ASSERT_FALSE(expression::isSubsetOf(onlyB.get(), filter.get()));
@@ -405,18 +379,18 @@ TEST(ExpressionAlgoIsSubsetOf, MultipleFields) {
}
TEST(ExpressionAlgoIsSubsetOf, Compare_LT_In) {
- ParsedMatchExpression lt("{a: {$lt: 5}}");
+ ParsedMatchExpressionForTest lt("{a: {$lt: 5}}");
- ParsedMatchExpression inLt("{a: {$in: [4.9]}}");
- ParsedMatchExpression inEq("{a: {$in: [5]}}");
- ParsedMatchExpression inGt("{a: {$in: [5.1]}}");
- ParsedMatchExpression inNull("{a: {$in: [null]}}");
+ ParsedMatchExpressionForTest inLt("{a: {$in: [4.9]}}");
+ ParsedMatchExpressionForTest inEq("{a: {$in: [5]}}");
+ ParsedMatchExpressionForTest inGt("{a: {$in: [5.1]}}");
+ ParsedMatchExpressionForTest inNull("{a: {$in: [null]}}");
- ParsedMatchExpression inAllEq("{a: {$in: [5, 5.0]}}");
- ParsedMatchExpression inAllLte("{a: {$in: [4.9, 5]}}");
- ParsedMatchExpression inAllLt("{a: {$in: [2, 3, 4]}}");
- ParsedMatchExpression inStraddle("{a: {$in: [4, 6]}}");
- ParsedMatchExpression inLtAndNull("{a: {$in: [1, null]}}");
+ ParsedMatchExpressionForTest inAllEq("{a: {$in: [5, 5.0]}}");
+ ParsedMatchExpressionForTest inAllLte("{a: {$in: [4.9, 5]}}");
+ ParsedMatchExpressionForTest inAllLt("{a: {$in: [2, 3, 4]}}");
+ ParsedMatchExpressionForTest inStraddle("{a: {$in: [4, 6]}}");
+ ParsedMatchExpressionForTest inLtAndNull("{a: {$in: [1, null]}}");
ASSERT_TRUE(expression::isSubsetOf(inLt.get(), lt.get()));
ASSERT_FALSE(expression::isSubsetOf(inEq.get(), lt.get()));
@@ -433,18 +407,18 @@ TEST(ExpressionAlgoIsSubsetOf, Compare_LT_In) {
}
TEST(ExpressionAlgoIsSubsetOf, Compare_LTE_In) {
- ParsedMatchExpression lte("{a: {$lte: 5}}");
+ ParsedMatchExpressionForTest lte("{a: {$lte: 5}}");
- ParsedMatchExpression inLt("{a: {$in: [4.9]}}");
- ParsedMatchExpression inEq("{a: {$in: [5]}}");
- ParsedMatchExpression inGt("{a: {$in: [5.1]}}");
- ParsedMatchExpression inNull("{a: {$in: [null]}}");
+ ParsedMatchExpressionForTest inLt("{a: {$in: [4.9]}}");
+ ParsedMatchExpressionForTest inEq("{a: {$in: [5]}}");
+ ParsedMatchExpressionForTest inGt("{a: {$in: [5.1]}}");
+ ParsedMatchExpressionForTest inNull("{a: {$in: [null]}}");
- ParsedMatchExpression inAllEq("{a: {$in: [5, 5.0]}}");
- ParsedMatchExpression inAllLte("{a: {$in: [4.9, 5]}}");
- ParsedMatchExpression inAllLt("{a: {$in: [2, 3, 4]}}");
- ParsedMatchExpression inStraddle("{a: {$in: [4, 6]}}");
- ParsedMatchExpression inLtAndNull("{a: {$in: [1, null]}}");
+ ParsedMatchExpressionForTest inAllEq("{a: {$in: [5, 5.0]}}");
+ ParsedMatchExpressionForTest inAllLte("{a: {$in: [4.9, 5]}}");
+ ParsedMatchExpressionForTest inAllLt("{a: {$in: [2, 3, 4]}}");
+ ParsedMatchExpressionForTest inStraddle("{a: {$in: [4, 6]}}");
+ ParsedMatchExpressionForTest inLtAndNull("{a: {$in: [1, null]}}");
ASSERT_TRUE(expression::isSubsetOf(inLt.get(), lte.get()));
ASSERT_TRUE(expression::isSubsetOf(inEq.get(), lte.get()));
@@ -461,16 +435,16 @@ TEST(ExpressionAlgoIsSubsetOf, Compare_LTE_In) {
}
TEST(ExpressionAlgoIsSubsetOf, Compare_EQ_In) {
- ParsedMatchExpression eq("{a: 5}");
+ ParsedMatchExpressionForTest eq("{a: 5}");
- ParsedMatchExpression inLt("{a: {$in: [4.9]}}");
- ParsedMatchExpression inEq("{a: {$in: [5]}}");
- ParsedMatchExpression inGt("{a: {$in: [5.1]}}");
- ParsedMatchExpression inNull("{a: {$in: [null]}}");
+ ParsedMatchExpressionForTest inLt("{a: {$in: [4.9]}}");
+ ParsedMatchExpressionForTest inEq("{a: {$in: [5]}}");
+ ParsedMatchExpressionForTest inGt("{a: {$in: [5.1]}}");
+ ParsedMatchExpressionForTest inNull("{a: {$in: [null]}}");
- ParsedMatchExpression inAllEq("{a: {$in: [5, 5.0]}}");
- ParsedMatchExpression inStraddle("{a: {$in: [4, 6]}}");
- ParsedMatchExpression inEqAndNull("{a: {$in: [5, null]}}");
+ ParsedMatchExpressionForTest inAllEq("{a: {$in: [5, 5.0]}}");
+ ParsedMatchExpressionForTest inStraddle("{a: {$in: [4, 6]}}");
+ ParsedMatchExpressionForTest inEqAndNull("{a: {$in: [5, null]}}");
ASSERT_FALSE(expression::isSubsetOf(inLt.get(), eq.get()));
ASSERT_TRUE(expression::isSubsetOf(inEq.get(), eq.get()));
@@ -485,18 +459,18 @@ TEST(ExpressionAlgoIsSubsetOf, Compare_EQ_In) {
}
TEST(ExpressionAlgoIsSubsetOf, Compare_GT_In) {
- ParsedMatchExpression gt("{a: {$gt: 5}}");
+ ParsedMatchExpressionForTest gt("{a: {$gt: 5}}");
- ParsedMatchExpression inLt("{a: {$in: [4.9]}}");
- ParsedMatchExpression inEq("{a: {$in: [5]}}");
- ParsedMatchExpression inGt("{a: {$in: [5.1]}}");
- ParsedMatchExpression inNull("{a: {$in: [null]}}");
+ ParsedMatchExpressionForTest inLt("{a: {$in: [4.9]}}");
+ ParsedMatchExpressionForTest inEq("{a: {$in: [5]}}");
+ ParsedMatchExpressionForTest inGt("{a: {$in: [5.1]}}");
+ ParsedMatchExpressionForTest inNull("{a: {$in: [null]}}");
- ParsedMatchExpression inAllEq("{a: {$in: [5, 5.0]}}");
- ParsedMatchExpression inAllGte("{a: {$in: [5, 5.1]}}");
- ParsedMatchExpression inAllGt("{a: {$in: [6, 7, 8]}}");
- ParsedMatchExpression inStraddle("{a: {$in: [4, 6]}}");
- ParsedMatchExpression inGtAndNull("{a: {$in: [9, null]}}");
+ ParsedMatchExpressionForTest inAllEq("{a: {$in: [5, 5.0]}}");
+ ParsedMatchExpressionForTest inAllGte("{a: {$in: [5, 5.1]}}");
+ ParsedMatchExpressionForTest inAllGt("{a: {$in: [6, 7, 8]}}");
+ ParsedMatchExpressionForTest inStraddle("{a: {$in: [4, 6]}}");
+ ParsedMatchExpressionForTest inGtAndNull("{a: {$in: [9, null]}}");
ASSERT_FALSE(expression::isSubsetOf(inLt.get(), gt.get()));
ASSERT_FALSE(expression::isSubsetOf(inEq.get(), gt.get()));
@@ -513,18 +487,18 @@ TEST(ExpressionAlgoIsSubsetOf, Compare_GT_In) {
}
TEST(ExpressionAlgoIsSubsetOf, Compare_GTE_In) {
- ParsedMatchExpression gte("{a: {$gte: 5}}");
+ ParsedMatchExpressionForTest gte("{a: {$gte: 5}}");
- ParsedMatchExpression inLt("{a: {$in: [4.9]}}");
- ParsedMatchExpression inEq("{a: {$in: [5]}}");
- ParsedMatchExpression inGt("{a: {$in: [5.1]}}");
- ParsedMatchExpression inNull("{a: {$in: [null]}}");
+ ParsedMatchExpressionForTest inLt("{a: {$in: [4.9]}}");
+ ParsedMatchExpressionForTest inEq("{a: {$in: [5]}}");
+ ParsedMatchExpressionForTest inGt("{a: {$in: [5.1]}}");
+ ParsedMatchExpressionForTest inNull("{a: {$in: [null]}}");
- ParsedMatchExpression inAllEq("{a: {$in: [5, 5.0]}}");
- ParsedMatchExpression inAllGte("{a: {$in: [5, 5.1]}}");
- ParsedMatchExpression inAllGt("{a: {$in: [6, 7, 8]}}");
- ParsedMatchExpression inStraddle("{a: {$in: [4, 6]}}");
- ParsedMatchExpression inGtAndNull("{a: {$in: [9, null]}}");
+ ParsedMatchExpressionForTest inAllEq("{a: {$in: [5, 5.0]}}");
+ ParsedMatchExpressionForTest inAllGte("{a: {$in: [5, 5.1]}}");
+ ParsedMatchExpressionForTest inAllGt("{a: {$in: [6, 7, 8]}}");
+ ParsedMatchExpressionForTest inStraddle("{a: {$in: [4, 6]}}");
+ ParsedMatchExpressionForTest inGtAndNull("{a: {$in: [9, null]}}");
ASSERT_FALSE(expression::isSubsetOf(inLt.get(), gte.get()));
ASSERT_TRUE(expression::isSubsetOf(inEq.get(), gte.get()));
@@ -541,12 +515,12 @@ TEST(ExpressionAlgoIsSubsetOf, Compare_GTE_In) {
}
TEST(ExpressionAlgoIsSubsetOf, RegexAndIn) {
- ParsedMatchExpression eq1("{x: 1}");
- ParsedMatchExpression eqA("{x: 'a'}");
- ParsedMatchExpression inRegexA("{x: {$in: [/a/]}}");
- ParsedMatchExpression inRegexAbc("{x: {$in: [/abc/]}}");
- ParsedMatchExpression inRegexAOrEq1("{x: {$in: [/a/, 1]}}");
- ParsedMatchExpression inRegexAOrNull("{x: {$in: [/a/, null]}}");
+ ParsedMatchExpressionForTest eq1("{x: 1}");
+ ParsedMatchExpressionForTest eqA("{x: 'a'}");
+ ParsedMatchExpressionForTest inRegexA("{x: {$in: [/a/]}}");
+ ParsedMatchExpressionForTest inRegexAbc("{x: {$in: [/abc/]}}");
+ ParsedMatchExpressionForTest inRegexAOrEq1("{x: {$in: [/a/, 1]}}");
+ ParsedMatchExpressionForTest inRegexAOrNull("{x: {$in: [/a/, null]}}");
ASSERT_FALSE(expression::isSubsetOf(inRegexAOrEq1.get(), eq1.get()));
ASSERT_FALSE(expression::isSubsetOf(inRegexA.get(), eqA.get()));
@@ -558,10 +532,10 @@ TEST(ExpressionAlgoIsSubsetOf, RegexAndIn) {
}
TEST(ExpressionAlgoIsSubsetOf, Exists) {
- ParsedMatchExpression aExists("{a: {$exists: true}}");
- ParsedMatchExpression bExists("{b: {$exists: true}}");
- ParsedMatchExpression aExistsBExists("{a: {$exists: true}, b: {$exists: true}}");
- ParsedMatchExpression aExistsBExistsC5("{a: {$exists: true}, b: {$exists: true}, c: 5}");
+ ParsedMatchExpressionForTest aExists("{a: {$exists: true}}");
+ ParsedMatchExpressionForTest bExists("{b: {$exists: true}}");
+ ParsedMatchExpressionForTest aExistsBExists("{a: {$exists: true}, b: {$exists: true}}");
+ ParsedMatchExpressionForTest aExistsBExistsC5("{a: {$exists: true}, b: {$exists: true}, c: 5}");
ASSERT_TRUE(expression::isSubsetOf(aExists.get(), aExists.get()));
ASSERT_FALSE(expression::isSubsetOf(aExists.get(), bExists.get()));
@@ -576,10 +550,10 @@ TEST(ExpressionAlgoIsSubsetOf, Exists) {
}
TEST(ExpressionAlgoIsSubsetOf, Compare_Exists) {
- ParsedMatchExpression exists("{a: {$exists: true}}");
- ParsedMatchExpression eq("{a: 1}");
- ParsedMatchExpression gt("{a: {$gt: 4}}");
- ParsedMatchExpression lte("{a: {$lte: 7}}");
+ ParsedMatchExpressionForTest exists("{a: {$exists: true}}");
+ ParsedMatchExpressionForTest eq("{a: 1}");
+ ParsedMatchExpressionForTest gt("{a: {$gt: 4}}");
+ ParsedMatchExpressionForTest lte("{a: {$lte: 7}}");
ASSERT_TRUE(expression::isSubsetOf(eq.get(), exists.get()));
ASSERT_TRUE(expression::isSubsetOf(gt.get(), exists.get()));
@@ -591,9 +565,9 @@ TEST(ExpressionAlgoIsSubsetOf, Compare_Exists) {
}
TEST(ExpressionAlgoIsSubsetOf, Type) {
- ParsedMatchExpression aType1("{a: {$type: 1}}");
- ParsedMatchExpression aType2("{a: {$type: 2}}");
- ParsedMatchExpression bType2("{b: {$type: 2}}");
+ ParsedMatchExpressionForTest aType1("{a: {$type: 1}}");
+ ParsedMatchExpressionForTest aType2("{a: {$type: 2}}");
+ ParsedMatchExpressionForTest bType2("{b: {$type: 2}}");
ASSERT_FALSE(expression::isSubsetOf(aType1.get(), aType2.get()));
ASSERT_FALSE(expression::isSubsetOf(aType2.get(), aType1.get()));
@@ -603,9 +577,9 @@ TEST(ExpressionAlgoIsSubsetOf, Type) {
}
TEST(ExpressionAlgoIsSubsetOf, TypeAndExists) {
- ParsedMatchExpression aExists("{a: {$exists: true}}");
- ParsedMatchExpression aType2("{a: {$type: 2}}");
- ParsedMatchExpression bType2("{b: {$type: 2}}");
+ ParsedMatchExpressionForTest aExists("{a: {$exists: true}}");
+ ParsedMatchExpressionForTest aType2("{a: {$type: 2}}");
+ ParsedMatchExpressionForTest bType2("{b: {$type: 2}}");
ASSERT_TRUE(expression::isSubsetOf(aType2.get(), aExists.get()));
ASSERT_FALSE(expression::isSubsetOf(aExists.get(), aType2.get()));
@@ -613,10 +587,10 @@ TEST(ExpressionAlgoIsSubsetOf, TypeAndExists) {
}
TEST(ExpressionAlgoIsSubsetOf, AllAndExists) {
- ParsedMatchExpression aExists("{a: {$exists: true}}");
- ParsedMatchExpression aAll("{a: {$all: ['x', 'y', 'z']}}");
- ParsedMatchExpression bAll("{b: {$all: ['x', 'y', 'z']}}");
- ParsedMatchExpression aAllWithNull("{a: {$all: ['x', null, 'z']}}");
+ ParsedMatchExpressionForTest aExists("{a: {$exists: true}}");
+ ParsedMatchExpressionForTest aAll("{a: {$all: ['x', 'y', 'z']}}");
+ ParsedMatchExpressionForTest bAll("{b: {$all: ['x', 'y', 'z']}}");
+ ParsedMatchExpressionForTest aAllWithNull("{a: {$all: ['x', null, 'z']}}");
ASSERT_TRUE(expression::isSubsetOf(aAll.get(), aExists.get()));
ASSERT_FALSE(expression::isSubsetOf(bAll.get(), aExists.get()));
@@ -624,10 +598,10 @@ TEST(ExpressionAlgoIsSubsetOf, AllAndExists) {
}
TEST(ExpressionAlgoIsSubsetOf, ElemMatchAndExists_Value) {
- ParsedMatchExpression aExists("{a: {$exists: true}}");
- ParsedMatchExpression aElemMatch("{a: {$elemMatch: {$gt: 5, $lte: 10}}}");
- ParsedMatchExpression bElemMatch("{b: {$elemMatch: {$gt: 5, $lte: 10}}}");
- ParsedMatchExpression aElemMatchNull("{a: {$elemMatch: {$eq: null}}}");
+ ParsedMatchExpressionForTest aExists("{a: {$exists: true}}");
+ ParsedMatchExpressionForTest aElemMatch("{a: {$elemMatch: {$gt: 5, $lte: 10}}}");
+ ParsedMatchExpressionForTest bElemMatch("{b: {$elemMatch: {$gt: 5, $lte: 10}}}");
+ ParsedMatchExpressionForTest aElemMatchNull("{a: {$elemMatch: {$eq: null}}}");
ASSERT_TRUE(expression::isSubsetOf(aElemMatch.get(), aExists.get()));
ASSERT_FALSE(expression::isSubsetOf(aExists.get(), aElemMatch.get()));
@@ -636,10 +610,10 @@ TEST(ExpressionAlgoIsSubsetOf, ElemMatchAndExists_Value) {
}
TEST(ExpressionAlgoIsSubsetOf, ElemMatchAndExists_Object) {
- ParsedMatchExpression aExists("{a: {$exists: true}}");
- ParsedMatchExpression aElemMatch("{a: {$elemMatch: {x: {$gt: 5}, y: {$lte: 10}}}}");
- ParsedMatchExpression bElemMatch("{b: {$elemMatch: {x: {$gt: 5}, y: {$lte: 10}}}}");
- ParsedMatchExpression aElemMatchNull("{a: {$elemMatch: {x: null, y: null}}}");
+ ParsedMatchExpressionForTest aExists("{a: {$exists: true}}");
+ ParsedMatchExpressionForTest aElemMatch("{a: {$elemMatch: {x: {$gt: 5}, y: {$lte: 10}}}}");
+ ParsedMatchExpressionForTest bElemMatch("{b: {$elemMatch: {x: {$gt: 5}, y: {$lte: 10}}}}");
+ ParsedMatchExpressionForTest aElemMatchNull("{a: {$elemMatch: {x: null, y: null}}}");
ASSERT_TRUE(expression::isSubsetOf(aElemMatch.get(), aExists.get()));
ASSERT_FALSE(expression::isSubsetOf(aExists.get(), aElemMatch.get()));
@@ -648,11 +622,11 @@ TEST(ExpressionAlgoIsSubsetOf, ElemMatchAndExists_Object) {
}
TEST(ExpressionAlgoIsSubsetOf, SizeAndExists) {
- ParsedMatchExpression aExists("{a: {$exists: true}}");
- ParsedMatchExpression aSize0("{a: {$size: 0}}");
- ParsedMatchExpression aSize1("{a: {$size: 1}}");
- ParsedMatchExpression aSize3("{a: {$size: 3}}");
- ParsedMatchExpression bSize3("{b: {$size: 3}}");
+ ParsedMatchExpressionForTest aExists("{a: {$exists: true}}");
+ ParsedMatchExpressionForTest aSize0("{a: {$size: 0}}");
+ ParsedMatchExpressionForTest aSize1("{a: {$size: 1}}");
+ ParsedMatchExpressionForTest aSize3("{a: {$size: 3}}");
+ ParsedMatchExpressionForTest bSize3("{b: {$size: 3}}");
ASSERT_TRUE(expression::isSubsetOf(aSize0.get(), aExists.get()));
ASSERT_TRUE(expression::isSubsetOf(aSize1.get(), aExists.get()));
@@ -662,28 +636,28 @@ TEST(ExpressionAlgoIsSubsetOf, SizeAndExists) {
}
TEST(ExpressionAlgoIsSubsetOf, ModAndExists) {
- ParsedMatchExpression aExists("{a: {$exists: true}}");
- ParsedMatchExpression aMod5("{a: {$mod: [5, 0]}}");
- ParsedMatchExpression bMod5("{b: {$mod: [5, 0]}}");
+ ParsedMatchExpressionForTest aExists("{a: {$exists: true}}");
+ ParsedMatchExpressionForTest aMod5("{a: {$mod: [5, 0]}}");
+ ParsedMatchExpressionForTest bMod5("{b: {$mod: [5, 0]}}");
ASSERT_TRUE(expression::isSubsetOf(aMod5.get(), aExists.get()));
ASSERT_FALSE(expression::isSubsetOf(bMod5.get(), aExists.get()));
}
TEST(ExpressionAlgoIsSubsetOf, RegexAndExists) {
- ParsedMatchExpression aExists("{a: {$exists: true}}");
- ParsedMatchExpression aRegex("{a: {$regex: 'pattern'}}");
- ParsedMatchExpression bRegex("{b: {$regex: 'pattern'}}");
+ ParsedMatchExpressionForTest aExists("{a: {$exists: true}}");
+ ParsedMatchExpressionForTest aRegex("{a: {$regex: 'pattern'}}");
+ ParsedMatchExpressionForTest bRegex("{b: {$regex: 'pattern'}}");
ASSERT_TRUE(expression::isSubsetOf(aRegex.get(), aExists.get()));
ASSERT_FALSE(expression::isSubsetOf(bRegex.get(), aExists.get()));
}
TEST(ExpressionAlgoIsSubsetOf, InAndExists) {
- ParsedMatchExpression aExists("{a: {$exists: true}}");
- ParsedMatchExpression aIn("{a: {$in: [1, 2, 3]}}");
- ParsedMatchExpression bIn("{b: {$in: [1, 2, 3]}}");
- ParsedMatchExpression aInWithNull("{a: {$in: [1, null, 3]}}");
+ ParsedMatchExpressionForTest aExists("{a: {$exists: true}}");
+ ParsedMatchExpressionForTest aIn("{a: {$in: [1, 2, 3]}}");
+ ParsedMatchExpressionForTest bIn("{b: {$in: [1, 2, 3]}}");
+ ParsedMatchExpressionForTest aInWithNull("{a: {$in: [1, null, 3]}}");
ASSERT_TRUE(expression::isSubsetOf(aIn.get(), aExists.get()));
ASSERT_FALSE(expression::isSubsetOf(bIn.get(), aExists.get()));
@@ -695,10 +669,10 @@ TEST(ExpressionAlgoIsSubsetOf, InAndExists) {
}
TEST(ExpressionAlgoIsSubsetOf, NinAndExists) {
- ParsedMatchExpression aExists("{a: {$exists: true}}");
- ParsedMatchExpression aNin("{a: {$nin: [1, 2, 3]}}");
- ParsedMatchExpression bNin("{b: {$nin: [1, 2, 3]}}");
- ParsedMatchExpression aNinWithNull("{a: {$nin: [1, null, 3]}}");
+ ParsedMatchExpressionForTest aExists("{a: {$exists: true}}");
+ ParsedMatchExpressionForTest aNin("{a: {$nin: [1, 2, 3]}}");
+ ParsedMatchExpressionForTest bNin("{b: {$nin: [1, 2, 3]}}");
+ ParsedMatchExpressionForTest aNinWithNull("{a: {$nin: [1, null, 3]}}");
ASSERT_FALSE(expression::isSubsetOf(aNin.get(), aExists.get()));
ASSERT_FALSE(expression::isSubsetOf(bNin.get(), aExists.get()));
@@ -706,10 +680,10 @@ TEST(ExpressionAlgoIsSubsetOf, NinAndExists) {
}
TEST(ExpressionAlgoIsSubsetOf, Compare_Exists_NE) {
- ParsedMatchExpression aExists("{a: {$exists: true}}");
- ParsedMatchExpression aNotEqual1("{a: {$ne: 1}}");
- ParsedMatchExpression bNotEqual1("{b: {$ne: 1}}");
- ParsedMatchExpression aNotEqualNull("{a: {$ne: null}}");
+ ParsedMatchExpressionForTest aExists("{a: {$exists: true}}");
+ ParsedMatchExpressionForTest aNotEqual1("{a: {$ne: 1}}");
+ ParsedMatchExpressionForTest bNotEqual1("{b: {$ne: 1}}");
+ ParsedMatchExpressionForTest aNotEqualNull("{a: {$ne: null}}");
ASSERT_FALSE(expression::isSubsetOf(aNotEqual1.get(), aExists.get()));
ASSERT_FALSE(expression::isSubsetOf(bNotEqual1.get(), aExists.get()));
@@ -718,13 +692,13 @@ TEST(ExpressionAlgoIsSubsetOf, Compare_Exists_NE) {
TEST(ExpressionAlgoIsSubsetOf, CollationAwareStringComparison) {
CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString);
- ParsedMatchExpression lhs("{a: {$gt: 'abc'}}", &collator);
- ParsedMatchExpression rhs("{a: {$gt: 'cba'}}", &collator);
+ ParsedMatchExpressionForTest lhs("{a: {$gt: 'abc'}}", &collator);
+ ParsedMatchExpressionForTest rhs("{a: {$gt: 'cba'}}", &collator);
ASSERT_TRUE(expression::isSubsetOf(lhs.get(), rhs.get()));
- ParsedMatchExpression lhsLT("{a: {$lt: 'abc'}}", &collator);
- ParsedMatchExpression rhsLT("{a: {$lt: 'cba'}}", &collator);
+ ParsedMatchExpressionForTest lhsLT("{a: {$lt: 'abc'}}", &collator);
+ ParsedMatchExpressionForTest rhsLT("{a: {$lt: 'cba'}}", &collator);
ASSERT_FALSE(expression::isSubsetOf(lhsLT.get(), rhsLT.get()));
}
@@ -732,27 +706,27 @@ TEST(ExpressionAlgoIsSubsetOf, CollationAwareStringComparison) {
TEST(ExpressionAlgoIsSubsetOf, NonMatchingCollationsStringComparison) {
CollatorInterfaceMock collatorAlwaysEqual(CollatorInterfaceMock::MockType::kAlwaysEqual);
CollatorInterfaceMock collatorReverseString(CollatorInterfaceMock::MockType::kReverseString);
- ParsedMatchExpression lhs("{a: {$gt: 'abc'}}", &collatorAlwaysEqual);
- ParsedMatchExpression rhs("{a: {$gt: 'cba'}}", &collatorReverseString);
+ ParsedMatchExpressionForTest lhs("{a: {$gt: 'abc'}}", &collatorAlwaysEqual);
+ ParsedMatchExpressionForTest rhs("{a: {$gt: 'cba'}}", &collatorReverseString);
ASSERT_FALSE(expression::isSubsetOf(lhs.get(), rhs.get()));
- ParsedMatchExpression lhsLT("{a: {$lt: 'abc'}}", &collatorAlwaysEqual);
- ParsedMatchExpression rhsLT("{a: {$lt: 'cba'}}", &collatorReverseString);
+ ParsedMatchExpressionForTest lhsLT("{a: {$lt: 'abc'}}", &collatorAlwaysEqual);
+ ParsedMatchExpressionForTest rhsLT("{a: {$lt: 'cba'}}", &collatorReverseString);
ASSERT_FALSE(expression::isSubsetOf(lhsLT.get(), rhsLT.get()));
}
TEST(ExpressionAlgoIsSubsetOf, CollationAwareStringComparisonIn) {
CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString);
- ParsedMatchExpression lhsAllGTcba("{a: {$in: ['abc', 'cbc']}}", &collator);
- ParsedMatchExpression lhsSomeGTcba("{a: {$in: ['abc', 'aba']}}", &collator);
- ParsedMatchExpression rhs("{a: {$gt: 'cba'}}", &collator);
+ ParsedMatchExpressionForTest lhsAllGTcba("{a: {$in: ['abc', 'cbc']}}", &collator);
+ ParsedMatchExpressionForTest lhsSomeGTcba("{a: {$in: ['abc', 'aba']}}", &collator);
+ ParsedMatchExpressionForTest rhs("{a: {$gt: 'cba'}}", &collator);
ASSERT_TRUE(expression::isSubsetOf(lhsAllGTcba.get(), rhs.get()));
ASSERT_FALSE(expression::isSubsetOf(lhsSomeGTcba.get(), rhs.get()));
- ParsedMatchExpression rhsLT("{a: {$lt: 'cba'}}", &collator);
+ ParsedMatchExpressionForTest rhsLT("{a: {$lt: 'cba'}}", &collator);
ASSERT_FALSE(expression::isSubsetOf(lhsAllGTcba.get(), rhsLT.get()));
ASSERT_FALSE(expression::isSubsetOf(lhsSomeGTcba.get(), rhsLT.get()));
@@ -762,8 +736,8 @@ TEST(ExpressionAlgoIsSubsetOf, CollationAwareStringComparisonIn) {
TEST(ExpressionAlgoIsSubsetOf, NonMatchingCollationsNoStringComparisonLHS) {
CollatorInterfaceMock collatorAlwaysEqual(CollatorInterfaceMock::MockType::kAlwaysEqual);
CollatorInterfaceMock collatorReverseString(CollatorInterfaceMock::MockType::kReverseString);
- ParsedMatchExpression lhs("{a: {b: 1}}", &collatorAlwaysEqual);
- ParsedMatchExpression rhs("{a: {$lt: {b: 'abc'}}}", &collatorReverseString);
+ ParsedMatchExpressionForTest lhs("{a: {b: 1}}", &collatorAlwaysEqual);
+ ParsedMatchExpressionForTest rhs("{a: {$lt: {b: 'abc'}}}", &collatorReverseString);
ASSERT_FALSE(expression::isSubsetOf(lhs.get(), rhs.get()));
}
@@ -771,72 +745,75 @@ TEST(ExpressionAlgoIsSubsetOf, NonMatchingCollationsNoStringComparisonLHS) {
TEST(ExpressionAlgoIsSubsetOf, NonMatchingCollationsNoStringComparison) {
CollatorInterfaceMock collatorAlwaysEqual(CollatorInterfaceMock::MockType::kAlwaysEqual);
CollatorInterfaceMock collatorReverseString(CollatorInterfaceMock::MockType::kReverseString);
- ParsedMatchExpression lhs("{a: 1}", &collatorAlwaysEqual);
- ParsedMatchExpression rhs("{a: {$gt: 0}}", &collatorReverseString);
+ ParsedMatchExpressionForTest lhs("{a: 1}", &collatorAlwaysEqual);
+ ParsedMatchExpressionForTest rhs("{a: {$gt: 0}}", &collatorReverseString);
ASSERT_TRUE(expression::isSubsetOf(lhs.get(), rhs.get()));
}
TEST(ExpressionAlgoIsSubsetOf, InternalExprEqIsSubsetOfNothing) {
- ParsedMatchExpression exprEq("{a: {$_internalExprEq: 0}}");
- ParsedMatchExpression regularEq("{a: {$eq: 0}}");
+ ParsedMatchExpressionForTest exprEq("{a: {$_internalExprEq: 0}}");
+ ParsedMatchExpressionForTest regularEq("{a: {$eq: 0}}");
{
- ParsedMatchExpression rhs("{a: {$gte: 0}}");
+ ParsedMatchExpressionForTest rhs("{a: {$gte: 0}}");
ASSERT_FALSE(expression::isSubsetOf(exprEq.get(), rhs.get()));
ASSERT_TRUE(expression::isSubsetOf(regularEq.get(), rhs.get()));
}
{
- ParsedMatchExpression rhs("{a: {$lte: 0}}");
+ ParsedMatchExpressionForTest rhs("{a: {$lte: 0}}");
ASSERT_FALSE(expression::isSubsetOf(exprEq.get(), rhs.get()));
ASSERT_TRUE(expression::isSubsetOf(regularEq.get(), rhs.get()));
}
}
TEST(ExpressionAlgoIsSubsetOf, IsSubsetOfRHSAndWithinOr) {
- ParsedMatchExpression rhs("{$or: [{a: 3}, {$and: [{a: 5}, {b: 5}]}]}");
+ ParsedMatchExpressionForTest rhs("{$or: [{a: 3}, {$and: [{a: 5}, {b: 5}]}]}");
{
- ParsedMatchExpression lhs("{a:5, b:5}");
+ ParsedMatchExpressionForTest lhs("{a:5, b:5}");
ASSERT_TRUE(expression::isSubsetOf(lhs.get(), rhs.get()));
}
}
TEST(ExpressionAlgoIsSubsetOf, IsSubsetOfComplexRHSExpression) {
- ParsedMatchExpression complex("{$or: [{z: 1}, {$and: [{x: 1}, {$or: [{y: 1}, {y: 2}]}]}]}");
+ ParsedMatchExpressionForTest complex(
+ "{$or: [{z: 1}, {$and: [{x: 1}, {$or: [{y: 1}, {y: 2}]}]}]}");
{
- ParsedMatchExpression lhs("{z: 1}");
+ ParsedMatchExpressionForTest lhs("{z: 1}");
ASSERT_TRUE(expression::isSubsetOf(lhs.get(), complex.get()));
}
{
- ParsedMatchExpression lhs("{z: 1, x: 1, y:2}");
+ ParsedMatchExpressionForTest lhs("{z: 1, x: 1, y:2}");
ASSERT_TRUE(expression::isSubsetOf(lhs.get(), complex.get()));
}
{
- ParsedMatchExpression lhs("{$or: [{z: 1}, {$and: [{x: 1}, {$or: [{y: 1}, {y: 2}]}]}]}");
+ ParsedMatchExpressionForTest lhs(
+ "{$or: [{z: 1}, {$and: [{x: 1}, {$or: [{y: 1}, {y: 2}]}]}]}");
ASSERT_TRUE(expression::isSubsetOf(lhs.get(), complex.get()));
}
{
- ParsedMatchExpression lhs("{$or: [{z: 2}, {$and: [{x: 2}, {$or: [{y: 3}, {y: 4}]}]}]}");
+ ParsedMatchExpressionForTest lhs(
+ "{$or: [{z: 2}, {$and: [{x: 2}, {$or: [{y: 3}, {y: 4}]}]}]}");
ASSERT_FALSE(expression::isSubsetOf(lhs.get(), complex.get()));
}
{
- ParsedMatchExpression lhs("{z: 1, y:2}");
+ ParsedMatchExpressionForTest lhs("{z: 1, y:2}");
ASSERT_TRUE(expression::isSubsetOf(lhs.get(), complex.get()));
}
{
- ParsedMatchExpression lhs("{z: 2, y: 1}");
+ ParsedMatchExpressionForTest lhs("{z: 2, y: 1}");
ASSERT_FALSE(expression::isSubsetOf(lhs.get(), complex.get()));
}
{
- ParsedMatchExpression lhs("{x: 1, y: 3}");
+ ParsedMatchExpressionForTest lhs("{x: 1, y: 3}");
ASSERT_FALSE(expression::isSubsetOf(lhs.get(), complex.get()));
}
}
@@ -1010,15 +987,11 @@ TEST(SplitMatchExpression, AndWithSplittableChildrenIsSplittable) {
expression::splitMatchExpressionBy(std::move(status.getValue()), {"b"}, {});
ASSERT_TRUE(splitExpr.first.get());
- BSONObjBuilder firstBob;
- splitExpr.first->serialize(&firstBob, true);
ASSERT_TRUE(splitExpr.second.get());
- BSONObjBuilder secondBob;
- splitExpr.second->serialize(&secondBob, true);
- ASSERT_BSONOBJ_EQ(firstBob.obj(), fromjson("{a: {$eq: 1}}"));
- ASSERT_BSONOBJ_EQ(secondBob.obj(), fromjson("{b: {$eq: 1}}"));
+ ASSERT_BSONOBJ_EQ(splitExpr.first->serialize(), fromjson("{a: {$eq: 1}}"));
+ ASSERT_BSONOBJ_EQ(splitExpr.second->serialize(), fromjson("{b: {$eq: 1}}"));
}
TEST(SplitMatchExpression, NorWithIndependentChildrenIsSplittable) {
@@ -1032,15 +1005,11 @@ TEST(SplitMatchExpression, NorWithIndependentChildrenIsSplittable) {
expression::splitMatchExpressionBy(std::move(status.getValue()), {"b"}, {});
ASSERT_TRUE(splitExpr.first.get());
- BSONObjBuilder firstBob;
- splitExpr.first->serialize(&firstBob, true);
ASSERT_TRUE(splitExpr.second.get());
- BSONObjBuilder secondBob;
- splitExpr.second->serialize(&secondBob, true);
- ASSERT_BSONOBJ_EQ(firstBob.obj(), fromjson("{$nor: [{a: {$eq: 1}}]}"));
- ASSERT_BSONOBJ_EQ(secondBob.obj(), fromjson("{$nor: [{b: {$eq: 1}}]}"));
+ ASSERT_BSONOBJ_EQ(splitExpr.first->serialize(), fromjson("{$nor: [{a: {$eq: 1}}]}"));
+ ASSERT_BSONOBJ_EQ(splitExpr.second->serialize(), fromjson("{$nor: [{b: {$eq: 1}}]}"));
}
TEST(SplitMatchExpression, NotWithIndependentChildIsSplittable) {
@@ -1054,10 +1023,8 @@ TEST(SplitMatchExpression, NotWithIndependentChildIsSplittable) {
expression::splitMatchExpressionBy(std::move(status.getValue()), {"y"}, {});
ASSERT_TRUE(splitExpr.first.get());
- BSONObjBuilder firstBob;
- splitExpr.first->serialize(&firstBob, true);
- ASSERT_BSONOBJ_EQ(firstBob.obj(), fromjson("{x: {$not: {$gt: 4}}}"));
+ ASSERT_BSONOBJ_EQ(splitExpr.first->serialize(), fromjson("{x: {$not: {$gt: 4}}}"));
ASSERT_FALSE(splitExpr.second);
}
@@ -1072,11 +1039,10 @@ TEST(SplitMatchExpression, OrWithOnlyIndependentChildrenIsNotSplittable) {
expression::splitMatchExpressionBy(std::move(status.getValue()), {"b"}, {});
ASSERT_TRUE(splitExpr.second.get());
- BSONObjBuilder bob;
- splitExpr.second->serialize(&bob, true);
ASSERT_FALSE(splitExpr.first);
- ASSERT_BSONOBJ_EQ(bob.obj(), fromjson("{$or: [{a: {$eq: 1}}, {b: {$eq: 1}}]}"));
+ ASSERT_BSONOBJ_EQ(splitExpr.second->serialize(),
+ fromjson("{$or: [{a: {$eq: 1}}, {b: {$eq: 1}}]}"));
}
TEST(SplitMatchExpression, ComplexMatchExpressionSplitsCorrectly) {
@@ -1093,15 +1059,12 @@ TEST(SplitMatchExpression, ComplexMatchExpressionSplitsCorrectly) {
expression::splitMatchExpressionBy(std::move(status.getValue()), {"x"}, {});
ASSERT_TRUE(splitExpr.first.get());
- BSONObjBuilder firstBob;
- splitExpr.first->serialize(&firstBob, true);
ASSERT_TRUE(splitExpr.second.get());
- BSONObjBuilder secondBob;
- splitExpr.second->serialize(&secondBob, true);
- ASSERT_BSONOBJ_EQ(firstBob.obj(), fromjson("{$or: [{'a.b': {$eq: 3}}, {'a.b.c': {$eq: 4}}]}"));
- ASSERT_BSONOBJ_EQ(secondBob.obj(),
+ ASSERT_BSONOBJ_EQ(splitExpr.first->serialize(),
+ fromjson("{$or: [{'a.b': {$eq: 3}}, {'a.b.c': {$eq: 4}}]}"));
+ ASSERT_BSONOBJ_EQ(splitExpr.second->serialize(),
fromjson("{$and: [{x: {$not: {$size: 2}}}, {$nor: [{x: {$gt: 4}}, {$and: "
"[{x: {$not: {$eq: 1}}}, {y: {$eq: 3}}]}]}]}"));
}
@@ -1118,15 +1081,12 @@ TEST(SplitMatchExpression, ShouldNotExtractPrefixOfDottedPathAsIndependent) {
expression::splitMatchExpressionBy(std::move(status.getValue()), {"a.b"}, {});
ASSERT_TRUE(splitExpr.first.get());
- BSONObjBuilder firstBob;
- splitExpr.first->serialize(&firstBob, true);
ASSERT_TRUE(splitExpr.second.get());
- BSONObjBuilder secondBob;
- splitExpr.second->serialize(&secondBob, true);
- ASSERT_BSONOBJ_EQ(firstBob.obj(), fromjson("{'a.c': {$eq: 1}}"));
- ASSERT_BSONOBJ_EQ(secondBob.obj(), fromjson("{$and: [{a: {$eq: 1}}, {'a.b': {$eq: 1}}]}"));
+ ASSERT_BSONOBJ_EQ(splitExpr.first->serialize(), fromjson("{'a.c': {$eq: 1}}"));
+ ASSERT_BSONOBJ_EQ(splitExpr.second->serialize(),
+ fromjson("{$and: [{a: {$eq: 1}}, {'a.b': {$eq: 1}}]}"));
}
TEST(SplitMatchExpression, ShouldMoveIndependentLeafPredicateAcrossRename) {
@@ -1140,9 +1100,7 @@ TEST(SplitMatchExpression, ShouldMoveIndependentLeafPredicateAcrossRename) {
expression::splitMatchExpressionBy(std::move(matcher.getValue()), {}, renames);
ASSERT_TRUE(splitExpr.first.get());
- BSONObjBuilder firstBob;
- splitExpr.first->serialize(&firstBob, true);
- ASSERT_BSONOBJ_EQ(firstBob.obj(), fromjson("{b: {$eq: 1}}"));
+ ASSERT_BSONOBJ_EQ(splitExpr.first->serialize(), fromjson("{b: {$eq: 1}}"));
ASSERT_FALSE(splitExpr.second.get());
}
@@ -1158,9 +1116,8 @@ TEST(SplitMatchExpression, ShouldMoveIndependentAndPredicateAcrossRename) {
expression::splitMatchExpressionBy(std::move(matcher.getValue()), {}, renames);
ASSERT_TRUE(splitExpr.first.get());
- BSONObjBuilder firstBob;
- splitExpr.first->serialize(&firstBob, true);
- ASSERT_BSONOBJ_EQ(firstBob.obj(), fromjson("{$and: [{c: {$eq: 1}}, {b: {$eq: 2}}]}"));
+ ASSERT_BSONOBJ_EQ(splitExpr.first->serialize(),
+ fromjson("{$and: [{c: {$eq: 1}}, {b: {$eq: 2}}]}"));
ASSERT_FALSE(splitExpr.second.get());
}
@@ -1176,14 +1133,10 @@ TEST(SplitMatchExpression, ShouldSplitPartiallyDependentAndPredicateAcrossRename
expression::splitMatchExpressionBy(std::move(matcher.getValue()), {"b"}, renames);
ASSERT_TRUE(splitExpr.first.get());
- BSONObjBuilder firstBob;
- splitExpr.first->serialize(&firstBob, true);
- ASSERT_BSONOBJ_EQ(firstBob.obj(), fromjson("{c: {$eq: 1}}"));
+ ASSERT_BSONOBJ_EQ(splitExpr.first->serialize(), fromjson("{c: {$eq: 1}}"));
ASSERT_TRUE(splitExpr.second.get());
- BSONObjBuilder secondBob;
- splitExpr.second->serialize(&secondBob, true);
- ASSERT_BSONOBJ_EQ(secondBob.obj(), fromjson("{b: {$eq: 2}}"));
+ ASSERT_BSONOBJ_EQ(splitExpr.second->serialize(), fromjson("{b: {$eq: 2}}"));
}
TEST(SplitMatchExpression, ShouldSplitPartiallyDependentComplexPredicateMultipleRenames) {
@@ -1197,14 +1150,11 @@ TEST(SplitMatchExpression, ShouldSplitPartiallyDependentComplexPredicateMultiple
expression::splitMatchExpressionBy(std::move(matcher.getValue()), {"a"}, renames);
ASSERT_TRUE(splitExpr.first.get());
- BSONObjBuilder firstBob;
- splitExpr.first->serialize(&firstBob, true);
- ASSERT_BSONOBJ_EQ(firstBob.obj(), fromjson("{$or: [{d: {$eq: 2}}, {e: {$eq: 3}}]}"));
+ ASSERT_BSONOBJ_EQ(splitExpr.first->serialize(),
+ fromjson("{$or: [{d: {$eq: 2}}, {e: {$eq: 3}}]}"));
ASSERT_TRUE(splitExpr.second.get());
- BSONObjBuilder secondBob;
- splitExpr.second->serialize(&secondBob, true);
- ASSERT_BSONOBJ_EQ(secondBob.obj(), fromjson("{a: {$eq: 1}}"));
+ ASSERT_BSONOBJ_EQ(splitExpr.second->serialize(), fromjson("{a: {$eq: 1}}"));
}
TEST(SplitMatchExpression,
@@ -1219,14 +1169,11 @@ TEST(SplitMatchExpression,
expression::splitMatchExpressionBy(std::move(matcher.getValue()), {"a"}, renames);
ASSERT_TRUE(splitExpr.first.get());
- BSONObjBuilder firstBob;
- splitExpr.first->serialize(&firstBob, true);
- ASSERT_BSONOBJ_EQ(firstBob.obj(), fromjson("{$or: [{x: {$eq: 2}}, {y: {$eq: 3}}]}"));
+ ASSERT_BSONOBJ_EQ(splitExpr.first->serialize(),
+ fromjson("{$or: [{x: {$eq: 2}}, {y: {$eq: 3}}]}"));
ASSERT_TRUE(splitExpr.second.get());
- BSONObjBuilder secondBob;
- splitExpr.second->serialize(&secondBob, true);
- ASSERT_BSONOBJ_EQ(secondBob.obj(), fromjson("{a: {$eq: 1}}"));
+ ASSERT_BSONOBJ_EQ(splitExpr.second->serialize(), fromjson("{a: {$eq: 1}}"));
}
TEST(SplitMatchExpression, ShouldNotMoveElemMatchObjectAcrossRename) {
@@ -1242,9 +1189,7 @@ TEST(SplitMatchExpression, ShouldNotMoveElemMatchObjectAcrossRename) {
ASSERT_FALSE(splitExpr.first.get());
ASSERT_TRUE(splitExpr.second.get());
- BSONObjBuilder secondBob;
- splitExpr.second->serialize(&secondBob, true);
- ASSERT_BSONOBJ_EQ(secondBob.obj(), fromjson("{a: {$elemMatch: {b: {$eq: 3}}}}"));
+ ASSERT_BSONOBJ_EQ(splitExpr.second->serialize(), fromjson("{a: {$elemMatch: {b: {$eq: 3}}}}"));
}
TEST(SplitMatchExpression, ShouldNotMoveElemMatchValueAcrossRename) {
@@ -1260,9 +1205,7 @@ TEST(SplitMatchExpression, ShouldNotMoveElemMatchValueAcrossRename) {
ASSERT_FALSE(splitExpr.first.get());
ASSERT_TRUE(splitExpr.second.get());
- BSONObjBuilder secondBob;
- splitExpr.second->serialize(&secondBob, true);
- ASSERT_BSONOBJ_EQ(secondBob.obj(), fromjson("{a: {$elemMatch: {$eq: 3}}}"));
+ ASSERT_BSONOBJ_EQ(splitExpr.second->serialize(), fromjson("{a: {$elemMatch: {$eq: 3}}}"));
}
TEST(SplitMatchExpression, ShouldMoveTypeAcrossRename) {
@@ -1275,10 +1218,7 @@ TEST(SplitMatchExpression, ShouldMoveTypeAcrossRename) {
std::pair<unique_ptr<MatchExpression>, unique_ptr<MatchExpression>> splitExpr =
expression::splitMatchExpressionBy(std::move(matcher.getValue()), {}, renames);
- ASSERT_TRUE(splitExpr.first.get());
- BSONObjBuilder firstBob;
- splitExpr.first->serialize(&firstBob, true);
- ASSERT_BSONOBJ_EQ(firstBob.obj(), fromjson("{c: {$type: [16]}}"));
+ ASSERT_BSONOBJ_EQ(splitExpr.first->serialize(), fromjson("{c: {$type: [16]}}"));
ASSERT_FALSE(splitExpr.second.get());
}
@@ -1296,9 +1236,7 @@ TEST(SplitMatchExpression, ShouldNotMoveSizeAcrossRename) {
ASSERT_FALSE(splitExpr.first.get());
ASSERT_TRUE(splitExpr.second.get());
- BSONObjBuilder secondBob;
- splitExpr.second->serialize(&secondBob, true);
- ASSERT_BSONOBJ_EQ(secondBob.obj(), fromjson("{a: {$size: 3}}"));
+ ASSERT_BSONOBJ_EQ(splitExpr.second->serialize(), fromjson("{a: {$size: 3}}"));
}
TEST(SplitMatchExpression, ShouldNotMoveMinItemsAcrossRename) {
@@ -1314,9 +1252,8 @@ TEST(SplitMatchExpression, ShouldNotMoveMinItemsAcrossRename) {
ASSERT_FALSE(splitExpr.first.get());
ASSERT_TRUE(splitExpr.second.get());
- BSONObjBuilder secondBob;
- splitExpr.second->serialize(&secondBob, true);
- ASSERT_BSONOBJ_EQ(secondBob.obj(), fromjson("{a: {$_internalSchemaMinItems: 3}}"));
+ ASSERT_BSONOBJ_EQ(splitExpr.second->serialize(),
+ fromjson("{a: {$_internalSchemaMinItems: 3}}"));
}
TEST(SplitMatchExpression, ShouldNotMoveMaxItemsAcrossRename) {
@@ -1332,9 +1269,8 @@ TEST(SplitMatchExpression, ShouldNotMoveMaxItemsAcrossRename) {
ASSERT_FALSE(splitExpr.first.get());
ASSERT_TRUE(splitExpr.second.get());
- BSONObjBuilder secondBob;
- splitExpr.second->serialize(&secondBob, true);
- ASSERT_BSONOBJ_EQ(secondBob.obj(), fromjson("{a: {$_internalSchemaMaxItems: 3}}"));
+ ASSERT_BSONOBJ_EQ(splitExpr.second->serialize(),
+ fromjson("{a: {$_internalSchemaMaxItems: 3}}"));
}
TEST(SplitMatchExpression, ShouldNotMoveMaxItemsInLogicalExpressionAcrossRename) {
@@ -1352,9 +1288,7 @@ TEST(SplitMatchExpression, ShouldNotMoveMaxItemsInLogicalExpressionAcrossRename)
ASSERT_FALSE(splitExpr.first.get());
ASSERT_TRUE(splitExpr.second.get());
- BSONObjBuilder secondBob;
- splitExpr.second->serialize(&secondBob, true);
- ASSERT_BSONOBJ_EQ(secondBob.obj(),
+ ASSERT_BSONOBJ_EQ(splitExpr.second->serialize(),
fromjson("{$or: [{a: {$_internalSchemaMaxItems: 3}},"
" {a: {$_internalSchemaMaxItems: 4}}]}"));
}
@@ -1374,9 +1308,7 @@ TEST(SplitMatchExpression, ShouldNotMoveInternalSchemaObjectMatchInLogicalExpres
ASSERT_FALSE(splitExpr.first.get());
ASSERT_TRUE(splitExpr.second.get());
- BSONObjBuilder secondBob;
- splitExpr.second->serialize(&secondBob, true);
- ASSERT_BSONOBJ_EQ(secondBob.obj(),
+ ASSERT_BSONOBJ_EQ(splitExpr.second->serialize(),
fromjson("{$or: [{a: {$_internalSchemaObjectMatch: {b: {$eq: 1}}}},"
" {a: {$_internalSchemaObjectMatch: {b: {$eq: 1}}}}]}"));
}
@@ -1392,9 +1324,8 @@ TEST(SplitMatchExpression, ShouldMoveMinLengthAcrossRename) {
expression::splitMatchExpressionBy(std::move(matcher.getValue()), {}, renames);
ASSERT_TRUE(splitExpr.first.get());
- BSONObjBuilder firstBob;
- splitExpr.first->serialize(&firstBob, true);
- ASSERT_BSONOBJ_EQ(firstBob.obj(), fromjson("{c: {$_internalSchemaMinLength: 3}}"));
+ ASSERT_BSONOBJ_EQ(splitExpr.first->serialize(),
+ fromjson("{c: {$_internalSchemaMinLength: 3}}"));
ASSERT_FALSE(splitExpr.second.get());
}
@@ -1410,9 +1341,8 @@ TEST(SplitMatchExpression, ShouldMoveMaxLengthAcrossRename) {
expression::splitMatchExpressionBy(std::move(matcher.getValue()), {}, renames);
ASSERT_TRUE(splitExpr.first.get());
- BSONObjBuilder firstBob;
- splitExpr.first->serialize(&firstBob, true);
- ASSERT_BSONOBJ_EQ(firstBob.obj(), fromjson("{c: {$_internalSchemaMaxLength: 3}}"));
+ ASSERT_BSONOBJ_EQ(splitExpr.first->serialize(),
+ fromjson("{c: {$_internalSchemaMaxLength: 3}}"));
ASSERT_FALSE(splitExpr.second.get());
}
@@ -1429,9 +1359,7 @@ TEST(SplitMatchExpression, ShouldMoveIndependentPredicateWhenThereAreMultipleRen
expression::splitMatchExpressionBy(std::move(matcher.getValue()), {}, renames);
ASSERT_TRUE(splitExpr.first.get());
- BSONObjBuilder firstBob;
- splitExpr.first->serialize(&firstBob, true);
- ASSERT_BSONOBJ_EQ(firstBob.obj(), fromjson("{x: {$eq: 3}}"));
+ ASSERT_BSONOBJ_EQ(splitExpr.first->serialize(), fromjson("{x: {$eq: 3}}"));
ASSERT_FALSE(splitExpr.second.get());
}
@@ -1449,9 +1377,7 @@ TEST(SplitMatchExpression, ShouldNotSplitWhenRand) {
ASSERT_FALSE(split.get());
ASSERT_TRUE(residual.get());
- BSONObjBuilder oldBob;
- residual->serialize(&oldBob, true);
- ASSERT_BSONOBJ_EQ(oldBob.obj(), fromjson(randExpr));
+ ASSERT_BSONOBJ_EQ(residual->serialize(), fromjson(randExpr));
};
// We should not push down a $match with a $rand expression.
@@ -1462,85 +1388,73 @@ TEST(SplitMatchExpression, ShouldNotSplitWhenRand) {
}
TEST(SplitMatchExpression, ShouldSplitJsonSchemaRequiredByMetaField) {
- ParsedMatchExpression matcher(R"({$and: [{b: 1}, {$jsonSchema: {required: ["a"]}}]})");
+ ParsedMatchExpressionForTest matcher(R"({$and: [{b: 1}, {$jsonSchema: {required: ["a"]}}]})");
auto [splitOutExpr, residualExpr] =
- expression::splitMatchExpressionBy(matcher.extractExpr(), {"a"}, {});
+ expression::splitMatchExpressionBy(matcher.release(), {"a"}, {});
ASSERT_TRUE(splitOutExpr.get());
- BSONObjBuilder splitOutBob;
- splitOutExpr->serialize(&splitOutBob, true);
- ASSERT_BSONOBJ_EQ(splitOutBob.obj(), fromjson("{b: {$eq: 1}}"));
+ ASSERT_BSONOBJ_EQ(splitOutExpr->serialize(), fromjson("{b: {$eq: 1}}"));
ASSERT_TRUE(residualExpr.get());
- BSONObjBuilder residualBob;
- residualExpr->serialize(&residualBob, true);
- ASSERT_BSONOBJ_EQ(residualBob.obj(), fromjson("{a: {$exists: true}}"));
+ ASSERT_BSONOBJ_EQ(residualExpr->serialize(), fromjson("{a: {$exists: true}}"));
}
TEST(SplitMatchExpression,
ShouldSplitOutAndRenameJsonSchemaRequiredAndTheRestIsNullByIsOnlyDependentOn) {
- ParsedMatchExpression matcher(R"({$jsonSchema: {required: ["a"]}})");
+ ParsedMatchExpressionForTest matcher(R"({$jsonSchema: {required: ["a"]}})");
// $jsonSchema expression will be split out by the meta field "a" and the meta field "a" will be
// renamed to "meta".
auto [splitOutExpr, residualExpr] = expression::splitMatchExpressionBy(
- matcher.extractExpr(), {"a"}, {{"a", "meta"}}, expression::isOnlyDependentOn);
+ matcher.release(), {"a"}, {{"a", "meta"}}, expression::isOnlyDependentOn);
ASSERT_TRUE(splitOutExpr.get());
- BSONObjBuilder splitOutBob;
- splitOutExpr->serialize(&splitOutBob, true);
- ASSERT_BSONOBJ_EQ(splitOutBob.obj(), fromjson("{$and: [{$and: [{meta: {$exists: true}}]}]}"));
+ ASSERT_BSONOBJ_EQ(splitOutExpr->serialize(),
+ fromjson("{$and: [{$and: [{meta: {$exists: true}}]}]}"));
ASSERT_FALSE(residualExpr.get());
}
TEST(SplitMatchExpression,
ShouldSplitOutAndRenameJsonSchemaRequiredAndTheRestIs_NOT_NullByIsOnlyDependentOn) {
- ParsedMatchExpression matcher(R"({$jsonSchema: {required: ["a", "b"]}})");
+ ParsedMatchExpressionForTest matcher(R"({$jsonSchema: {required: ["a", "b"]}})");
// $jsonSchema expression will be split out by the meta field "a" and the meta field "a" will be
// renamed to "meta". The expression for the non-meta field "b" remains.
auto [splitOutExpr, residualExpr] = expression::splitMatchExpressionBy(
- matcher.extractExpr(), {"a"}, {{"a", "meta"}}, expression::isOnlyDependentOn);
+ matcher.release(), {"a"}, {{"a", "meta"}}, expression::isOnlyDependentOn);
ASSERT_TRUE(splitOutExpr.get());
- BSONObjBuilder splitOutBob;
- splitOutExpr->serialize(&splitOutBob, true);
- ASSERT_BSONOBJ_EQ(splitOutBob.obj(), fromjson("{meta: {$exists: true}}"));
+ ASSERT_BSONOBJ_EQ(splitOutExpr->serialize(), fromjson("{meta: {$exists: true}}"));
ASSERT_TRUE(residualExpr.get());
- BSONObjBuilder residualBob;
- residualExpr->serialize(&residualBob, true);
- ASSERT_BSONOBJ_EQ(residualBob.obj(), fromjson("{b: {$exists: true}}"));
+ ASSERT_BSONOBJ_EQ(residualExpr->serialize(), fromjson("{b: {$exists: true}}"));
}
TEST(SplitMatchExpression, ShouldSplitOutAndRenameJsonSchemaRequiredByIsOnlyDependentOn) {
- ParsedMatchExpression matcher(R"({$and: [{b: 1}, {$jsonSchema: {required: ["a"]}}]})");
+ ParsedMatchExpressionForTest matcher(R"({$and: [{b: 1}, {$jsonSchema: {required: ["a"]}}]})");
// $jsonSchema expression will be split out by the meta field "a" and the meta field "a" will be
// renamed to "meta". We have a residual expression too in this test case.
auto [splitOutExpr, residualExpr] = expression::splitMatchExpressionBy(
- matcher.extractExpr(), {"a"}, {{"a", "meta"}}, expression::isOnlyDependentOn);
+ matcher.release(), {"a"}, {{"a", "meta"}}, expression::isOnlyDependentOn);
ASSERT_TRUE(splitOutExpr.get());
- BSONObjBuilder splitOutBob;
- splitOutExpr->serialize(&splitOutBob, true);
- ASSERT_BSONOBJ_EQ(splitOutBob.obj(), fromjson("{$and: [{$and: [{meta: {$exists: true}}]}]}"));
+ ASSERT_BSONOBJ_EQ(splitOutExpr->serialize(),
+ fromjson("{$and: [{$and: [{meta: {$exists: true}}]}]}"));
ASSERT_TRUE(residualExpr.get());
- BSONObjBuilder residualBob;
- residualExpr->serialize(&residualBob, true);
- ASSERT_BSONOBJ_EQ(residualBob.obj(), fromjson("{b: {$eq: 1}}"));
+ ASSERT_BSONOBJ_EQ(residualExpr->serialize(), fromjson("{b: {$eq: 1}}"));
}
// Verifies that $jsonSchema 'type' is supported by splitMatchExpressionBy().
TEST(SplitMatchExpression, ShouldSplitOutAndRenameJsonSchemaTypeByIsOnlyDependentOn) {
- ParsedMatchExpression matcher(R"({$jsonSchema: {properties: {a: {type: "number"}}}})");
+ ParsedMatchExpressionForTest matcher(R"({$jsonSchema: {properties: {a: {type: "number"}}}})");
// $jsonSchema expression will be split out by the meta field "a" and the meta field "a" will be
// renamed to "meta".
auto [splitOutExpr, residualExpr] = expression::splitMatchExpressionBy(
- matcher.extractExpr(), {"a"}, {{"a", "meta"}}, expression::isOnlyDependentOn);
+ matcher.release(), {"a"}, {{"a", "meta"}}, expression::isOnlyDependentOn);
ASSERT_TRUE(splitOutExpr.get());
ASSERT_BSONOBJ_EQ(splitOutExpr->serialize(), fromjson(R"(
@@ -1561,12 +1475,12 @@ TEST(SplitMatchExpression, ShouldSplitOutAndRenameJsonSchemaTypeByIsOnlyDependen
// Verifies that $jsonSchema 'bsonType' is supported by splitMatchExpressionBy().
TEST(SplitMatchExpression, ShouldSplitOutAndRenameJsonSchemaBsonTypeByIsOnlyDependentOn) {
- ParsedMatchExpression matcher(R"({$jsonSchema: {properties: {a: {bsonType: "long"}}}})");
+ ParsedMatchExpressionForTest matcher(R"({$jsonSchema: {properties: {a: {bsonType: "long"}}}})");
// $jsonSchema expression will be split out by the meta field "a" and the meta field "a" will be
// renamed to "meta".
auto [splitOutExpr, residualExpr] = expression::splitMatchExpressionBy(
- matcher.extractExpr(), {"a"}, {{"a", "meta"}}, expression::isOnlyDependentOn);
+ matcher.release(), {"a"}, {{"a", "meta"}}, expression::isOnlyDependentOn);
ASSERT_TRUE(splitOutExpr.get());
ASSERT_BSONOBJ_EQ(splitOutExpr->serialize(), fromjson(R"(
@@ -1584,12 +1498,12 @@ TEST(SplitMatchExpression, ShouldSplitOutAndRenameJsonSchemaBsonTypeByIsOnlyDepe
// Verifies that $jsonSchema 'enum' is supported by splitMatchExpressionBy().
TEST(SplitMatchExpression, ShouldSplitOutAndRenameJsonSchemaEnumByIsOnlyDependentOn) {
- ParsedMatchExpression matcher(R"({$jsonSchema: {properties: {a: {enum: [1,2]}}}})");
+ ParsedMatchExpressionForTest matcher(R"({$jsonSchema: {properties: {a: {enum: [1,2]}}}})");
// $jsonSchema expression will be split out by the meta field "a" and the meta field "a" will be
// renamed to "meta".
auto [splitOutExpr, residualExpr] = expression::splitMatchExpressionBy(
- matcher.extractExpr(), {"a"}, {{"a", "meta"}}, expression::isOnlyDependentOn);
+ matcher.release(), {"a"}, {{"a", "meta"}}, expression::isOnlyDependentOn);
ASSERT_TRUE(splitOutExpr.get());
ASSERT_BSONOBJ_EQ(splitOutExpr->serialize(), fromjson(R"(
@@ -1610,12 +1524,13 @@ TEST(SplitMatchExpression, ShouldSplitOutAndRenameJsonSchemaEnumByIsOnlyDependen
// Verifies that $jsonSchema 'minimum' and 'maximum' are supported by splitMatchExpressionBy().
TEST(SplitMatchExpression, ShouldSplitOutAndRenameJsonSchemaMinMaxByIsOnlyDependentOn) {
- ParsedMatchExpression matcher("{$jsonSchema: {properties: {a: {minimum: 1, maximum: 10}}}}");
+ ParsedMatchExpressionForTest matcher(
+ "{$jsonSchema: {properties: {a: {minimum: 1, maximum: 10}}}}");
// $jsonSchema expression will be split out by the meta field "a" and the meta field "a" will be
// renamed to "meta".
auto [splitOutExpr, residualExpr] = expression::splitMatchExpressionBy(
- matcher.extractExpr(), {"a"}, {{"a", "meta"}}, expression::isOnlyDependentOn);
+ matcher.release(), {"a"}, {{"a", "meta"}}, expression::isOnlyDependentOn);
ASSERT_TRUE(splitOutExpr.get());
ASSERT_BSONOBJ_EQ(splitOutExpr->serialize(), fromjson(R"(
@@ -1651,13 +1566,13 @@ TEST(SplitMatchExpression, ShouldSplitOutAndRenameJsonSchemaMinMaxByIsOnlyDepend
// Verifies that $jsonSchema 'minLength' and 'maxLength' are supported by splitMatchExpressionBy().
TEST(SplitMatchExpression, ShouldSplitOutAndRenameJsonSchemaMinMaxLengthByIsOnlyDependentOn) {
- ParsedMatchExpression matcher(
+ ParsedMatchExpressionForTest matcher(
"{$jsonSchema: {properties: {a: {minLength: 1, maxLength: 10}}}}");
// $jsonSchema expression will be split out by the meta field "a" and the meta field "a" will be
// renamed to "meta".
auto [splitOutExpr, residualExpr] = expression::splitMatchExpressionBy(
- matcher.extractExpr(), {"a"}, {{"a", "meta"}}, expression::isOnlyDependentOn);
+ matcher.release(), {"a"}, {{"a", "meta"}}, expression::isOnlyDependentOn);
ASSERT_TRUE(splitOutExpr.get());
ASSERT_BSONOBJ_EQ(splitOutExpr->serialize(), fromjson(R"(
@@ -1693,12 +1608,12 @@ TEST(SplitMatchExpression, ShouldSplitOutAndRenameJsonSchemaMinMaxLengthByIsOnly
// Verifies that $jsonSchema 'multipleOf' is supported by splitMatchExpressionBy().
TEST(SplitMatchExpression, ShouldSplitOutAndRenameJsonSchemaMultipleOfByIsOnlyDependentOn) {
- ParsedMatchExpression matcher("{$jsonSchema: {properties: {a: {multipleOf: 10}}}}");
+ ParsedMatchExpressionForTest matcher("{$jsonSchema: {properties: {a: {multipleOf: 10}}}}");
// $jsonSchema expression will be split out by the meta field "a" and the meta field "a" will be
// renamed to "meta".
auto [splitOutExpr, residualExpr] = expression::splitMatchExpressionBy(
- matcher.extractExpr(), {"a"}, {{"a", "meta"}}, expression::isOnlyDependentOn);
+ matcher.release(), {"a"}, {{"a", "meta"}}, expression::isOnlyDependentOn);
ASSERT_TRUE(splitOutExpr.get());
ASSERT_BSONOBJ_EQ(splitOutExpr->serialize(), fromjson(R"(
@@ -1726,8 +1641,9 @@ TEST(SplitMatchExpression, ShouldSplitOutAndRenameJsonSchemaMultipleOfByIsOnlyDe
// Verifies that $jsonSchema 'pattern' is supported by splitMatchExpressionBy().
TEST(SplitMatchExpression, ShouldSplitOutAndRenameJsonSchemaPatternByIsOnlyDependentOn) {
- ParsedMatchExpression matcher(R"({$jsonSchema: {properties: {a: {pattern: "[0-9]*"}}}})");
- auto originalExpr = matcher.extractExpr();
+ ParsedMatchExpressionForTest matcher(
+ R"({$jsonSchema: {properties: {a: {pattern: "[0-9]*"}}}})");
+ auto originalExpr = matcher.release();
auto originalExprCopy = originalExpr->shallowClone();
// $jsonSchema expression will be split out by the meta field "a" and the meta field "a" will be
@@ -1938,7 +1854,7 @@ TEST(HasExistencePredicateOnPath, ReturnsFalseWhenExistsOnSubpath) {
}
TEST(SplitMatchExpressionForColumns, PreservesEmptyPredicates) {
- ParsedMatchExpression empty("{}");
+ ParsedMatchExpressionForTest empty("{}");
auto&& [splitUp, residual] = expression::splitMatchExpressionForColumns(empty.get());
ASSERT_EQ(splitUp.size(), 0) << splitUp.size();
ASSERT(residual == nullptr);
@@ -1947,7 +1863,7 @@ TEST(SplitMatchExpressionForColumns, PreservesEmptyPredicates) {
TEST(SplitMatchExpressionForColumns, RejectsUnsupportedPredicates) {
{
// Future work.
- ParsedMatchExpression orClause("{$or: [{a: 1}, {b: 2}]}");
+ ParsedMatchExpressionForTest orClause("{$or: [{a: 1}, {b: 2}]}");
auto&& [splitUp, residual] = expression::splitMatchExpressionForColumns(orClause.get());
ASSERT_EQ(splitUp.size(), 0) << splitUp.size();
assertMatchesEqual(orClause, residual);
@@ -1955,7 +1871,7 @@ TEST(SplitMatchExpressionForColumns, RejectsUnsupportedPredicates) {
{
// Would match missing values, not safe for a columnar index.
- ParsedMatchExpression alwaysTrue("{$alwaysTrue: 1}");
+ ParsedMatchExpressionForTest alwaysTrue("{$alwaysTrue: 1}");
auto&& [splitUp, residual] = expression::splitMatchExpressionForColumns(alwaysTrue.get());
ASSERT_EQ(splitUp.size(), 0) << splitUp.size();
assertMatchesEqual(alwaysTrue, residual);
@@ -1963,7 +1879,7 @@ TEST(SplitMatchExpressionForColumns, RejectsUnsupportedPredicates) {
{
// Future work.
- ParsedMatchExpression exprClause("{$expr: {$eq: ['$x', 0]}}");
+ ParsedMatchExpressionForTest exprClause("{$expr: {$eq: ['$x', 0]}}");
auto&& [splitUp, residual] = expression::splitMatchExpressionForColumns(exprClause.get());
ASSERT_EQ(splitUp.size(), 0) << splitUp.size();
assertMatchesEqual(exprClause, residual);
@@ -1974,7 +1890,7 @@ TEST(SplitMatchExpressionForColumns, RejectsUnsupportedPredicates) {
TEST(SplitMatchExpressionForColumns, SplitsSafeEqualities) {
{
- ParsedMatchExpression singleEqualsNumber("{albatross: 1}");
+ ParsedMatchExpressionForTest singleEqualsNumber("{albatross: 1}");
auto&& [splitUp, residual] =
expression::splitMatchExpressionForColumns(singleEqualsNumber.get());
ASSERT_EQ(splitUp.size(), 1) << splitUp.size();
@@ -1985,7 +1901,7 @@ TEST(SplitMatchExpressionForColumns, SplitsSafeEqualities) {
}
{
- ParsedMatchExpression singleEqualsString("{albatross: 'flying'}");
+ ParsedMatchExpressionForTest singleEqualsString("{albatross: 'flying'}");
auto&& [splitUp, residual] =
expression::splitMatchExpressionForColumns(singleEqualsString.get());
ASSERT_EQ(splitUp.size(), 1) << splitUp.size();
@@ -1996,7 +1912,7 @@ TEST(SplitMatchExpressionForColumns, SplitsSafeEqualities) {
}
{
- ParsedMatchExpression doubleEqualsNumber("{albatross: 1, blackbird: 2}");
+ ParsedMatchExpressionForTest doubleEqualsNumber("{albatross: 1, blackbird: 2}");
auto&& [splitUp, residual] =
expression::splitMatchExpressionForColumns(doubleEqualsNumber.get());
ASSERT_EQ(splitUp.size(), 2) << splitUp.size();
@@ -2010,7 +1926,7 @@ TEST(SplitMatchExpressionForColumns, SplitsSafeEqualities) {
}
{
- ParsedMatchExpression mixedEquals(
+ ParsedMatchExpressionForTest mixedEquals(
"{albatross: 1,"
" blackbird: 'flying',"
" cowbird: {$eq: /oreo/},"
@@ -2023,7 +1939,7 @@ TEST(SplitMatchExpressionForColumns, SplitsSafeEqualities) {
" kiwi: NumberDecimal('22'),"
" 'loggerhead shrike': {$minKey: 1},"
" mallard: {$maxKey: 1}}");
- ParsedMatchExpression minMaxKeyComps(
+ ParsedMatchExpressionForTest minMaxKeyComps(
"{'loggerhead shrike': {$minKey: 1},"
" mallard: {$maxKey: 1}}");
@@ -2043,7 +1959,7 @@ TEST(SplitMatchExpressionForColumns, SplitsSafeEqualities) {
TEST(SplitMatchExpressionForColumns, SupportsEqualityToEmptyObjects) {
{
- ParsedMatchExpression equalsEmptyObj("{albatross: {}}");
+ ParsedMatchExpressionForTest equalsEmptyObj("{albatross: {}}");
auto&& [splitUp, residual] =
expression::splitMatchExpressionForColumns(equalsEmptyObj.get());
ASSERT_EQ(splitUp.size(), 1) << splitUp.size();
@@ -2056,7 +1972,7 @@ TEST(SplitMatchExpressionForColumns, SupportsEqualityToEmptyObjects) {
TEST(SplitMatchExpressionForColumns, SupportsEqualityToEmptyArray) {
{
- ParsedMatchExpression equalsEmptyArray("{albatross: []}");
+ ParsedMatchExpressionForTest equalsEmptyArray("{albatross: []}");
auto&& [splitUp, residual] =
expression::splitMatchExpressionForColumns(equalsEmptyArray.get());
ASSERT_EQ(splitUp.size(), 1) << splitUp.size();
@@ -2069,7 +1985,7 @@ TEST(SplitMatchExpressionForColumns, SupportsEqualityToEmptyArray) {
TEST(SplitMatchExpressionForColumns, DoesNotSupportEqualsNull) {
{
- ParsedMatchExpression equalsNull("{a: null}");
+ ParsedMatchExpressionForTest equalsNull("{a: null}");
auto&& [splitUp, residual] = expression::splitMatchExpressionForColumns(equalsNull.get());
ASSERT_EQ(splitUp.size(), 0) << splitUp.size();
assertMatchesEqual(equalsNull, residual);
@@ -2078,28 +1994,28 @@ TEST(SplitMatchExpressionForColumns, DoesNotSupportEqualsNull) {
TEST(SplitMatchExpressionForColumns, DoesNotSupportCompoundEquals) {
{
- ParsedMatchExpression implicitEqualsArray("{a: [1, 2]}");
+ ParsedMatchExpressionForTest implicitEqualsArray("{a: [1, 2]}");
auto&& [splitUp, residual] =
expression::splitMatchExpressionForColumns(implicitEqualsArray.get());
ASSERT_EQ(splitUp.size(), 0) << splitUp.size();
assertMatchesEqual(implicitEqualsArray, residual);
}
{
- ParsedMatchExpression explicitEqualsArray("{a: {$eq: [1, 2]}}");
+ ParsedMatchExpressionForTest explicitEqualsArray("{a: {$eq: [1, 2]}}");
auto&& [splitUp, residual] =
expression::splitMatchExpressionForColumns(explicitEqualsArray.get());
ASSERT_EQ(splitUp.size(), 0) << splitUp.size();
assertMatchesEqual(explicitEqualsArray, residual);
}
{
- ParsedMatchExpression implicitEqualsObject("{a: {boats: 1, planes: 2}}");
+ ParsedMatchExpressionForTest implicitEqualsObject("{a: {boats: 1, planes: 2}}");
auto&& [splitUp, residual] =
expression::splitMatchExpressionForColumns(implicitEqualsObject.get());
ASSERT_EQ(splitUp.size(), 0) << splitUp.size();
assertMatchesEqual(implicitEqualsObject, residual);
}
{
- ParsedMatchExpression explicitEqualsObject("{a: {$eq: {boats: 1, planes: 2}}}");
+ ParsedMatchExpressionForTest explicitEqualsObject("{a: {$eq: {boats: 1, planes: 2}}}");
auto&& [splitUp, residual] =
expression::splitMatchExpressionForColumns(explicitEqualsObject.get());
ASSERT_EQ(splitUp.size(), 0) << splitUp.size();
@@ -2107,7 +2023,7 @@ TEST(SplitMatchExpressionForColumns, DoesNotSupportCompoundEquals) {
}
// We should be able to do dotted path version though, as a potential workaround.
{
- ParsedMatchExpression equalsDotted("{'a.boats': 1, 'a.planes': 2}");
+ ParsedMatchExpressionForTest equalsDotted("{'a.boats': 1, 'a.planes': 2}");
auto&& [splitUp, residual] = expression::splitMatchExpressionForColumns(equalsDotted.get());
ASSERT_GT(splitUp.size(), 0);
ASSERT(splitUp.size() == 2);
@@ -2125,7 +2041,7 @@ TEST(SplitMatchExpressionForColumns, DoesNotSupportCompoundEquals) {
TEST(SplitMatchExpressionForColumns, SupportsComparisonsLikeEqualities) {
{
- ParsedMatchExpression singleLtNumber("{albatross: {$lt: 1}}");
+ ParsedMatchExpressionForTest singleLtNumber("{albatross: {$lt: 1}}");
auto&& [splitUp, residual] =
expression::splitMatchExpressionForColumns(singleLtNumber.get());
ASSERT_EQ(splitUp.size(), 1) << splitUp.size();
@@ -2135,7 +2051,7 @@ TEST(SplitMatchExpressionForColumns, SupportsComparisonsLikeEqualities) {
ASSERT(residual == nullptr);
}
{
- ParsedMatchExpression singleLteNumber("{albatross: {$lte: 1}}");
+ ParsedMatchExpressionForTest singleLteNumber("{albatross: {$lte: 1}}");
auto&& [splitUp, residual] =
expression::splitMatchExpressionForColumns(singleLteNumber.get());
ASSERT_EQ(splitUp.size(), 1) << splitUp.size();
@@ -2145,7 +2061,7 @@ TEST(SplitMatchExpressionForColumns, SupportsComparisonsLikeEqualities) {
ASSERT(residual == nullptr);
}
{
- ParsedMatchExpression singleGtNumber("{albatross: {$gt: 1}}");
+ ParsedMatchExpressionForTest singleGtNumber("{albatross: {$gt: 1}}");
auto&& [splitUp, residual] =
expression::splitMatchExpressionForColumns(singleGtNumber.get());
ASSERT_EQ(splitUp.size(), 1) << splitUp.size();
@@ -2155,7 +2071,7 @@ TEST(SplitMatchExpressionForColumns, SupportsComparisonsLikeEqualities) {
ASSERT(residual == nullptr);
}
{
- ParsedMatchExpression singleGteNumber("{albatross: {$gte: 1}}");
+ ParsedMatchExpressionForTest singleGteNumber("{albatross: {$gte: 1}}");
auto&& [splitUp, residual] =
expression::splitMatchExpressionForColumns(singleGteNumber.get());
ASSERT_EQ(splitUp.size(), 1) << splitUp.size();
@@ -2165,7 +2081,7 @@ TEST(SplitMatchExpressionForColumns, SupportsComparisonsLikeEqualities) {
ASSERT(residual == nullptr);
}
{
- ParsedMatchExpression combinationPredicate(
+ ParsedMatchExpressionForTest combinationPredicate(
"{"
" albatross: {$lt: 100},"
" blackbird: {$gt: 0},"
@@ -2190,49 +2106,49 @@ TEST(SplitMatchExpressionForColumns, SupportsComparisonsLikeEqualities) {
// While equality to [] or {} is OK, inequality is not so obvious. Left as future work.
TEST(SplitMatchExpressionForColumns, DoesNotSupportInequalitiesToObjectsOrArrays) {
{
- ParsedMatchExpression ltArray("{albatross: {$lt: []}}");
+ ParsedMatchExpressionForTest ltArray("{albatross: {$lt: []}}");
auto&& [splitUp, residual] = expression::splitMatchExpressionForColumns(ltArray.get());
ASSERT_EQ(splitUp.size(), 0) << splitUp.size();
assertMatchesEqual(ltArray, residual);
}
{
- ParsedMatchExpression ltObject("{albatross: {$lt: {}}}");
+ ParsedMatchExpressionForTest ltObject("{albatross: {$lt: {}}}");
auto&& [splitUp, residual] = expression::splitMatchExpressionForColumns(ltObject.get());
ASSERT_EQ(splitUp.size(), 0) << splitUp.size();
assertMatchesEqual(ltObject, residual);
}
{
- ParsedMatchExpression lteArray("{albatross: {$lte: []}}");
+ ParsedMatchExpressionForTest lteArray("{albatross: {$lte: []}}");
auto&& [splitUp, residual] = expression::splitMatchExpressionForColumns(lteArray.get());
ASSERT_EQ(splitUp.size(), 0) << splitUp.size();
assertMatchesEqual(lteArray, residual);
}
{
- ParsedMatchExpression lteObject("{albatross: {$lte: {}}}");
+ ParsedMatchExpressionForTest lteObject("{albatross: {$lte: {}}}");
auto&& [splitUp, residual] = expression::splitMatchExpressionForColumns(lteObject.get());
ASSERT_EQ(splitUp.size(), 0) << splitUp.size();
assertMatchesEqual(lteObject, residual);
}
{
- ParsedMatchExpression gtArray("{albatross: {$gt: []}}");
+ ParsedMatchExpressionForTest gtArray("{albatross: {$gt: []}}");
auto&& [splitUp, residual] = expression::splitMatchExpressionForColumns(gtArray.get());
ASSERT_EQ(splitUp.size(), 0) << splitUp.size();
assertMatchesEqual(gtArray, residual);
}
{
- ParsedMatchExpression gtObject("{albatross: {$gt: {}}}");
+ ParsedMatchExpressionForTest gtObject("{albatross: {$gt: {}}}");
auto&& [splitUp, residual] = expression::splitMatchExpressionForColumns(gtObject.get());
ASSERT_EQ(splitUp.size(), 0) << splitUp.size();
assertMatchesEqual(gtObject, residual);
}
{
- ParsedMatchExpression gteArray("{albatross: {$gte: []}}");
+ ParsedMatchExpressionForTest gteArray("{albatross: {$gte: []}}");
auto&& [splitUp, residual] = expression::splitMatchExpressionForColumns(gteArray.get());
ASSERT_EQ(splitUp.size(), 0) << splitUp.size();
assertMatchesEqual(gteArray, residual);
}
{
- ParsedMatchExpression gteObject("{albatross: {$gte: {}}}");
+ ParsedMatchExpressionForTest gteObject("{albatross: {$gte: {}}}");
auto&& [splitUp, residual] = expression::splitMatchExpressionForColumns(gteObject.get());
ASSERT_EQ(splitUp.size(), 0) << splitUp.size();
assertMatchesEqual(gteObject, residual);
@@ -2241,7 +2157,7 @@ TEST(SplitMatchExpressionForColumns, DoesNotSupportInequalitiesToObjectsOrArrays
// Tests that comparisons which only match values of a certain type are allowed.
TEST(SplitMatchExpressionForColumns, SupportsTypeSpecificPredicates) {
- ParsedMatchExpression combinationPredicate(
+ ParsedMatchExpressionForTest combinationPredicate(
"{"
" albatross: /oreo/,"
" blackbird: {$mod: [2, 0]},"
@@ -2275,7 +2191,7 @@ TEST(SplitMatchExpressionForColumns, SupportsTypeSpecificPredicates) {
}
TEST(SplitMatchExpressionForColumns, SupportsExistsTrue) {
- ParsedMatchExpression existsPredicate("{albatross: {$exists: true}}");
+ ParsedMatchExpressionForTest existsPredicate("{albatross: {$exists: true}}");
auto&& [splitUp, residual] = expression::splitMatchExpressionForColumns(existsPredicate.get());
ASSERT_EQ(splitUp.size(), 1) << splitUp.size();
ASSERT(splitUp.contains("albatross"));
@@ -2285,7 +2201,7 @@ TEST(SplitMatchExpressionForColumns, SupportsExistsTrue) {
}
TEST(SplitMatchExpressionForColumns, DoesNotSupportExistsFalse) {
- ParsedMatchExpression existsPredicate("{albatross: {$exists: false}}");
+ ParsedMatchExpressionForTest existsPredicate("{albatross: {$exists: false}}");
auto&& [splitUp, residual] = expression::splitMatchExpressionForColumns(existsPredicate.get());
ASSERT_EQ(splitUp.size(), 0) << splitUp.size();
assertMatchesEqual(existsPredicate, residual);
@@ -2295,7 +2211,7 @@ TEST(SplitMatchExpressionForColumns, DoesNotSupportExistsFalse) {
// next test.
TEST(SplitMatchExpressionForColumns, SupportsSomeInPredicates) {
{
- ParsedMatchExpression emptyIn("{albatross: {$in: []}}");
+ ParsedMatchExpressionForTest emptyIn("{albatross: {$in: []}}");
auto&& [splitUp, residual] = expression::splitMatchExpressionForColumns(emptyIn.get());
ASSERT_EQ(splitUp.size(), 1) << splitUp.size();
ASSERT(splitUp.contains("albatross"));
@@ -2304,7 +2220,7 @@ TEST(SplitMatchExpressionForColumns, SupportsSomeInPredicates) {
ASSERT(residual == nullptr);
}
{
- ParsedMatchExpression singleElementIn("{albatross: {$in: [4]}}");
+ ParsedMatchExpressionForTest singleElementIn("{albatross: {$in: [4]}}");
auto&& [splitUp, residual] =
expression::splitMatchExpressionForColumns(singleElementIn.get());
ASSERT_EQ(splitUp.size(), 1) << splitUp.size();
@@ -2314,7 +2230,7 @@ TEST(SplitMatchExpressionForColumns, SupportsSomeInPredicates) {
ASSERT(residual == nullptr);
}
{
- ParsedMatchExpression multiElementIn("{albatross: {$in: [4, 5, 9]}}");
+ ParsedMatchExpressionForTest multiElementIn("{albatross: {$in: [4, 5, 9]}}");
auto&& [splitUp, residual] =
expression::splitMatchExpressionForColumns(multiElementIn.get());
ASSERT_EQ(splitUp.size(), 1) << splitUp.size();
@@ -2324,7 +2240,7 @@ TEST(SplitMatchExpressionForColumns, SupportsSomeInPredicates) {
ASSERT(residual == nullptr);
}
{
- ParsedMatchExpression inWithEmptyArray("{albatross: {$in: [[]]}}");
+ ParsedMatchExpressionForTest inWithEmptyArray("{albatross: {$in: [[]]}}");
auto&& [splitUp, residual] =
expression::splitMatchExpressionForColumns(inWithEmptyArray.get());
ASSERT_EQ(splitUp.size(), 1) << splitUp.size();
@@ -2334,7 +2250,7 @@ TEST(SplitMatchExpressionForColumns, SupportsSomeInPredicates) {
ASSERT(residual == nullptr);
}
{
- ParsedMatchExpression inWithEmptyObject("{albatross: {$in: [{}]}}");
+ ParsedMatchExpressionForTest inWithEmptyObject("{albatross: {$in: [{}]}}");
auto&& [splitUp, residual] =
expression::splitMatchExpressionForColumns(inWithEmptyObject.get());
ASSERT_EQ(splitUp.size(), 1) << splitUp.size();
@@ -2345,7 +2261,7 @@ TEST(SplitMatchExpressionForColumns, SupportsSomeInPredicates) {
}
{
- ParsedMatchExpression mixedTypeIn("{albatross: {$in: [4, {}, [], 'string']}}");
+ ParsedMatchExpressionForTest mixedTypeIn("{albatross: {$in: [4, {}, [], 'string']}}");
auto&& [splitUp, residual] = expression::splitMatchExpressionForColumns(mixedTypeIn.get());
ASSERT_EQ(splitUp.size(), 1) << splitUp.size();
ASSERT(splitUp.contains("albatross"));
@@ -2357,7 +2273,7 @@ TEST(SplitMatchExpressionForColumns, SupportsSomeInPredicates) {
TEST(SplitMatchExpressionForColumns, DoesNotSupportSomeInPredicates) {
{
- ParsedMatchExpression regexIn("{albatross: {$in: [/regex/]}}");
+ ParsedMatchExpressionForTest regexIn("{albatross: {$in: [/regex/]}}");
auto&& [splitUp, residual] = expression::splitMatchExpressionForColumns(regexIn.get());
ASSERT_EQ(splitUp.size(), 0) << splitUp.size();
ASSERT(residual != nullptr);
@@ -2365,7 +2281,7 @@ TEST(SplitMatchExpressionForColumns, DoesNotSupportSomeInPredicates) {
}
{
- ParsedMatchExpression nonEmptyObj("{albatross: {$in: [{a: 1}]}}");
+ ParsedMatchExpressionForTest nonEmptyObj("{albatross: {$in: [{a: 1}]}}");
auto&& [splitUp, residual] = expression::splitMatchExpressionForColumns(nonEmptyObj.get());
ASSERT_EQ(splitUp.size(), 0) << splitUp.size();
ASSERT(residual != nullptr);
@@ -2373,7 +2289,7 @@ TEST(SplitMatchExpressionForColumns, DoesNotSupportSomeInPredicates) {
}
{
- ParsedMatchExpression nonEmptyArr("{albatross: {$in: [[1, 2]]}}");
+ ParsedMatchExpressionForTest nonEmptyArr("{albatross: {$in: [[1, 2]]}}");
auto&& [splitUp, residual] = expression::splitMatchExpressionForColumns(nonEmptyArr.get());
ASSERT_EQ(splitUp.size(), 0) << splitUp.size();
ASSERT(residual != nullptr);
@@ -2384,25 +2300,25 @@ TEST(SplitMatchExpressionForColumns, DoesNotSupportSomeInPredicates) {
// We can't support compound types, just like for equality.
TEST(SplitMatchExpressionForColumns, DoesNotSupportCertainInEdgeCases) {
{
- ParsedMatchExpression inWithArray("{albatross: {$in: [[2,3]]}}");
+ ParsedMatchExpressionForTest inWithArray("{albatross: {$in: [[2,3]]}}");
auto&& [splitUp, residual] = expression::splitMatchExpressionForColumns(inWithArray.get());
ASSERT_EQ(splitUp.size(), 0) << splitUp.size();
assertMatchesEqual(inWithArray, residual);
}
{
- ParsedMatchExpression inWithObject("{albatross: {$in: [{wings: 2}]}}");
+ ParsedMatchExpressionForTest inWithObject("{albatross: {$in: [{wings: 2}]}}");
auto&& [splitUp, residual] = expression::splitMatchExpressionForColumns(inWithObject.get());
ASSERT_EQ(splitUp.size(), 0) << splitUp.size();
assertMatchesEqual(inWithObject, residual);
}
{
- ParsedMatchExpression inWithNull("{albatross: {$in: [null]}}");
+ ParsedMatchExpressionForTest inWithNull("{albatross: {$in: [null]}}");
auto&& [splitUp, residual] = expression::splitMatchExpressionForColumns(inWithNull.get());
ASSERT_EQ(splitUp.size(), 0) << splitUp.size();
assertMatchesEqual(inWithNull, residual);
}
{
- ParsedMatchExpression unsupportedMixedInWithSupported(
+ ParsedMatchExpressionForTest unsupportedMixedInWithSupported(
"{albatross: {$in: ['strings', 1, null, {x: 4}, [0, 0], 4]}}");
auto&& [splitUp, residual] =
expression::splitMatchExpressionForColumns(unsupportedMixedInWithSupported.get());
@@ -2412,7 +2328,7 @@ TEST(SplitMatchExpressionForColumns, DoesNotSupportCertainInEdgeCases) {
}
TEST(SplitMatchExpressionForColumns, SupportsTypePredicatesInt) {
- ParsedMatchExpression intFilter("{albatross: {$type: 'int'}}");
+ ParsedMatchExpressionForTest intFilter("{albatross: {$type: 'int'}}");
auto&& [splitUp, residual] = expression::splitMatchExpressionForColumns(intFilter.get());
ASSERT_GT(splitUp.size(), 0);
ASSERT(splitUp.contains("albatross"));
@@ -2423,7 +2339,7 @@ TEST(SplitMatchExpressionForColumns, SupportsTypePredicatesInt) {
}
TEST(SplitMatchExpressionForColumns, SupportsTypePredicatesNumber) {
- ParsedMatchExpression numberFilter("{albatross: {$type: 'number'}}");
+ ParsedMatchExpressionForTest numberFilter("{albatross: {$type: 'number'}}");
auto&& [splitUp, residual] = expression::splitMatchExpressionForColumns(numberFilter.get());
ASSERT_GT(splitUp.size(), 0);
ASSERT(splitUp.contains("albatross"));
@@ -2434,7 +2350,7 @@ TEST(SplitMatchExpressionForColumns, SupportsTypePredicatesNumber) {
}
TEST(SplitMatchExpressionForColumns, SupportsTypePredicatesMultiple) {
- ParsedMatchExpression stringFilter("{albatross: {$type: ['string', 'double']}}");
+ ParsedMatchExpressionForTest stringFilter("{albatross: {$type: ['string', 'double']}}");
auto&& [splitUp, residual] = expression::splitMatchExpressionForColumns(stringFilter.get());
ASSERT_EQ(splitUp.size(), 1) << splitUp.size();
ASSERT(splitUp.contains("albatross"));
@@ -2444,7 +2360,7 @@ TEST(SplitMatchExpressionForColumns, SupportsTypePredicatesMultiple) {
}
TEST(SplitMatchExpressionForColumns, SupportsTypePredicatesNull) {
- ParsedMatchExpression nullFilter("{albatross: {$type: 'null'}}");
+ ParsedMatchExpressionForTest nullFilter("{albatross: {$type: 'null'}}");
auto&& [splitUp, residual] = expression::splitMatchExpressionForColumns(nullFilter.get());
ASSERT_EQ(splitUp.size(), 1) << splitUp.size();
ASSERT(splitUp.contains("albatross"));
@@ -2454,7 +2370,7 @@ TEST(SplitMatchExpressionForColumns, SupportsTypePredicatesNull) {
}
TEST(SplitMatchExpressionForColumns, SupportsTypePredicatesObject) {
- ParsedMatchExpression objectFilter("{albatross: {$type: 'object'}}");
+ ParsedMatchExpressionForTest objectFilter("{albatross: {$type: 'object'}}");
auto&& [splitUp, residual] = expression::splitMatchExpressionForColumns(objectFilter.get());
ASSERT_EQ(splitUp.size(), 1) << splitUp.size();
ASSERT(splitUp.contains("albatross"));
@@ -2464,7 +2380,7 @@ TEST(SplitMatchExpressionForColumns, SupportsTypePredicatesObject) {
}
TEST(SplitMatchExpressionForColumns, SupportsTypePredicatesArray) {
- ParsedMatchExpression arrayFilter("{albatross: {$type: 'array'}}");
+ ParsedMatchExpressionForTest arrayFilter("{albatross: {$type: 'array'}}");
auto&& [splitUp, residual] = expression::splitMatchExpressionForColumns(arrayFilter.get());
ASSERT_EQ(splitUp.size(), 1) << splitUp.size();
ASSERT(splitUp.contains("albatross"));
@@ -2474,7 +2390,7 @@ TEST(SplitMatchExpressionForColumns, SupportsTypePredicatesArray) {
}
TEST(SplitMatchExpressionForColumns, SupportsCombiningPredicatesWithAnd) {
- ParsedMatchExpression compoundFilter(
+ ParsedMatchExpressionForTest compoundFilter(
"{"
" albatross: {$gte: 100},"
" albatross: {$lt: 200},"
@@ -2502,7 +2418,7 @@ TEST(SplitMatchExpressionForColumns, SupportsCombiningPredicatesWithAnd) {
}
TEST(SplitMatchExpressionForColumns, SupportsAndFlattensNestedAnd) {
- ParsedMatchExpression compoundFilter(
+ ParsedMatchExpressionForTest compoundFilter(
"{"
" $and: ["
" {albatross: {$gte: 100}},"
@@ -2532,13 +2448,13 @@ TEST(SplitMatchExpressionForColumns, SupportsAndFlattensNestedAnd) {
TEST(SplitMatchExpressionForColumns, DoesNotSupportStandaloneNotQueries) {
{
- ParsedMatchExpression notEqFilter("{albatross: {$not: {$eq: 2}}}");
+ ParsedMatchExpressionForTest notEqFilter("{albatross: {$not: {$eq: 2}}}");
auto&& [splitUp, residual] = expression::splitMatchExpressionForColumns(notEqFilter.get());
ASSERT_EQ(splitUp.size(), 0) << splitUp.size();
assertMatchesEqual(notEqFilter, residual);
}
{
- ParsedMatchExpression notAndFilter("{albatross: {$not: {$type: 'number'}}}");
+ ParsedMatchExpressionForTest notAndFilter("{albatross: {$not: {$type: 'number'}}}");
auto&& [splitUp, residual] = expression::splitMatchExpressionForColumns(notAndFilter.get());
ASSERT_EQ(splitUp.size(), 0) << splitUp.size();
assertMatchesEqual(notAndFilter, residual);
@@ -2547,7 +2463,7 @@ TEST(SplitMatchExpressionForColumns, DoesNotSupportStandaloneNotQueries) {
TEST(SplitMatchExpressionForColumns, SupportsNotQueriesInPresenceOfOtherSupportedOnSamePath) {
{
- ParsedMatchExpression notEqFilter(
+ ParsedMatchExpressionForTest notEqFilter(
"{$and: [{a: {$gt: 0, $lt: 50}}, {a: {$ne: 2}}, {a: {$ne: 20}}]}");
auto&& [splitUp, residual] = expression::splitMatchExpressionForColumns(notEqFilter.get());
ASSERT_EQ(splitUp.size(), 1) << splitUp.size();
@@ -2559,29 +2475,30 @@ TEST(SplitMatchExpressionForColumns, SupportsNotQueriesInPresenceOfOtherSupporte
// {$ne: null} is the same as {$not: {$eq: null}} and while it could be evaluated against
// the index, because {$eq: null} isn't supported for push down, we don't push down its
// negation either, but {$ne: 2} should be lowered.
- ParsedMatchExpression notAndFilter(
+ ParsedMatchExpressionForTest notAndFilter(
"{$and: [{a: {$exists: true}}, {a: {$ne: 2}}, {a: {$ne: null}}]}");
auto&& [splitUp, residual] = expression::splitMatchExpressionForColumns(notAndFilter.get());
ASSERT_EQ(splitUp.size(), 1) << splitUp.size();
ASSERT(splitUp.at("a")->matchType() == MatchExpression::AND) << splitUp.at("a")->toString();
- assertMatchesEqual(ParsedMatchExpression("{a: {$ne: null}}"), residual);
+ assertMatchesEqual(ParsedMatchExpressionForTest("{a: {$ne: null}}"), residual);
}
{
// $not on multiple paths should only be lowered if there are supported predicates on the
// same path with them
- ParsedMatchExpression notEqFilter("{a: {$gt: 0, $ne: 2}, b:{$ne: 2, $lt: 5}, c: {$ne: 2}}");
+ ParsedMatchExpressionForTest notEqFilter(
+ "{a: {$gt: 0, $ne: 2}, b:{$ne: 2, $lt: 5}, c: {$ne: 2}}");
auto&& [splitUp, residual] = expression::splitMatchExpressionForColumns(notEqFilter.get());
ASSERT_EQ(splitUp.size(), 2) << splitUp.size();
ASSERT(splitUp.contains("a"));
ASSERT(splitUp.at("a")->matchType() == MatchExpression::AND) << splitUp.at("a")->toString();
ASSERT(splitUp.contains("b"));
ASSERT(splitUp.at("b")->matchType() == MatchExpression::AND) << splitUp.at("a")->toString();
- assertMatchesEqual(ParsedMatchExpression("{c: {$ne: 2}}"), residual);
+ assertMatchesEqual(ParsedMatchExpressionForTest("{c: {$ne: 2}}"), residual);
}
}
TEST(SplitMatchExpressionForColumns, CanSplitPredicate) {
- ParsedMatchExpression complexPredicate(R"({
+ ParsedMatchExpressionForTest complexPredicate(R"({
a: {$gte: 0},
unsubscribed: false,
specialAddress: {$exists: false}
@@ -2592,12 +2509,12 @@ TEST(SplitMatchExpressionForColumns, CanSplitPredicate) {
ASSERT(splitUp.at("a")->matchType() == MatchExpression::GTE) << splitUp.at("a")->toString();
ASSERT(splitUp.contains("unsubscribed"));
ASSERT(!splitUp.contains("specialAddress"));
- ParsedMatchExpression expectedResidual("{specialAddress: {$exists: false}}");
+ ParsedMatchExpressionForTest expectedResidual("{specialAddress: {$exists: false}}");
assertMatchesEqual(expectedResidual, residual);
}
TEST(SplitMatchExpressionForColumns, SupportsDottedPaths) {
- ParsedMatchExpression compoundFilter(
+ ParsedMatchExpressionForTest compoundFilter(
"{"
" albatross: /oreo/,"
" \"blackbird.feet\": {$mod: [2, 0]},"
@@ -2631,7 +2548,7 @@ TEST(SplitMatchExpressionForColumns, SupportsDottedPaths) {
}
TEST(SplitMatchExpressionForColumns, LeavesOriginalMatchExpressionFunctional) {
- ParsedMatchExpression combinationPredicate(
+ ParsedMatchExpressionForTest combinationPredicate(
"{"
" albatross: {$lt: 100},"
" blackbird: {$gt: 0},"
diff --git a/src/mongo/db/matcher/expression_always_boolean.h b/src/mongo/db/matcher/expression_always_boolean.h
index 6a97367aff5..468e34ccab8 100644
--- a/src/mongo/db/matcher/expression_always_boolean.h
+++ b/src/mongo/db/matcher/expression_always_boolean.h
@@ -62,7 +62,8 @@ public:
debug << name() << ": 1\n";
}
- void serialize(BSONObjBuilder* out, bool includePath) const final {
+ void serialize(BSONObjBuilder* out, SerializationOptions opts) const final {
+ // TODO support 'opts.redactFieldNames.'
out->append(name(), 1);
}
diff --git a/src/mongo/db/matcher/expression_arity.h b/src/mongo/db/matcher/expression_arity.h
index a16dcf346a2..e699b6ae363 100644
--- a/src/mongo/db/matcher/expression_arity.h
+++ b/src/mongo/db/matcher/expression_arity.h
@@ -58,7 +58,7 @@ public:
_debugAddSpace(debug, indentationLevel);
BSONObjBuilder builder;
- serialize(&builder, true);
+ serialize(&builder, {});
debug << builder.obj().toString();
}
@@ -107,11 +107,12 @@ public:
/**
* Serializes each subexpression sequentially in a BSONArray.
*/
- void serialize(BSONObjBuilder* builder, bool includePath) const final {
+ void serialize(BSONObjBuilder* builder, SerializationOptions opts) const final {
+ // TODO SERVER-73678 respect 'opts'.
BSONArrayBuilder exprArray(builder->subarrayStart(name()));
for (const auto& expr : _expressions) {
BSONObjBuilder exprBuilder(exprArray.subobjStart());
- expr->serialize(&exprBuilder, includePath);
+ expr->serialize(&exprBuilder, opts);
exprBuilder.doneFast();
}
exprArray.doneFast();
diff --git a/src/mongo/db/matcher/expression_array.cpp b/src/mongo/db/matcher/expression_array.cpp
index 833390eaa62..6ed79d1de5e 100644
--- a/src/mongo/db/matcher/expression_array.cpp
+++ b/src/mongo/db/matcher/expression_array.cpp
@@ -103,10 +103,10 @@ void ElemMatchObjectMatchExpression::debugString(StringBuilder& debug, int inden
_sub->debugString(debug, indentationLevel + 1);
}
-BSONObj ElemMatchObjectMatchExpression::getSerializedRightHandSide() const {
- BSONObjBuilder subBob;
- _sub->serialize(&subBob, true);
- return BSON("$elemMatch" << subBob.obj());
+BSONObj ElemMatchObjectMatchExpression::getSerializedRightHandSide(
+ boost::optional<StringData> replacementForLiteralArgs) const {
+ // TODO SERVER-73673 respect and test 'replacementForLiteralArgs'.
+ return BSON("$elemMatch" << _sub->serialize());
}
MatchExpression::ExpressionOptimizerFunc ElemMatchObjectMatchExpression::getOptimizer() const {
@@ -174,11 +174,15 @@ void ElemMatchValueMatchExpression::debugString(StringBuilder& debug, int indent
}
}
-BSONObj ElemMatchValueMatchExpression::getSerializedRightHandSide() const {
+BSONObj ElemMatchValueMatchExpression::getSerializedRightHandSide(
+ boost::optional<StringData> replacementForLiteralArgs) const {
BSONObjBuilder emBob;
+ // TODO SERVER-73673 respect and test 'replacementForLiteralArgs'.
for (auto&& child : _subs) {
- child->serialize(&emBob, false);
+ SerializationOptions opts;
+ opts.includePath = false;
+ child->serialize(&emBob, opts);
}
return BSON("$elemMatch" << emBob.obj());
@@ -219,7 +223,9 @@ void SizeMatchExpression::debugString(StringBuilder& debug, int indentationLevel
}
}
-BSONObj SizeMatchExpression::getSerializedRightHandSide() const {
+BSONObj SizeMatchExpression::getSerializedRightHandSide(
+ boost::optional<StringData> replacementForLiteralArgs) const {
+ // TODO SERVER-73676 respect 'replacementForLiteralArgs.'
return BSON("$size" << _size);
}
diff --git a/src/mongo/db/matcher/expression_array.h b/src/mongo/db/matcher/expression_array.h
index 5c37046a201..39ccc69acff 100644
--- a/src/mongo/db/matcher/expression_array.h
+++ b/src/mongo/db/matcher/expression_array.h
@@ -90,7 +90,8 @@ public:
virtual void debugString(StringBuilder& debug, int indentationLevel) const;
- BSONObj getSerializedRightHandSide() const final;
+ BSONObj getSerializedRightHandSide(
+ boost::optional<StringData> replacementForLiteralArgs) const final;
std::vector<std::unique_ptr<MatchExpression>>* getChildVector() final {
return nullptr;
@@ -158,7 +159,8 @@ public:
virtual void debugString(StringBuilder& debug, int indentationLevel) const;
- BSONObj getSerializedRightHandSide() const final;
+ BSONObj getSerializedRightHandSide(
+ boost::optional<StringData> replacementForLiteralArgs) const final;
std::vector<std::unique_ptr<MatchExpression>>* getChildVector() final {
return &_subs;
@@ -233,7 +235,8 @@ public:
virtual void debugString(StringBuilder& debug, int indentationLevel) const;
- BSONObj getSerializedRightHandSide() const final;
+ BSONObj getSerializedRightHandSide(
+ boost::optional<StringData> replacementForLiteralArgs) const final;
virtual bool equivalent(const MatchExpression* other) const;
diff --git a/src/mongo/db/matcher/expression_expr.cpp b/src/mongo/db/matcher/expression_expr.cpp
index a1d151b8e1c..cd7f27fa9ba 100644
--- a/src/mongo/db/matcher/expression_expr.cpp
+++ b/src/mongo/db/matcher/expression_expr.cpp
@@ -77,7 +77,8 @@ Value ExprMatchExpression::evaluateExpression(const MatchableDocument* doc) cons
return _expression->evaluate(document, &variables);
}
-void ExprMatchExpression::serialize(BSONObjBuilder* out, bool includePath) const {
+void ExprMatchExpression::serialize(BSONObjBuilder* out, SerializationOptions opts) const {
+ // TODO aggregation expressions should support some of the new options.
*out << "$expr" << _expression->serialize(false);
}
diff --git a/src/mongo/db/matcher/expression_expr.h b/src/mongo/db/matcher/expression_expr.h
index d6966479df4..51a1c273c59 100644
--- a/src/mongo/db/matcher/expression_expr.h
+++ b/src/mongo/db/matcher/expression_expr.h
@@ -75,7 +75,7 @@ public:
debug << "$expr " << _expression->serialize(false).toString();
}
- void serialize(BSONObjBuilder* out, bool includePath) const final;
+ void serialize(BSONObjBuilder* out, SerializationOptions opts) const final;
bool isTriviallyTrue() const final;
diff --git a/src/mongo/db/matcher/expression_expr_test.cpp b/src/mongo/db/matcher/expression_expr_test.cpp
index 01bd01818b0..6401357cc61 100644
--- a/src/mongo/db/matcher/expression_expr_test.cpp
+++ b/src/mongo/db/matcher/expression_expr_test.cpp
@@ -724,17 +724,10 @@ TEST(ExprMatchTest, OptimizingExprAbsorbsAndOfAnd) {
// The optimized match expression should not have and AND children of AND nodes. This should be
// collapsed during optimization.
- BSONObj serialized;
- {
- BSONObjBuilder builder;
- optimized->serialize(&builder, true);
- serialized = builder.obj();
- }
-
BSONObj expectedSerialization = fromjson(
"{$and: [{$expr: {$and: [{$eq: ['$a', {$const: 1}]}, {$eq: ['$b', {$const: 2}]}]}},"
"{a: {$_internalExprEq: 1}}, {b: {$_internalExprEq: 2}}]}");
- ASSERT_BSONOBJ_EQ(serialized, expectedSerialization);
+ ASSERT_BSONOBJ_EQ(optimized->serialize(), expectedSerialization);
}
TEST(ExprMatchTest, OptimizingExprRemovesTrueConstantExpression) {
diff --git a/src/mongo/db/matcher/expression_geo.cpp b/src/mongo/db/matcher/expression_geo.cpp
index 68b9af7fa55..437cfd2b32f 100644
--- a/src/mongo/db/matcher/expression_geo.cpp
+++ b/src/mongo/db/matcher/expression_geo.cpp
@@ -434,7 +434,7 @@ void GeoMatchExpression::debugString(StringBuilder& debug, int indentationLevel)
_debugAddSpace(debug, indentationLevel);
BSONObjBuilder builder;
- serialize(&builder, true);
+ serialize(&builder, {});
debug << "GEO raw = " << builder.obj().toString();
MatchExpression::TagData* td = getTag();
@@ -445,8 +445,11 @@ void GeoMatchExpression::debugString(StringBuilder& debug, int indentationLevel)
debug << "\n";
}
-BSONObj GeoMatchExpression::getSerializedRightHandSide() const {
+BSONObj GeoMatchExpression::getSerializedRightHandSide(
+ boost::optional<StringData> replacementForLiteralArgs) const {
BSONObjBuilder subobj;
+ // TODO SERVER-73672 looks like we'll need to traverse '_rawObj' if 'replacementForLiteralArgs'
+ // is set.
subobj.appendElements(_rawObj);
return subobj.obj();
}
@@ -503,7 +506,10 @@ void GeoNearMatchExpression::debugString(StringBuilder& debug, int indentationLe
debug << "\n";
}
-BSONObj GeoNearMatchExpression::getSerializedRightHandSide() const {
+BSONObj GeoNearMatchExpression::getSerializedRightHandSide(
+ boost::optional<StringData> replacementForLiteralArgs) const {
+ // TODO SERVER-73672 looks like we'll need to traverse '_rawObj' if 'replacementForLiteralArgs'
+ // is set.
BSONObjBuilder objBuilder;
objBuilder.appendElements(_rawObj);
return objBuilder.obj();
diff --git a/src/mongo/db/matcher/expression_geo.h b/src/mongo/db/matcher/expression_geo.h
index f2a15c2db89..01968db7fd4 100644
--- a/src/mongo/db/matcher/expression_geo.h
+++ b/src/mongo/db/matcher/expression_geo.h
@@ -106,7 +106,8 @@ public:
virtual void debugString(StringBuilder& debug, int indentationLevel = 0) const;
- BSONObj getSerializedRightHandSide() const final;
+ BSONObj getSerializedRightHandSide(
+ boost::optional<StringData> replacementForLiteralArgs) const final;
virtual bool equivalent(const MatchExpression* other) const;
@@ -208,7 +209,8 @@ public:
virtual void debugString(StringBuilder& debug, int indentationLevel = 0) const;
- BSONObj getSerializedRightHandSide() const final;
+ BSONObj getSerializedRightHandSide(
+ boost::optional<StringData> replacementForLiteralArgs) const final;
virtual bool equivalent(const MatchExpression* other) const;
@@ -248,7 +250,7 @@ public:
TwoDPtInAnnulusExpression(const R2Annulus& annulus, boost::optional<StringData> twoDPath)
: LeafMatchExpression(INTERNAL_2D_POINT_IN_ANNULUS, twoDPath), _annulus(annulus) {}
- void serialize(BSONObjBuilder* out, bool includePath) const final {
+ void serialize(BSONObjBuilder* out, SerializationOptions opts) const final {
out->append("TwoDPtInAnnulusExpression", true);
}
@@ -267,7 +269,9 @@ public:
// These won't be called.
//
- BSONObj getSerializedRightHandSide() const final {
+ BSONObj getSerializedRightHandSide(
+ boost::optional<StringData> replacementForLiteralArgs) const final {
+ // TODO SERVER-73676 is this going to be a problem? I don't see how this is unreachable...
MONGO_UNREACHABLE;
}
diff --git a/src/mongo/db/matcher/expression_internal_bucket_geo_within.cpp b/src/mongo/db/matcher/expression_internal_bucket_geo_within.cpp
index 5bf75f8218f..860384bf102 100644
--- a/src/mongo/db/matcher/expression_internal_bucket_geo_within.cpp
+++ b/src/mongo/db/matcher/expression_internal_bucket_geo_within.cpp
@@ -51,7 +51,7 @@ void InternalBucketGeoWithinMatchExpression::debugString(StringBuilder& debug,
_debugAddSpace(debug, indentationLevel);
BSONObjBuilder builder;
- serialize(&builder, true);
+ serialize(&builder, {});
debug << builder.obj().toString() << "\n";
const auto* tag = getTag();
@@ -194,7 +194,8 @@ bool InternalBucketGeoWithinMatchExpression::_matchesBSONObj(const BSONObj& obj)
}
void InternalBucketGeoWithinMatchExpression::serialize(BSONObjBuilder* builder,
- bool includePath) const {
+ SerializationOptions opts) const {
+ // TODO SERVER-73676 respect 'opts'.
BSONObjBuilder bob(builder->subobjStart(InternalBucketGeoWithinMatchExpression::kName));
BSONObjBuilder withinRegionBob(
bob.subobjStart(InternalBucketGeoWithinMatchExpression::kWithinRegion));
diff --git a/src/mongo/db/matcher/expression_internal_bucket_geo_within.h b/src/mongo/db/matcher/expression_internal_bucket_geo_within.h
index 2f0986a7008..5c7f32ef77f 100644
--- a/src/mongo/db/matcher/expression_internal_bucket_geo_within.h
+++ b/src/mongo/db/matcher/expression_internal_bucket_geo_within.h
@@ -94,7 +94,7 @@ public:
return false;
}
- void serialize(BSONObjBuilder* builder, bool includePath) const final;
+ void serialize(BSONObjBuilder* builder, SerializationOptions opts) const final;
std::unique_ptr<MatchExpression> shallowClone() const final;
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 d1be964fc7c..445f49c9f12 100644
--- a/src/mongo/db/matcher/expression_internal_expr_eq_test.cpp
+++ b/src/mongo/db/matcher/expression_internal_expr_eq_test.cpp
@@ -264,7 +264,7 @@ TEST(InternalExprEqMatchExpression, SerializesCorrectly) {
operand.firstElement());
BSONObjBuilder bob;
- eq.serialize(&bob, true);
+ eq.serialize(&bob, {});
ASSERT_BSONOBJ_EQ(BSON("x" << BSON("$_internalExprEq" << 5)), bob.obj());
}
diff --git a/src/mongo/db/matcher/expression_leaf.cpp b/src/mongo/db/matcher/expression_leaf.cpp
index 9ee94f9a7c3..7e0bd6f2791 100644
--- a/src/mongo/db/matcher/expression_leaf.cpp
+++ b/src/mongo/db/matcher/expression_leaf.cpp
@@ -94,8 +94,13 @@ void ComparisonMatchExpressionBase::debugString(StringBuilder& debug, int indent
debug << "\n";
}
-BSONObj ComparisonMatchExpressionBase::getSerializedRightHandSide() const {
- return BSON(name() << _rhs);
+BSONObj ComparisonMatchExpressionBase::getSerializedRightHandSide(
+ boost::optional<StringData> replacementForLiteralArgs) const {
+ if (replacementForLiteralArgs) {
+ return BSON(name() << *replacementForLiteralArgs);
+ } else {
+ return BSON(name() << _rhs);
+ }
}
ComparisonMatchExpression::ComparisonMatchExpression(MatchType type,
@@ -285,12 +290,13 @@ void RegexMatchExpression::debugString(StringBuilder& debug, int indentationLeve
debug << "\n";
}
-BSONObj RegexMatchExpression::getSerializedRightHandSide() const {
+BSONObj RegexMatchExpression::getSerializedRightHandSide(
+ boost::optional<StringData> replacementForLiteralArgs) const {
BSONObjBuilder regexBuilder;
- regexBuilder.append("$regex", _regex);
+ regexBuilder.append("$regex", replacementForLiteralArgs.value_or(_regex));
if (!_flags.empty()) {
- regexBuilder.append("$options", _flags);
+ regexBuilder.append("$options", replacementForLiteralArgs.value_or(_flags));
}
return regexBuilder.obj();
@@ -366,8 +372,13 @@ void ModMatchExpression::debugString(StringBuilder& debug, int indentationLevel)
debug << "\n";
}
-BSONObj ModMatchExpression::getSerializedRightHandSide() const {
- return BSON("$mod" << BSON_ARRAY(_divisor << _remainder));
+BSONObj ModMatchExpression::getSerializedRightHandSide(
+ boost::optional<StringData> replacementForLiteralArgs) const {
+ if (auto str = replacementForLiteralArgs) {
+ return BSON("$mod" << *str);
+ } else {
+ return BSON("$mod" << BSON_ARRAY(_divisor << _remainder));
+ }
}
bool ModMatchExpression::equivalent(const MatchExpression* other) const {
@@ -402,8 +413,13 @@ void ExistsMatchExpression::debugString(StringBuilder& debug, int indentationLev
debug << "\n";
}
-BSONObj ExistsMatchExpression::getSerializedRightHandSide() const {
- return BSON("$exists" << true);
+BSONObj ExistsMatchExpression::getSerializedRightHandSide(
+ boost::optional<StringData> replacementForLiteralArgs) const {
+ if (replacementForLiteralArgs) {
+ return BSON("$exists" << *replacementForLiteralArgs);
+ } else {
+ return BSON("$exists" << true);
+ }
}
bool ExistsMatchExpression::equivalent(const MatchExpression* other) const {
@@ -487,7 +503,13 @@ void InMatchExpression::debugString(StringBuilder& debug, int indentationLevel)
debug << "\n";
}
-BSONObj InMatchExpression::getSerializedRightHandSide() const {
+BSONObj InMatchExpression::getSerializedRightHandSide(
+ boost::optional<StringData> replacementForLiteralArgs) const {
+ if (replacementForLiteralArgs) {
+ // In this case, treat an '$in' with any number of arguments as equivalent.
+ return BSON("$in" << BSON_ARRAY(*replacementForLiteralArgs));
+ }
+
BSONObjBuilder inBob;
BSONArrayBuilder arrBob(inBob.subarrayStart("$in"));
for (auto&& _equality : _equalitySet) {
@@ -841,7 +863,8 @@ void BitTestMatchExpression::debugString(StringBuilder& debug, int indentationLe
}
}
-BSONObj BitTestMatchExpression::getSerializedRightHandSide() const {
+BSONObj BitTestMatchExpression::getSerializedRightHandSide(
+ boost::optional<StringData> replacementForLiteralArgs) const {
std::string opString = "";
switch (matchType()) {
@@ -861,6 +884,10 @@ BSONObj BitTestMatchExpression::getSerializedRightHandSide() const {
MONGO_UNREACHABLE;
}
+ if (replacementForLiteralArgs) {
+ return BSON(opString << *replacementForLiteralArgs);
+ }
+
BSONArrayBuilder arrBob;
for (auto bitPosition : _bitPositions) {
arrBob.append(static_cast<int32_t>(bitPosition));
diff --git a/src/mongo/db/matcher/expression_leaf.h b/src/mongo/db/matcher/expression_leaf.h
index 64c20b4b2fe..f18c55e9cc3 100644
--- a/src/mongo/db/matcher/expression_leaf.h
+++ b/src/mongo/db/matcher/expression_leaf.h
@@ -161,7 +161,8 @@ public:
virtual void debugString(StringBuilder& debug, int indentationLevel = 0) const;
- BSONObj getSerializedRightHandSide() const final;
+ BSONObj getSerializedRightHandSide(
+ boost::optional<StringData> replacementForLiteralArgs) const final;
virtual bool equivalent(const MatchExpression* other) const;
@@ -506,7 +507,8 @@ public:
virtual void debugString(StringBuilder& debug, int indentationLevel) const;
- BSONObj getSerializedRightHandSide() const final;
+ BSONObj getSerializedRightHandSide(
+ boost::optional<StringData> replacementForLiteralArgs) const final;
void serializeToBSONTypeRegex(BSONObjBuilder* out) const;
@@ -586,7 +588,8 @@ public:
virtual void debugString(StringBuilder& debug, int indentationLevel) const;
- BSONObj getSerializedRightHandSide() const final;
+ BSONObj getSerializedRightHandSide(
+ boost::optional<StringData> replacementForLiteralArgs) const final;
virtual bool equivalent(const MatchExpression* other) const;
@@ -651,7 +654,8 @@ public:
virtual void debugString(StringBuilder& debug, int indentationLevel) const;
- BSONObj getSerializedRightHandSide() const final;
+ BSONObj getSerializedRightHandSide(
+ boost::optional<StringData> replacementForLiteralArgs) const final;
virtual bool equivalent(const MatchExpression* other) const;
@@ -683,7 +687,8 @@ public:
virtual void debugString(StringBuilder& debug, int indentationLevel) const;
- BSONObj getSerializedRightHandSide() const final;
+ BSONObj getSerializedRightHandSide(
+ boost::optional<StringData> replacementForLiteralArgs) const final;
virtual bool equivalent(const MatchExpression* other) const;
@@ -826,7 +831,8 @@ public:
virtual void debugString(StringBuilder& debug, int indentationLevel) const;
- BSONObj getSerializedRightHandSide() const final;
+ BSONObj getSerializedRightHandSide(
+ boost::optional<StringData> replacementForLiteralArgs) const final;
virtual bool equivalent(const MatchExpression* other) const;
diff --git a/src/mongo/db/matcher/expression_optimize_test.cpp b/src/mongo/db/matcher/expression_optimize_test.cpp
index 204a2ed10ad..1cc91d127e4 100644
--- a/src/mongo/db/matcher/expression_optimize_test.cpp
+++ b/src/mongo/db/matcher/expression_optimize_test.cpp
@@ -351,18 +351,14 @@ TEST(ExpressionOptimizeTest, AndWithAlwaysFalseChildOptimizesToAlwaysFalse) {
BSONObj obj = fromjson("{$and: [{a: 1}, {$alwaysFalse: 1}]}");
std::unique_ptr<MatchExpression> matchExpression(parseMatchExpression(obj));
auto optimizedMatchExpression = MatchExpression::optimize(std::move(matchExpression));
- BSONObjBuilder bob;
- optimizedMatchExpression->serialize(&bob, true);
- ASSERT_BSONOBJ_EQ(bob.obj(), fromjson("{$alwaysFalse: 1}"));
+ ASSERT_BSONOBJ_EQ(optimizedMatchExpression->serialize(), fromjson("{$alwaysFalse: 1}"));
}
TEST(ExpressionOptimizeTest, AndRemovesAlwaysTrueChildren) {
BSONObj obj = fromjson("{$and: [{a: 1}, {$alwaysTrue: 1}]}");
std::unique_ptr<MatchExpression> matchExpression(parseMatchExpression(obj));
auto optimizedMatchExpression = MatchExpression::optimize(std::move(matchExpression));
- BSONObjBuilder bob;
- optimizedMatchExpression->serialize(&bob, true);
- ASSERT_BSONOBJ_EQ(bob.obj(), fromjson("{a: {$eq: 1}}"));
+ ASSERT_BSONOBJ_EQ(optimizedMatchExpression->serialize(), fromjson("{a: {$eq: 1}}"));
}
TEST(ExpressionOptimizeTest, AndWithSingleChildAlwaysTrueOptimizesToEmptyAnd) {
@@ -371,9 +367,7 @@ TEST(ExpressionOptimizeTest, AndWithSingleChildAlwaysTrueOptimizesToEmptyAnd) {
auto optimizedMatchExpression = MatchExpression::optimize(std::move(matchExpression));
// TODO SERVER-34759 We want this to optimize to an AlwaysTrueMatchExpression.
ASSERT_TRUE(dynamic_cast<AndMatchExpression*>(optimizedMatchExpression.get()));
- BSONObjBuilder bob;
- optimizedMatchExpression->serialize(&bob, true);
- ASSERT_BSONOBJ_EQ(bob.obj(), fromjson("{}"));
+ ASSERT_BSONOBJ_EQ(optimizedMatchExpression->serialize(), fromjson("{}"));
}
TEST(ExpressionOptimizeTest, AndWithEachChildAlwaysTrueOptimizesToEmptyAnd) {
@@ -382,36 +376,28 @@ TEST(ExpressionOptimizeTest, AndWithEachChildAlwaysTrueOptimizesToEmptyAnd) {
auto optimizedMatchExpression = MatchExpression::optimize(std::move(matchExpression));
// TODO SERVER-34759 We want this to optimize to an AlwaysTrueMatchExpression.
ASSERT_TRUE(dynamic_cast<AndMatchExpression*>(optimizedMatchExpression.get()));
- BSONObjBuilder bob;
- optimizedMatchExpression->serialize(&bob, true);
- ASSERT_BSONOBJ_EQ(bob.obj(), fromjson("{}"));
+ ASSERT_BSONOBJ_EQ(optimizedMatchExpression->serialize(), fromjson("{}"));
}
TEST(ExpressionOptimizeTest, NestedAndWithAlwaysFalseOptimizesToAlwaysFalse) {
BSONObj obj = fromjson("{$and: [{$and: [{$alwaysFalse: 1}, {a: 1}]}, {b: 1}]}");
std::unique_ptr<MatchExpression> matchExpression(parseMatchExpression(obj));
auto optimizedMatchExpression = MatchExpression::optimize(std::move(matchExpression));
- BSONObjBuilder bob;
- optimizedMatchExpression->serialize(&bob, true);
- ASSERT_BSONOBJ_EQ(bob.obj(), fromjson("{$alwaysFalse: 1}"));
+ ASSERT_BSONOBJ_EQ(optimizedMatchExpression->serialize(), fromjson("{$alwaysFalse: 1}"));
}
TEST(ExpressionOptimizeTest, OrWithAlwaysTrueOptimizesToAlwaysTrue) {
BSONObj obj = fromjson("{$or: [{a: 1}, {$alwaysTrue: 1}]}");
std::unique_ptr<MatchExpression> matchExpression(parseMatchExpression(obj));
auto optimizedMatchExpression = MatchExpression::optimize(std::move(matchExpression));
- BSONObjBuilder bob;
- optimizedMatchExpression->serialize(&bob, true);
- ASSERT_BSONOBJ_EQ(bob.obj(), fromjson("{$alwaysTrue: 1}"));
+ ASSERT_BSONOBJ_EQ(optimizedMatchExpression->serialize(), fromjson("{$alwaysTrue: 1}"));
}
TEST(ExpressionOptimizeTest, OrRemovesAlwaysFalseChildren) {
BSONObj obj = fromjson("{$or: [{a: 1}, {$alwaysFalse: 1}]}");
std::unique_ptr<MatchExpression> matchExpression(parseMatchExpression(obj));
auto optimizedMatchExpression = MatchExpression::optimize(std::move(matchExpression));
- BSONObjBuilder bob;
- optimizedMatchExpression->serialize(&bob, true);
- ASSERT_BSONOBJ_EQ(bob.obj(), fromjson("{a: {$eq: 1}}"));
+ ASSERT_BSONOBJ_EQ(optimizedMatchExpression->serialize(), fromjson("{a: {$eq: 1}}"));
}
TEST(ExpressionOptimizeTest, OrPromotesSingleAlwaysFalseAfterOptimize) {
@@ -420,9 +406,7 @@ TEST(ExpressionOptimizeTest, OrPromotesSingleAlwaysFalseAfterOptimize) {
std::unique_ptr<MatchExpression> matchExpression(parseMatchExpression(obj));
auto optimizedMatchExpression = MatchExpression::optimize(std::move(matchExpression));
ASSERT_TRUE(dynamic_cast<AlwaysFalseMatchExpression*>(optimizedMatchExpression.get()));
- BSONObjBuilder bob;
- optimizedMatchExpression->serialize(&bob, true);
- ASSERT_BSONOBJ_EQ(bob.obj(), fromjson("{$alwaysFalse: 1}"));
+ ASSERT_BSONOBJ_EQ(optimizedMatchExpression->serialize(), fromjson("{$alwaysFalse: 1}"));
}
TEST(ExpressionOptimizeTest, OrPromotesSingleAlwaysFalse) {
@@ -430,9 +414,7 @@ TEST(ExpressionOptimizeTest, OrPromotesSingleAlwaysFalse) {
std::unique_ptr<MatchExpression> matchExpression(parseMatchExpression(obj));
auto optimizedMatchExpression = MatchExpression::optimize(std::move(matchExpression));
ASSERT_TRUE(dynamic_cast<AlwaysFalseMatchExpression*>(optimizedMatchExpression.get()));
- BSONObjBuilder bob;
- optimizedMatchExpression->serialize(&bob, true);
- ASSERT_BSONOBJ_EQ(bob.obj(), fromjson("{$alwaysFalse: 1}"));
+ ASSERT_BSONOBJ_EQ(optimizedMatchExpression->serialize(), fromjson("{$alwaysFalse: 1}"));
}
TEST(ExpressionOptimizeTest, OrPromotesMultipleAlwaysFalse) {
@@ -440,18 +422,14 @@ TEST(ExpressionOptimizeTest, OrPromotesMultipleAlwaysFalse) {
std::unique_ptr<MatchExpression> matchExpression(parseMatchExpression(obj));
auto optimizedMatchExpression = MatchExpression::optimize(std::move(matchExpression));
ASSERT_TRUE(dynamic_cast<AlwaysFalseMatchExpression*>(optimizedMatchExpression.get()));
- BSONObjBuilder bob;
- optimizedMatchExpression->serialize(&bob, true);
- ASSERT_BSONOBJ_EQ(bob.obj(), fromjson("{$alwaysFalse: 1}"));
+ ASSERT_BSONOBJ_EQ(optimizedMatchExpression->serialize(), fromjson("{$alwaysFalse: 1}"));
}
TEST(ExpressionOptimizeTest, NestedOrWithAlwaysTrueOptimizesToAlwaysTrue) {
BSONObj obj = fromjson("{$or: [{$or: [{$alwaysTrue: 1}, {a: 1}]}, {b: 1}]}");
std::unique_ptr<MatchExpression> matchExpression(parseMatchExpression(obj));
auto optimizedMatchExpression = MatchExpression::optimize(std::move(matchExpression));
- BSONObjBuilder bob;
- optimizedMatchExpression->serialize(&bob, true);
- ASSERT_BSONOBJ_EQ(bob.obj(), fromjson("{$alwaysTrue: 1}"));
+ ASSERT_BSONOBJ_EQ(optimizedMatchExpression->serialize(), fromjson("{$alwaysTrue: 1}"));
}
TEST(ExpressionOptimizeTest, OrRewrittenToIn) {
@@ -478,9 +456,7 @@ TEST(ExpressionOptimizeTest, OrRewrittenToIn) {
auto obj = fromjson(exprStr);
std::unique_ptr<MatchExpression> matchExpression(parseMatchExpression(obj));
auto optimizedMatchExpression = MatchExpression::optimize(std::move(matchExpression));
- BSONObjBuilder bob;
- optimizedMatchExpression->serialize(&bob, true);
- return bob.obj();
+ return optimizedMatchExpression->serialize();
};
ASSERT_BSONOBJ_EQ(optimizeExpr(queries[0].first), fromjson(queries[0].second));
diff --git a/src/mongo/db/matcher/expression_path.h b/src/mongo/db/matcher/expression_path.h
index 99387a2c080..14a1fbf2edb 100644
--- a/src/mongo/db/matcher/expression_path.h
+++ b/src/mongo/db/matcher/expression_path.h
@@ -143,11 +143,17 @@ public:
}
}
- void serialize(BSONObjBuilder* out, bool includePath) const override {
- if (includePath) {
- out->append(path(), getSerializedRightHandSide());
+ void serialize(BSONObjBuilder* out, SerializationOptions opts) const override {
+ // TODO SERVER-73678 do we need to pass 'includePath' or other options to
+ // `getSerializedRightHandSide()` here? I don't think we need 'includePath' for
+ // LeafMatchExpression subclasses, but the class comment on PathMatchExpression leaves me a
+ // bit confused over 'includePath' semantics here. Before we changed anything for query
+ // shape, it looks like 'includePath' was not forwarded through, so it's either not needed
+ // or there was a pre-existing bug.
+ if (opts.includePath) {
+ out->append(path(), getSerializedRightHandSide(opts.replacementForLiteralArgs));
} else {
- out->appendElements(getSerializedRightHandSide());
+ out->appendElements(getSerializedRightHandSide(opts.replacementForLiteralArgs));
}
}
@@ -156,8 +162,13 @@ public:
* serialization of PathMatchExpression in cases where we do not want to serialize the path in
* line with the expression. For example {x: {$not: {$eq: 1}}}, where $eq is the
* PathMatchExpression.
+ *
+ * If the 'replacementForLiteralArgs' option is set, then any literal argument (like the number
+ * 1 in the example above), should be replaced with this string. 'literal' here is in contrast
+ * to another expression, if that is possible syntactically.
*/
- virtual BSONObj getSerializedRightHandSide() const = 0;
+ virtual BSONObj getSerializedRightHandSide(
+ boost::optional<StringData> replacementForLiteralArgs = boost::none) const = 0;
private:
// ElementPath holds a FieldRef, which owns the underlying path string.
diff --git a/src/mongo/db/matcher/expression_serialization_test.cpp b/src/mongo/db/matcher/expression_serialization_test.cpp
index 78517a52634..214c0d31ffb 100644
--- a/src/mongo/db/matcher/expression_serialization_test.cpp
+++ b/src/mongo/db/matcher/expression_serialization_test.cpp
@@ -49,9 +49,7 @@ using std::string;
using std::unique_ptr;
BSONObj serialize(MatchExpression* match) {
- BSONObjBuilder bob;
- match->serialize(&bob, true);
- return bob.obj();
+ return match->serialize();
}
TEST(SerializeBasic, AndExpressionWithOneChildSerializesCorrectly) {
diff --git a/src/mongo/db/matcher/expression_text_base.cpp b/src/mongo/db/matcher/expression_text_base.cpp
index 56ad05af3d3..68af83a2932 100644
--- a/src/mongo/db/matcher/expression_text_base.cpp
+++ b/src/mongo/db/matcher/expression_text_base.cpp
@@ -56,7 +56,8 @@ void TextMatchExpressionBase::debugString(StringBuilder& debug, int indentationL
debug << "\n";
}
-void TextMatchExpressionBase::serialize(BSONObjBuilder* out, bool includePath) const {
+void TextMatchExpressionBase::serialize(BSONObjBuilder* out, SerializationOptions opts) const {
+ // TODO support 'opts'
const fts::FTSQuery& ftsQuery = getFTSQuery();
out->append("$text",
BSON("$search" << ftsQuery.getQuery() << "$language" << ftsQuery.getLanguage()
diff --git a/src/mongo/db/matcher/expression_text_base.h b/src/mongo/db/matcher/expression_text_base.h
index 22fc581a2e2..22f15b23bea 100644
--- a/src/mongo/db/matcher/expression_text_base.h
+++ b/src/mongo/db/matcher/expression_text_base.h
@@ -60,7 +60,9 @@ public:
*/
virtual const fts::FTSQuery& getFTSQuery() const = 0;
- BSONObj getSerializedRightHandSide() const final {
+ BSONObj getSerializedRightHandSide(
+ boost::optional<StringData> replacementForLiteralArgs) const final {
+ // TODO SERVER-73676 is this going to be a problem? I don't see how this is unreachable...
MONGO_UNREACHABLE;
}
@@ -70,7 +72,7 @@ public:
void debugString(StringBuilder& debug, int indentationLevel = 0) const final;
- void serialize(BSONObjBuilder* out, bool includePath) const final;
+ void serialize(BSONObjBuilder* out, SerializationOptions opts) const final;
bool equivalent(const MatchExpression* other) const final;
diff --git a/src/mongo/db/matcher/expression_tree.cpp b/src/mongo/db/matcher/expression_tree.cpp
index df70ad278c3..33f8ac9f07a 100644
--- a/src/mongo/db/matcher/expression_tree.cpp
+++ b/src/mongo/db/matcher/expression_tree.cpp
@@ -46,10 +46,10 @@ void ListOfMatchExpression::_debugList(StringBuilder& debug, int indentationLeve
_expressions[i]->debugString(debug, indentationLevel + 1);
}
-void ListOfMatchExpression::_listToBSON(BSONArrayBuilder* out, bool includePath) const {
+void ListOfMatchExpression::_listToBSON(BSONArrayBuilder* out, SerializationOptions opts) const {
for (unsigned i = 0; i < _expressions.size(); i++) {
BSONObjBuilder childBob(out->subobjStart());
- _expressions[i]->serialize(&childBob, includePath);
+ _expressions[i]->serialize(&childBob, opts);
}
out->doneFast();
}
@@ -341,15 +341,16 @@ void AndMatchExpression::debugString(StringBuilder& debug, int indentationLevel)
_debugList(debug, indentationLevel);
}
-void AndMatchExpression::serialize(BSONObjBuilder* out, bool includePath) const {
+void AndMatchExpression::serialize(BSONObjBuilder* out, SerializationOptions opts) 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.
return;
}
+ // TODO SERVER-73673 this looks correct - just need to test.
BSONArrayBuilder arrBob(out->subarrayStart("$and"));
- _listToBSON(&arrBob, includePath);
+ _listToBSON(&arrBob, opts);
arrBob.doneFast();
}
@@ -385,7 +386,7 @@ void OrMatchExpression::debugString(StringBuilder& debug, int indentationLevel)
_debugList(debug, indentationLevel);
}
-void OrMatchExpression::serialize(BSONObjBuilder* out, bool includePath) const {
+void OrMatchExpression::serialize(BSONObjBuilder* out, SerializationOptions opts) 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
@@ -394,7 +395,7 @@ void OrMatchExpression::serialize(BSONObjBuilder* out, bool includePath) const {
return;
}
BSONArrayBuilder arrBob(out->subarrayStart("$or"));
- _listToBSON(&arrBob, includePath);
+ _listToBSON(&arrBob, opts);
}
bool OrMatchExpression::isTriviallyFalse() const {
@@ -427,9 +428,9 @@ void NorMatchExpression::debugString(StringBuilder& debug, int indentationLevel)
_debugList(debug, indentationLevel);
}
-void NorMatchExpression::serialize(BSONObjBuilder* out, bool includePath) const {
+void NorMatchExpression::serialize(BSONObjBuilder* out, SerializationOptions opts) const {
BSONArrayBuilder arrBob(out->subarrayStart("$nor"));
- _listToBSON(&arrBob, includePath);
+ _listToBSON(&arrBob, opts);
}
// -------
@@ -442,9 +443,9 @@ void NotMatchExpression::debugString(StringBuilder& debug, int indentationLevel)
void NotMatchExpression::serializeNotExpressionToNor(MatchExpression* exp,
BSONObjBuilder* out,
- bool includePath) {
+ SerializationOptions opts) {
BSONObjBuilder childBob;
- exp->serialize(&childBob, includePath);
+ exp->serialize(&childBob, opts);
BSONObj tempObj = childBob.obj();
BSONArrayBuilder tBob(out->subarrayStart("$nor"));
@@ -452,23 +453,24 @@ void NotMatchExpression::serializeNotExpressionToNor(MatchExpression* exp,
tBob.doneFast();
}
-void NotMatchExpression::serialize(BSONObjBuilder* out, bool includePath) const {
+void NotMatchExpression::serialize(BSONObjBuilder* out, SerializationOptions opts) const {
+ // TODO SERVER-73676 respect 'opts.'
if (_exp->matchType() == MatchType::AND && _exp->numChildren() == 0) {
out->append("$alwaysFalse", 1);
return;
}
- if (!includePath) {
+ if (!opts.includePath) {
BSONObjBuilder notBob(out->subobjStart("$not"));
// Our parser does not accept a $and directly within a $not, instead expecting the direct
// notation like {x: {$not: {$gt: 5, $lt: 0}}}. We represent such an expression with an AND
// 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);
}
} else {
- _exp->serialize(&notBob, includePath);
+ _exp->serialize(&notBob, opts);
}
return;
}
@@ -491,10 +493,11 @@ void NotMatchExpression::serialize(BSONObjBuilder* out, bool includePath) const
pathMatch && !dynamic_cast<TextMatchExpressionBase*>(expressionToNegate)) {
const auto path = pathMatch->path();
BSONObjBuilder pathBob(out->subobjStart(path));
- pathBob.append("$not", pathMatch->getSerializedRightHandSide());
+ pathBob.append("$not",
+ pathMatch->getSerializedRightHandSide(opts.replacementForLiteralArgs));
return;
}
- return serializeNotExpressionToNor(expressionToNegate, out, includePath);
+ return serializeNotExpressionToNor(expressionToNegate, out, opts);
}
bool NotMatchExpression::equivalent(const MatchExpression* other) const {
diff --git a/src/mongo/db/matcher/expression_tree.h b/src/mongo/db/matcher/expression_tree.h
index f5ab4bb5a8e..252538b0ea6 100644
--- a/src/mongo/db/matcher/expression_tree.h
+++ b/src/mongo/db/matcher/expression_tree.h
@@ -99,7 +99,7 @@ public:
protected:
void _debugList(StringBuilder& debug, int indentationLevel) const;
- void _listToBSON(BSONArrayBuilder* out, bool includePath) const;
+ void _listToBSON(BSONArrayBuilder* out, SerializationOptions opts) const;
private:
ExpressionOptimizerFunc getOptimizer() const final;
@@ -138,7 +138,7 @@ public:
virtual void debugString(StringBuilder& debug, int indentationLevel = 0) const;
- virtual void serialize(BSONObjBuilder* out, bool includePath) const;
+ virtual void serialize(BSONObjBuilder* out, SerializationOptions opts) const;
bool isTriviallyTrue() const final;
@@ -182,7 +182,7 @@ public:
virtual void debugString(StringBuilder& debug, int indentationLevel = 0) const;
- virtual void serialize(BSONObjBuilder* out, bool includePath) const;
+ virtual void serialize(BSONObjBuilder* out, SerializationOptions opts) const;
bool isTriviallyFalse() const final;
@@ -226,7 +226,7 @@ public:
virtual void debugString(StringBuilder& debug, int indentationLevel = 0) const;
- virtual void serialize(BSONObjBuilder* out, bool includePath) const;
+ virtual void serialize(BSONObjBuilder* out, SerializationOptions opts) const;
void acceptVisitor(MatchExpressionMutableVisitor* visitor) final {
visitor->visit(this);
@@ -267,7 +267,7 @@ public:
virtual void debugString(StringBuilder& debug, int indentationLevel = 0) const;
- virtual void serialize(BSONObjBuilder* out, bool includePath) const;
+ virtual void serialize(BSONObjBuilder* out, SerializationOptions opts) const;
bool equivalent(const MatchExpression* other) const final;
@@ -309,7 +309,7 @@ public:
private:
static void serializeNotExpressionToNor(MatchExpression* exp,
BSONObjBuilder* out,
- bool includePath);
+ SerializationOptions opts);
ExpressionOptimizerFunc getOptimizer() const final;
diff --git a/src/mongo/db/matcher/expression_type.h b/src/mongo/db/matcher/expression_type.h
index 95e1416486e..51b3d4ae507 100644
--- a/src/mongo/db/matcher/expression_type.h
+++ b/src/mongo/db/matcher/expression_type.h
@@ -82,7 +82,9 @@ public:
debug << "\n";
}
- BSONObj getSerializedRightHandSide() const final {
+ BSONObj getSerializedRightHandSide(
+ boost::optional<StringData> replacementForLiteralArgs) const final {
+ // TODO SERVER-73678 respect 'replacementForLiteralArgs'.
BSONObjBuilder subBuilder;
BSONArrayBuilder arrBuilder(subBuilder.subarrayStart(name()));
_typeSet.toBSONArray(&arrBuilder);
@@ -252,7 +254,9 @@ public:
debug << "\n";
}
- BSONObj getSerializedRightHandSide() const final {
+ BSONObj getSerializedRightHandSide(
+ boost::optional<StringData> replacementForLiteralArgs) const final {
+ // TODO SERVER-73678 respect 'replacementForLiteralArgs'.
BSONObjBuilder bob;
bob.append(name(), _binDataSubType);
return bob.obj();
diff --git a/src/mongo/db/matcher/expression_where_base.cpp b/src/mongo/db/matcher/expression_where_base.cpp
index 30148f9a42b..17568f76321 100644
--- a/src/mongo/db/matcher/expression_where_base.cpp
+++ b/src/mongo/db/matcher/expression_where_base.cpp
@@ -46,7 +46,8 @@ void WhereMatchExpressionBase::debugString(StringBuilder& debug, int indentation
debug << "code: " << getCode() << "\n";
}
-void WhereMatchExpressionBase::serialize(BSONObjBuilder* out, bool includePath) const {
+void WhereMatchExpressionBase::serialize(BSONObjBuilder* out, SerializationOptions opts) const {
+ // TODO SERVER-73676 respect 'opts.'
out->appendCode("$where", getCode());
}
diff --git a/src/mongo/db/matcher/expression_where_base.h b/src/mongo/db/matcher/expression_where_base.h
index 3f5099d7180..6e029c6bab6 100644
--- a/src/mongo/db/matcher/expression_where_base.h
+++ b/src/mongo/db/matcher/expression_where_base.h
@@ -69,7 +69,7 @@ public:
void debugString(StringBuilder& debug, int indentationLevel = 0) const final;
- void serialize(BSONObjBuilder* out, bool includePath) const final;
+ void serialize(BSONObjBuilder* out, SerializationOptions opts) const final;
bool equivalent(const MatchExpression* other) const final;
diff --git a/src/mongo/db/matcher/parsed_match_expression_for_test.h b/src/mongo/db/matcher/parsed_match_expression_for_test.h
new file mode 100644
index 00000000000..89e965a45ca
--- /dev/null
+++ b/src/mongo/db/matcher/parsed_match_expression_for_test.h
@@ -0,0 +1,75 @@
+/**
+ * Copyright (C) 2023-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.h"
+#include "mongo/db/pipeline/expression_context_for_test.h"
+#include "mongo/unittest/unittest.h"
+
+namespace mongo {
+
+/**
+ * A MatchExpression may store BSONElements as arguments for expressions, to avoid copying large
+ * values. A BSONElement is essentially a pointer into a BSONObj, so use
+ * ParsedMatchExpressionForTest to ensure that the BSONObj outlives the MatchExpression, and the
+ * BSONElement arguments remain pointing to allocated memory.
+ */
+class ParsedMatchExpressionForTest {
+public:
+ ParsedMatchExpressionForTest(const std::string& str,
+ const CollatorInterface* collator = nullptr)
+ : _obj(fromjson(str)) {
+ _expCtx = make_intrusive<ExpressionContextForTest>();
+ _expCtx->setCollator(CollatorInterface::cloneCollator(collator));
+ StatusWithMatchExpression result = MatchExpressionParser::parse(_obj, _expCtx);
+ ASSERT_OK(result.getStatus());
+ _expr = std::move(result.getValue());
+ }
+
+ const MatchExpression* get() const {
+ return _expr.get();
+ }
+
+ /**
+ * Relinquishes ownership of the parsed expression and returns it as a unique_ptr to the caller.
+ * This 'ParsedMatchExpressionForTest' object still must outlive the returned value so that the
+ * BSONObj used to create it remains alive.
+ */
+ std::unique_ptr<MatchExpression> release() {
+ return std::move(_expr);
+ }
+
+
+private:
+ const BSONObj _obj;
+ std::unique_ptr<MatchExpression> _expr;
+ boost::intrusive_ptr<ExpressionContext> _expCtx;
+};
+} // namespace mongo
diff --git a/src/mongo/db/matcher/rewrite_expr_test.cpp b/src/mongo/db/matcher/rewrite_expr_test.cpp
index 948add02c26..c4f1a4b7619 100644
--- a/src/mongo/db/matcher/rewrite_expr_test.cpp
+++ b/src/mongo/db/matcher/rewrite_expr_test.cpp
@@ -57,7 +57,7 @@ void testExprRewrite(BSONObj expr, BSONObj expectedMatch) {
if (!expectedMatch.isEmpty()) {
ASSERT(result.matchExpression());
BSONObjBuilder bob;
- result.matchExpression()->serialize(&bob, true);
+ result.matchExpression()->serialize(&bob, {});
ASSERT_BSONOBJ_EQ(expectedMatch, bob.obj());
} else {
ASSERT_FALSE(result.matchExpression());
diff --git a/src/mongo/db/matcher/schema/assert_serializes_to.h b/src/mongo/db/matcher/schema/assert_serializes_to.h
index e62b5e62e1d..7f9b3d14292 100644
--- a/src/mongo/db/matcher/schema/assert_serializes_to.h
+++ b/src/mongo/db/matcher/schema/assert_serializes_to.h
@@ -34,11 +34,9 @@ namespace mongo {
/**
* Asserts that the given MatchExpression 'match' serializes to the BSONObj 'expected'.
*/
-#define ASSERT_SERIALIZES_TO(match, expected) \
- do { \
- BSONObjBuilder bob; \
- match->serialize(&bob, true); \
- ASSERT_BSONOBJ_EQ(bob.obj(), expected); \
+#define ASSERT_SERIALIZES_TO(match, expected) \
+ do { \
+ ASSERT_BSONOBJ_EQ(match->serialize(), expected); \
} while (false)
} // namespace mongo
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.cpp b/src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.cpp
index 52a225a2ee7..6fbeb59be73 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.cpp
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.cpp
@@ -76,13 +76,15 @@ void InternalSchemaAllElemMatchFromIndexMatchExpression::debugString(StringBuild
_expression->getFilter()->debugString(debug, indentationLevel + 1);
}
-BSONObj InternalSchemaAllElemMatchFromIndexMatchExpression::getSerializedRightHandSide() const {
+BSONObj InternalSchemaAllElemMatchFromIndexMatchExpression::getSerializedRightHandSide(
+ boost::optional<StringData> replacementForLiteralArgs) const {
+ // TODO SERVER-73678 respect 'replacementForLiteralArgs'.
BSONObjBuilder allElemMatchBob;
BSONArrayBuilder subArray(allElemMatchBob.subarrayStart(kName));
subArray.append(_index);
{
BSONObjBuilder eBuilder(subArray.subobjStart());
- _expression->getFilter()->serialize(&eBuilder, true);
+ _expression->getFilter()->serialize(&eBuilder, {});
eBuilder.doneFast();
}
subArray.doneFast();
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.h b/src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.h
index a73c5a255cb..9ca495448f9 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.h
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.h
@@ -78,7 +78,8 @@ public:
void debugString(StringBuilder& debug, int indentationLevel) const final;
- BSONObj getSerializedRightHandSide() const final;
+ BSONObj getSerializedRightHandSide(
+ boost::optional<StringData> replacementForLiteralArgs) const final;
bool equivalent(const MatchExpression* other) const final;
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_allowed_properties.cpp b/src/mongo/db/matcher/schema/expression_internal_schema_allowed_properties.cpp
index 861bdc2d989..5d86e5571d2 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_allowed_properties.cpp
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_allowed_properties.cpp
@@ -60,7 +60,7 @@ void InternalSchemaAllowedPropertiesMatchExpression::debugString(StringBuilder&
_debugAddSpace(debug, indentationLevel);
BSONObjBuilder builder;
- serialize(&builder, true);
+ serialize(&builder, {});
debug << builder.obj().toString() << "\n";
const auto* tag = getTag();
@@ -129,7 +129,8 @@ bool InternalSchemaAllowedPropertiesMatchExpression::_matchesBSONObj(const BSONO
}
void InternalSchemaAllowedPropertiesMatchExpression::serialize(BSONObjBuilder* builder,
- bool includePath) const {
+ SerializationOptions opts) const {
+ // TODO SERVER-73678 respect 'opts'.
BSONObjBuilder expressionBuilder(
builder->subobjStart(InternalSchemaAllowedPropertiesMatchExpression::kName));
@@ -145,13 +146,13 @@ void InternalSchemaAllowedPropertiesMatchExpression::serialize(BSONObjBuilder* b
itemBuilder.appendRegex("regex", item.first.rawRegex);
BSONObjBuilder subexpressionBuilder(itemBuilder.subobjStart("expression"));
- item.second->getFilter()->serialize(&subexpressionBuilder, includePath);
+ item.second->getFilter()->serialize(&subexpressionBuilder, opts);
subexpressionBuilder.doneFast();
}
patternPropertiesBuilder.doneFast();
BSONObjBuilder otherwiseBuilder(expressionBuilder.subobjStart("otherwise"));
- _otherwise->getFilter()->serialize(&otherwiseBuilder, includePath);
+ _otherwise->getFilter()->serialize(&otherwiseBuilder, opts);
otherwiseBuilder.doneFast();
expressionBuilder.doneFast();
}
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_allowed_properties.h b/src/mongo/db/matcher/schema/expression_internal_schema_allowed_properties.h
index 6c23028eac6..052b10a4b78 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_allowed_properties.h
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_allowed_properties.h
@@ -136,7 +136,7 @@ public:
bool matches(const MatchableDocument* doc, MatchDetails* details) const final;
bool matchesSingleElement(const BSONElement& element, MatchDetails* details) const final;
- void serialize(BSONObjBuilder* builder, bool includePath) const final;
+ void serialize(BSONObjBuilder* builder, SerializationOptions opts) const final;
std::unique_ptr<MatchExpression> shallowClone() const final;
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_eq.cpp b/src/mongo/db/matcher/schema/expression_internal_schema_eq.cpp
index eda67973bed..496dac6a29d 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_eq.cpp
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_eq.cpp
@@ -69,7 +69,9 @@ void InternalSchemaEqMatchExpression::debugString(StringBuilder& debug,
debug << "\n";
}
-BSONObj InternalSchemaEqMatchExpression::getSerializedRightHandSide() const {
+BSONObj InternalSchemaEqMatchExpression::getSerializedRightHandSide(
+ boost::optional<StringData> replacementForLiteralArgs) const {
+ // TODO SERVER-73678 respect 'replacementForLiteralArgs.'
BSONObjBuilder eqObj;
eqObj.appendAs(_rhsElem, kName);
return eqObj.obj();
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_eq.h b/src/mongo/db/matcher/schema/expression_internal_schema_eq.h
index cc30f428c8c..9accba917d0 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_eq.h
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_eq.h
@@ -59,7 +59,8 @@ public:
void debugString(StringBuilder& debug, int indentationLevel) const final;
- BSONObj getSerializedRightHandSide() const final;
+ BSONObj getSerializedRightHandSide(
+ boost::optional<StringData> replacementForLiteralArgs) const final;
bool equivalent(const MatchExpression* other) const final;
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_fmod.cpp b/src/mongo/db/matcher/schema/expression_internal_schema_fmod.cpp
index 8ac81ead25f..887f382a0ad 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_fmod.cpp
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_fmod.cpp
@@ -76,7 +76,9 @@ void InternalSchemaFmodMatchExpression::debugString(StringBuilder& debug,
debug << "\n";
}
-BSONObj InternalSchemaFmodMatchExpression::getSerializedRightHandSide() const {
+BSONObj InternalSchemaFmodMatchExpression::getSerializedRightHandSide(
+ boost::optional<StringData> replacementForLiteralArgs) const {
+ // TODO SERVER-73678 respect 'replacementForLiteralArgs'.
BSONObjBuilder objMatchBob;
BSONArrayBuilder arrBuilder(objMatchBob.subarrayStart("$_internalSchemaFmod"));
arrBuilder.append(_divisor);
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_fmod.h b/src/mongo/db/matcher/schema/expression_internal_schema_fmod.h
index 29edd030372..cc426c810a2 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_fmod.h
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_fmod.h
@@ -58,7 +58,8 @@ public:
void debugString(StringBuilder& debug, int indentationLevel) const final;
- BSONObj getSerializedRightHandSide() const final;
+ BSONObj getSerializedRightHandSide(
+ boost::optional<StringData> replacementForLiteralArgs) const final;
bool equivalent(const MatchExpression* other) const final;
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_match_array_index.cpp b/src/mongo/db/matcher/schema/expression_internal_schema_match_array_index.cpp
index 3e44ce00c01..a3b53e6890f 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_match_array_index.cpp
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_match_array_index.cpp
@@ -51,7 +51,7 @@ void InternalSchemaMatchArrayIndexMatchExpression::debugString(StringBuilder& de
_debugAddSpace(debug, indentationLevel);
BSONObjBuilder builder;
- serialize(&builder, true);
+ serialize(&builder, {});
debug << builder.obj().toString() << "\n";
const auto* tag = getTag();
@@ -72,7 +72,9 @@ bool InternalSchemaMatchArrayIndexMatchExpression::equivalent(const MatchExpress
_expression->equivalent(other->_expression.get());
}
-BSONObj InternalSchemaMatchArrayIndexMatchExpression::getSerializedRightHandSide() const {
+BSONObj InternalSchemaMatchArrayIndexMatchExpression::getSerializedRightHandSide(
+ boost::optional<StringData> replacementForLiteralArgs) const {
+ // TODO SERVER-73678 respect 'replacementForLiteralArgs'.
BSONObjBuilder objBuilder;
{
BSONObjBuilder matchArrayElemSubobj(objBuilder.subobjStart(kName));
@@ -80,7 +82,7 @@ BSONObj InternalSchemaMatchArrayIndexMatchExpression::getSerializedRightHandSide
matchArrayElemSubobj.append("namePlaceholder", _expression->getPlaceholder().value_or(""));
{
BSONObjBuilder subexprSubObj(matchArrayElemSubobj.subobjStart("expression"));
- _expression->getFilter()->serialize(&subexprSubObj, true);
+ _expression->getFilter()->serialize(&subexprSubObj, {});
subexprSubObj.doneFast();
}
matchArrayElemSubobj.doneFast();
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_match_array_index.h b/src/mongo/db/matcher/schema/expression_internal_schema_match_array_index.h
index 623e32a7e1e..56d115cc5ab 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_match_array_index.h
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_match_array_index.h
@@ -74,7 +74,8 @@ public:
return _expression->matchesBSONElement(element, details);
}
- BSONObj getSerializedRightHandSide() const final;
+ BSONObj getSerializedRightHandSide(
+ boost::optional<StringData> replacementForLiteralArgs) const final;
std::unique_ptr<MatchExpression> shallowClone() const final;
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_num_array_items.cpp b/src/mongo/db/matcher/schema/expression_internal_schema_num_array_items.cpp
index 4e87b7fece1..705f726e447 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_num_array_items.cpp
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_num_array_items.cpp
@@ -56,7 +56,9 @@ void InternalSchemaNumArrayItemsMatchExpression::debugString(StringBuilder& debu
debug << "\n";
}
-BSONObj InternalSchemaNumArrayItemsMatchExpression::getSerializedRightHandSide() const {
+BSONObj InternalSchemaNumArrayItemsMatchExpression::getSerializedRightHandSide(
+ boost::optional<StringData> replacementForLiteralArgs) const {
+ // TODO SERVER-73678 respect 'replacementForLiteralArgs'.
BSONObjBuilder objBuilder;
objBuilder.append(_name, _numItems);
return objBuilder.obj();
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_num_array_items.h b/src/mongo/db/matcher/schema/expression_internal_schema_num_array_items.h
index d651a006dbd..dc046bda15d 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_num_array_items.h
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_num_array_items.h
@@ -52,7 +52,8 @@ public:
void debugString(StringBuilder& debug, int indentationLevel) const final;
- BSONObj getSerializedRightHandSide() const final;
+ BSONObj getSerializedRightHandSide(
+ boost::optional<StringData> replacementForLiteralArgs) const final;
bool equivalent(const MatchExpression* other) const final;
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_num_properties.cpp b/src/mongo/db/matcher/schema/expression_internal_schema_num_properties.cpp
index e640a58ec71..3dd54f0f94f 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_num_properties.cpp
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_num_properties.cpp
@@ -37,12 +37,13 @@ void InternalSchemaNumPropertiesMatchExpression::debugString(StringBuilder& debu
int indentationLevel) const {
_debugAddSpace(debug, indentationLevel);
BSONObjBuilder builder;
- serialize(&builder, true);
+ serialize(&builder, {});
debug << builder.obj().toString() << "\n";
}
void InternalSchemaNumPropertiesMatchExpression::serialize(BSONObjBuilder* out,
- bool includePath) const {
+ SerializationOptions opts) const {
+ // TODO SERVER-73678 respect 'opts'.
out->append(_name, _numProperties);
}
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_num_properties.h b/src/mongo/db/matcher/schema/expression_internal_schema_num_properties.h
index 260cbea7812..845e8822476 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_num_properties.h
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_num_properties.h
@@ -71,7 +71,7 @@ public:
void debugString(StringBuilder& debug, int indentationLevel) const final;
- void serialize(BSONObjBuilder* out, bool includePath) const final;
+ void serialize(BSONObjBuilder* out, SerializationOptions opts) const final;
bool equivalent(const MatchExpression* other) const final;
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_object_match.cpp b/src/mongo/db/matcher/schema/expression_internal_schema_object_match.cpp
index ca33ea3025e..e057d5c2d26 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_object_match.cpp
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_object_match.cpp
@@ -61,10 +61,12 @@ void InternalSchemaObjectMatchExpression::debugString(StringBuilder& debug,
_sub->debugString(debug, indentationLevel + 1);
}
-BSONObj InternalSchemaObjectMatchExpression::getSerializedRightHandSide() const {
+BSONObj InternalSchemaObjectMatchExpression::getSerializedRightHandSide(
+ boost::optional<StringData> replacementForLiteralArgs) const {
+ // TODO SERVER-73678 respect 'replacementForLiteralArgs'.
BSONObjBuilder objMatchBob;
BSONObjBuilder subBob(objMatchBob.subobjStart(kName));
- _sub->serialize(&subBob, true);
+ _sub->serialize(&subBob, {});
subBob.doneFast();
return objMatchBob.obj();
}
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_object_match.h b/src/mongo/db/matcher/schema/expression_internal_schema_object_match.h
index 8d131e85d6c..44e74fdd8bc 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_object_match.h
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_object_match.h
@@ -50,7 +50,8 @@ public:
void debugString(StringBuilder& debug, int indentationLevel = 0) const final;
- BSONObj getSerializedRightHandSide() const final;
+ BSONObj getSerializedRightHandSide(
+ boost::optional<StringData> replacementForLiteralArgs) const final;
bool equivalent(const MatchExpression* other) const final;
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_root_doc_eq.cpp b/src/mongo/db/matcher/schema/expression_internal_schema_root_doc_eq.cpp
index c4b1e3f7aa2..c9d607b6c0a 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_root_doc_eq.cpp
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_root_doc_eq.cpp
@@ -55,7 +55,8 @@ void InternalSchemaRootDocEqMatchExpression::debugString(StringBuilder& debug,
}
void InternalSchemaRootDocEqMatchExpression::serialize(BSONObjBuilder* out,
- bool includePath) const {
+ SerializationOptions opts) const {
+ // TODO SERVER-73678 respect 'opts.'
BSONObjBuilder subObj(out->subobjStart(kName));
subObj.appendElements(_rhsObj);
subObj.doneFast();
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_root_doc_eq.h b/src/mongo/db/matcher/schema/expression_internal_schema_root_doc_eq.h
index c9ff8c20fe3..571bf32d3e6 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_root_doc_eq.h
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_root_doc_eq.h
@@ -72,7 +72,7 @@ public:
void debugString(StringBuilder& debug, int indentationLevel = 0) const final;
- void serialize(BSONObjBuilder* out, bool includePath) const final;
+ void serialize(BSONObjBuilder* out, SerializationOptions opts) const final;
bool equivalent(const MatchExpression* other) const final;
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_str_length.cpp b/src/mongo/db/matcher/schema/expression_internal_schema_str_length.cpp
index 9bba7d99b43..b643393b548 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_str_length.cpp
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_str_length.cpp
@@ -56,7 +56,9 @@ void InternalSchemaStrLengthMatchExpression::debugString(StringBuilder& debug,
debug << "\n";
}
-BSONObj InternalSchemaStrLengthMatchExpression::getSerializedRightHandSide() const {
+BSONObj InternalSchemaStrLengthMatchExpression::getSerializedRightHandSide(
+ boost::optional<StringData> replacementForLiteralArgs) const {
+ // TODO SERVER-73678 respect 'replacementForLiteralArgs'.
BSONObjBuilder objBuilder;
objBuilder.append(_name, _strLen);
return objBuilder.obj();
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_str_length.h b/src/mongo/db/matcher/schema/expression_internal_schema_str_length.h
index 027d5254be7..8c10f3f5253 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_str_length.h
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_str_length.h
@@ -62,7 +62,8 @@ public:
void debugString(StringBuilder& debug, int indentationLevel) const final;
- BSONObj getSerializedRightHandSide() const final;
+ BSONObj getSerializedRightHandSide(
+ boost::optional<StringData> replacementForLiteralArgs) const final;
bool equivalent(const MatchExpression* other) const final;
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_unique_items.cpp b/src/mongo/db/matcher/schema/expression_internal_schema_unique_items.cpp
index f78dca0c37f..a5dcfd9c9cd 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_unique_items.cpp
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_unique_items.cpp
@@ -39,7 +39,7 @@ void InternalSchemaUniqueItemsMatchExpression::debugString(StringBuilder& debug,
_debugAddSpace(debug, indentationLevel);
BSONObjBuilder builder;
- serialize(&builder, true);
+ serialize(&builder, {});
debug << builder.obj().toString() << "\n";
const auto* tag = getTag();
@@ -59,7 +59,9 @@ bool InternalSchemaUniqueItemsMatchExpression::equivalent(const MatchExpression*
return path() == other->path();
}
-BSONObj InternalSchemaUniqueItemsMatchExpression::getSerializedRightHandSide() const {
+BSONObj InternalSchemaUniqueItemsMatchExpression::getSerializedRightHandSide(
+ boost::optional<StringData> replacementForLiteralArgs) const {
+ // TODO SERVER-73678 respect 'replacementForLiteralArgs.'
BSONObjBuilder bob;
bob.append(kName, true);
return bob.obj();
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_unique_items.h b/src/mongo/db/matcher/schema/expression_internal_schema_unique_items.h
index 77fce7be0d0..20f78d85bc4 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_unique_items.h
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_unique_items.h
@@ -86,7 +86,8 @@ public:
bool equivalent(const MatchExpression* other) const final;
- BSONObj getSerializedRightHandSide() const final;
+ BSONObj getSerializedRightHandSide(
+ boost::optional<StringData> replacementForLiteralArgs) const final;
std::unique_ptr<MatchExpression> shallowClone() const final;
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_xor.cpp b/src/mongo/db/matcher/schema/expression_internal_schema_xor.cpp
index ad265266f52..50b4b813470 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_xor.cpp
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_xor.cpp
@@ -73,8 +73,9 @@ void InternalSchemaXorMatchExpression::debugString(StringBuilder& debug,
_debugList(debug, indentationLevel);
}
-void InternalSchemaXorMatchExpression::serialize(BSONObjBuilder* out, bool includePath) const {
+void InternalSchemaXorMatchExpression::serialize(BSONObjBuilder* out,
+ SerializationOptions opts) const {
BSONArrayBuilder arrBob(out->subarrayStart(kName));
- _listToBSON(&arrBob, includePath);
+ _listToBSON(&arrBob, opts);
}
} // namespace mongo
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_xor.h b/src/mongo/db/matcher/schema/expression_internal_schema_xor.h
index 928a13413f1..2755098390c 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_xor.h
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_xor.h
@@ -69,7 +69,7 @@ public:
void debugString(StringBuilder& debug, int indentationLevel = 0) const final;
- void serialize(BSONObjBuilder* out, bool includePath) const final;
+ void serialize(BSONObjBuilder* out, SerializationOptions opts) const final;
void acceptVisitor(MatchExpressionMutableVisitor* visitor) final {
visitor->visit(this);
diff --git a/src/mongo/db/matcher/schema/object_keywords_test.cpp b/src/mongo/db/matcher/schema/object_keywords_test.cpp
index 73bb4b7d7dd..468c0bb8cc9 100644
--- a/src/mongo/db/matcher/schema/object_keywords_test.cpp
+++ b/src/mongo/db/matcher/schema/object_keywords_test.cpp
@@ -256,11 +256,11 @@ TEST(JSONSchemaObjectKeywordTest, SharedJsonAndBsonTypeAliasesTranslateIdentical
ASSERT_OK(bsonTypeResult.getStatus());
BSONObjBuilder typeBuilder;
- MatchExpression::optimize(std::move(typeResult.getValue()))->serialize(&typeBuilder, true);
+ MatchExpression::optimize(std::move(typeResult.getValue()))->serialize(&typeBuilder, {});
BSONObjBuilder bsonTypeBuilder;
MatchExpression::optimize(std::move(bsonTypeResult.getValue()))
- ->serialize(&bsonTypeBuilder, true);
+ ->serialize(&bsonTypeBuilder, {});
ASSERT_BSONOBJ_EQ(typeBuilder.obj(), bsonTypeBuilder.obj());
}
diff --git a/src/mongo/db/pipeline/document_source_internal_unpack_bucket.cpp b/src/mongo/db/pipeline/document_source_internal_unpack_bucket.cpp
index 256804b978f..5a36703db13 100644
--- a/src/mongo/db/pipeline/document_source_internal_unpack_bucket.cpp
+++ b/src/mongo/db/pipeline/document_source_internal_unpack_bucket.cpp
@@ -1373,9 +1373,8 @@ Pipeline::SourceContainer::iterator DocumentSourceInternalUnpackBucket::doOptimi
// Create a loose bucket predicate and push it before the unpacking stage.
if (predicates.loosePredicate) {
- BSONObjBuilder bob;
- predicates.loosePredicate->serialize(&bob);
- container->insert(itr, DocumentSourceMatch::create(bob.obj(), pExpCtx));
+ container->insert(
+ itr, DocumentSourceMatch::create(predicates.loosePredicate->serialize(), pExpCtx));
// Give other stages a chance to optimize with the new $match.
return std::prev(itr) == container->begin() ? std::prev(itr)
diff --git a/src/mongo/db/pipeline/document_source_internal_unpack_bucket_test/create_predicates_on_bucket_level_field_test.cpp b/src/mongo/db/pipeline/document_source_internal_unpack_bucket_test/create_predicates_on_bucket_level_field_test.cpp
index 63d3b4a0b23..a054bee04dc 100644
--- a/src/mongo/db/pipeline/document_source_internal_unpack_bucket_test/create_predicates_on_bucket_level_field_test.cpp
+++ b/src/mongo/db/pipeline/document_source_internal_unpack_bucket_test/create_predicates_on_bucket_level_field_test.cpp
@@ -55,7 +55,7 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
auto predicate = dynamic_cast<DocumentSourceInternalUnpackBucket*>(container.front().get())
->createPredicatesOnBucketLevelField(original->getMatchExpression());
- ASSERT_BSONOBJ_EQ(predicate.loosePredicate->serialize(true),
+ ASSERT_BSONOBJ_EQ(predicate.loosePredicate->serialize(),
fromjson("{$or: [ {'control.max.a': {$_internalExprGt: 1}},"
"{$expr: {$ne: [ {$type: [ \"$control.min.a\" ]},"
"{$type: [ \"$control.max.a\" ]} ]}} ]}"));
@@ -77,7 +77,7 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
auto predicate = dynamic_cast<DocumentSourceInternalUnpackBucket*>(container.front().get())
->createPredicatesOnBucketLevelField(original->getMatchExpression());
- ASSERT_BSONOBJ_EQ(predicate.loosePredicate->serialize(true),
+ ASSERT_BSONOBJ_EQ(predicate.loosePredicate->serialize(),
fromjson("{$or: [ {'control.max.a': {$_internalExprGte: 1}},"
"{$expr: {$ne: [ {$type: [ \"$control.min.a\" ]},"
"{$type: [ \"$control.max.a\" ]} ]}} ]}"));
@@ -99,7 +99,7 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
auto predicate = dynamic_cast<DocumentSourceInternalUnpackBucket*>(container.front().get())
->createPredicatesOnBucketLevelField(original->getMatchExpression());
- ASSERT_BSONOBJ_EQ(predicate.loosePredicate->serialize(true),
+ ASSERT_BSONOBJ_EQ(predicate.loosePredicate->serialize(),
fromjson("{$or: [ {'control.min.a': {$_internalExprLt: 1}},"
"{$expr: {$ne: [ {$type: [ \"$control.min.a\" ]},"
"{$type: [ \"$control.max.a\" ]} ]}} ]}"));
@@ -121,7 +121,7 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
auto predicate = dynamic_cast<DocumentSourceInternalUnpackBucket*>(container.front().get())
->createPredicatesOnBucketLevelField(original->getMatchExpression());
- ASSERT_BSONOBJ_EQ(predicate.loosePredicate->serialize(true),
+ ASSERT_BSONOBJ_EQ(predicate.loosePredicate->serialize(),
fromjson("{$or: [ {'control.min.a': {$_internalExprLte: 1}},"
"{$expr: {$ne: [ {$type: [ \"$control.min.a\" ]},"
"{$type: [ \"$control.max.a\" ]} ]}} ]}"));
@@ -143,7 +143,7 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
auto predicate = dynamic_cast<DocumentSourceInternalUnpackBucket*>(container.front().get())
->createPredicatesOnBucketLevelField(original->getMatchExpression());
- ASSERT_BSONOBJ_EQ(predicate.loosePredicate->serialize(true),
+ ASSERT_BSONOBJ_EQ(predicate.loosePredicate->serialize(),
fromjson("{$or: [ {$and:[{'control.min.a': {$_internalExprLte: 1}},"
"{'control.max.a': {$_internalExprGte: 1}}]},"
"{$expr: {$ne: [ {$type: [ \"$control.min.a\" ]},"
@@ -190,7 +190,7 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
" ]}}"
" ]}"
"]}");
- ASSERT_BSONOBJ_EQ(predicate.loosePredicate->serialize(true), expected);
+ ASSERT_BSONOBJ_EQ(predicate.loosePredicate->serialize(), expected);
ASSERT_FALSE(predicate.tightPredicate);
}
@@ -214,7 +214,7 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
auto predicate = dynamic_cast<DocumentSourceInternalUnpackBucket*>(container.front().get())
->createPredicatesOnBucketLevelField(original->getMatchExpression());
- ASSERT_BSONOBJ_EQ(predicate.loosePredicate->serialize(true),
+ ASSERT_BSONOBJ_EQ(predicate.loosePredicate->serialize(),
fromjson("{$or: [ {'control.max.a': {$_internalExprGt: 1}},"
"{$expr: {$ne: [ {$type: [ \"$control.min.a\" ]},"
"{$type: [ \"$control.max.a\" ]} ]}} ]}"));
@@ -241,7 +241,7 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
auto predicate = dynamic_cast<DocumentSourceInternalUnpackBucket*>(container.front().get())
->createPredicatesOnBucketLevelField(original->getMatchExpression());
- ASSERT_BSONOBJ_EQ(predicate.loosePredicate->serialize(true),
+ ASSERT_BSONOBJ_EQ(predicate.loosePredicate->serialize(),
fromjson("{$or: [ {'control.max.a': {$_internalExprGte: 1}},"
"{$expr: {$ne: [ {$type: [ \"$control.min.a\" ]},"
"{$type: [ \"$control.max.a\" ]} ]}} ]}"));
@@ -268,7 +268,7 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
auto predicate = dynamic_cast<DocumentSourceInternalUnpackBucket*>(container.front().get())
->createPredicatesOnBucketLevelField(original->getMatchExpression());
- ASSERT_BSONOBJ_EQ(predicate.loosePredicate->serialize(true),
+ ASSERT_BSONOBJ_EQ(predicate.loosePredicate->serialize(),
fromjson("{$or: [ {'control.min.a': {$_internalExprLt: 1}},"
"{$expr: {$ne: [ {$type: [ \"$control.min.a\" ]},"
"{$type: [ \"$control.max.a\" ]} ]}} ]}"));
@@ -295,7 +295,7 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
auto predicate = dynamic_cast<DocumentSourceInternalUnpackBucket*>(container.front().get())
->createPredicatesOnBucketLevelField(original->getMatchExpression());
- ASSERT_BSONOBJ_EQ(predicate.loosePredicate->serialize(true),
+ ASSERT_BSONOBJ_EQ(predicate.loosePredicate->serialize(),
fromjson("{$or: [ {'control.min.a': {$_internalExprLte: 1}},"
"{$expr: {$ne: [ {$type: [ \"$control.min.a\" ]},"
"{$type: [ \"$control.max.a\" ]} ]}} ]}"));
@@ -322,7 +322,7 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
auto predicate = dynamic_cast<DocumentSourceInternalUnpackBucket*>(container.front().get())
->createPredicatesOnBucketLevelField(original->getMatchExpression());
- ASSERT_BSONOBJ_EQ(predicate.loosePredicate->serialize(true),
+ ASSERT_BSONOBJ_EQ(predicate.loosePredicate->serialize(),
fromjson("{$or: [ {$and:[{'control.min.a': {$_internalExprLte: 1}},"
"{'control.max.a': {$_internalExprGte: 1}}]},"
"{$expr: {$ne: [ {$type: [ \"$control.min.a\" ]},"
@@ -345,7 +345,7 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
auto predicate = dynamic_cast<DocumentSourceInternalUnpackBucket*>(container.front().get())
->createPredicatesOnBucketLevelField(original->getMatchExpression());
- ASSERT_BSONOBJ_EQ(predicate.loosePredicate->serialize(true),
+ ASSERT_BSONOBJ_EQ(predicate.loosePredicate->serialize(),
fromjson("{$and: [ {$or: [ {'control.max.b': {$_internalExprGt: 1}},"
"{$expr: {$ne: [ {$type: [ \"$control.min.b\" ]},"
"{$type: [ \"$control.max.b\" ]} ]}} ]},"
@@ -389,7 +389,7 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
auto predicate = dynamic_cast<DocumentSourceInternalUnpackBucket*>(container.front().get())
->createPredicatesOnBucketLevelField(original->getMatchExpression());
- ASSERT_BSONOBJ_EQ(predicate.loosePredicate->serialize(true),
+ ASSERT_BSONOBJ_EQ(predicate.loosePredicate->serialize(),
fromjson("{$or: ["
" {'control.max.b': {$_internalExprGt: 1}},"
" {$expr: {$ne: ["
@@ -416,7 +416,7 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
auto predicate = dynamic_cast<DocumentSourceInternalUnpackBucket*>(container.front().get())
->createPredicatesOnBucketLevelField(original->getMatchExpression());
- ASSERT_BSONOBJ_EQ(predicate.loosePredicate->serialize(true),
+ ASSERT_BSONOBJ_EQ(predicate.loosePredicate->serialize(),
fromjson("{$and: [ {$or: [ {'control.max.b': {$_internalExprGte: 2}},"
"{$expr: {$ne: [ {$type: [ \"$control.min.b\" ]},"
"{$type: [ \"$control.max.b\" ]} ]}} ]},"
@@ -445,7 +445,7 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
->createPredicatesOnBucketLevelField(original->getMatchExpression());
ASSERT(predicate.loosePredicate);
- ASSERT_BSONOBJ_EQ(predicate.loosePredicate->serialize(true),
+ ASSERT_BSONOBJ_EQ(predicate.loosePredicate->serialize(),
fromjson("{$or: ["
" {$or: ["
" {'control.max.b': {$_internalExprGt: 1}},"
@@ -522,7 +522,7 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
auto predicate = dynamic_cast<DocumentSourceInternalUnpackBucket*>(container.front().get())
->createPredicatesOnBucketLevelField(original->getMatchExpression());
- ASSERT_BSONOBJ_EQ(predicate.loosePredicate->serialize(true),
+ ASSERT_BSONOBJ_EQ(predicate.loosePredicate->serialize(),
fromjson("{$or: ["
" {$or: ["
" {'control.max.b': {$_internalExprGte: 2}},"
@@ -683,8 +683,8 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
->createPredicatesOnBucketLevelField(original->getMatchExpression());
// Meta predicates are mapped to the meta field, not the control min/max fields.
- ASSERT_BSONOBJ_EQ(predicate.loosePredicate->serialize(true), fromjson("{meta: {$gt: 5}}"));
- ASSERT_BSONOBJ_EQ(predicate.tightPredicate->serialize(true), fromjson("{meta: {$gt: 5}}"));
+ ASSERT_BSONOBJ_EQ(predicate.loosePredicate->serialize(), fromjson("{meta: {$gt: 5}}"));
+ ASSERT_BSONOBJ_EQ(predicate.tightPredicate->serialize(), fromjson("{meta: {$gt: 5}}"));
}
TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
@@ -703,10 +703,8 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
->createPredicatesOnBucketLevelField(original->getMatchExpression());
// Meta predicates are mapped to the meta field, not the control min/max fields.
- ASSERT_BSONOBJ_EQ(predicate.loosePredicate->serialize(true),
- fromjson("{'meta.foo': {$gt: 5}}"));
- ASSERT_BSONOBJ_EQ(predicate.tightPredicate->serialize(true),
- fromjson("{'meta.foo': {$gt: 5}}"));
+ ASSERT_BSONOBJ_EQ(predicate.loosePredicate->serialize(), fromjson("{'meta.foo': {$gt: 5}}"));
+ ASSERT_BSONOBJ_EQ(predicate.tightPredicate->serialize(), fromjson("{'meta.foo': {$gt: 5}}"));
}
TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
@@ -724,7 +722,7 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
auto predicate = dynamic_cast<DocumentSourceInternalUnpackBucket*>(container.front().get())
->createPredicatesOnBucketLevelField(original->getMatchExpression());
- ASSERT_BSONOBJ_EQ(predicate.loosePredicate->serialize(true),
+ ASSERT_BSONOBJ_EQ(predicate.loosePredicate->serialize(),
fromjson("{$and: ["
" {$or: ["
" {'control.max.a': {$_internalExprGt: 1}},"
@@ -776,10 +774,10 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest, OptimizeMapsTimePre
auto children = andExpr->getChildVector();
ASSERT_EQ(children->size(), 3);
- ASSERT_BSONOBJ_EQ((*children)[0]->serialize(true),
+ ASSERT_BSONOBJ_EQ((*children)[0]->serialize(),
BSON("control.min.time" << BSON("$_internalExprLt" << date)));
ASSERT_BSONOBJ_EQ(
- (*children)[1]->serialize(true),
+ (*children)[1]->serialize(),
BSON("control.max.time" << BSON("$_internalExprLt" << datePlusBucketSpan)));
auto idPred = dynamic_cast<ComparisonMatchExpressionBase*>((*children)[2].get());
@@ -833,10 +831,10 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest, OptimizeMapsTimePre
auto children = andExpr->getChildVector();
ASSERT_EQ(children->size(), 3);
- ASSERT_BSONOBJ_EQ((*children)[0]->serialize(true),
+ ASSERT_BSONOBJ_EQ((*children)[0]->serialize(),
BSON("control.min.time" << BSON("$_internalExprLte" << date)));
ASSERT_BSONOBJ_EQ(
- (*children)[1]->serialize(true),
+ (*children)[1]->serialize(),
BSON("control.max.time" << BSON("$_internalExprLte" << datePlusBucketSpan)));
auto idPred = dynamic_cast<ComparisonMatchExpressionBase*>((*children)[2].get());
@@ -882,15 +880,15 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest, OptimizeMapsTimePre
auto children = andExpr->getChildVector();
ASSERT_EQ(children->size(), 6);
- ASSERT_BSONOBJ_EQ((*children)[0]->serialize(true),
+ ASSERT_BSONOBJ_EQ((*children)[0]->serialize(),
BSON("control.min.time" << BSON("$_internalExprLte" << date)));
ASSERT_BSONOBJ_EQ(
- (*children)[1]->serialize(true),
+ (*children)[1]->serialize(),
BSON("control.min.time" << BSON("$_internalExprGte" << dateMinusBucketSpan)));
- ASSERT_BSONOBJ_EQ((*children)[2]->serialize(true),
+ ASSERT_BSONOBJ_EQ((*children)[2]->serialize(),
BSON("control.max.time" << BSON("$_internalExprGte" << date)));
ASSERT_BSONOBJ_EQ(
- (*children)[3]->serialize(true),
+ (*children)[3]->serialize(),
BSON("control.max.time" << BSON("$_internalExprLte" << datePlusBucketSpan)));
auto idPred = dynamic_cast<ComparisonMatchExpressionBase*>((*children)[4].get());
@@ -944,10 +942,10 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest, OptimizeMapsTimePre
auto children = andExpr->getChildVector();
ASSERT_EQ(children->size(), 3);
- ASSERT_BSONOBJ_EQ((*children)[0]->serialize(true),
+ ASSERT_BSONOBJ_EQ((*children)[0]->serialize(),
BSON("control.max.time" << BSON("$_internalExprGt" << date)));
ASSERT_BSONOBJ_EQ(
- (*children)[1]->serialize(true),
+ (*children)[1]->serialize(),
BSON("control.min.time" << BSON("$_internalExprGt" << dateMinusBucketSpan)));
auto idPred = dynamic_cast<ComparisonMatchExpressionBase*>((*children)[2].get());
@@ -993,10 +991,10 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest, OptimizeMapsTimePre
auto children = andExpr->getChildVector();
ASSERT_EQ(children->size(), 3);
- ASSERT_BSONOBJ_EQ((*children)[0]->serialize(true),
+ ASSERT_BSONOBJ_EQ((*children)[0]->serialize(),
BSON("control.max.time" << BSON("$_internalExprGte" << date)));
ASSERT_BSONOBJ_EQ(
- (*children)[1]->serialize(true),
+ (*children)[1]->serialize(),
BSON("control.min.time" << BSON("$_internalExprGte" << dateMinusBucketSpan)));
auto idPred = dynamic_cast<ComparisonMatchExpressionBase*>((*children)[2].get());
@@ -1144,7 +1142,7 @@ TEST_F(InternalUnpackBucketPredicateMappingOptimizationTest,
auto predicate = dynamic_cast<DocumentSourceInternalUnpackBucket*>(container.front().get())
->createPredicatesOnBucketLevelField(original->getMatchExpression());
- ASSERT_BSONOBJ_EQ(predicate.loosePredicate->serialize(true),
+ ASSERT_BSONOBJ_EQ(predicate.loosePredicate->serialize(),
fromjson("{$_internalBucketGeoWithin: { withinRegion: { $geometry: { type : "
"\"Polygon\" ,coordinates: [ [ [ 0, 0 ], [ 3, 6 ], [ 6, 1 ], [ 0, 0 "
"] ] ]}},field: \"loc\"}}"));
diff --git a/src/mongo/db/pipeline/document_source_lookup_test.cpp b/src/mongo/db/pipeline/document_source_lookup_test.cpp
index 1682a375569..e1582b23a5a 100644
--- a/src/mongo/db/pipeline/document_source_lookup_test.cpp
+++ b/src/mongo/db/pipeline/document_source_lookup_test.cpp
@@ -1139,13 +1139,10 @@ TEST_F(DocumentSourceLookUpTest, ExprEmbeddedInMatchExpressionShouldBeOptimized)
auto& matchSource = dynamic_cast<const DocumentSourceMatch&>(*secondSource);
// Ensure that the '$$var' in the embedded expression got optimized to ExpressionConstant.
- BSONObjBuilder builder;
- matchSource.getMatchExpression()->serialize(&builder);
- auto serializedMatch = builder.obj();
auto expectedMatch =
fromjson("{$and: [{_id: {$_internalExprEq: 5}}, {$expr: {$eq: ['$_id', {$const: 5}]}}]}");
- ASSERT_VALUE_EQ(Value(serializedMatch), Value(expectedMatch));
+ ASSERT_VALUE_EQ(Value(matchSource.getMatchExpression()->serialize()), Value(expectedMatch));
}
TEST_F(DocumentSourceLookUpTest,
diff --git a/src/mongo/db/pipeline/document_source_match.cpp b/src/mongo/db/pipeline/document_source_match.cpp
index d0e0f12ebd8..3deaf5ab453 100644
--- a/src/mongo/db/pipeline/document_source_match.cpp
+++ b/src/mongo/db/pipeline/document_source_match.cpp
@@ -70,9 +70,7 @@ const char* DocumentSourceMatch::getSourceName() const {
Value DocumentSourceMatch::serialize(boost::optional<ExplainOptions::Verbosity> explain) const {
if (explain) {
- BSONObjBuilder builder;
- _expression->serialize(&builder);
- return Value(DOC(getSourceName() << Document(builder.obj())));
+ return Value(DOC(getSourceName() << Document(_expression->serialize())));
}
return Value(DOC(getSourceName() << Document(getQuery())));
}
@@ -450,15 +448,11 @@ DocumentSourceMatch::splitSourceByFunc(const OrderedPathSet& fields,
// the corresponding BSONObj may not exist. Therefore, we take each of these expressions,
// serialize them, and then re-parse them, constructing new BSON that is owned by the
// DocumentSourceMatch.
- BSONObjBuilder firstBob;
- newExpr.first->serialize(&firstBob);
- auto firstMatch = DocumentSourceMatch::create(firstBob.obj(), pExpCtx);
+ auto firstMatch = DocumentSourceMatch::create(newExpr.first->serialize(), pExpCtx);
intrusive_ptr<DocumentSourceMatch> secondMatch;
if (newExpr.second) {
- BSONObjBuilder secondBob;
- newExpr.second->serialize(&secondBob);
- secondMatch = DocumentSourceMatch::create(secondBob.obj(), pExpCtx);
+ secondMatch = DocumentSourceMatch::create(newExpr.second->serialize(), pExpCtx);
}
return {std::move(firstMatch), std::move(secondMatch)};
@@ -491,9 +485,7 @@ boost::intrusive_ptr<DocumentSourceMatch> DocumentSourceMatch::descendMatchOnPat
}
});
- BSONObjBuilder query;
- matchExpr->serialize(&query);
- return new DocumentSourceMatch(query.obj(), expCtx);
+ return new DocumentSourceMatch(matchExpr->serialize(), expCtx);
}
std::pair<boost::intrusive_ptr<DocumentSourceMatch>, boost::intrusive_ptr<DocumentSourceMatch>>
diff --git a/src/mongo/db/pipeline/document_source_match_test.cpp b/src/mongo/db/pipeline/document_source_match_test.cpp
index 5d3ac170cae..e8dcd2ce2c0 100644
--- a/src/mongo/db/pipeline/document_source_match_test.cpp
+++ b/src/mongo/db/pipeline/document_source_match_test.cpp
@@ -606,8 +606,6 @@ DEATH_TEST_REGEX_F(DocumentSourceMatchTest,
const auto matchSpec = BSON("a" << BSON("$elemMatch" << BSON("a.b" << 1)));
const auto matchExpression =
unittest::assertGet(MatchExpressionParser::parse(matchSpec, expCtx));
- BSONObjBuilder out;
- matchExpression->serialize(&out);
DocumentSourceMatch::descendMatchOnPath(matchExpression.get(), "a", expCtx);
}
diff --git a/src/mongo/db/query/SConscript b/src/mongo/db/query/SConscript
index 305038f5e74..6163d2e3fce 100644
--- a/src/mongo/db/query/SConscript
+++ b/src/mongo/db/query/SConscript
@@ -23,10 +23,7 @@ env.SConscript(
env.Library(
target='canonical_query',
- source=[
- 'canonical_query.cpp',
- 'canonical_query_encoder.cpp',
- ],
+ source=['canonical_query.cpp', 'canonical_query_encoder.cpp', 'query_shape.cpp'],
LIBDEPS=[
'$BUILD_DIR/mongo/crypto/encrypted_field_config',
'$BUILD_DIR/mongo/db/cst/cst',
@@ -439,8 +436,6 @@ env.CppUnitTest(
"interval_test.cpp",
"killcursors_request_test.cpp",
"lru_key_value_test.cpp",
- 'map_reduce_output_format_test.cpp',
- "util/memory_util_test.cpp",
"parsed_distinct_test.cpp",
"plan_cache_indexability_test.cpp",
"plan_cache_key_info_test.cpp",
@@ -456,30 +451,33 @@ env.CppUnitTest(
"query_planner_collation_test.cpp",
"query_planner_columnar_test.cpp",
"query_planner_geo_test.cpp",
- "query_planner_pipeline_pushdown_test.cpp",
"query_planner_hashed_index_test.cpp",
- "query_planner_partialidx_test.cpp",
"query_planner_index_test.cpp",
"query_planner_operator_test.cpp",
"query_planner_options_test.cpp",
- "query_planner_tree_test.cpp",
+ "query_planner_partialidx_test.cpp",
+ "query_planner_pipeline_pushdown_test.cpp",
"query_planner_text_test.cpp",
+ "query_planner_tree_test.cpp",
"query_planner_wildcard_index_test.cpp",
"query_request_test.cpp",
"query_settings_test.cpp",
+ "query_shape_test.cpp",
"query_solution_test.cpp",
"rate_limiting_test.cpp",
"sbe_and_hash_test.cpp",
"sbe_and_sorted_test.cpp",
+ "sbe_shard_filter_test.cpp",
"sbe_stage_builder_accumulator_test.cpp",
"sbe_stage_builder_const_eval_test.cpp",
"sbe_stage_builder_lookup_test.cpp",
"sbe_stage_builder_test.cpp",
"sbe_stage_builder_test_fixture.cpp",
- "sbe_shard_filter_test.cpp",
"shard_filterer_factory_mock.cpp",
"telemetry_store_test.cpp",
+ "util/memory_util_test.cpp",
"view_response_formatter_test.cpp",
+ 'map_reduce_output_format_test.cpp',
],
LIBDEPS=[
"$BUILD_DIR/mongo/db/auth/authmocks",
diff --git a/src/mongo/db/query/canonical_query.cpp b/src/mongo/db/query/canonical_query.cpp
index 45fc3ef9c41..07bcedfc496 100644
--- a/src/mongo/db/query/canonical_query.cpp
+++ b/src/mongo/db/query/canonical_query.cpp
@@ -153,9 +153,7 @@ StatusWith<std::unique_ptr<CanonicalQuery>> CanonicalQuery::canonicalize(
StatusWith<std::unique_ptr<CanonicalQuery>> CanonicalQuery::canonicalize(
OperationContext* opCtx, const CanonicalQuery& baseQuery, MatchExpression* root) {
auto findCommand = std::make_unique<FindCommandRequest>(baseQuery.nss());
- BSONObjBuilder builder;
- root->serialize(&builder, true);
- findCommand->setFilter(builder.obj());
+ findCommand->setFilter(root->serialize());
findCommand->setProjection(baseQuery.getFindCommandRequest().getProjection().getOwned());
findCommand->setSort(baseQuery.getFindCommandRequest().getSort().getOwned());
findCommand->setCollation(baseQuery.getFindCommandRequest().getCollation().getOwned());
diff --git a/src/mongo/db/query/canonical_query_test.cpp b/src/mongo/db/query/canonical_query_test.cpp
index 8fbb4eec5c3..be8b5eb6fc7 100644
--- a/src/mongo/db/query/canonical_query_test.cpp
+++ b/src/mongo/db/query/canonical_query_test.cpp
@@ -282,9 +282,7 @@ TEST(CanonicalQueryTest, CanonicalizeFromBaseQuery) {
MatchExpression* firstClauseExpr = baseCq->root()->getChild(0);
auto childCq = assertGet(CanonicalQuery::canonicalize(opCtx.get(), *baseCq, firstClauseExpr));
- BSONObjBuilder expectedFilter;
- firstClauseExpr->serialize(&expectedFilter);
- ASSERT_BSONOBJ_EQ(childCq->getFindCommandRequest().getFilter(), expectedFilter.obj());
+ ASSERT_BSONOBJ_EQ(childCq->getFindCommandRequest().getFilter(), firstClauseExpr->serialize());
ASSERT_BSONOBJ_EQ(childCq->getFindCommandRequest().getProjection(),
baseCq->getFindCommandRequest().getProjection());
diff --git a/src/mongo/db/query/explain.cpp b/src/mongo/db/query/explain.cpp
index b9b69eac18d..2b7bb335bfb 100644
--- a/src/mongo/db/query/explain.cpp
+++ b/src/mongo/db/query/explain.cpp
@@ -123,7 +123,7 @@ void generatePlannerInfo(PlanExecutor* exec,
auto query = exec->getCanonicalQuery();
if (nullptr != query) {
BSONObjBuilder parsedQueryBob(plannerBob.subobjStart("parsedQuery"));
- query->root()->serialize(&parsedQueryBob);
+ query->root()->serialize(&parsedQueryBob, {});
parsedQueryBob.doneFast();
if (query->getCollator()) {
diff --git a/src/mongo/db/query/plan_explainer_sbe.cpp b/src/mongo/db/query/plan_explainer_sbe.cpp
index f37382a23b6..d1fbe796d2b 100644
--- a/src/mongo/db/query/plan_explainer_sbe.cpp
+++ b/src/mongo/db/query/plan_explainer_sbe.cpp
@@ -193,7 +193,9 @@ void statsToBSON(const QuerySolutionNode* node,
if (!cisn->filtersByPath.empty()) {
BSONObjBuilder filtersBob(bob->subobjStart("filtersByPath"));
for (const auto& [path, matchExpr] : cisn->filtersByPath) {
- filtersBob.append(path, matchExpr->serialize(false /* includePath */));
+ SerializationOptions opts;
+ opts.includePath = false;
+ filtersBob.append(path, matchExpr->serialize(opts));
}
}
diff --git a/src/mongo/db/query/planner_access_test.cpp b/src/mongo/db/query/planner_access_test.cpp
index 52df32c871e..d30544b43fa 100644
--- a/src/mongo/db/query/planner_access_test.cpp
+++ b/src/mongo/db/query/planner_access_test.cpp
@@ -38,9 +38,7 @@ namespace mongo {
namespace {
BSONObj serializeMatcher(Matcher* matcher) {
- BSONObjBuilder builder;
- matcher->getMatchExpression()->serialize(&builder);
- return builder.obj();
+ return matcher->getMatchExpression()->serialize();
}
TEST(PlannerAccessTest, PrepareForAccessPlanningSortsEqualNodesByTheirChildren) {
diff --git a/src/mongo/db/query/projection_ast_util.cpp b/src/mongo/db/query/projection_ast_util.cpp
index e5b4cc1a9c4..50649f8a5ed 100644
--- a/src/mongo/db/query/projection_ast_util.cpp
+++ b/src/mongo/db/query/projection_ast_util.cpp
@@ -47,7 +47,7 @@ public:
virtual void visit(const MatchExpressionASTNode* node) {
static_cast<const MatchExpressionASTNode*>(node)->matchExpression()->serialize(
- &_builders.top(), true);
+ &_builders.top(), {});
}
virtual void visit(const ProjectionPathASTNode* node) {
diff --git a/src/mongo/db/query/query_shape.cpp b/src/mongo/db/query/query_shape.cpp
new file mode 100644
index 00000000000..cc6829a72f4
--- /dev/null
+++ b/src/mongo/db/query/query_shape.cpp
@@ -0,0 +1,39 @@
+/**
+ * Copyright (C) 2023-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/db/query/query_shape.h"
+
+namespace mongo::query_shape {
+
+BSONObj predicateShape(const MatchExpression* predicate) {
+ SerializationOptions opts;
+ opts.replacementForLiteralArgs = kLiteralArgString;
+ return predicate->serialize(opts);
+}
+} // namespace mongo::query_shape
diff --git a/src/mongo/db/query/query_shape.h b/src/mongo/db/query/query_shape.h
new file mode 100644
index 00000000000..78733ac54b9
--- /dev/null
+++ b/src/mongo/db/query/query_shape.h
@@ -0,0 +1,54 @@
+/**
+ * Copyright (C) 2023-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.h"
+
+namespace mongo::query_shape {
+
+constexpr StringData kLiteralArgString = "?"_sd;
+
+/**
+ * Computes a BSONObj that is meant to be used to classify queries according to their shape, for the
+ * purposes of collecting telemetry.
+ *
+ * For example, if the MatchExpression represents {a: 2}, it will return the same BSONObj as the
+ * MatchExpression for {a: 1}, {a: 10}, and {a: {$eq: 2}} (identical bits but not sharing memory)
+ * because they are considered to be the same shape.
+ *
+ * Note that the shape of a MatchExpression is only part of the overall query shape - which should
+ * include other options like the sort and projection.
+ *
+ * TODO better consider how this interacts with persistent query settings project, and document it.
+ * TODO (TODO SERVER ticket) better distinguish this from a plan cache or CQ 'query shape'.
+ */
+BSONObj predicateShape(const MatchExpression* predicate);
+
+} // namespace mongo::query_shape
diff --git a/src/mongo/db/query/query_shape_test.cpp b/src/mongo/db/query/query_shape_test.cpp
new file mode 100644
index 00000000000..5ee00a416dd
--- /dev/null
+++ b/src/mongo/db/query/query_shape_test.cpp
@@ -0,0 +1,135 @@
+/**
+ * Copyright (C) 2023-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/bson/bsonmisc.h"
+#include "mongo/bson/bsonobj.h"
+#include "mongo/db/matcher/parsed_match_expression_for_test.h"
+#include "mongo/db/operation_context.h"
+#include "mongo/db/pipeline/expression_context_for_test.h"
+#include "mongo/db/query/optimizer/utils/unit_test_utils.h"
+#include "mongo/db/query/query_planner_params.h"
+#include "mongo/db/query/query_shape.h"
+#include "mongo/unittest/unittest.h"
+
+namespace mongo {
+namespace {
+void assertShapeIs(std::string filterJson, BSONObj expectedShape) {
+ ParsedMatchExpressionForTest expr(filterJson);
+ ASSERT_BSONOBJ_EQ(expectedShape, query_shape::predicateShape(expr.get()));
+}
+
+void assertShapeIs(std::string filterJson, std::string expectedShapeJson) {
+ return assertShapeIs(filterJson, fromjson(expectedShapeJson));
+}
+
+} // namespace
+
+TEST(QueryPredicateShape, Equals) {
+ assertShapeIs("{a: 5}", "{a: {$eq: '?'}}"); // Implicit equals
+ assertShapeIs("{a: {$eq: 5}}", "{a: {$eq: '?'}}"); // Explicit equals
+ assertShapeIs("{a: 5, b: 6}", "{$and: [{a: {$eq: '?'}}, {b: {$eq: '?'}}]}"); // implicit $and
+}
+
+TEST(QueryPredicateShape, Comparisons) {
+ assertShapeIs("{a: {$lt: 5}, b: {$gt: 6}, c: {$gte: 3, $lte: 10}}",
+ "{$and: [{a: {$lt: '?'}}, {b: {$gt: '?'}}, {c: {$gte: '?'}}, {c: {$lte: '?'}}]}");
+}
+
+TEST(QueryPredicateShape, Regex) {
+ // Note/warning: 'fromjson' will parse $regex into a /regex/. If you want to keep it as-is,
+ // construct the BSON yourself.
+ assertShapeIs("{a: /a+/}", BSON("a" << BSON("$regex" << query_shape::kLiteralArgString)));
+ assertShapeIs("{a: /a+/i}",
+ BSON("a" << BSON("$regex" << query_shape::kLiteralArgString << "$options"
+ << query_shape::kLiteralArgString)));
+}
+
+TEST(QueryPredicateShape, Mod) {
+ assertShapeIs("{a: {$mod: [2, 0]}}", "{a: {$mod: '?'}}");
+}
+
+TEST(QueryPredicateShape, Exists) {
+ assertShapeIs("{a: {$exists: true}}", "{a: {$exists: '?'}}");
+}
+
+TEST(QueryPredicateShape, In) {
+ // Any number of children is always the same shape
+ assertShapeIs("{a: {$in: [1]}}", "{a: {$in: ['?']}}");
+ assertShapeIs("{a: {$in: [1, 4, 'str', /regex/]}}", "{a: {$in: ['?']}}");
+}
+
+TEST(QueryPredicateShape, BitTestOperators) {
+ assertShapeIs("{a: {$bitsAllSet: [1, 5]}}", "{a: {$bitsAllSet: '?'}}");
+ assertShapeIs("{a: {$bitsAllSet: 50}}", "{a: {$bitsAllSet: '?'}}");
+
+ assertShapeIs("{a: {$bitsAnySet: [1, 5]}}", "{a: {$bitsAnySet: '?'}}");
+ assertShapeIs("{a: {$bitsAnySet: 50}}", "{a: {$bitsAnySet: '?'}}");
+
+ assertShapeIs("{a: {$bitsAllClear: [1, 5]}}", "{a: {$bitsAllClear: '?'}}");
+ assertShapeIs("{a: {$bitsAllClear: 50}}", "{a: {$bitsAllClear: '?'}}");
+
+ assertShapeIs("{a: {$bitsAnyClear: [1, 5]}}", "{a: {$bitsAnyClear: '?'}}");
+ assertShapeIs("{a: {$bitsAnyClear: 50}}", "{a: {$bitsAnyClear: '?'}}");
+}
+
+BSONObj queryShapeForOptimizedExprExpression(std::string exprPredicateJson) {
+ ParsedMatchExpressionForTest expr(exprPredicateJson);
+ // We need to optimize an $expr expression in order to generate an $_internalExprEq. It's not
+ // clear we'd want to do optimization before computing the query shape, but we should support
+ // the computation on any MatchExpression, and this is the easiest way we can create this type
+ // of MatchExpression node.
+ auto optimized = MatchExpression::optimize(expr.release());
+ return query_shape::predicateShape(optimized.get());
+}
+
+TEST(QueryPredicateShape, OptimizedExprPredicates) {
+ // TODO SERVER-73709 $expr should respect the literal redaction and hide the '2' - here and in
+ // all following assertions.
+ ASSERT_BSONOBJ_EQ(
+ queryShapeForOptimizedExprExpression("{$expr: {$eq: ['$a', 2]}}"),
+ fromjson("{$and: [{a: {$_internalExprEq: '?'}}, {$expr: {$eq: ['$a', {$const: 2}]}}]}"));
+
+ ASSERT_BSONOBJ_EQ(
+ queryShapeForOptimizedExprExpression("{$expr: {$lt: ['$a', 2]}}"),
+ fromjson("{$and: [{a: {$_internalExprLt: '?'}}, {$expr: {$lt: ['$a', {$const: 2}]}}]}"));
+
+ ASSERT_BSONOBJ_EQ(
+ queryShapeForOptimizedExprExpression("{$expr: {$lte: ['$a', 2]}}"),
+ fromjson("{$and: [{a: {$_internalExprLte: '?'}}, {$expr: {$lte: ['$a', {$const: 2}]}}]}"));
+
+ ASSERT_BSONOBJ_EQ(
+ queryShapeForOptimizedExprExpression("{$expr: {$gt: ['$a', 2]}}"),
+ fromjson("{$and: [{a: {$_internalExprGt: '?'}}, {$expr: {$gt: ['$a', {$const: 2}]}}]}"));
+
+ ASSERT_BSONOBJ_EQ(
+ queryShapeForOptimizedExprExpression("{$expr: {$gte: ['$a', 2]}}"),
+ fromjson("{$and: [{a: {$_internalExprGte: '?'}}, {$expr: {$gte: ['$a', {$const: 2}]}}]}"));
+}
+
+} // namespace mongo
diff --git a/src/mongo/db/query/serialization_options.h b/src/mongo/db/query/serialization_options.h
new file mode 100644
index 00000000000..301617a8768
--- /dev/null
+++ b/src/mongo/db/query/serialization_options.h
@@ -0,0 +1,70 @@
+/**
+ * Copyright (C) 2023-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/base/string_data.h"
+#include "third_party/boost/boost/optional.hpp"
+
+namespace mongo {
+
+/**
+ * A struct with options for how you want to serialize a match expression.
+ */
+struct SerializationOptions {
+ SerializationOptions() {}
+
+ // 'replacementForLiteralArgs' is an independent option to serialize in a genericized format
+ // with the aim of similar "shaped" queries serializing to the same object. For example, if
+ // set to '?' then the serialization of {a: {$gt: 2}} will result in {a: {$gt: '?'}}, as
+ // will the serialization of {a: {$gt: 3}}.
+ //
+ // "Literal" here is meant to stand in contrast to expression arguements, as in the $gt
+ // expressions in {$and: [{a: {$gt: 3}}, {b: {$gt: 4}}]}. There the only literals are 3 and
+ // 4, so the serialization expected would be {$and: [{a: {$gt: '?'}}, {b: {$lt: '?'}}]}.
+ boost::optional<StringData> replacementForLiteralArgs = boost::none;
+
+ // TODO SERVER-73663 'redactFieldNames' could be here - a callback function?
+
+ // If set, serializes without including the path. For example {a: {$gt: 2}} would serialize
+ // as just {$gt: 2}.
+ //
+ // It is expected that most callers want to set 'includePath' to true to
+ // get a correct serialization. Internally, we may set this to false if we have a situation
+ // where an outer expression serializes a path and we don't want to repeat the path in the
+ // inner expression.
+ //
+ // For example in {a: {$elemMatch: {$eq: 2}}} the "a" is serialized by the $elemMatch, and
+ // should not be serialized by the EQ child.
+ // The $elemMatch will serialize {a: {$elemMatch: <recurse>}} and the EQ will serialize just
+ // {$eq: 2} instead of its usual {a: {$eq: 2}}.
+ bool includePath = true;
+};
+
+} // namespace mongo
diff --git a/src/mongo/dbtests/extensions_callback_real_test.cpp b/src/mongo/dbtests/extensions_callback_real_test.cpp
index 6cf71af0f8c..bd75718e375 100644
--- a/src/mongo/dbtests/extensions_callback_real_test.cpp
+++ b/src/mongo/dbtests/extensions_callback_real_test.cpp
@@ -255,13 +255,10 @@ TEST_F(ExtensionsCallbackRealTest, WhereExpressionDesugarsToExprAndInternalJs) {
auto expr1 = unittest::assertGet(
ExtensionsCallbackReal(&_opCtx, &_nss).parseWhere(expCtx, query1.firstElement()));
- BSONObjBuilder gotMatch;
- expr1->serialize(&gotMatch);
-
auto expectedMatch = fromjson(
"{$expr: {$function: {'body': 'function() { return this.x == 10; }', 'args': "
"['$$CURRENT'], 'lang': 'js', '_internalSetObjToThis': true}}}");
- ASSERT_BSONOBJ_EQ(gotMatch.obj(), expectedMatch);
+ ASSERT_BSONOBJ_EQ(expr1->serialize(), expectedMatch);
}
}