summaryrefslogtreecommitdiff
path: root/src/mongo/db/matcher
diff options
context:
space:
mode:
authorLucas de Castro Borges <lucas@gnuabordo.com.br>2025-02-11 15:07:35 -0300
committerLucas de Castro Borges <lucas@gnuabordo.com.br>2025-02-11 15:07:35 -0300
commit4cb8841196d0625dfa3825aa326f071cd27c7b8b (patch)
tree1682a647d4463397c119183369ae6f750d5fdcff /src/mongo/db/matcher
parentaa03c6362cbaa767638e6eed9b031d86dd2643d1 (diff)
parent8f0827553e09872941945a093b647a4211a9db7f (diff)
Update upstream source from tag 'upstream/6.0.0'master
Update to upstream version '6.0.0' with Debian dir 5604a80ec1c96ca76f25f40d78e6ef855abec322
Diffstat (limited to 'src/mongo/db/matcher')
-rw-r--r--src/mongo/db/matcher/SConscript2
-rw-r--r--src/mongo/db/matcher/expression.h40
-rw-r--r--src/mongo/db/matcher/expression_algo.cpp81
-rw-r--r--src/mongo/db/matcher/expression_algo.h40
-rw-r--r--src/mongo/db/matcher/expression_algo_test.cpp741
-rw-r--r--src/mongo/db/matcher/expression_always_boolean.h6
-rw-r--r--src/mongo/db/matcher/expression_arity.h8
-rw-r--r--src/mongo/db/matcher/expression_array.cpp30
-rw-r--r--src/mongo/db/matcher/expression_array.h12
-rw-r--r--src/mongo/db/matcher/expression_expr.cpp21
-rw-r--r--src/mongo/db/matcher/expression_expr.h6
-rw-r--r--src/mongo/db/matcher/expression_expr_test.cpp208
-rw-r--r--src/mongo/db/matcher/expression_geo.cpp32
-rw-r--r--src/mongo/db/matcher/expression_geo.h21
-rw-r--r--src/mongo/db/matcher/expression_geo_serializer.cpp391
-rw-r--r--src/mongo/db/matcher/expression_geo_serializer.h67
-rw-r--r--src/mongo/db/matcher/expression_geo_test.cpp415
-rw-r--r--src/mongo/db/matcher/expression_internal_bucket_geo_within.cpp10
-rw-r--r--src/mongo/db/matcher/expression_internal_bucket_geo_within.h9
-rw-r--r--src/mongo/db/matcher/expression_internal_expr_eq_test.cpp2
-rw-r--r--src/mongo/db/matcher/expression_leaf.cpp111
-rw-r--r--src/mongo/db/matcher/expression_leaf.h41
-rw-r--r--src/mongo/db/matcher/expression_leaf_test.cpp37
-rw-r--r--src/mongo/db/matcher/expression_optimize_test.cpp85
-rw-r--r--src/mongo/db/matcher/expression_parser.cpp6
-rw-r--r--src/mongo/db/matcher/expression_path.h28
-rw-r--r--src/mongo/db/matcher/expression_serialization_test.cpp352
-rw-r--r--src/mongo/db/matcher/expression_text_base.cpp12
-rw-r--r--src/mongo/db/matcher/expression_text_base.h8
-rw-r--r--src/mongo/db/matcher/expression_tree.cpp195
-rw-r--r--src/mongo/db/matcher/expression_tree.h23
-rw-r--r--src/mongo/db/matcher/expression_type.h27
-rw-r--r--src/mongo/db/matcher/expression_type_test.cpp24
-rw-r--r--src/mongo/db/matcher/expression_where_base.cpp6
-rw-r--r--src/mongo/db/matcher/expression_where_base.h4
-rw-r--r--src/mongo/db/matcher/parsed_match_expression_for_test.h80
-rw-r--r--src/mongo/db/matcher/rewrite_expr.cpp15
-rw-r--r--src/mongo/db/matcher/rewrite_expr.h35
-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.cpp16
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.h4
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index_test.cpp1
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_allowed_properties.cpp22
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_allowed_properties.h4
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_eq.cpp14
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_eq.h4
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_fmod.cpp11
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_fmod.h4
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_match_array_index.cpp24
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_match_array_index.h4
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_num_array_items.cpp7
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_num_array_items.h4
-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.h4
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_object_match.cpp9
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_object_match.h4
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_root_doc_eq.cpp4
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_root_doc_eq.h4
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_str_length.cpp7
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_str_length.h4
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_unique_items.cpp9
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_unique_items.h4
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_xor.cpp6
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_xor.h4
-rw-r--r--src/mongo/db/matcher/schema/object_keywords_test.cpp4
66 files changed, 737 insertions, 2691 deletions
diff --git a/src/mongo/db/matcher/SConscript b/src/mongo/db/matcher/SConscript
index 27ae56fac0e..4d484b0fadb 100644
--- a/src/mongo/db/matcher/SConscript
+++ b/src/mongo/db/matcher/SConscript
@@ -27,7 +27,6 @@ env.Library(
'expression_array.cpp',
'expression_expr.cpp',
'expression_geo.cpp',
- 'expression_geo_serializer.cpp',
'expression_internal_bucket_geo_within.cpp',
'expression_leaf.cpp',
'expression_parameterization.cpp',
@@ -74,7 +73,6 @@ env.Library(
'$BUILD_DIR/mongo/db/pipeline/expression_context',
'$BUILD_DIR/mongo/db/query/collation/collator_interface',
'$BUILD_DIR/mongo/db/query/query_knobs',
- '$BUILD_DIR/mongo/db/query/query_shape/query_shape_common',
'$BUILD_DIR/mongo/db/stats/counters',
'$BUILD_DIR/mongo/idl/idl_parser',
'$BUILD_DIR/mongo/util/regex_util',
diff --git a/src/mongo/db/matcher/expression.h b/src/mongo/db/matcher/expression.h
index fb6f5f6296a..33ea3ce8790 100644
--- a/src/mongo/db/matcher/expression.h
+++ b/src/mongo/db/matcher/expression.h
@@ -41,7 +41,6 @@
#include "mongo/db/matcher/match_details.h"
#include "mongo/db/matcher/matchable.h"
#include "mongo/db/pipeline/dependencies.h"
-#include "mongo/db/query/query_shape/serialization_options.h"
#include "mongo/util/fail_point.h"
namespace mongo {
@@ -478,42 +477,19 @@ public:
void addDependencies(DepsTracker* deps) const;
/**
- * 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.literalPolicy' is set to 'kToDebugTypeString', 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).
- *
- * includePath:
- * If set to false, 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}}.
+ * 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.
*/
- virtual void serialize(BSONObjBuilder* out,
- const SerializationOptions& options = {},
- bool includePath = true) const = 0;
+ virtual void serialize(BSONObjBuilder* out, bool includePath = true) const = 0;
/**
- * Convenience method which serializes this MatchExpression to a BSONObj. See the override with
- * a BSONObjBuilder* argument for details.
+ * Convenience method which serializes this MatchExpression to a BSONObj.
*/
- BSONObj serialize(const SerializationOptions& options = {}, bool includePath = true) const {
+ BSONObj serialize(bool includePath = true) const {
BSONObjBuilder bob;
- serialize(&bob, options, includePath);
+ serialize(&bob, includePath);
return bob.obj();
}
diff --git a/src/mongo/db/matcher/expression_algo.cpp b/src/mongo/db/matcher/expression_algo.cpp
index 36ae0ecbc92..5d56608227d 100644
--- a/src/mongo/db/matcher/expression_algo.cpp
+++ b/src/mongo/db/matcher/expression_algo.cpp
@@ -31,7 +31,6 @@
#include "mongo/platform/basic.h"
#include "mongo/base/checked_cast.h"
-#include "mongo/bson/unordered_fields_bsonobj_comparator.h"
#include "mongo/db/matcher/expression.h"
#include "mongo/db/matcher/expression_algo.h"
#include "mongo/db/matcher/expression_array.h"
@@ -375,7 +374,7 @@ unique_ptr<MatchExpression> createNorOfNodes(std::vector<unique_ptr<MatchExpress
*/
std::pair<unique_ptr<MatchExpression>, unique_ptr<MatchExpression>> splitMatchExpressionByFunction(
unique_ptr<MatchExpression> expr,
- const OrderedPathSet& fields,
+ const std::set<std::string>& fields,
expression::ShouldSplitExprFunc shouldSplitOut) {
if (shouldSplitOut(*expr, fields)) {
// 'expr' satisfies our split condition and can be completely split out.
@@ -441,7 +440,7 @@ std::pair<unique_ptr<MatchExpression>, unique_ptr<MatchExpression>> splitMatchEx
bool pathDependenciesAreExact(StringData key, const MatchExpression* expr) {
DepsTracker columnDeps;
expr->addDependencies(&columnDeps);
- return !columnDeps.needWholeDocument && columnDeps.fields == OrderedPathSet{key.toString()};
+ return !columnDeps.needWholeDocument && columnDeps.fields == std::set{key.toString()};
}
bool tryAddExprHelper(StringData path,
@@ -770,6 +769,7 @@ bool isSubsetOf(const MatchExpression* lhs, const MatchExpression* rhs) {
return false;
}
+// Checks if 'expr' has any children which do not have renaming implemented.
bool hasOnlyRenameableMatchExpressionChildren(const MatchExpression& expr) {
if (expr.matchType() == MatchExpression::MatchType::EXPRESSION) {
return true;
@@ -786,33 +786,7 @@ bool hasOnlyRenameableMatchExpressionChildren(const MatchExpression& expr) {
return true;
}
-bool containsDependency(const OrderedPathSet& testSet, const OrderedPathSet& prefixCandidates) {
- if (testSet.empty()) {
- return false;
- }
-
- PathComparator pathComparator;
- auto i2 = testSet.begin();
- for (auto p1 : prefixCandidates) {
- while (pathComparator(*i2, p1)) {
- ++i2;
- if (i2 == testSet.end()) {
- return false;
- }
- }
- // At this point we know that p1 <= *i2, so it may be identical or a path prefix.
- if (p1 == *i2 || isPathPrefixOf(p1, *i2)) {
- return true;
- }
- }
- return false;
-}
-
-bool areIndependent(const OrderedPathSet& pathSet1, const OrderedPathSet& pathSet2) {
- return !containsDependency(pathSet1, pathSet2) && !containsDependency(pathSet2, pathSet1);
-}
-
-bool isIndependentOf(const MatchExpression& expr, const OrderedPathSet& pathSet) {
+bool isIndependentOf(const MatchExpression& expr, const std::set<std::string>& pathSet) {
// Any expression types that do not have renaming implemented cannot have their independence
// evaluated here. See applyRenamesToExpression().
if (!hasOnlyRenameableMatchExpressionChildren(expr)) {
@@ -821,43 +795,35 @@ bool isIndependentOf(const MatchExpression& expr, const OrderedPathSet& pathSet)
auto depsTracker = DepsTracker{};
expr.addDependencies(&depsTracker);
- // Match expressions that generate random numbers can't be safely split out and pushed down.
- if (depsTracker.needRandomGenerator || depsTracker.needWholeDocument) {
- return false;
- }
- return areIndependent(pathSet, depsTracker.fields);
+ return std::none_of(
+ depsTracker.fields.begin(), depsTracker.fields.end(), [&pathSet](auto&& field) {
+ return pathSet.find(field) != pathSet.end() ||
+ std::any_of(pathSet.begin(), pathSet.end(), [&field](auto&& path) {
+ return expression::isPathPrefixOf(field, path) ||
+ expression::isPathPrefixOf(path, field);
+ });
+ });
}
-bool isOnlyDependentOn(const MatchExpression& expr, const OrderedPathSet& pathSet) {
+bool isOnlyDependentOn(const MatchExpression& expr, const std::set<std::string>& pathSet) {
// Any expression types that do not have renaming implemented cannot have their independence
// evaluated here. See applyRenamesToExpression().
if (!hasOnlyRenameableMatchExpressionChildren(expr)) {
return false;
}
- // The approach below takes only O(n log n) time.
-
- // Find the unique dependencies of pathSet.
- auto pathsDeps =
- DepsTracker::simplifyDependencies(pathSet, DepsTracker::TruncateToRootLevel::no);
- auto pathsDepsCopy = OrderedPathSet(pathsDeps.begin(), pathsDeps.end());
-
- // Now add the match expression's paths and see if the dependencies are the same.
- auto exprDepsTracker = DepsTracker{};
- expr.addDependencies(&exprDepsTracker);
- // Match expressions that generate random numbers can't be safely split out and pushed down.
- if (exprDepsTracker.needRandomGenerator) {
- return false;
- }
- pathsDepsCopy.insert(exprDepsTracker.fields.begin(), exprDepsTracker.fields.end());
-
- return pathsDeps ==
- DepsTracker::simplifyDependencies(pathsDepsCopy, DepsTracker::TruncateToRootLevel::no);
+ auto depsTracker = DepsTracker{};
+ expr.addDependencies(&depsTracker);
+ return std::all_of(depsTracker.fields.begin(), depsTracker.fields.end(), [&](auto&& field) {
+ return std::any_of(pathSet.begin(), pathSet.end(), [&](auto&& path) {
+ return path == field || isPathPrefixOf(path, field);
+ });
+ });
}
std::pair<unique_ptr<MatchExpression>, unique_ptr<MatchExpression>> splitMatchExpressionBy(
unique_ptr<MatchExpression> expr,
- const OrderedPathSet& fields,
+ const std::set<std::string>& fields,
const StringMap<std::string>& renames,
ShouldSplitExprFunc func /*= isIndependentOf */) {
auto splitExpr = splitMatchExpressionByFunction(std::move(expr), fields, func);
@@ -913,6 +879,11 @@ bool isPathPrefixOf(StringData first, StringData second) {
return second.startsWith(first) && second[first.size()] == '.';
}
+bool bidirectionalPathPrefixOf(StringData first, StringData second) {
+ return first == second || expression::isPathPrefixOf(first, second) ||
+ expression::isPathPrefixOf(second, first);
+}
+
boost::optional<StringMap<std::unique_ptr<MatchExpression>>> splitMatchExpressionForColumns(
const MatchExpression* me) {
boost::optional<StringMap<std::unique_ptr<MatchExpression>>> out;
diff --git a/src/mongo/db/matcher/expression_algo.h b/src/mongo/db/matcher/expression_algo.h
index 8c7c9b71c21..0fdbac756a0 100644
--- a/src/mongo/db/matcher/expression_algo.h
+++ b/src/mongo/db/matcher/expression_algo.h
@@ -34,7 +34,6 @@
#include <set>
#include "mongo/base/string_data.h"
-#include "mongo/db/pipeline/dependencies.h"
#include "mongo/util/string_map.h"
namespace mongo {
@@ -54,11 +53,6 @@ using NodeTraversalFunc = std::function<void(MatchExpression*, std::string)>;
bool hasExistencePredicateOnPath(const MatchExpression& expr, StringData path);
/**
- * Checks if 'expr' has any children which do not have renaming implemented.
- */
-bool hasOnlyRenameableMatchExpressionChildren(const MatchExpression& expr);
-
-/**
* Returns true if the documents matched by 'lhs' are a subset of the documents matched by
* 'rhs', i.e. a document matched by 'lhs' must also be matched by 'rhs', and false otherwise.
*
@@ -91,32 +85,17 @@ bool isSubsetOf(const MatchExpression* lhs, const MatchExpression* rhs);
* For example, {a: "foo", b: "bar"} is splittable by "b", while
* {$or: [{a: {$eq: "foo"}}, {b: {$eq: "bar"}}]} is not splittable by "b", due to the $or.
*/
-bool isSplittableBy(const MatchExpression& expr, const OrderedPathSet& pathSet);
-
-/**
- * True if no path in either set is contained by a path in the other. Does not check for
- * dependencies within each of the sets, just across sets. Runs in 0(n) time.
- *
- * areIndependent([a.b, b, a], [c]) --> true
- * areIndependent([a.b, b, a], [a.b.f]) --> false
- */
-bool areIndependent(const OrderedPathSet& pathSet1, const OrderedPathSet& pathSet2);
-
-/**
- * Return true if any of the fieldPaths in prefixCandidates are identical to or an ancestor of any
- * of the fieldpaths in testSet. The order of the parameters matters -- it's not commutative.
- */
-bool containsDependency(const OrderedPathSet& testSet, const OrderedPathSet& prefixCandidates);
+bool isSplittableBy(const MatchExpression& expr, const std::set<std::string>& pathSet);
/**
* Determine if 'expr' is reliant upon any path from 'pathSet'.
*/
-bool isIndependentOf(const MatchExpression& expr, const OrderedPathSet& pathSet);
+bool isIndependentOf(const MatchExpression& expr, const std::set<std::string>& pathSet);
/**
* Determine if 'expr' is reliant only upon paths from 'pathSet'.
*/
-bool isOnlyDependentOn(const MatchExpression& expr, const OrderedPathSet& pathSet);
+bool isOnlyDependentOn(const MatchExpression& expr, const std::set<std::string>& pathSet);
/**
* Returns whether the path represented by 'first' is an prefix of the path represented by 'second'.
@@ -131,13 +110,20 @@ bool isOnlyDependentOn(const MatchExpression& expr, const OrderedPathSet& pathSe
bool isPathPrefixOf(StringData first, StringData second);
/**
+ * Returns true if the first path is equal to the second path or if either is a prefix
+ * of the other.
+ */
+bool bidirectionalPathPrefixOf(StringData first, StringData second);
+
+/**
* Applies 'func' to each node of 'expr', where the first argument is a pointer to that actual node
* (not a copy), and the second argument is the path to that node. Callers should not depend on the
* order of the traversal of the nodes.
*/
void mapOver(MatchExpression* expr, NodeTraversalFunc func, std::string path = "");
-using ShouldSplitExprFunc = std::function<bool(const MatchExpression&, const OrderedPathSet&)>;
+using ShouldSplitExprFunc =
+ std::function<bool(const MatchExpression&, const std::set<std::string>&)>;
/**
* Attempt to split 'expr' into two MatchExpressions according to 'func'. 'func' describes the
@@ -160,7 +146,7 @@ using ShouldSplitExprFunc = std::function<bool(const MatchExpression&, const Ord
*/
std::pair<std::unique_ptr<MatchExpression>, std::unique_ptr<MatchExpression>>
splitMatchExpressionBy(std::unique_ptr<MatchExpression> expr,
- const OrderedPathSet& fields,
+ const std::set<std::string>& fields,
const StringMap<std::string>& renames,
ShouldSplitExprFunc func = isIndependentOf);
@@ -169,8 +155,6 @@ splitMatchExpressionBy(std::unique_ptr<MatchExpression> expr,
* to the new values of those paths. For example, suppose the original match expression is
* {old: {$gt: 3}} and 'renames' contains the mapping "old" => "new". At the end, 'expr' will be
* {new: {$gt: 3}}.
- *
- * The caller should make sure that `expr` is renamable as a whole.
*/
void applyRenamesToExpression(MatchExpression* expr, const StringMap<std::string>& renames);
diff --git a/src/mongo/db/matcher/expression_algo_test.cpp b/src/mongo/db/matcher/expression_algo_test.cpp
index 23d3e2f4606..8679c759cd5 100644
--- a/src/mongo/db/matcher/expression_algo_test.cpp
+++ b/src/mongo/db/matcher/expression_algo_test.cpp
@@ -36,7 +36,6 @@
#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"
@@ -45,16 +44,30 @@ 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());
+ }
-void assertMatchesEqual(const ParsedMatchExpressionForTest& expected,
- const std::unique_ptr<MatchExpression>& actual) {
- if (expected.get() == nullptr) {
- ASSERT(actual == nullptr);
- return;
+ const MatchExpression* get() const {
+ return _expr.get();
}
- ASSERT(actual != nullptr);
- ASSERT_EQ(expected.get()->toString(), actual.get()->toString());
-}
+
+private:
+ const BSONObj _obj;
+ std::unique_ptr<MatchExpression> _expr;
+ boost::intrusive_ptr<ExpressionContext> _expCtx;
+};
TEST(ExpressionAlgoIsSubsetOf, NullAndOmittedField) {
// Verify that the ComparisonMatchExpression constructor prohibits creating a match expression
@@ -64,29 +77,29 @@ TEST(ExpressionAlgoIsSubsetOf, NullAndOmittedField) {
ASSERT_EQUALS(ErrorCodes::BadValue,
MatchExpressionParser::parse(undefined, std::move(expCtx)).getStatus());
- ParsedMatchExpressionForTest empty("{}");
- ParsedMatchExpressionForTest null("{a: null}");
+ ParsedMatchExpression empty("{}");
+ ParsedMatchExpression null("{a: null}");
ASSERT_TRUE(expression::isSubsetOf(null.get(), empty.get()));
ASSERT_FALSE(expression::isSubsetOf(empty.get(), null.get()));
- ParsedMatchExpressionForTest b1("{b: 1}");
- ParsedMatchExpressionForTest aNullB1("{a: null, b: 1}");
+ ParsedMatchExpression b1("{b: 1}");
+ ParsedMatchExpression aNullB1("{a: null, b: 1}");
ASSERT_TRUE(expression::isSubsetOf(aNullB1.get(), b1.get()));
ASSERT_FALSE(expression::isSubsetOf(b1.get(), aNullB1.get()));
- ParsedMatchExpressionForTest a1C3("{a: 1, c: 3}");
- ParsedMatchExpressionForTest a1BNullC3("{a: 1, b: null, c: 3}");
+ ParsedMatchExpression a1C3("{a: 1, c: 3}");
+ ParsedMatchExpression 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) {
- ParsedMatchExpressionForTest eqNull("{x: null}");
- ParsedMatchExpressionForTest inNull("{x: {$in: [null]}}");
- ParsedMatchExpressionForTest inNullOr2("{x: {$in: [null, 2]}}");
+ ParsedMatchExpression eqNull("{x: null}");
+ ParsedMatchExpression inNull("{x: {$in: [null]}}");
+ ParsedMatchExpression inNullOr2("{x: {$in: [null, 2]}}");
ASSERT_TRUE(expression::isSubsetOf(inNull.get(), eqNull.get()));
ASSERT_FALSE(expression::isSubsetOf(inNullOr2.get(), eqNull.get()));
@@ -96,19 +109,19 @@ TEST(ExpressionAlgoIsSubsetOf, NullAndIn) {
}
TEST(ExpressionAlgoIsSubsetOf, NullAndExists) {
- ParsedMatchExpressionForTest null("{x: null}");
- ParsedMatchExpressionForTest exists("{x: {$exists: true}}");
+ ParsedMatchExpression null("{x: null}");
+ ParsedMatchExpression exists("{x: {$exists: true}}");
ASSERT_FALSE(expression::isSubsetOf(null.get(), exists.get()));
ASSERT_FALSE(expression::isSubsetOf(exists.get(), null.get()));
}
TEST(ExpressionAlgoIsSubsetOf, Compare_NaN) {
- 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]}}");
+ 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]}}");
ASSERT_TRUE(expression::isSubsetOf(nan.get(), nan.get()));
ASSERT_FALSE(expression::isSubsetOf(nan.get(), lt.get()));
@@ -122,7 +135,7 @@ TEST(ExpressionAlgoIsSubsetOf, Compare_NaN) {
ASSERT_FALSE(expression::isSubsetOf(nan.get(), in.get()));
ASSERT_FALSE(expression::isSubsetOf(in.get(), nan.get()));
- ParsedMatchExpressionForTest decNan("{x : NumberDecimal(\"NaN\") }");
+ ParsedMatchExpression 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()));
@@ -137,9 +150,9 @@ TEST(ExpressionAlgoIsSubsetOf, Compare_NaN) {
}
TEST(ExpressionAlgoIsSubsetOf, Compare_EQ) {
- ParsedMatchExpressionForTest a5("{a: 5}");
- ParsedMatchExpressionForTest a6("{a: 6}");
- ParsedMatchExpressionForTest b5("{b: 5}");
+ ParsedMatchExpression a5("{a: 5}");
+ ParsedMatchExpression a6("{a: 6}");
+ ParsedMatchExpression b5("{b: 5}");
ASSERT_TRUE(expression::isSubsetOf(a5.get(), a5.get()));
ASSERT_FALSE(expression::isSubsetOf(a5.get(), a6.get()));
@@ -147,10 +160,10 @@ TEST(ExpressionAlgoIsSubsetOf, Compare_EQ) {
}
TEST(ExpressionAlgoIsSubsetOf, CompareAnd_EQ) {
- ParsedMatchExpressionForTest a1B2("{a: 1, b: 2}");
- ParsedMatchExpressionForTest a1B7("{a: 1, b: 7}");
- ParsedMatchExpressionForTest a1("{a: 1}");
- ParsedMatchExpressionForTest b2("{b: 2}");
+ ParsedMatchExpression a1B2("{a: 1, b: 2}");
+ ParsedMatchExpression a1B7("{a: 1, b: 7}");
+ ParsedMatchExpression a1("{a: 1}");
+ ParsedMatchExpression b2("{b: 2}");
ASSERT_TRUE(expression::isSubsetOf(a1B2.get(), a1B2.get()));
ASSERT_FALSE(expression::isSubsetOf(a1B2.get(), a1B7.get()));
@@ -161,74 +174,66 @@ TEST(ExpressionAlgoIsSubsetOf, CompareAnd_EQ) {
}
TEST(ExpressionAlgoIsSubsetOf, CompareAnd_GT) {
- 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) {
- ParsedMatchExpressionForTest filter("{a: {$gt: 5, $lt: 7}}");
- ParsedMatchExpressionForTest query("{a: {$gt: 5, $lt: 6}}");
+ ParsedMatchExpression filter("{a: {$gt: 5}, b: {$gt: 6}}");
+ ParsedMatchExpression 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, CompareOr_LT) {
- 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}]}");
+ 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}]}");
ASSERT_TRUE(expression::isSubsetOf(eq2OrEq3.get(), lt5.get()));
ASSERT_FALSE(expression::isSubsetOf(eq4OrEq5.get(), lt5.get()));
ASSERT_FALSE(expression::isSubsetOf(eq4OrEq6.get(), lt5.get()));
- ParsedMatchExpressionForTest lt4OrLt5("{$or: [{a: {$lt: 4}}, {a: {$lt: 5}}]}");
+ ParsedMatchExpression lt4OrLt5("{$or: [{a: {$lt: 4}}, {a: {$lt: 5}}]}");
ASSERT_TRUE(expression::isSubsetOf(lt4OrLt5.get(), lt5.get()));
ASSERT_TRUE(expression::isSubsetOf(lt5.get(), lt4OrLt5.get()));
- ParsedMatchExpressionForTest lt7OrLt8("{$or: [{a: {$lt: 7}}, {a: {$lt: 8}}]}");
+ ParsedMatchExpression 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) {
- 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}]}");
+ 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}]}");
ASSERT_FALSE(expression::isSubsetOf(eq4OrEq6.get(), gte5.get()));
ASSERT_TRUE(expression::isSubsetOf(eq5OrEq6.get(), gte5.get()));
ASSERT_TRUE(expression::isSubsetOf(eq7OrEq8.get(), gte5.get()));
- ParsedMatchExpressionForTest gte5OrGte6("{$or: [{a: {$gte: 5}}, {a: {$gte: 6}}]}");
+ ParsedMatchExpression gte5OrGte6("{$or: [{a: {$gte: 5}}, {a: {$gte: 6}}]}");
ASSERT_TRUE(expression::isSubsetOf(gte5OrGte6.get(), gte5.get()));
ASSERT_TRUE(expression::isSubsetOf(gte5.get(), gte5OrGte6.get()));
- ParsedMatchExpressionForTest gte3OrGte4("{$or: [{a: {$gte: 3}}, {a: {$gte: 4}}]}");
+ ParsedMatchExpression 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) {
- ParsedMatchExpressionForTest number("{x: {$gt: 1}}");
- ParsedMatchExpressionForTest string("{x: {$gt: 'a'}}");
+ ParsedMatchExpression number("{x: {$gt: 1}}");
+ ParsedMatchExpression string("{x: {$gt: 'a'}}");
ASSERT_FALSE(expression::isSubsetOf(number.get(), string.get()));
ASSERT_FALSE(expression::isSubsetOf(string.get(), number.get()));
}
TEST(ExpressionAlgoIsSubsetOf, DifferentNumberTypes) {
- ParsedMatchExpressionForTest numberDouble("{x: 5.0}");
- ParsedMatchExpressionForTest numberInt("{x: NumberInt(5)}");
- ParsedMatchExpressionForTest numberLong("{x: NumberLong(5)}");
+ ParsedMatchExpression numberDouble("{x: 5.0}");
+ ParsedMatchExpression numberInt("{x: NumberInt(5)}");
+ ParsedMatchExpression numberLong("{x: NumberLong(5)}");
ASSERT_TRUE(expression::isSubsetOf(numberDouble.get(), numberInt.get()));
ASSERT_TRUE(expression::isSubsetOf(numberDouble.get(), numberLong.get()));
@@ -239,15 +244,15 @@ TEST(ExpressionAlgoIsSubsetOf, DifferentNumberTypes) {
}
TEST(ExpressionAlgoIsSubsetOf, PointInUnboundedRange) {
- ParsedMatchExpressionForTest a4("{a: 4}");
- ParsedMatchExpressionForTest a5("{a: 5}");
- ParsedMatchExpressionForTest a6("{a: 6}");
- ParsedMatchExpressionForTest b5("{b: 5}");
+ ParsedMatchExpression a4("{a: 4}");
+ ParsedMatchExpression a5("{a: 5}");
+ ParsedMatchExpression a6("{a: 6}");
+ ParsedMatchExpression b5("{b: 5}");
- ParsedMatchExpressionForTest lt5("{a: {$lt: 5}}");
- ParsedMatchExpressionForTest lte5("{a: {$lte: 5}}");
- ParsedMatchExpressionForTest gte5("{a: {$gte: 5}}");
- ParsedMatchExpressionForTest gt5("{a: {$gt: 5}}");
+ ParsedMatchExpression lt5("{a: {$lt: 5}}");
+ ParsedMatchExpression lte5("{a: {$lte: 5}}");
+ ParsedMatchExpression gte5("{a: {$gte: 5}}");
+ ParsedMatchExpression gt5("{a: {$gt: 5}}");
ASSERT_TRUE(expression::isSubsetOf(a4.get(), lte5.get()));
ASSERT_TRUE(expression::isSubsetOf(a5.get(), lte5.get()));
@@ -279,26 +284,26 @@ TEST(ExpressionAlgoIsSubsetOf, PointInUnboundedRange) {
}
TEST(ExpressionAlgoIsSubsetOf, PointInBoundedRange) {
- ParsedMatchExpressionForTest filter("{a: {$gt: 5, $lt: 10}}");
- ParsedMatchExpressionForTest query("{a: 6}");
+ ParsedMatchExpression filter("{a: {$gt: 5, $lt: 10}}");
+ ParsedMatchExpression query("{a: 6}");
ASSERT_TRUE(expression::isSubsetOf(query.get(), filter.get()));
ASSERT_FALSE(expression::isSubsetOf(filter.get(), query.get()));
}
TEST(ExpressionAlgoIsSubsetOf, PointInBoundedRange_FakeAnd) {
- ParsedMatchExpressionForTest filter("{a: {$gt: 5, $lt: 10}}");
- ParsedMatchExpressionForTest query("{$and: [{a: 6}, {a: 6}]}");
+ ParsedMatchExpression filter("{a: {$gt: 5, $lt: 10}}");
+ ParsedMatchExpression 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) {
- 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]}}");
+ 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]}}");
ASSERT_TRUE(expression::isSubsetOf(queryAllInside.get(), filter.get()));
ASSERT_FALSE(expression::isSubsetOf(queryStraddleLower.get(), filter.get()));
@@ -306,18 +311,18 @@ TEST(ExpressionAlgoIsSubsetOf, MultiplePointsInBoundedRange) {
}
TEST(ExpressionAlgoIsSubsetOf, PointInCompoundRange) {
- ParsedMatchExpressionForTest filter("{a: {$gt: 5}, b: {$gt: 6}, c: {$gt: 7}}");
- ParsedMatchExpressionForTest query("{a: 10, b: 10, c: 10}");
+ ParsedMatchExpression filter("{a: {$gt: 5}, b: {$gt: 6}, c: {$gt: 7}}");
+ ParsedMatchExpression 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) {
- ParsedMatchExpressionForTest lte4("{x: {$lte: 4}}");
- ParsedMatchExpressionForTest lt5("{x: {$lt: 5}}");
- ParsedMatchExpressionForTest lte5("{x: {$lte: 5}}");
- ParsedMatchExpressionForTest lt6("{x: {$lt: 6}}");
+ ParsedMatchExpression lte4("{x: {$lte: 4}}");
+ ParsedMatchExpression lt5("{x: {$lt: 5}}");
+ ParsedMatchExpression lte5("{x: {$lte: 5}}");
+ ParsedMatchExpression lt6("{x: {$lt: 6}}");
ASSERT_TRUE(expression::isSubsetOf(lte4.get(), lte5.get()));
ASSERT_TRUE(expression::isSubsetOf(lt5.get(), lte5.get()));
@@ -331,10 +336,10 @@ TEST(ExpressionAlgoIsSubsetOf, Compare_LT_LTE) {
}
TEST(ExpressionAlgoIsSubsetOf, Compare_GT_GTE) {
- ParsedMatchExpressionForTest gte6("{x: {$gte: 6}}");
- ParsedMatchExpressionForTest gt5("{x: {$gt: 5}}");
- ParsedMatchExpressionForTest gte5("{x: {$gte: 5}}");
- ParsedMatchExpressionForTest gt4("{x: {$gt: 4}}");
+ ParsedMatchExpression gte6("{x: {$gte: 6}}");
+ ParsedMatchExpression gt5("{x: {$gt: 5}}");
+ ParsedMatchExpression gte5("{x: {$gte: 5}}");
+ ParsedMatchExpression gt4("{x: {$gt: 4}}");
ASSERT_TRUE(expression::isSubsetOf(gte6.get(), gte5.get()));
ASSERT_TRUE(expression::isSubsetOf(gt5.get(), gte5.get()));
@@ -348,19 +353,18 @@ TEST(ExpressionAlgoIsSubsetOf, Compare_GT_GTE) {
}
TEST(ExpressionAlgoIsSubsetOf, BoundedRangeInUnboundedRange) {
- ParsedMatchExpressionForTest filter("{a: {$gt: 1}}");
- ParsedMatchExpressionForTest query("{a: {$gt: 5, $lt: 10}}");
+ ParsedMatchExpression filter("{a: {$gt: 1}}");
+ ParsedMatchExpression 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) {
- 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}}]}");
+ 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}}]}");
ASSERT_FALSE(expression::isSubsetOf(negative.get(), filter.get()));
ASSERT_TRUE(expression::isSubsetOf(unbounded.get(), filter.get()));
@@ -368,10 +372,10 @@ TEST(ExpressionAlgoIsSubsetOf, MultipleRangesInUnboundedRange) {
}
TEST(ExpressionAlgoIsSubsetOf, MultipleFields) {
- 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}]}");
+ 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}]}");
ASSERT_FALSE(expression::isSubsetOf(onlyA.get(), filter.get()));
ASSERT_FALSE(expression::isSubsetOf(onlyB.get(), filter.get()));
@@ -379,18 +383,18 @@ TEST(ExpressionAlgoIsSubsetOf, MultipleFields) {
}
TEST(ExpressionAlgoIsSubsetOf, Compare_LT_In) {
- ParsedMatchExpressionForTest lt("{a: {$lt: 5}}");
+ ParsedMatchExpression lt("{a: {$lt: 5}}");
- ParsedMatchExpressionForTest inLt("{a: {$in: [4.9]}}");
- ParsedMatchExpressionForTest inEq("{a: {$in: [5]}}");
- ParsedMatchExpressionForTest inGt("{a: {$in: [5.1]}}");
- ParsedMatchExpressionForTest inNull("{a: {$in: [null]}}");
+ ParsedMatchExpression inLt("{a: {$in: [4.9]}}");
+ ParsedMatchExpression inEq("{a: {$in: [5]}}");
+ ParsedMatchExpression inGt("{a: {$in: [5.1]}}");
+ ParsedMatchExpression inNull("{a: {$in: [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]}}");
+ 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]}}");
ASSERT_TRUE(expression::isSubsetOf(inLt.get(), lt.get()));
ASSERT_FALSE(expression::isSubsetOf(inEq.get(), lt.get()));
@@ -407,18 +411,18 @@ TEST(ExpressionAlgoIsSubsetOf, Compare_LT_In) {
}
TEST(ExpressionAlgoIsSubsetOf, Compare_LTE_In) {
- ParsedMatchExpressionForTest lte("{a: {$lte: 5}}");
+ ParsedMatchExpression lte("{a: {$lte: 5}}");
- ParsedMatchExpressionForTest inLt("{a: {$in: [4.9]}}");
- ParsedMatchExpressionForTest inEq("{a: {$in: [5]}}");
- ParsedMatchExpressionForTest inGt("{a: {$in: [5.1]}}");
- ParsedMatchExpressionForTest inNull("{a: {$in: [null]}}");
+ ParsedMatchExpression inLt("{a: {$in: [4.9]}}");
+ ParsedMatchExpression inEq("{a: {$in: [5]}}");
+ ParsedMatchExpression inGt("{a: {$in: [5.1]}}");
+ ParsedMatchExpression inNull("{a: {$in: [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]}}");
+ 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]}}");
ASSERT_TRUE(expression::isSubsetOf(inLt.get(), lte.get()));
ASSERT_TRUE(expression::isSubsetOf(inEq.get(), lte.get()));
@@ -435,16 +439,16 @@ TEST(ExpressionAlgoIsSubsetOf, Compare_LTE_In) {
}
TEST(ExpressionAlgoIsSubsetOf, Compare_EQ_In) {
- ParsedMatchExpressionForTest eq("{a: 5}");
+ ParsedMatchExpression eq("{a: 5}");
- ParsedMatchExpressionForTest inLt("{a: {$in: [4.9]}}");
- ParsedMatchExpressionForTest inEq("{a: {$in: [5]}}");
- ParsedMatchExpressionForTest inGt("{a: {$in: [5.1]}}");
- ParsedMatchExpressionForTest inNull("{a: {$in: [null]}}");
+ ParsedMatchExpression inLt("{a: {$in: [4.9]}}");
+ ParsedMatchExpression inEq("{a: {$in: [5]}}");
+ ParsedMatchExpression inGt("{a: {$in: [5.1]}}");
+ ParsedMatchExpression inNull("{a: {$in: [null]}}");
- ParsedMatchExpressionForTest inAllEq("{a: {$in: [5, 5.0]}}");
- ParsedMatchExpressionForTest inStraddle("{a: {$in: [4, 6]}}");
- ParsedMatchExpressionForTest inEqAndNull("{a: {$in: [5, null]}}");
+ ParsedMatchExpression inAllEq("{a: {$in: [5, 5.0]}}");
+ ParsedMatchExpression inStraddle("{a: {$in: [4, 6]}}");
+ ParsedMatchExpression inEqAndNull("{a: {$in: [5, null]}}");
ASSERT_FALSE(expression::isSubsetOf(inLt.get(), eq.get()));
ASSERT_TRUE(expression::isSubsetOf(inEq.get(), eq.get()));
@@ -459,18 +463,18 @@ TEST(ExpressionAlgoIsSubsetOf, Compare_EQ_In) {
}
TEST(ExpressionAlgoIsSubsetOf, Compare_GT_In) {
- ParsedMatchExpressionForTest gt("{a: {$gt: 5}}");
+ ParsedMatchExpression gt("{a: {$gt: 5}}");
- ParsedMatchExpressionForTest inLt("{a: {$in: [4.9]}}");
- ParsedMatchExpressionForTest inEq("{a: {$in: [5]}}");
- ParsedMatchExpressionForTest inGt("{a: {$in: [5.1]}}");
- ParsedMatchExpressionForTest inNull("{a: {$in: [null]}}");
+ ParsedMatchExpression inLt("{a: {$in: [4.9]}}");
+ ParsedMatchExpression inEq("{a: {$in: [5]}}");
+ ParsedMatchExpression inGt("{a: {$in: [5.1]}}");
+ ParsedMatchExpression inNull("{a: {$in: [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]}}");
+ 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]}}");
ASSERT_FALSE(expression::isSubsetOf(inLt.get(), gt.get()));
ASSERT_FALSE(expression::isSubsetOf(inEq.get(), gt.get()));
@@ -487,18 +491,18 @@ TEST(ExpressionAlgoIsSubsetOf, Compare_GT_In) {
}
TEST(ExpressionAlgoIsSubsetOf, Compare_GTE_In) {
- ParsedMatchExpressionForTest gte("{a: {$gte: 5}}");
+ ParsedMatchExpression gte("{a: {$gte: 5}}");
- ParsedMatchExpressionForTest inLt("{a: {$in: [4.9]}}");
- ParsedMatchExpressionForTest inEq("{a: {$in: [5]}}");
- ParsedMatchExpressionForTest inGt("{a: {$in: [5.1]}}");
- ParsedMatchExpressionForTest inNull("{a: {$in: [null]}}");
+ ParsedMatchExpression inLt("{a: {$in: [4.9]}}");
+ ParsedMatchExpression inEq("{a: {$in: [5]}}");
+ ParsedMatchExpression inGt("{a: {$in: [5.1]}}");
+ ParsedMatchExpression inNull("{a: {$in: [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]}}");
+ 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]}}");
ASSERT_FALSE(expression::isSubsetOf(inLt.get(), gte.get()));
ASSERT_TRUE(expression::isSubsetOf(inEq.get(), gte.get()));
@@ -515,12 +519,12 @@ TEST(ExpressionAlgoIsSubsetOf, Compare_GTE_In) {
}
TEST(ExpressionAlgoIsSubsetOf, RegexAndIn) {
- 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]}}");
+ 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]}}");
ASSERT_FALSE(expression::isSubsetOf(inRegexAOrEq1.get(), eq1.get()));
ASSERT_FALSE(expression::isSubsetOf(inRegexA.get(), eqA.get()));
@@ -532,10 +536,10 @@ TEST(ExpressionAlgoIsSubsetOf, RegexAndIn) {
}
TEST(ExpressionAlgoIsSubsetOf, Exists) {
- 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}");
+ 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}");
ASSERT_TRUE(expression::isSubsetOf(aExists.get(), aExists.get()));
ASSERT_FALSE(expression::isSubsetOf(aExists.get(), bExists.get()));
@@ -550,10 +554,10 @@ TEST(ExpressionAlgoIsSubsetOf, Exists) {
}
TEST(ExpressionAlgoIsSubsetOf, Compare_Exists) {
- ParsedMatchExpressionForTest exists("{a: {$exists: true}}");
- ParsedMatchExpressionForTest eq("{a: 1}");
- ParsedMatchExpressionForTest gt("{a: {$gt: 4}}");
- ParsedMatchExpressionForTest lte("{a: {$lte: 7}}");
+ ParsedMatchExpression exists("{a: {$exists: true}}");
+ ParsedMatchExpression eq("{a: 1}");
+ ParsedMatchExpression gt("{a: {$gt: 4}}");
+ ParsedMatchExpression lte("{a: {$lte: 7}}");
ASSERT_TRUE(expression::isSubsetOf(eq.get(), exists.get()));
ASSERT_TRUE(expression::isSubsetOf(gt.get(), exists.get()));
@@ -565,9 +569,9 @@ TEST(ExpressionAlgoIsSubsetOf, Compare_Exists) {
}
TEST(ExpressionAlgoIsSubsetOf, Type) {
- ParsedMatchExpressionForTest aType1("{a: {$type: 1}}");
- ParsedMatchExpressionForTest aType2("{a: {$type: 2}}");
- ParsedMatchExpressionForTest bType2("{b: {$type: 2}}");
+ ParsedMatchExpression aType1("{a: {$type: 1}}");
+ ParsedMatchExpression aType2("{a: {$type: 2}}");
+ ParsedMatchExpression bType2("{b: {$type: 2}}");
ASSERT_FALSE(expression::isSubsetOf(aType1.get(), aType2.get()));
ASSERT_FALSE(expression::isSubsetOf(aType2.get(), aType1.get()));
@@ -577,9 +581,9 @@ TEST(ExpressionAlgoIsSubsetOf, Type) {
}
TEST(ExpressionAlgoIsSubsetOf, TypeAndExists) {
- ParsedMatchExpressionForTest aExists("{a: {$exists: true}}");
- ParsedMatchExpressionForTest aType2("{a: {$type: 2}}");
- ParsedMatchExpressionForTest bType2("{b: {$type: 2}}");
+ ParsedMatchExpression aExists("{a: {$exists: true}}");
+ ParsedMatchExpression aType2("{a: {$type: 2}}");
+ ParsedMatchExpression bType2("{b: {$type: 2}}");
ASSERT_TRUE(expression::isSubsetOf(aType2.get(), aExists.get()));
ASSERT_FALSE(expression::isSubsetOf(aExists.get(), aType2.get()));
@@ -587,10 +591,10 @@ TEST(ExpressionAlgoIsSubsetOf, TypeAndExists) {
}
TEST(ExpressionAlgoIsSubsetOf, AllAndExists) {
- 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']}}");
+ 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']}}");
ASSERT_TRUE(expression::isSubsetOf(aAll.get(), aExists.get()));
ASSERT_FALSE(expression::isSubsetOf(bAll.get(), aExists.get()));
@@ -598,10 +602,10 @@ TEST(ExpressionAlgoIsSubsetOf, AllAndExists) {
}
TEST(ExpressionAlgoIsSubsetOf, ElemMatchAndExists_Value) {
- 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}}}");
+ 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}}}");
ASSERT_TRUE(expression::isSubsetOf(aElemMatch.get(), aExists.get()));
ASSERT_FALSE(expression::isSubsetOf(aExists.get(), aElemMatch.get()));
@@ -610,10 +614,10 @@ TEST(ExpressionAlgoIsSubsetOf, ElemMatchAndExists_Value) {
}
TEST(ExpressionAlgoIsSubsetOf, ElemMatchAndExists_Object) {
- 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}}}");
+ 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}}}");
ASSERT_TRUE(expression::isSubsetOf(aElemMatch.get(), aExists.get()));
ASSERT_FALSE(expression::isSubsetOf(aExists.get(), aElemMatch.get()));
@@ -622,11 +626,11 @@ TEST(ExpressionAlgoIsSubsetOf, ElemMatchAndExists_Object) {
}
TEST(ExpressionAlgoIsSubsetOf, SizeAndExists) {
- 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}}");
+ 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}}");
ASSERT_TRUE(expression::isSubsetOf(aSize0.get(), aExists.get()));
ASSERT_TRUE(expression::isSubsetOf(aSize1.get(), aExists.get()));
@@ -636,28 +640,28 @@ TEST(ExpressionAlgoIsSubsetOf, SizeAndExists) {
}
TEST(ExpressionAlgoIsSubsetOf, ModAndExists) {
- ParsedMatchExpressionForTest aExists("{a: {$exists: true}}");
- ParsedMatchExpressionForTest aMod5("{a: {$mod: [5, 0]}}");
- ParsedMatchExpressionForTest bMod5("{b: {$mod: [5, 0]}}");
+ ParsedMatchExpression aExists("{a: {$exists: true}}");
+ ParsedMatchExpression aMod5("{a: {$mod: [5, 0]}}");
+ ParsedMatchExpression bMod5("{b: {$mod: [5, 0]}}");
ASSERT_TRUE(expression::isSubsetOf(aMod5.get(), aExists.get()));
ASSERT_FALSE(expression::isSubsetOf(bMod5.get(), aExists.get()));
}
TEST(ExpressionAlgoIsSubsetOf, RegexAndExists) {
- ParsedMatchExpressionForTest aExists("{a: {$exists: true}}");
- ParsedMatchExpressionForTest aRegex("{a: {$regex: 'pattern'}}");
- ParsedMatchExpressionForTest bRegex("{b: {$regex: 'pattern'}}");
+ ParsedMatchExpression aExists("{a: {$exists: true}}");
+ ParsedMatchExpression aRegex("{a: {$regex: 'pattern'}}");
+ ParsedMatchExpression bRegex("{b: {$regex: 'pattern'}}");
ASSERT_TRUE(expression::isSubsetOf(aRegex.get(), aExists.get()));
ASSERT_FALSE(expression::isSubsetOf(bRegex.get(), aExists.get()));
}
TEST(ExpressionAlgoIsSubsetOf, InAndExists) {
- 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]}}");
+ 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]}}");
ASSERT_TRUE(expression::isSubsetOf(aIn.get(), aExists.get()));
ASSERT_FALSE(expression::isSubsetOf(bIn.get(), aExists.get()));
@@ -669,10 +673,10 @@ TEST(ExpressionAlgoIsSubsetOf, InAndExists) {
}
TEST(ExpressionAlgoIsSubsetOf, NinAndExists) {
- 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]}}");
+ 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]}}");
ASSERT_FALSE(expression::isSubsetOf(aNin.get(), aExists.get()));
ASSERT_FALSE(expression::isSubsetOf(bNin.get(), aExists.get()));
@@ -680,10 +684,10 @@ TEST(ExpressionAlgoIsSubsetOf, NinAndExists) {
}
TEST(ExpressionAlgoIsSubsetOf, Compare_Exists_NE) {
- ParsedMatchExpressionForTest aExists("{a: {$exists: true}}");
- ParsedMatchExpressionForTest aNotEqual1("{a: {$ne: 1}}");
- ParsedMatchExpressionForTest bNotEqual1("{b: {$ne: 1}}");
- ParsedMatchExpressionForTest aNotEqualNull("{a: {$ne: null}}");
+ ParsedMatchExpression aExists("{a: {$exists: true}}");
+ ParsedMatchExpression aNotEqual1("{a: {$ne: 1}}");
+ ParsedMatchExpression bNotEqual1("{b: {$ne: 1}}");
+ ParsedMatchExpression aNotEqualNull("{a: {$ne: null}}");
ASSERT_FALSE(expression::isSubsetOf(aNotEqual1.get(), aExists.get()));
ASSERT_FALSE(expression::isSubsetOf(bNotEqual1.get(), aExists.get()));
@@ -692,13 +696,13 @@ TEST(ExpressionAlgoIsSubsetOf, Compare_Exists_NE) {
TEST(ExpressionAlgoIsSubsetOf, CollationAwareStringComparison) {
CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString);
- ParsedMatchExpressionForTest lhs("{a: {$gt: 'abc'}}", &collator);
- ParsedMatchExpressionForTest rhs("{a: {$gt: 'cba'}}", &collator);
+ ParsedMatchExpression lhs("{a: {$gt: 'abc'}}", &collator);
+ ParsedMatchExpression rhs("{a: {$gt: 'cba'}}", &collator);
ASSERT_TRUE(expression::isSubsetOf(lhs.get(), rhs.get()));
- ParsedMatchExpressionForTest lhsLT("{a: {$lt: 'abc'}}", &collator);
- ParsedMatchExpressionForTest rhsLT("{a: {$lt: 'cba'}}", &collator);
+ ParsedMatchExpression lhsLT("{a: {$lt: 'abc'}}", &collator);
+ ParsedMatchExpression rhsLT("{a: {$lt: 'cba'}}", &collator);
ASSERT_FALSE(expression::isSubsetOf(lhsLT.get(), rhsLT.get()));
}
@@ -706,27 +710,27 @@ TEST(ExpressionAlgoIsSubsetOf, CollationAwareStringComparison) {
TEST(ExpressionAlgoIsSubsetOf, NonMatchingCollationsStringComparison) {
CollatorInterfaceMock collatorAlwaysEqual(CollatorInterfaceMock::MockType::kAlwaysEqual);
CollatorInterfaceMock collatorReverseString(CollatorInterfaceMock::MockType::kReverseString);
- ParsedMatchExpressionForTest lhs("{a: {$gt: 'abc'}}", &collatorAlwaysEqual);
- ParsedMatchExpressionForTest rhs("{a: {$gt: 'cba'}}", &collatorReverseString);
+ ParsedMatchExpression lhs("{a: {$gt: 'abc'}}", &collatorAlwaysEqual);
+ ParsedMatchExpression rhs("{a: {$gt: 'cba'}}", &collatorReverseString);
ASSERT_FALSE(expression::isSubsetOf(lhs.get(), rhs.get()));
- ParsedMatchExpressionForTest lhsLT("{a: {$lt: 'abc'}}", &collatorAlwaysEqual);
- ParsedMatchExpressionForTest rhsLT("{a: {$lt: 'cba'}}", &collatorReverseString);
+ ParsedMatchExpression lhsLT("{a: {$lt: 'abc'}}", &collatorAlwaysEqual);
+ ParsedMatchExpression rhsLT("{a: {$lt: 'cba'}}", &collatorReverseString);
ASSERT_FALSE(expression::isSubsetOf(lhsLT.get(), rhsLT.get()));
}
TEST(ExpressionAlgoIsSubsetOf, CollationAwareStringComparisonIn) {
CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString);
- ParsedMatchExpressionForTest lhsAllGTcba("{a: {$in: ['abc', 'cbc']}}", &collator);
- ParsedMatchExpressionForTest lhsSomeGTcba("{a: {$in: ['abc', 'aba']}}", &collator);
- ParsedMatchExpressionForTest rhs("{a: {$gt: 'cba'}}", &collator);
+ ParsedMatchExpression lhsAllGTcba("{a: {$in: ['abc', 'cbc']}}", &collator);
+ ParsedMatchExpression lhsSomeGTcba("{a: {$in: ['abc', 'aba']}}", &collator);
+ ParsedMatchExpression rhs("{a: {$gt: 'cba'}}", &collator);
ASSERT_TRUE(expression::isSubsetOf(lhsAllGTcba.get(), rhs.get()));
ASSERT_FALSE(expression::isSubsetOf(lhsSomeGTcba.get(), rhs.get()));
- ParsedMatchExpressionForTest rhsLT("{a: {$lt: 'cba'}}", &collator);
+ ParsedMatchExpression rhsLT("{a: {$lt: 'cba'}}", &collator);
ASSERT_FALSE(expression::isSubsetOf(lhsAllGTcba.get(), rhsLT.get()));
ASSERT_FALSE(expression::isSubsetOf(lhsSomeGTcba.get(), rhsLT.get()));
@@ -736,8 +740,8 @@ TEST(ExpressionAlgoIsSubsetOf, CollationAwareStringComparisonIn) {
TEST(ExpressionAlgoIsSubsetOf, NonMatchingCollationsNoStringComparisonLHS) {
CollatorInterfaceMock collatorAlwaysEqual(CollatorInterfaceMock::MockType::kAlwaysEqual);
CollatorInterfaceMock collatorReverseString(CollatorInterfaceMock::MockType::kReverseString);
- ParsedMatchExpressionForTest lhs("{a: {b: 1}}", &collatorAlwaysEqual);
- ParsedMatchExpressionForTest rhs("{a: {$lt: {b: 'abc'}}}", &collatorReverseString);
+ ParsedMatchExpression lhs("{a: {b: 1}}", &collatorAlwaysEqual);
+ ParsedMatchExpression rhs("{a: {$lt: {b: 'abc'}}}", &collatorReverseString);
ASSERT_FALSE(expression::isSubsetOf(lhs.get(), rhs.get()));
}
@@ -745,75 +749,72 @@ TEST(ExpressionAlgoIsSubsetOf, NonMatchingCollationsNoStringComparisonLHS) {
TEST(ExpressionAlgoIsSubsetOf, NonMatchingCollationsNoStringComparison) {
CollatorInterfaceMock collatorAlwaysEqual(CollatorInterfaceMock::MockType::kAlwaysEqual);
CollatorInterfaceMock collatorReverseString(CollatorInterfaceMock::MockType::kReverseString);
- ParsedMatchExpressionForTest lhs("{a: 1}", &collatorAlwaysEqual);
- ParsedMatchExpressionForTest rhs("{a: {$gt: 0}}", &collatorReverseString);
+ ParsedMatchExpression lhs("{a: 1}", &collatorAlwaysEqual);
+ ParsedMatchExpression rhs("{a: {$gt: 0}}", &collatorReverseString);
ASSERT_TRUE(expression::isSubsetOf(lhs.get(), rhs.get()));
}
TEST(ExpressionAlgoIsSubsetOf, InternalExprEqIsSubsetOfNothing) {
- ParsedMatchExpressionForTest exprEq("{a: {$_internalExprEq: 0}}");
- ParsedMatchExpressionForTest regularEq("{a: {$eq: 0}}");
+ ParsedMatchExpression exprEq("{a: {$_internalExprEq: 0}}");
+ ParsedMatchExpression regularEq("{a: {$eq: 0}}");
{
- ParsedMatchExpressionForTest rhs("{a: {$gte: 0}}");
+ ParsedMatchExpression rhs("{a: {$gte: 0}}");
ASSERT_FALSE(expression::isSubsetOf(exprEq.get(), rhs.get()));
ASSERT_TRUE(expression::isSubsetOf(regularEq.get(), rhs.get()));
}
{
- ParsedMatchExpressionForTest rhs("{a: {$lte: 0}}");
+ ParsedMatchExpression rhs("{a: {$lte: 0}}");
ASSERT_FALSE(expression::isSubsetOf(exprEq.get(), rhs.get()));
ASSERT_TRUE(expression::isSubsetOf(regularEq.get(), rhs.get()));
}
}
TEST(ExpressionAlgoIsSubsetOf, IsSubsetOfRHSAndWithinOr) {
- ParsedMatchExpressionForTest rhs("{$or: [{a: 3}, {$and: [{a: 5}, {b: 5}]}]}");
+ ParsedMatchExpression rhs("{$or: [{a: 3}, {$and: [{a: 5}, {b: 5}]}]}");
{
- ParsedMatchExpressionForTest lhs("{a:5, b:5}");
+ ParsedMatchExpression lhs("{a:5, b:5}");
ASSERT_TRUE(expression::isSubsetOf(lhs.get(), rhs.get()));
}
}
TEST(ExpressionAlgoIsSubsetOf, IsSubsetOfComplexRHSExpression) {
- ParsedMatchExpressionForTest complex(
- "{$or: [{z: 1}, {$and: [{x: 1}, {$or: [{y: 1}, {y: 2}]}]}]}");
+ ParsedMatchExpression complex("{$or: [{z: 1}, {$and: [{x: 1}, {$or: [{y: 1}, {y: 2}]}]}]}");
{
- ParsedMatchExpressionForTest lhs("{z: 1}");
+ ParsedMatchExpression lhs("{z: 1}");
ASSERT_TRUE(expression::isSubsetOf(lhs.get(), complex.get()));
}
{
- ParsedMatchExpressionForTest lhs("{z: 1, x: 1, y:2}");
+ ParsedMatchExpression lhs("{z: 1, x: 1, y:2}");
ASSERT_TRUE(expression::isSubsetOf(lhs.get(), complex.get()));
}
{
- ParsedMatchExpressionForTest lhs(
- "{$or: [{z: 1}, {$and: [{x: 1}, {$or: [{y: 1}, {y: 2}]}]}]}");
+ ParsedMatchExpression lhs("{$or: [{z: 1}, {$and: [{x: 1}, {$or: [{y: 1}, {y: 2}]}]}]}");
ASSERT_TRUE(expression::isSubsetOf(lhs.get(), complex.get()));
}
{
- ParsedMatchExpressionForTest lhs(
- "{$or: [{z: 2}, {$and: [{x: 2}, {$or: [{y: 3}, {y: 4}]}]}]}");
+ ParsedMatchExpression lhs("{$or: [{z: 2}, {$and: [{x: 2}, {$or: [{y: 3}, {y: 4}]}]}]}");
ASSERT_FALSE(expression::isSubsetOf(lhs.get(), complex.get()));
}
{
- ParsedMatchExpressionForTest lhs("{z: 1, y:2}");
+ ParsedMatchExpression lhs("{z: 1, y:2}");
ASSERT_TRUE(expression::isSubsetOf(lhs.get(), complex.get()));
}
{
- ParsedMatchExpressionForTest lhs("{z: 2, y: 1}");
+ ParsedMatchExpression lhs("{z: 2, y: 1}");
ASSERT_FALSE(expression::isSubsetOf(lhs.get(), complex.get()));
}
{
- ParsedMatchExpressionForTest lhs("{x: 1, y: 3}");
+ ParsedMatchExpression lhs("{x: 1, y: 3}");
ASSERT_FALSE(expression::isSubsetOf(lhs.get(), complex.get()));
}
}
@@ -930,15 +931,6 @@ TEST(IsIndependent, NonRenameableExpressionIsNotIndependent) {
}
}
-TEST(IsIndependent, EmptyDependencySetsPassIsOnlyDependentOn) {
- BSONObj matchPredicate = fromjson("{}");
- boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- auto swMatchExpression = MatchExpressionParser::parse(matchPredicate, std::move(expCtx));
- ASSERT_OK(swMatchExpression.getStatus());
- auto matchExpression = std::move(swMatchExpression.getValue());
- ASSERT_TRUE(expression::isOnlyDependentOn(*matchExpression.get(), {}));
-}
-
TEST(SplitMatchExpression, AndWithSplittableChildrenIsSplittable) {
BSONObj matchPredicate = fromjson("{$and: [{a: 1}, {b: 1}]}");
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
@@ -950,11 +942,15 @@ 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(splitExpr.first->serialize(), fromjson("{a: {$eq: 1}}"));
- ASSERT_BSONOBJ_EQ(splitExpr.second->serialize(), fromjson("{b: {$eq: 1}}"));
+ ASSERT_BSONOBJ_EQ(firstBob.obj(), fromjson("{a: {$eq: 1}}"));
+ ASSERT_BSONOBJ_EQ(secondBob.obj(), fromjson("{b: {$eq: 1}}"));
}
TEST(SplitMatchExpression, NorWithIndependentChildrenIsSplittable) {
@@ -968,11 +964,15 @@ 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(splitExpr.first->serialize(), fromjson("{$nor: [{a: {$eq: 1}}]}"));
- ASSERT_BSONOBJ_EQ(splitExpr.second->serialize(), fromjson("{$nor: [{b: {$eq: 1}}]}"));
+ ASSERT_BSONOBJ_EQ(firstBob.obj(), fromjson("{$nor: [{a: {$eq: 1}}]}"));
+ ASSERT_BSONOBJ_EQ(secondBob.obj(), fromjson("{$nor: [{b: {$eq: 1}}]}"));
}
TEST(SplitMatchExpression, NotWithIndependentChildIsSplittable) {
@@ -986,8 +986,10 @@ 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(splitExpr.first->serialize(), fromjson("{x: {$not: {$gt: 4}}}"));
+ ASSERT_BSONOBJ_EQ(firstBob.obj(), fromjson("{x: {$not: {$gt: 4}}}"));
ASSERT_FALSE(splitExpr.second);
}
@@ -1002,10 +1004,11 @@ 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(splitExpr.second->serialize(),
- fromjson("{$or: [{a: {$eq: 1}}, {b: {$eq: 1}}]}"));
+ ASSERT_BSONOBJ_EQ(bob.obj(), fromjson("{$or: [{a: {$eq: 1}}, {b: {$eq: 1}}]}"));
}
TEST(SplitMatchExpression, ComplexMatchExpressionSplitsCorrectly) {
@@ -1022,12 +1025,15 @@ 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(splitExpr.first->serialize(),
- fromjson("{$or: [{'a.b': {$eq: 3}}, {'a.b.c': {$eq: 4}}]}"));
- ASSERT_BSONOBJ_EQ(splitExpr.second->serialize(),
+ ASSERT_BSONOBJ_EQ(firstBob.obj(), fromjson("{$or: [{'a.b': {$eq: 3}}, {'a.b.c': {$eq: 4}}]}"));
+ ASSERT_BSONOBJ_EQ(secondBob.obj(),
fromjson("{$and: [{x: {$not: {$size: 2}}}, {$nor: [{x: {$gt: 4}}, {$and: "
"[{x: {$not: {$eq: 1}}}, {y: {$eq: 3}}]}]}]}"));
}
@@ -1044,12 +1050,15 @@ 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(splitExpr.first->serialize(), fromjson("{'a.c': {$eq: 1}}"));
- ASSERT_BSONOBJ_EQ(splitExpr.second->serialize(),
- fromjson("{$and: [{a: {$eq: 1}}, {'a.b': {$eq: 1}}]}"));
+ ASSERT_BSONOBJ_EQ(firstBob.obj(), fromjson("{'a.c': {$eq: 1}}"));
+ ASSERT_BSONOBJ_EQ(secondBob.obj(), fromjson("{$and: [{a: {$eq: 1}}, {'a.b': {$eq: 1}}]}"));
}
TEST(SplitMatchExpression, ShouldMoveIndependentLeafPredicateAcrossRename) {
@@ -1063,7 +1072,9 @@ TEST(SplitMatchExpression, ShouldMoveIndependentLeafPredicateAcrossRename) {
expression::splitMatchExpressionBy(std::move(matcher.getValue()), {}, renames);
ASSERT_TRUE(splitExpr.first.get());
- ASSERT_BSONOBJ_EQ(splitExpr.first->serialize(), fromjson("{b: {$eq: 1}}"));
+ BSONObjBuilder firstBob;
+ splitExpr.first->serialize(&firstBob, true);
+ ASSERT_BSONOBJ_EQ(firstBob.obj(), fromjson("{b: {$eq: 1}}"));
ASSERT_FALSE(splitExpr.second.get());
}
@@ -1079,8 +1090,9 @@ TEST(SplitMatchExpression, ShouldMoveIndependentAndPredicateAcrossRename) {
expression::splitMatchExpressionBy(std::move(matcher.getValue()), {}, renames);
ASSERT_TRUE(splitExpr.first.get());
- ASSERT_BSONOBJ_EQ(splitExpr.first->serialize(),
- fromjson("{$and: [{c: {$eq: 1}}, {b: {$eq: 2}}]}"));
+ BSONObjBuilder firstBob;
+ splitExpr.first->serialize(&firstBob, true);
+ ASSERT_BSONOBJ_EQ(firstBob.obj(), fromjson("{$and: [{c: {$eq: 1}}, {b: {$eq: 2}}]}"));
ASSERT_FALSE(splitExpr.second.get());
}
@@ -1096,10 +1108,14 @@ TEST(SplitMatchExpression, ShouldSplitPartiallyDependentAndPredicateAcrossRename
expression::splitMatchExpressionBy(std::move(matcher.getValue()), {"b"}, renames);
ASSERT_TRUE(splitExpr.first.get());
- ASSERT_BSONOBJ_EQ(splitExpr.first->serialize(), fromjson("{c: {$eq: 1}}"));
+ BSONObjBuilder firstBob;
+ splitExpr.first->serialize(&firstBob, true);
+ ASSERT_BSONOBJ_EQ(firstBob.obj(), fromjson("{c: {$eq: 1}}"));
ASSERT_TRUE(splitExpr.second.get());
- ASSERT_BSONOBJ_EQ(splitExpr.second->serialize(), fromjson("{b: {$eq: 2}}"));
+ BSONObjBuilder secondBob;
+ splitExpr.second->serialize(&secondBob, true);
+ ASSERT_BSONOBJ_EQ(secondBob.obj(), fromjson("{b: {$eq: 2}}"));
}
TEST(SplitMatchExpression, ShouldSplitPartiallyDependentComplexPredicateMultipleRenames) {
@@ -1113,11 +1129,14 @@ TEST(SplitMatchExpression, ShouldSplitPartiallyDependentComplexPredicateMultiple
expression::splitMatchExpressionBy(std::move(matcher.getValue()), {"a"}, renames);
ASSERT_TRUE(splitExpr.first.get());
- ASSERT_BSONOBJ_EQ(splitExpr.first->serialize(),
- fromjson("{$or: [{d: {$eq: 2}}, {e: {$eq: 3}}]}"));
+ BSONObjBuilder firstBob;
+ splitExpr.first->serialize(&firstBob, true);
+ ASSERT_BSONOBJ_EQ(firstBob.obj(), fromjson("{$or: [{d: {$eq: 2}}, {e: {$eq: 3}}]}"));
ASSERT_TRUE(splitExpr.second.get());
- ASSERT_BSONOBJ_EQ(splitExpr.second->serialize(), fromjson("{a: {$eq: 1}}"));
+ BSONObjBuilder secondBob;
+ splitExpr.second->serialize(&secondBob, true);
+ ASSERT_BSONOBJ_EQ(secondBob.obj(), fromjson("{a: {$eq: 1}}"));
}
TEST(SplitMatchExpression,
@@ -1132,11 +1151,14 @@ TEST(SplitMatchExpression,
expression::splitMatchExpressionBy(std::move(matcher.getValue()), {"a"}, renames);
ASSERT_TRUE(splitExpr.first.get());
- ASSERT_BSONOBJ_EQ(splitExpr.first->serialize(),
- fromjson("{$or: [{x: {$eq: 2}}, {y: {$eq: 3}}]}"));
+ BSONObjBuilder firstBob;
+ splitExpr.first->serialize(&firstBob, true);
+ ASSERT_BSONOBJ_EQ(firstBob.obj(), fromjson("{$or: [{x: {$eq: 2}}, {y: {$eq: 3}}]}"));
ASSERT_TRUE(splitExpr.second.get());
- ASSERT_BSONOBJ_EQ(splitExpr.second->serialize(), fromjson("{a: {$eq: 1}}"));
+ BSONObjBuilder secondBob;
+ splitExpr.second->serialize(&secondBob, true);
+ ASSERT_BSONOBJ_EQ(secondBob.obj(), fromjson("{a: {$eq: 1}}"));
}
TEST(SplitMatchExpression, ShouldNotMoveElemMatchObjectAcrossRename) {
@@ -1152,7 +1174,9 @@ TEST(SplitMatchExpression, ShouldNotMoveElemMatchObjectAcrossRename) {
ASSERT_FALSE(splitExpr.first.get());
ASSERT_TRUE(splitExpr.second.get());
- ASSERT_BSONOBJ_EQ(splitExpr.second->serialize(), fromjson("{a: {$elemMatch: {b: {$eq: 3}}}}"));
+ BSONObjBuilder secondBob;
+ splitExpr.second->serialize(&secondBob, true);
+ ASSERT_BSONOBJ_EQ(secondBob.obj(), fromjson("{a: {$elemMatch: {b: {$eq: 3}}}}"));
}
TEST(SplitMatchExpression, ShouldNotMoveElemMatchValueAcrossRename) {
@@ -1168,7 +1192,9 @@ TEST(SplitMatchExpression, ShouldNotMoveElemMatchValueAcrossRename) {
ASSERT_FALSE(splitExpr.first.get());
ASSERT_TRUE(splitExpr.second.get());
- ASSERT_BSONOBJ_EQ(splitExpr.second->serialize(), fromjson("{a: {$elemMatch: {$eq: 3}}}"));
+ BSONObjBuilder secondBob;
+ splitExpr.second->serialize(&secondBob, true);
+ ASSERT_BSONOBJ_EQ(secondBob.obj(), fromjson("{a: {$elemMatch: {$eq: 3}}}"));
}
TEST(SplitMatchExpression, ShouldMoveTypeAcrossRename) {
@@ -1181,7 +1207,10 @@ TEST(SplitMatchExpression, ShouldMoveTypeAcrossRename) {
std::pair<unique_ptr<MatchExpression>, unique_ptr<MatchExpression>> splitExpr =
expression::splitMatchExpressionBy(std::move(matcher.getValue()), {}, renames);
- ASSERT_BSONOBJ_EQ(splitExpr.first->serialize(), fromjson("{c: {$type: [16]}}"));
+ ASSERT_TRUE(splitExpr.first.get());
+ BSONObjBuilder firstBob;
+ splitExpr.first->serialize(&firstBob, true);
+ ASSERT_BSONOBJ_EQ(firstBob.obj(), fromjson("{c: {$type: [16]}}"));
ASSERT_FALSE(splitExpr.second.get());
}
@@ -1199,7 +1228,9 @@ TEST(SplitMatchExpression, ShouldNotMoveSizeAcrossRename) {
ASSERT_FALSE(splitExpr.first.get());
ASSERT_TRUE(splitExpr.second.get());
- ASSERT_BSONOBJ_EQ(splitExpr.second->serialize(), fromjson("{a: {$size: 3}}"));
+ BSONObjBuilder secondBob;
+ splitExpr.second->serialize(&secondBob, true);
+ ASSERT_BSONOBJ_EQ(secondBob.obj(), fromjson("{a: {$size: 3}}"));
}
TEST(SplitMatchExpression, ShouldNotMoveMinItemsAcrossRename) {
@@ -1215,8 +1246,9 @@ TEST(SplitMatchExpression, ShouldNotMoveMinItemsAcrossRename) {
ASSERT_FALSE(splitExpr.first.get());
ASSERT_TRUE(splitExpr.second.get());
- ASSERT_BSONOBJ_EQ(splitExpr.second->serialize(),
- fromjson("{a: {$_internalSchemaMinItems: 3}}"));
+ BSONObjBuilder secondBob;
+ splitExpr.second->serialize(&secondBob, true);
+ ASSERT_BSONOBJ_EQ(secondBob.obj(), fromjson("{a: {$_internalSchemaMinItems: 3}}"));
}
TEST(SplitMatchExpression, ShouldNotMoveMaxItemsAcrossRename) {
@@ -1232,8 +1264,9 @@ TEST(SplitMatchExpression, ShouldNotMoveMaxItemsAcrossRename) {
ASSERT_FALSE(splitExpr.first.get());
ASSERT_TRUE(splitExpr.second.get());
- ASSERT_BSONOBJ_EQ(splitExpr.second->serialize(),
- fromjson("{a: {$_internalSchemaMaxItems: 3}}"));
+ BSONObjBuilder secondBob;
+ splitExpr.second->serialize(&secondBob, true);
+ ASSERT_BSONOBJ_EQ(secondBob.obj(), fromjson("{a: {$_internalSchemaMaxItems: 3}}"));
}
TEST(SplitMatchExpression, ShouldNotMoveMaxItemsInLogicalExpressionAcrossRename) {
@@ -1251,7 +1284,9 @@ TEST(SplitMatchExpression, ShouldNotMoveMaxItemsInLogicalExpressionAcrossRename)
ASSERT_FALSE(splitExpr.first.get());
ASSERT_TRUE(splitExpr.second.get());
- ASSERT_BSONOBJ_EQ(splitExpr.second->serialize(),
+ BSONObjBuilder secondBob;
+ splitExpr.second->serialize(&secondBob, true);
+ ASSERT_BSONOBJ_EQ(secondBob.obj(),
fromjson("{$or: [{a: {$_internalSchemaMaxItems: 3}},"
" {a: {$_internalSchemaMaxItems: 4}}]}"));
}
@@ -1271,7 +1306,9 @@ TEST(SplitMatchExpression, ShouldNotMoveInternalSchemaObjectMatchInLogicalExpres
ASSERT_FALSE(splitExpr.first.get());
ASSERT_TRUE(splitExpr.second.get());
- ASSERT_BSONOBJ_EQ(splitExpr.second->serialize(),
+ BSONObjBuilder secondBob;
+ splitExpr.second->serialize(&secondBob, true);
+ ASSERT_BSONOBJ_EQ(secondBob.obj(),
fromjson("{$or: [{a: {$_internalSchemaObjectMatch: {b: {$eq: 1}}}},"
" {a: {$_internalSchemaObjectMatch: {b: {$eq: 1}}}}]}"));
}
@@ -1287,8 +1324,9 @@ TEST(SplitMatchExpression, ShouldMoveMinLengthAcrossRename) {
expression::splitMatchExpressionBy(std::move(matcher.getValue()), {}, renames);
ASSERT_TRUE(splitExpr.first.get());
- ASSERT_BSONOBJ_EQ(splitExpr.first->serialize(),
- fromjson("{c: {$_internalSchemaMinLength: 3}}"));
+ BSONObjBuilder firstBob;
+ splitExpr.first->serialize(&firstBob, true);
+ ASSERT_BSONOBJ_EQ(firstBob.obj(), fromjson("{c: {$_internalSchemaMinLength: 3}}"));
ASSERT_FALSE(splitExpr.second.get());
}
@@ -1304,8 +1342,9 @@ TEST(SplitMatchExpression, ShouldMoveMaxLengthAcrossRename) {
expression::splitMatchExpressionBy(std::move(matcher.getValue()), {}, renames);
ASSERT_TRUE(splitExpr.first.get());
- ASSERT_BSONOBJ_EQ(splitExpr.first->serialize(),
- fromjson("{c: {$_internalSchemaMaxLength: 3}}"));
+ BSONObjBuilder firstBob;
+ splitExpr.first->serialize(&firstBob, true);
+ ASSERT_BSONOBJ_EQ(firstBob.obj(), fromjson("{c: {$_internalSchemaMaxLength: 3}}"));
ASSERT_FALSE(splitExpr.second.get());
}
@@ -1322,34 +1361,13 @@ TEST(SplitMatchExpression, ShouldMoveIndependentPredicateWhenThereAreMultipleRen
expression::splitMatchExpressionBy(std::move(matcher.getValue()), {}, renames);
ASSERT_TRUE(splitExpr.first.get());
- ASSERT_BSONOBJ_EQ(splitExpr.first->serialize(), fromjson("{x: {$eq: 3}}"));
+ BSONObjBuilder firstBob;
+ splitExpr.first->serialize(&firstBob, true);
+ ASSERT_BSONOBJ_EQ(firstBob.obj(), fromjson("{x: {$eq: 3}}"));
ASSERT_FALSE(splitExpr.second.get());
}
-TEST(SplitMatchExpression, ShouldNotSplitWhenRand) {
- const auto randExpr = "{$expr: {$lt: [{$rand: {}}, {$const: 0.25}]}}";
- const auto assertMatchDoesNotSplit = [&](const std::string& exprString) {
- BSONObj matchPredicate = fromjson(exprString);
- boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- auto matcher = MatchExpressionParser::parse(matchPredicate, std::move(expCtx));
- ASSERT_OK(matcher.getStatus());
-
- auto&& [split, residual] =
- expression::splitMatchExpressionBy(std::move(matcher.getValue()), {}, {});
- ASSERT_FALSE(split.get());
- ASSERT_TRUE(residual.get());
-
- ASSERT_BSONOBJ_EQ(residual->serialize(), fromjson(randExpr));
- };
-
- // We should not push down a $match with a $rand expression.
- assertMatchDoesNotSplit(randExpr);
-
- // This is equivalent to 'randExpr'.
- assertMatchDoesNotSplit("{$sampleRate: 0.25}");
-}
-
TEST(ApplyRenamesToExpression, ShouldApplyBasicRenamesForAMatchWithExpr) {
BSONObj matchPredicate = fromjson("{$expr: {$eq: ['$a.b', '$c']}}");
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
@@ -1545,7 +1563,7 @@ TEST(HasExistencePredicateOnPath, ReturnsFalseWhenExistsOnSubpath) {
}
TEST(SplitMatchExpressionForColumns, PreservesEmptyPredicates) {
- ParsedMatchExpressionForTest empty("{}");
+ ParsedMatchExpression empty("{}");
auto splitUp = expression::splitMatchExpressionForColumns(empty.get());
ASSERT(splitUp);
ASSERT(splitUp->size() == 0);
@@ -1554,21 +1572,21 @@ TEST(SplitMatchExpressionForColumns, PreservesEmptyPredicates) {
TEST(SplitMatchExpressionForColumns, RejectsUnsupportedPredicates) {
{
// Future work.
- ParsedMatchExpressionForTest orClause("{$or: [{a: 1}, {b: 2}]}");
+ ParsedMatchExpression orClause("{$or: [{a: 1}, {b: 2}]}");
auto splitUp = expression::splitMatchExpressionForColumns(orClause.get());
ASSERT(!splitUp);
}
{
// Would match missing values, not safe for a columnar index.
- ParsedMatchExpressionForTest alwaysTrue("{$alwaysTrue: 1}");
+ ParsedMatchExpression alwaysTrue("{$alwaysTrue: 1}");
auto splitUp = expression::splitMatchExpressionForColumns(alwaysTrue.get());
ASSERT(!splitUp);
}
{
// Future work.
- ParsedMatchExpressionForTest exprClause("{$expr: {$eq: ['$x', 0]}}");
+ ParsedMatchExpression exprClause("{$expr: {$eq: ['$x', 0]}}");
auto splitUp = expression::splitMatchExpressionForColumns(exprClause.get());
ASSERT(!splitUp);
}
@@ -1578,7 +1596,7 @@ TEST(SplitMatchExpressionForColumns, RejectsUnsupportedPredicates) {
TEST(SplitMatchExpressionForColumns, SplitsSafeEqualities) {
{
- ParsedMatchExpressionForTest singleEqualsNumber("{albatross: 1}");
+ ParsedMatchExpression singleEqualsNumber("{albatross: 1}");
auto splitUp = expression::splitMatchExpressionForColumns(singleEqualsNumber.get());
ASSERT(splitUp);
ASSERT_EQ(splitUp->size(), 1) << splitUp->size();
@@ -1588,7 +1606,7 @@ TEST(SplitMatchExpressionForColumns, SplitsSafeEqualities) {
}
{
- ParsedMatchExpressionForTest singleEqualsString("{albatross: 'flying'}");
+ ParsedMatchExpression singleEqualsString("{albatross: 'flying'}");
auto splitUp = expression::splitMatchExpressionForColumns(singleEqualsString.get());
ASSERT(splitUp);
ASSERT_EQ(splitUp->size(), 1) << splitUp->size();
@@ -1598,7 +1616,7 @@ TEST(SplitMatchExpressionForColumns, SplitsSafeEqualities) {
}
{
- ParsedMatchExpressionForTest doubleEqualsNumber("{albatross: 1, blackbird: 2}");
+ ParsedMatchExpression doubleEqualsNumber("{albatross: 1, blackbird: 2}");
auto splitUp = expression::splitMatchExpressionForColumns(doubleEqualsNumber.get());
ASSERT(splitUp);
ASSERT_EQ(splitUp->size(), 2) << splitUp->size();
@@ -1611,7 +1629,7 @@ TEST(SplitMatchExpressionForColumns, SplitsSafeEqualities) {
}
{
- ParsedMatchExpressionForTest mixedEquals(
+ ParsedMatchExpression mixedEquals(
"{albatross: 1,"
" blackbird: 'flying',"
" cowbird: {$eq: /oreo/},"
@@ -1639,7 +1657,7 @@ TEST(SplitMatchExpressionForColumns, SplitsSafeEqualities) {
TEST(SplitMatchExpressionForColumns, SupportsEqualityToEmptyObjects) {
{
- ParsedMatchExpressionForTest equalsEmptyObj("{albatross: {}}");
+ ParsedMatchExpression equalsEmptyObj("{albatross: {}}");
auto splitUp = expression::splitMatchExpressionForColumns(equalsEmptyObj.get());
ASSERT(splitUp);
ASSERT_EQ(splitUp->size(), 1) << splitUp->size();
@@ -1651,7 +1669,7 @@ TEST(SplitMatchExpressionForColumns, SupportsEqualityToEmptyObjects) {
TEST(SplitMatchExpressionForColumns, SupportsEqualityToEmptyArray) {
{
- ParsedMatchExpressionForTest equalsEmptyArray("{albatross: []}");
+ ParsedMatchExpression equalsEmptyArray("{albatross: []}");
auto splitUp = expression::splitMatchExpressionForColumns(equalsEmptyArray.get());
ASSERT(splitUp);
ASSERT_EQ(splitUp->size(), 1) << splitUp->size();
@@ -1663,7 +1681,7 @@ TEST(SplitMatchExpressionForColumns, SupportsEqualityToEmptyArray) {
TEST(SplitMatchExpressionForColumns, DoesNotSupportEqualsNull) {
{
- ParsedMatchExpressionForTest equalsNull("{a: null}");
+ ParsedMatchExpression equalsNull("{a: null}");
auto splitUp = expression::splitMatchExpressionForColumns(equalsNull.get());
ASSERT(!splitUp);
}
@@ -1671,7 +1689,7 @@ TEST(SplitMatchExpressionForColumns, DoesNotSupportEqualsNull) {
TEST(SplitMatchExpressionForColumns, DoesSupportNotEqualsNull) {
{
- ParsedMatchExpressionForTest neNull("{a: {$ne: null}}");
+ ParsedMatchExpression neNull("{a: {$ne: null}}");
auto splitUp = expression::splitMatchExpressionForColumns(neNull.get());
ASSERT(splitUp);
ASSERT_EQ(splitUp->size(), 1) << splitUp->size();
@@ -1680,7 +1698,7 @@ TEST(SplitMatchExpressionForColumns, DoesSupportNotEqualsNull) {
<< splitUp->at("a")->toString();
}
{
- ParsedMatchExpressionForTest notEqualsNull("{a: {$not: {$eq: null}}}");
+ ParsedMatchExpression notEqualsNull("{a: {$not: {$eq: null}}}");
auto splitUp = expression::splitMatchExpressionForColumns(notEqualsNull.get());
ASSERT(splitUp) << notEqualsNull.get()->toString();
ASSERT_EQ(splitUp->size(), 1) << splitUp->size();
@@ -1692,28 +1710,28 @@ TEST(SplitMatchExpressionForColumns, DoesSupportNotEqualsNull) {
TEST(SplitMatchExpressionForColumns, DoesNotSupportCompoundEquals) {
{
- ParsedMatchExpressionForTest implicitEqualsArray("{a: [1, 2]}");
+ ParsedMatchExpression implicitEqualsArray("{a: [1, 2]}");
auto splitUp = expression::splitMatchExpressionForColumns(implicitEqualsArray.get());
ASSERT(!splitUp);
}
{
- ParsedMatchExpressionForTest explicitEqualsArray("{a: {$eq: [1, 2]}}");
+ ParsedMatchExpression explicitEqualsArray("{a: {$eq: [1, 2]}}");
auto splitUp = expression::splitMatchExpressionForColumns(explicitEqualsArray.get());
ASSERT(!splitUp);
}
{
- ParsedMatchExpressionForTest implicitEqualsObject("{a: {boats: 1, planes: 2}}");
+ ParsedMatchExpression implicitEqualsObject("{a: {boats: 1, planes: 2}}");
auto splitUp = expression::splitMatchExpressionForColumns(implicitEqualsObject.get());
ASSERT(!splitUp);
}
{
- ParsedMatchExpressionForTest explicitEqualsObject("{a: {$eq: {boats: 1, planes: 2}}}");
+ ParsedMatchExpression explicitEqualsObject("{a: {$eq: {boats: 1, planes: 2}}}");
auto splitUp = expression::splitMatchExpressionForColumns(explicitEqualsObject.get());
ASSERT(!splitUp);
}
// We should be able to do dotted path version though, as a potential workaround.
{
- ParsedMatchExpressionForTest equalsDotted("{'a.boats': 1, 'a.planes': 2}");
+ ParsedMatchExpression equalsDotted("{'a.boats': 1, 'a.planes': 2}");
auto splitUp = expression::splitMatchExpressionForColumns(equalsDotted.get());
ASSERT(splitUp);
ASSERT(splitUp->size() == 2);
@@ -1730,7 +1748,7 @@ TEST(SplitMatchExpressionForColumns, DoesNotSupportCompoundEquals) {
TEST(SplitMatchExpressionForColumns, SupportsComparisonsLikeEqualities) {
{
- ParsedMatchExpressionForTest singleLtNumber("{albatross: {$lt: 1}}");
+ ParsedMatchExpression singleLtNumber("{albatross: {$lt: 1}}");
auto splitUp = expression::splitMatchExpressionForColumns(singleLtNumber.get());
ASSERT(splitUp);
ASSERT_EQ(splitUp->size(), 1) << splitUp->size();
@@ -1739,7 +1757,7 @@ TEST(SplitMatchExpressionForColumns, SupportsComparisonsLikeEqualities) {
<< splitUp->at("albatross")->toString();
}
{
- ParsedMatchExpressionForTest singleLteNumber("{albatross: {$lte: 1}}");
+ ParsedMatchExpression singleLteNumber("{albatross: {$lte: 1}}");
auto splitUp = expression::splitMatchExpressionForColumns(singleLteNumber.get());
ASSERT(splitUp);
ASSERT_EQ(splitUp->size(), 1) << splitUp->size();
@@ -1748,7 +1766,7 @@ TEST(SplitMatchExpressionForColumns, SupportsComparisonsLikeEqualities) {
<< splitUp->at("albatross")->toString();
}
{
- ParsedMatchExpressionForTest singleGtNumber("{albatross: {$gt: 1}}");
+ ParsedMatchExpression singleGtNumber("{albatross: {$gt: 1}}");
auto splitUp = expression::splitMatchExpressionForColumns(singleGtNumber.get());
ASSERT(splitUp);
ASSERT_EQ(splitUp->size(), 1) << splitUp->size();
@@ -1757,7 +1775,7 @@ TEST(SplitMatchExpressionForColumns, SupportsComparisonsLikeEqualities) {
<< splitUp->at("albatross")->toString();
}
{
- ParsedMatchExpressionForTest singleGteNumber("{albatross: {$gte: 1}}");
+ ParsedMatchExpression singleGteNumber("{albatross: {$gte: 1}}");
auto splitUp = expression::splitMatchExpressionForColumns(singleGteNumber.get());
ASSERT(splitUp);
ASSERT_EQ(splitUp->size(), 1) << splitUp->size();
@@ -1766,7 +1784,7 @@ TEST(SplitMatchExpressionForColumns, SupportsComparisonsLikeEqualities) {
<< splitUp->at("albatross")->toString();
}
{
- ParsedMatchExpressionForTest combinationPredicate(
+ ParsedMatchExpression combinationPredicate(
"{"
" albatross: {$lt: 100},"
" blackbird: {$gt: 0},"
@@ -1790,42 +1808,42 @@ TEST(SplitMatchExpressionForColumns, SupportsComparisonsLikeEqualities) {
// While equality to [] or {} is OK, inequality is not so obvious. Left as future work.
TEST(SplitMatchExpressionForColumns, DoesNotSupportInequalitiesToObjectsOrArrays) {
{
- ParsedMatchExpressionForTest ltArray("{albatross: {$lt: []}}");
+ ParsedMatchExpression ltArray("{albatross: {$lt: []}}");
auto splitUp = expression::splitMatchExpressionForColumns(ltArray.get());
ASSERT(!splitUp);
}
{
- ParsedMatchExpressionForTest ltObject("{albatross: {$lt: {}}}");
+ ParsedMatchExpression ltObject("{albatross: {$lt: {}}}");
auto splitUp = expression::splitMatchExpressionForColumns(ltObject.get());
ASSERT(!splitUp);
}
{
- ParsedMatchExpressionForTest lteArray("{albatross: {$lte: []}}");
+ ParsedMatchExpression lteArray("{albatross: {$lte: []}}");
auto splitUp = expression::splitMatchExpressionForColumns(lteArray.get());
ASSERT(!splitUp);
}
{
- ParsedMatchExpressionForTest lteObject("{albatross: {$lte: {}}}");
+ ParsedMatchExpression lteObject("{albatross: {$lte: {}}}");
auto splitUp = expression::splitMatchExpressionForColumns(lteObject.get());
ASSERT(!splitUp);
}
{
- ParsedMatchExpressionForTest gtArray("{albatross: {$gt: []}}");
+ ParsedMatchExpression gtArray("{albatross: {$gt: []}}");
auto splitUp = expression::splitMatchExpressionForColumns(gtArray.get());
ASSERT(!splitUp);
}
{
- ParsedMatchExpressionForTest gtObject("{albatross: {$gt: {}}}");
+ ParsedMatchExpression gtObject("{albatross: {$gt: {}}}");
auto splitUp = expression::splitMatchExpressionForColumns(gtObject.get());
ASSERT(!splitUp);
}
{
- ParsedMatchExpressionForTest gteArray("{albatross: {$gte: []}}");
+ ParsedMatchExpression gteArray("{albatross: {$gte: []}}");
auto splitUp = expression::splitMatchExpressionForColumns(gteArray.get());
ASSERT(!splitUp);
}
{
- ParsedMatchExpressionForTest gteObject("{albatross: {$gte: {}}}");
+ ParsedMatchExpression gteObject("{albatross: {$gte: {}}}");
auto splitUp = expression::splitMatchExpressionForColumns(gteObject.get());
ASSERT(!splitUp);
}
@@ -1833,7 +1851,7 @@ TEST(SplitMatchExpressionForColumns, DoesNotSupportInequalitiesToObjectsOrArrays
// Tests that comparisons which only match values of a certain type are allowed.
TEST(SplitMatchExpressionForColumns, SupportsTypeSpecificPredicates) {
- ParsedMatchExpressionForTest combinationPredicate(
+ ParsedMatchExpression combinationPredicate(
"{"
" albatross: /oreo/,"
" blackbird: {$mod: [2, 0]},"
@@ -1868,7 +1886,7 @@ TEST(SplitMatchExpressionForColumns, SupportsTypeSpecificPredicates) {
TEST(SplitMatchExpressionForColumns, SupportsInWithRegexes) {
{
// First confirm a $in clause is supported without regexes.
- ParsedMatchExpressionForTest stringInClause("{albatross: {$in: ['big', 'ol', 'bird']}}");
+ ParsedMatchExpression stringInClause("{albatross: {$in: ['big', 'ol', 'bird']}}");
auto splitUp = expression::splitMatchExpressionForColumns(stringInClause.get());
ASSERT(splitUp);
ASSERT(splitUp->contains("albatross"));
@@ -1878,7 +1896,7 @@ TEST(SplitMatchExpressionForColumns, SupportsInWithRegexes) {
}
{
// Test that $in with regexes is supported also work.
- ParsedMatchExpressionForTest regexInClause("{albatross: {$in: [/big/, /bird/]}}");
+ ParsedMatchExpression regexInClause("{albatross: {$in: [/big/, /bird/]}}");
auto splitUp = expression::splitMatchExpressionForColumns(regexInClause.get());
ASSERT(splitUp);
ASSERT(splitUp->contains("albatross"));
@@ -1888,7 +1906,7 @@ TEST(SplitMatchExpressionForColumns, SupportsInWithRegexes) {
}
{
// Test that a mix of both is supported
- ParsedMatchExpressionForTest regexInClause("{albatross: {$in: [/big/, 'bird']}}");
+ ParsedMatchExpression regexInClause("{albatross: {$in: [/big/, 'bird']}}");
auto splitUp = expression::splitMatchExpressionForColumns(regexInClause.get());
ASSERT(splitUp);
ASSERT(splitUp->contains("albatross"));
@@ -1898,14 +1916,14 @@ TEST(SplitMatchExpressionForColumns, SupportsInWithRegexes) {
}
{
// Test that it is still disallowed if there's a disqualifying equality such as a null.
- ParsedMatchExpressionForTest regexInClause("{albatross: {$in: [/big/, null, 'bird']}}");
+ ParsedMatchExpression regexInClause("{albatross: {$in: [/big/, null, 'bird']}}");
auto splitUp = expression::splitMatchExpressionForColumns(regexInClause.get());
ASSERT(!splitUp);
}
}
TEST(SplitMatchExpressionForColumns, SupportsExistsTrue) {
- ParsedMatchExpressionForTest existsPredicate("{albatross: {$exists: true}}");
+ ParsedMatchExpression existsPredicate("{albatross: {$exists: true}}");
auto splitUp = expression::splitMatchExpressionForColumns(existsPredicate.get());
ASSERT(splitUp);
ASSERT(splitUp->contains("albatross"));
@@ -1915,7 +1933,7 @@ TEST(SplitMatchExpressionForColumns, SupportsExistsTrue) {
}
TEST(SplitMatchExpressionForColumns, DoesNotSupportExistsFalse) {
- ParsedMatchExpressionForTest existsPredicate("{albatross: {$exists: false}}");
+ ParsedMatchExpression existsPredicate("{albatross: {$exists: false}}");
auto splitUp = expression::splitMatchExpressionForColumns(existsPredicate.get());
ASSERT(!splitUp);
}
@@ -1924,7 +1942,7 @@ TEST(SplitMatchExpressionForColumns, DoesNotSupportExistsFalse) {
// next test.
TEST(SplitMatchExpressionForColumns, SupportsInPredicates) {
{
- ParsedMatchExpressionForTest emptyIn("{albatross: {$in: []}}");
+ ParsedMatchExpression emptyIn("{albatross: {$in: []}}");
auto splitUp = expression::splitMatchExpressionForColumns(emptyIn.get());
ASSERT(splitUp);
ASSERT(splitUp->contains("albatross"));
@@ -1933,7 +1951,7 @@ TEST(SplitMatchExpressionForColumns, SupportsInPredicates) {
ASSERT_EQ(splitUp->size(), 1) << splitUp->size();
}
{
- ParsedMatchExpressionForTest singleElementIn("{albatross: {$in: [4]}}");
+ ParsedMatchExpression singleElementIn("{albatross: {$in: [4]}}");
auto splitUp = expression::splitMatchExpressionForColumns(singleElementIn.get());
ASSERT(splitUp);
ASSERT(splitUp->contains("albatross"));
@@ -1942,7 +1960,7 @@ TEST(SplitMatchExpressionForColumns, SupportsInPredicates) {
ASSERT_EQ(splitUp->size(), 1) << splitUp->size();
}
{
- ParsedMatchExpressionForTest inWithEmptyArray("{albatross: {$in: [[]]}}");
+ ParsedMatchExpression inWithEmptyArray("{albatross: {$in: [[]]}}");
auto splitUp = expression::splitMatchExpressionForColumns(inWithEmptyArray.get());
ASSERT(splitUp);
ASSERT(splitUp->contains("albatross"));
@@ -1951,7 +1969,7 @@ TEST(SplitMatchExpressionForColumns, SupportsInPredicates) {
ASSERT_EQ(splitUp->size(), 1) << splitUp->size();
}
{
- ParsedMatchExpressionForTest inWithEmptyObject("{albatross: {$in: [{}]}}");
+ ParsedMatchExpression inWithEmptyObject("{albatross: {$in: [{}]}}");
auto splitUp = expression::splitMatchExpressionForColumns(inWithEmptyObject.get());
ASSERT(splitUp);
ASSERT(splitUp->contains("albatross"));
@@ -1960,8 +1978,7 @@ TEST(SplitMatchExpressionForColumns, SupportsInPredicates) {
ASSERT_EQ(splitUp->size(), 1) << splitUp->size();
}
{
- ParsedMatchExpressionForTest mixedTypeIn(
- "{albatross: {$in: [4, {}, [], 'string', /regex/]}}");
+ ParsedMatchExpression mixedTypeIn("{albatross: {$in: [4, {}, [], 'string', /regex/]}}");
auto splitUp = expression::splitMatchExpressionForColumns(mixedTypeIn.get());
ASSERT(splitUp);
ASSERT(splitUp->contains("albatross"));
@@ -1974,22 +1991,22 @@ TEST(SplitMatchExpressionForColumns, SupportsInPredicates) {
// We can't support compound types, just like for equality.
TEST(SplitMatchExpressionForColumns, DoesNotSupportCertainInEdgeCases) {
{
- ParsedMatchExpressionForTest inWithArray("{albatross: {$in: [[2,3]]}}");
+ ParsedMatchExpression inWithArray("{albatross: {$in: [[2,3]]}}");
auto splitUp = expression::splitMatchExpressionForColumns(inWithArray.get());
ASSERT(!splitUp);
}
{
- ParsedMatchExpressionForTest inWithObject("{albatross: {$in: [{wings: 2}]}}");
+ ParsedMatchExpression inWithObject("{albatross: {$in: [{wings: 2}]}}");
auto splitUp = expression::splitMatchExpressionForColumns(inWithObject.get());
ASSERT(!splitUp);
}
{
- ParsedMatchExpressionForTest inWithNull("{albatross: {$in: [null]}}");
+ ParsedMatchExpression inWithNull("{albatross: {$in: [null]}}");
auto splitUp = expression::splitMatchExpressionForColumns(inWithNull.get());
ASSERT(!splitUp);
}
{
- ParsedMatchExpressionForTest unsupporedMixedInWithSupported(
+ ParsedMatchExpression unsupporedMixedInWithSupported(
"{albatross: {$in: ['strings', 1, null, {x: 4}, [0, 0], 4]}}");
auto splitUp =
expression::splitMatchExpressionForColumns(unsupporedMixedInWithSupported.get());
@@ -1999,7 +2016,7 @@ TEST(SplitMatchExpressionForColumns, DoesNotSupportCertainInEdgeCases) {
TEST(SplitMatchExpressionForColumns, SupportsTypePredicates) {
{
- ParsedMatchExpressionForTest intFilter("{albatross: {$type: 'int'}}");
+ ParsedMatchExpression intFilter("{albatross: {$type: 'int'}}");
auto splitUp = expression::splitMatchExpressionForColumns(intFilter.get());
ASSERT(splitUp);
ASSERT(splitUp->contains("albatross"));
@@ -2008,7 +2025,7 @@ TEST(SplitMatchExpressionForColumns, SupportsTypePredicates) {
ASSERT_EQ(splitUp->size(), 1) << splitUp->size();
}
{
- ParsedMatchExpressionForTest numberFilter("{albatross: {$type: 'number'}}");
+ ParsedMatchExpression numberFilter("{albatross: {$type: 'number'}}");
auto splitUp = expression::splitMatchExpressionForColumns(numberFilter.get());
ASSERT(splitUp);
ASSERT(splitUp->contains("albatross"));
@@ -2017,7 +2034,7 @@ TEST(SplitMatchExpressionForColumns, SupportsTypePredicates) {
ASSERT_EQ(splitUp->size(), 1) << splitUp->size();
}
{
- ParsedMatchExpressionForTest stringFilter("{albatross: {$type: 'string'}}");
+ ParsedMatchExpression stringFilter("{albatross: {$type: 'string'}}");
auto splitUp = expression::splitMatchExpressionForColumns(stringFilter.get());
ASSERT(splitUp);
ASSERT(splitUp->contains("albatross"));
@@ -2026,7 +2043,7 @@ TEST(SplitMatchExpressionForColumns, SupportsTypePredicates) {
ASSERT_EQ(splitUp->size(), 1) << splitUp->size();
}
{
- ParsedMatchExpressionForTest nullFilter("{albatross: {$type: 'null'}}");
+ ParsedMatchExpression nullFilter("{albatross: {$type: 'null'}}");
auto splitUp = expression::splitMatchExpressionForColumns(nullFilter.get());
ASSERT(splitUp);
ASSERT(splitUp->contains("albatross"));
@@ -2037,20 +2054,20 @@ TEST(SplitMatchExpressionForColumns, SupportsTypePredicates) {
}
TEST(SplitMatchExpressionForColumns, DoesNotSupportQueriesForTypeObject) {
- ParsedMatchExpressionForTest objectFilter("{albatross: {$type: 'object'}}");
+ ParsedMatchExpression objectFilter("{albatross: {$type: 'object'}}");
auto splitUp = expression::splitMatchExpressionForColumns(objectFilter.get());
ASSERT(!splitUp);
}
// This may be workable. But until we can prove it we'll disallow {$type: "array"}.
TEST(SplitMatchExpressionForColumns, DoesNotSupportQueriesForTypeArray) {
- ParsedMatchExpressionForTest objectFilter("{albatross: {$type: 'array'}}");
+ ParsedMatchExpression objectFilter("{albatross: {$type: 'array'}}");
auto splitUp = expression::splitMatchExpressionForColumns(objectFilter.get());
ASSERT(!splitUp);
}
TEST(SplitMatchExpressionForColumns, CanCombinePredicates) {
- ParsedMatchExpressionForTest compoundFilter(
+ ParsedMatchExpression compoundFilter(
"{"
" albatross: {$gte: 100},"
" albatross: {$mod: [2, 0]}"
@@ -2075,7 +2092,7 @@ TEST(SplitMatchExpressionForColumns, CanCombinePredicates) {
}
TEST(SplitMatchExpressionForColumns, SupportsDottedPaths) {
- ParsedMatchExpressionForTest compoundFilter(
+ ParsedMatchExpression compoundFilter(
"{"
" albatross: /oreo/,"
" \"blackbird.feet\": {$mod: [2, 0]},"
@@ -2118,7 +2135,7 @@ TEST(SplitMatchExpressionForColumns, SupportsDottedPaths) {
}
TEST(SplitMatchExpressionForColumns, LeavesOriginalMatchExpressionFunctional) {
- ParsedMatchExpressionForTest combinationPredicate(
+ ParsedMatchExpression 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 2ad7e25d85c..6e0e2cdfbc8 100644
--- a/src/mongo/db/matcher/expression_always_boolean.h
+++ b/src/mongo/db/matcher/expression_always_boolean.h
@@ -62,10 +62,8 @@ public:
debug << name() << ": 1\n";
}
- void serialize(BSONObjBuilder* out,
- const SerializationOptions& opts = {},
- bool includePath = true) const final {
- opts.appendLiteral(out, name(), 1);
+ void serialize(BSONObjBuilder* out, bool includePath) const final {
+ out->append(name(), 1);
}
bool equivalent(const MatchExpression* other) const final {
diff --git a/src/mongo/db/matcher/expression_arity.h b/src/mongo/db/matcher/expression_arity.h
index 840d4be61f6..90286200e21 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, {});
+ serialize(&builder, true);
debug << builder.obj().toString();
}
@@ -107,13 +107,11 @@ public:
/**
* Serializes each subexpression sequentially in a BSONArray.
*/
- void serialize(BSONObjBuilder* builder,
- const SerializationOptions& opts = {},
- bool includePath = true) const final {
+ void serialize(BSONObjBuilder* builder, bool includePath) const final {
BSONArrayBuilder exprArray(builder->subarrayStart(name()));
for (const auto& expr : _expressions) {
BSONObjBuilder exprBuilder(exprArray.subobjStart());
- expr->serialize(&exprBuilder, opts, includePath);
+ expr->serialize(&exprBuilder, includePath);
exprBuilder.doneFast();
}
exprArray.doneFast();
diff --git a/src/mongo/db/matcher/expression_array.cpp b/src/mongo/db/matcher/expression_array.cpp
index 42d30aaf0f1..cbff0adf1ca 100644
--- a/src/mongo/db/matcher/expression_array.cpp
+++ b/src/mongo/db/matcher/expression_array.cpp
@@ -101,13 +101,10 @@ void ElemMatchObjectMatchExpression::debugString(StringBuilder& debug, int inden
_sub->debugString(debug, indentationLevel + 1);
}
-void ElemMatchObjectMatchExpression::appendSerializedRightHandSide(BSONObjBuilder* bob,
- const SerializationOptions& opts,
- bool includePath) const {
- BSONObjBuilder elemMatchBob = bob->subobjStart("$elemMatch");
- SerializationOptions options = opts;
- _sub->serialize(&elemMatchBob, options, true);
- elemMatchBob.doneFast();
+BSONObj ElemMatchObjectMatchExpression::getSerializedRightHandSide() const {
+ BSONObjBuilder subBob;
+ _sub->serialize(&subBob, true);
+ return BSON("$elemMatch" << subBob.obj());
}
MatchExpression::ExpressionOptimizerFunc ElemMatchObjectMatchExpression::getOptimizer() const {
@@ -173,15 +170,14 @@ void ElemMatchValueMatchExpression::debugString(StringBuilder& debug, int indent
}
}
-void ElemMatchValueMatchExpression::appendSerializedRightHandSide(BSONObjBuilder* bob,
- const SerializationOptions& opts,
- bool includePath) const {
- BSONObjBuilder emBob = bob->subobjStart("$elemMatch");
- SerializationOptions options = opts;
+BSONObj ElemMatchValueMatchExpression::getSerializedRightHandSide() const {
+ BSONObjBuilder emBob;
+
for (auto&& child : _subs) {
- child->serialize(&emBob, options, false);
+ child->serialize(&emBob, false);
}
- emBob.doneFast();
+
+ return BSON("$elemMatch" << emBob.obj());
}
MatchExpression::ExpressionOptimizerFunc ElemMatchValueMatchExpression::getOptimizer() const {
@@ -219,10 +215,8 @@ void SizeMatchExpression::debugString(StringBuilder& debug, int indentationLevel
}
}
-void SizeMatchExpression::appendSerializedRightHandSide(BSONObjBuilder* bob,
- const SerializationOptions& opts,
- bool includePath) const {
- opts.appendLiteral(bob, "$size", _size);
+BSONObj SizeMatchExpression::getSerializedRightHandSide() const {
+ return BSON("$size" << _size);
}
bool SizeMatchExpression::equivalent(const MatchExpression* other) const {
diff --git a/src/mongo/db/matcher/expression_array.h b/src/mongo/db/matcher/expression_array.h
index 10f30629c92..1b6cb5dfcc9 100644
--- a/src/mongo/db/matcher/expression_array.h
+++ b/src/mongo/db/matcher/expression_array.h
@@ -90,9 +90,7 @@ public:
virtual void debugString(StringBuilder& debug, int indentationLevel) const;
- void appendSerializedRightHandSide(BSONObjBuilder* bob,
- const SerializationOptions& opts = {},
- bool includePath = true) const final;
+ BSONObj getSerializedRightHandSide() const final;
std::vector<std::unique_ptr<MatchExpression>>* getChildVector() final {
return nullptr;
@@ -159,9 +157,7 @@ public:
virtual void debugString(StringBuilder& debug, int indentationLevel) const;
- void appendSerializedRightHandSide(BSONObjBuilder* bob,
- const SerializationOptions& opts = {},
- bool includePath = true) const final;
+ BSONObj getSerializedRightHandSide() const final;
std::vector<std::unique_ptr<MatchExpression>>* getChildVector() final {
return &_subs;
@@ -234,9 +230,7 @@ public:
virtual void debugString(StringBuilder& debug, int indentationLevel) const;
- void appendSerializedRightHandSide(BSONObjBuilder* bob,
- const SerializationOptions& opts = {},
- bool includePath = true) const final;
+ BSONObj getSerializedRightHandSide() 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 1ce4f0f6954..567039995f6 100644
--- a/src/mongo/db/matcher/expression_expr.cpp
+++ b/src/mongo/db/matcher/expression_expr.cpp
@@ -77,10 +77,8 @@ Value ExprMatchExpression::evaluateExpression(const MatchableDocument* doc) cons
return _expression->evaluate(document, &variables);
}
-void ExprMatchExpression::serialize(BSONObjBuilder* out,
- const SerializationOptions& opts,
- bool includePath) const {
- *out << "$expr" << _expression->serialize(opts);
+void ExprMatchExpression::serialize(BSONObjBuilder* out, bool includePath) const {
+ *out << "$expr" << _expression->serialize(false);
}
bool ExprMatchExpression::equivalent(const MatchExpression* other) const {
@@ -96,8 +94,8 @@ bool ExprMatchExpression::equivalent(const MatchExpression* other) const {
}
// TODO SERVER-30982: Add mechanism to allow for checking Expression equivalency.
- return ValueComparator().evaluate(_expression->serialize() ==
- realOther->_expression->serialize());
+ return ValueComparator().evaluate(_expression->serialize(false) ==
+ realOther->_expression->serialize(false));
}
void ExprMatchExpression::_doSetCollator(const CollatorInterface* collator) {
@@ -116,7 +114,7 @@ void ExprMatchExpression::_doSetCollator(const CollatorInterface* collator) {
std::unique_ptr<MatchExpression> ExprMatchExpression::shallowClone() const {
// TODO SERVER-31003: Replace Expression clone via serialization with Expression::clone().
BSONObjBuilder bob;
- bob << "" << _expression->serialize();
+ bob << "" << _expression->serialize(false);
boost::intrusive_ptr<Expression> clonedExpr = Expression::parseOperand(
_expCtx.get(), bob.obj().firstElement(), _expCtx->variablesParseState);
@@ -132,9 +130,8 @@ MatchExpression::ExpressionOptimizerFunc ExprMatchExpression::getOptimizer() con
return [](std::unique_ptr<MatchExpression> expression) {
auto& exprMatchExpr = static_cast<ExprMatchExpression&>(*expression);
- // $expr expressions can't take advantage of indexes. We attempt to rewrite the expressions
- // as a conjunction of internal match expressions, so the query planner can use the
- // internal match expressions to potentially generate an index scan.
+ // If '_expression' can be rewritten to a MatchExpression, we will return a $and node with
+ // both the original ExprMatchExpression and the MatchExpression rewrite as children.
// Exiting early prevents additional calls to optimize from performing additional rewrites
// and adding duplicate MatchExpression sub-trees to the tree.
if (exprMatchExpr._rewriteResult) {
@@ -146,10 +143,6 @@ MatchExpression::ExpressionOptimizerFunc ExprMatchExpression::getOptimizer() con
RewriteExpr::rewrite(exprMatchExpr._expression, exprMatchExpr._expCtx->getCollator());
if (exprMatchExpr._rewriteResult->matchExpression()) {
- // If '_expression' can be rewritten to a MatchExpression, we will return a $and node
- // with both the original ExprMatchExpression and the MatchExpression rewrite as
- // children. The rewritten expression might not be equivalent to the original one so we
- // still have to keep the latter for correctness.
auto andMatch = std::make_unique<AndMatchExpression>();
andMatch->add(exprMatchExpr._rewriteResult->releaseMatchExpression());
andMatch->add(std::move(expression));
diff --git a/src/mongo/db/matcher/expression_expr.h b/src/mongo/db/matcher/expression_expr.h
index b3f670e4597..281fdd3afd6 100644
--- a/src/mongo/db/matcher/expression_expr.h
+++ b/src/mongo/db/matcher/expression_expr.h
@@ -72,12 +72,10 @@ public:
void debugString(StringBuilder& debug, int indentationLevel = 0) const final {
_debugAddSpace(debug, indentationLevel);
- debug << "$expr " << _expression->serialize().toString();
+ debug << "$expr " << _expression->serialize(false).toString();
}
- void serialize(BSONObjBuilder* out,
- const SerializationOptions& opts = {},
- bool includePath = true) const final;
+ void serialize(BSONObjBuilder* out, bool includePath) const final;
bool equivalent(const MatchExpression* other) const final;
diff --git a/src/mongo/db/matcher/expression_expr_test.cpp b/src/mongo/db/matcher/expression_expr_test.cpp
index 6f91f12bc5d..5ff0cbf88e9 100644
--- a/src/mongo/db/matcher/expression_expr_test.cpp
+++ b/src/mongo/db/matcher/expression_expr_test.cpp
@@ -37,8 +37,6 @@
#include "mongo/db/matcher/matcher.h"
#include "mongo/db/pipeline/expression_context_for_test.h"
#include "mongo/db/query/collation/collator_interface_mock.h"
-#include "mongo/db/query/query_shape/serialization_options.h"
-#include "mongo/unittest/death_test.h"
#include "mongo/unittest/unittest.h"
namespace mongo {
@@ -83,10 +81,6 @@ public:
return checked_cast<ExprMatchExpression*>(_matchExpression.get());
}
- BSONObj serialize(const SerializationOptions& opts) {
- return _matchExpression->serialize(opts);
- }
-
private:
const boost::intrusive_ptr<ExpressionContextForTest> _expCtx;
std::unique_ptr<MatchExpression> _matchExpression;
@@ -720,10 +714,17 @@ 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(optimized->serialize(), expectedSerialization);
+ ASSERT_BSONOBJ_EQ(serialized, expectedSerialization);
}
TEST_F(ExprMatchTest, ExpressionEvaluationReturnsResultsCorrectly) {
@@ -734,198 +735,5 @@ TEST_F(ExprMatchTest, ExpressionEvaluationReturnsResultsCorrectly) {
ASSERT_EQUALS(-2, expressionResult.coerceToInt());
}
-TEST_F(ExprMatchTest, ExprRedactsCorrectly) {
- boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- createMatcher(fromjson("{$expr: {$sum: [\"$a\", \"$b\"]}}"));
-
- SerializationOptions opts = SerializationOptions::kDebugShapeAndMarkIdentifiers_FOR_TEST;
-
- ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
- R"({"$expr":{"$sum":["$HASH<a>","$HASH<b>"]}})",
- serialize(opts));
-
- createMatcher(fromjson("{$expr: {$sum: [\"$a\", \"b\"]}}"));
- ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
- R"({"$expr":{"$sum":["$HASH<a>","?string"]}})",
- serialize(opts));
-
- createMatcher(fromjson("{$expr: {$sum: [\"$a.b\", \"$b\"]}}"));
- ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
- R"({"$expr":{"$sum":["$HASH<a>.HASH<b>","$HASH<b>"]}})",
- serialize(opts));
-
- createMatcher(fromjson("{$expr: {$eq: [\"$a\", \"$$NOW\"]}}"));
- ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
- R"({
- "$and": [
- {
- "HASH<a>": {
- "$_internalExprEq": "?date"
- }
- },
- {
- "$expr": {
- "$eq": [
- "$HASH<a>",
- "?date"
- ]
- }
- }
- ]
- })",
- serialize(opts));
-
- createMatcher(fromjson("{$expr: {$eq: [\"$a\", \"$$NOW\"]}}"));
- ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
- R"({
- "$and": [
- {
- "HASH<a>": {
- "$_internalExprEq": "?date"
- }
- },
- {
- "$expr": {
- "$eq": [
- "$HASH<a>",
- "?date"
- ]
- }
- }
- ]
- })",
- serialize(opts));
-
- createMatcher(fromjson("{$expr: {$getField: {field: \"b\", input: {a: 1, b: 2}}}}"));
- ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
- R"({"$expr":{"$getField":{"field":"HASH<b>","input":"?object"}}})",
- serialize(opts));
-
- createMatcher(fromjson("{$expr: {$getField: {field: \"b\", input: \"$a\"}}}"));
- ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
- R"({"$expr":{"$getField":{"field":"HASH<b>","input":"$HASH<a>"}}})",
- serialize(opts));
-
- createMatcher(fromjson("{$expr: {$getField: {field: \"b\", input: {a: 1, b: \"$c\"}}}}"));
- ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
- R"({
- "$expr": {
- "$getField": {
- "field": "HASH<b>",
- "input": {
- "HASH<a>": "?number",
- "HASH<b>": "$HASH<c>"
- }
- }
- }
- })",
- serialize(opts));
-
- createMatcher(fromjson("{$expr: {$getField: {field: \"b.c\", input: {a: 1, b: \"$c\"}}}}"));
- ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
- R"({
- "$expr": {
- "$getField": {
- "field": "HASH<b>.HASH<c>",
- "input": {
- "HASH<a>": "?number",
- "HASH<b>": "$HASH<c>"
- }
- }
- }
- })",
- serialize(opts));
-
- createMatcher(
- fromjson("{$expr: {$setField: {field: \"b\", input: {a: 1, b: \"$c\"}, value: 5}}}"));
- ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
- R"({
- "$expr": {
- "$setField": {
- "field": "HASH<b>",
- "input": {
- "HASH<a>": "?number",
- "HASH<b>": "$HASH<c>"
- },
- "value": "?number"
- }
- }
- })",
- serialize(opts));
-
- createMatcher(fromjson(
- "{$expr: {$setField: {field: \"b.c\", input: {a: 1, b: \"$c\"}, value: \"$d\"}}}"));
- ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
- R"({
- "$expr": {
- "$setField": {
- "field": "HASH<b>.HASH<c>",
- "input": {
- "HASH<a>": "?number",
- "HASH<b>": "$HASH<c>"
- },
- "value": "$HASH<d>"
- }
- }
- })",
- serialize(opts));
-
- createMatcher(fromjson(
- "{$expr: {$setField: {field: \"b.c\", input: {a: 1, b: \"$c\"}, value: \"$d.e\"}}}"));
- ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
- R"({
- "$expr": {
- "$setField": {
- "field": "HASH<b>.HASH<c>",
- "input": {
- "HASH<a>": "?number",
- "HASH<b>": "$HASH<c>"
- },
- "value": "$HASH<d>.HASH<e>"
- }
- }
- })",
- serialize(opts));
-
- createMatcher(
- fromjson("{$expr: {$setField: {field: \"b\", input: {a: 1, b: \"$c\"}, value: {a: 1, b: 2, "
- "c: 3}}}}"));
- ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
- R"({
- "$expr": {
- "$setField": {
- "field": "HASH<b>",
- "input": {
- "HASH<a>": "?number",
- "HASH<b>": "$HASH<c>"
- },
- "value": "?object"
- }
- }
- })",
- serialize(opts));
-
- createMatcher(
- fromjson("{$expr: {$setField: {field: \"b\", input: {a: 1, b: \"$c\"}, value: {a: 1, b: 2, "
- "c: \"$d\"}}}}"));
- ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
- R"({
- "$expr": {
- "$setField": {
- "field": "HASH<b>",
- "input": {
- "HASH<a>": "?number",
- "HASH<b>": "$HASH<c>"
- },
- "value": {
- "HASH<a>": "?number",
- "HASH<b>": "?number",
- "HASH<c>": "$HASH<d>"
- }
- }
- }
- })",
- serialize(opts));
-}
} // namespace
} // namespace mongo
diff --git a/src/mongo/db/matcher/expression_geo.cpp b/src/mongo/db/matcher/expression_geo.cpp
index 9050de83741..fe592df742e 100644
--- a/src/mongo/db/matcher/expression_geo.cpp
+++ b/src/mongo/db/matcher/expression_geo.cpp
@@ -33,7 +33,6 @@
#include "mongo/bson/simple_bsonobj_comparator.h"
#include "mongo/db/geo/geoparser.h"
-#include "mongo/db/matcher/expression_geo_serializer.h"
#include "mongo/db/matcher/expression_parser.h"
#include "mongo/logv2/log.h"
#include "mongo/platform/basic.h"
@@ -245,11 +244,6 @@ Status GeoNearExpression::parseNewQuery(const BSONObj& obj) {
if (fieldName == "$geometry") {
if (e.isABSONObj()) {
BSONObj embeddedObj = e.embeddedObject();
- // TODO SERVER-84598: $geoNear queries don't do a type check on the type of the
- // geometry provided into the query. This is different than the behaviour we see for
- // other GeoExpressions that use a GeometryContainer. GeoNear is a special case that
- // only ever has type 'Point', however it may be a good idea to consider removing
- // this silent acceptance of invalid "type" in the long term.
Status status = GeoParser::parseQueryPoint(e, centroid.get());
if (!status.isOK()) {
return Status(ErrorCodes::BadValue,
@@ -432,7 +426,7 @@ void GeoMatchExpression::debugString(StringBuilder& debug, int indentationLevel)
_debugAddSpace(debug, indentationLevel);
BSONObjBuilder builder;
- serialize(&builder, {});
+ serialize(&builder, true);
debug << "GEO raw = " << builder.obj().toString();
MatchExpression::TagData* td = getTag();
@@ -443,14 +437,10 @@ void GeoMatchExpression::debugString(StringBuilder& debug, int indentationLevel)
debug << "\n";
}
-void GeoMatchExpression::appendSerializedRightHandSide(BSONObjBuilder* bob,
- const SerializationOptions& opts,
- bool includePath) const {
- if (opts.literalPolicy != LiteralSerializationPolicy::kUnchanged) {
- geoExpressionCustomSerialization(*bob, _rawObj, opts, includePath);
- return;
- }
- bob->appendElements(_rawObj);
+BSONObj GeoMatchExpression::getSerializedRightHandSide() const {
+ BSONObjBuilder subobj;
+ subobj.appendElements(_rawObj);
+ return subobj.obj();
}
bool GeoMatchExpression::equivalent(const MatchExpression* other) const {
@@ -505,14 +495,10 @@ void GeoNearMatchExpression::debugString(StringBuilder& debug, int indentationLe
debug << "\n";
}
-void GeoNearMatchExpression::appendSerializedRightHandSide(BSONObjBuilder* bob,
- const SerializationOptions& opts,
- bool includePath) const {
- if (opts.literalPolicy != LiteralSerializationPolicy::kUnchanged) {
- geoNearExpressionCustomSerialization(*bob, _rawObj, opts, includePath);
- return;
- }
- bob->appendElements(_rawObj);
+BSONObj GeoNearMatchExpression::getSerializedRightHandSide() const {
+ BSONObjBuilder objBuilder;
+ objBuilder.appendElements(_rawObj);
+ return objBuilder.obj();
}
bool GeoNearMatchExpression::equivalent(const MatchExpression* other) const {
diff --git a/src/mongo/db/matcher/expression_geo.h b/src/mongo/db/matcher/expression_geo.h
index b58904a7eff..bd3752399c8 100644
--- a/src/mongo/db/matcher/expression_geo.h
+++ b/src/mongo/db/matcher/expression_geo.h
@@ -73,8 +73,7 @@ private:
// e.g.
// { "$intersect" : { "$geometry" : { "type" : "Point", "coordinates": [ 40, 5 ] } } }
Status parseQuery(const BSONObj& obj);
- BSONObj redactGeoExpression(const BSONObj& obj,
- boost::optional<StringData> literalArgsReplacement);
+
// Name of the field in the query.
std::string field;
std::shared_ptr<GeometryContainer> geoContainer;
@@ -107,9 +106,7 @@ public:
virtual void debugString(StringBuilder& debug, int indentationLevel = 0) const;
- void appendSerializedRightHandSide(BSONObjBuilder* bob,
- const SerializationOptions& opts = {},
- bool includePath = true) const final;
+ BSONObj getSerializedRightHandSide() const final;
virtual bool equivalent(const MatchExpression* other) const;
@@ -209,9 +206,7 @@ public:
virtual void debugString(StringBuilder& debug, int indentationLevel = 0) const;
- void appendSerializedRightHandSide(BSONObjBuilder* bob,
- const SerializationOptions& opts = {},
- bool includePath = true) const final;
+ BSONObj getSerializedRightHandSide() const final;
virtual bool equivalent(const MatchExpression* other) const;
@@ -251,10 +246,8 @@ public:
TwoDPtInAnnulusExpression(const R2Annulus& annulus, StringData twoDPath)
: LeafMatchExpression(INTERNAL_2D_POINT_IN_ANNULUS, twoDPath), _annulus(annulus) {}
- void serialize(BSONObjBuilder* out,
- const SerializationOptions& opts = {},
- bool includePath = true) const final {
- out->append("$TwoDPtInAnnulusExpression", true);
+ void serialize(BSONObjBuilder* out, bool includePath) const final {
+ out->append("TwoDPtInAnnulusExpression", true);
}
bool matchesSingleElement(const BSONElement& e, MatchDetails* details = nullptr) const final {
@@ -272,9 +265,7 @@ public:
// These won't be called.
//
- void appendSerializedRightHandSide(BSONObjBuilder* bob,
- const SerializationOptions& opts = {},
- bool includePath = true) const final {
+ BSONObj getSerializedRightHandSide() const final {
MONGO_UNREACHABLE;
}
diff --git a/src/mongo/db/matcher/expression_geo_serializer.cpp b/src/mongo/db/matcher/expression_geo_serializer.cpp
deleted file mode 100644
index 04643133335..00000000000
--- a/src/mongo/db/matcher/expression_geo_serializer.cpp
+++ /dev/null
@@ -1,391 +0,0 @@
-/**
- * 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/matcher/expression_geo_serializer.h"
-
-#include "mongo/db/geo/geoparser.h"
-
-namespace mongo {
-namespace {
-void appendGeoNearLegacyArray(BSONObjBuilder& bob,
- const BSONElement& e,
- const SerializationOptions& opts) {
- if (opts.literalPolicy != LiteralSerializationPolicy::kToRepresentativeParseableValue) {
- opts.appendLiteral(&bob, e);
- } else {
- // Legacy $geoNear, $nearSphere, and $near require at minimum 2 coordinates to be
- // re-parseable, so the representative value is [1, 1].
- StringData fieldName = e.fieldNameStringData();
- bob.appendArray(fieldName, BSON_ARRAY(1 << 1));
- }
-}
-
-void appendShapeOperator(BSONObjBuilder& bob,
- const BSONElement& e,
- const SerializationOptions& opts) {
- if (opts.literalPolicy != LiteralSerializationPolicy::kToRepresentativeParseableValue) {
- opts.appendLiteral(&bob, e);
- return;
- }
-
- StringData fieldName = e.fieldNameStringData();
- if (fieldName == "$center"_sd || fieldName == "$centerSphere"_sd) {
- // $center and $centerSphere requires a pair of coordinates and a radius to be
- // re-parseable, so the representative value is [[1, 1],1].
- bob.appendArray(fieldName, BSON_ARRAY(BSON_ARRAY(1 << 1) << 1));
- } else if (fieldName == "$box"_sd) {
- // $box requires two pairs of coordinates to be re-parseable, so the
- // representative value is [[1, 1],[1,1]].
- bob.appendArray(fieldName, BSON_ARRAY(BSON_ARRAY(1 << 1) << BSON_ARRAY(1 << 1)));
- } else if (fieldName == "$polygon"_sd) {
- // $polygon requires three pairs of coordinates to be re-parseable, so the representative
- // value is [[0,0],[0,1],[1,1]].
- bob.appendArray(fieldName,
- BSON_ARRAY(BSON_ARRAY(0 << 0) << BSON_ARRAY(0 << 1) << BSON_ARRAY(1 << 1)));
- } else {
- opts.appendLiteral(&bob, e);
- }
-}
-
-void appendGeoJSONCoordinatesLiteral(BSONObjBuilder& bob,
- const BSONElement& coordinatesElem,
- const BSONElement& typeElem,
- const SerializationOptions& opts) {
- if (opts.literalPolicy != LiteralSerializationPolicy::kToRepresentativeParseableValue) {
- opts.appendLiteral(&bob, coordinatesElem);
- return;
- }
-
- StringData fieldName = coordinatesElem.fieldNameStringData();
-
- // When a $geoNear expression is parsed (see GeoNearExpression::parseNewQuery()), a $geometry
- // object defaults to being parsed as a point, without checking the type of the geometry object.
- // This means we can query for a $geoNear expression that specifies an invalid type, or no type
- // at all. In order to accomodate this case, we default to type: 'Point' to ensure our
- // representative shape is re-parseable.
- auto geoType = GeoParser::geoJSONTypeStringToEnum(typeElem.valueStringData());
- if (geoType == GeoParser::GEOJSON_UNKNOWN) {
- geoType = GeoParser::GEOJSON_POINT;
- }
- switch (geoType) {
- case GeoParser::GEOJSON_POLYGON: {
- // Polygon requires four pairs of coordinates in a closed loop wrapped in an array to be
- // re-parseable, so the representative value is [[[0,0],[0,1],[1,1],[0,0]]].
- bob.appendArray(
- fieldName,
- BSON_ARRAY(BSON_ARRAY(BSON_ARRAY(0 << 0) << BSON_ARRAY(0 << 1) << BSON_ARRAY(1 << 1)
- << BSON_ARRAY(0 << 0))));
- return;
- }
- case GeoParser::GEOJSON_MULTI_POLYGON: {
- // MultiPolygon requires four pairs of coordinates in a closed loop wrapped in 2 arrays
- // to be re-parseable, so the representative value is [[[[0,0],[0,1],[1,1],[0,0]]]].
- bob.appendArray(fieldName,
- BSON_ARRAY(BSON_ARRAY(BSON_ARRAY(
- BSON_ARRAY(0 << 0) << BSON_ARRAY(0 << 1) << BSON_ARRAY(1 << 1)
- << BSON_ARRAY(0 << 0)))));
- return;
- }
- case GeoParser::GEOJSON_POINT: {
- // Point requires a pair of coordinates to be re-parseable, so the representative
- // value is [1,1].
- bob.appendArray(fieldName, BSON_ARRAY(1 << 1));
- return;
- }
- case GeoParser::GEOJSON_MULTI_POINT: {
- // MultiPoint requires a pair of coordinates wrapped in an array to be re-parseable, so
- // the representative value is [[1,1]].
- bob.appendArray(fieldName, BSON_ARRAY(BSON_ARRAY(1 << 1)));
- return;
- }
- case GeoParser::GEOJSON_LINESTRING: {
- // LineString requires two pairs of coordinates to be re-parseable, so the
- // representative value is [[0,0],[1,1]].
- bob.appendArray(fieldName, BSON_ARRAY(BSON_ARRAY(0 << 0) << BSON_ARRAY(1 << 1)));
- return;
- }
- case GeoParser::GEOJSON_MULTI_LINESTRING: {
- // MultiLineString requires two LineStrings wrapped in an array to be re-parseable, so
- // the representative value is [[[0,0],[1,1]],[[0,0],[1,1]]].
- bob.appendArray(fieldName,
- BSON_ARRAY(BSON_ARRAY(BSON_ARRAY(0 << 0) << BSON_ARRAY(1 << 1))
- << BSON_ARRAY(BSON_ARRAY(0 << 0) << BSON_ARRAY(1 << 1))));
- return;
- }
- case GeoParser::GEOJSON_GEOMETRY_COLLECTION:
- opts.appendLiteral(&bob, coordinatesElem);
- return;
- case GeoParser::GEOJSON_UNKNOWN:
- break;
- }
-
- tasserted(8456600,
- str::stream() << "unexpected geo type found in coordinates serialization: "
- << geoType);
-}
-
-void appendCRSObject(BSONObjBuilder& bob,
- const BSONElement& crsObj,
- const SerializationOptions& opts) {
- // 'crs' is always an object.
- tassert(7559700, "Expected 'crs' to be an object", crsObj.type() == BSONType::Object);
- // 'crs' is required to have a 'type' field with the value 'name'.
- // Additionally, it is required to have an object properties field
- // with a single 'name' field.
- tassert(7559701,
- str::stream() << "Expected 'crs' to contain a string 'type' field, got " << crsObj,
- crsObj["type"] && crsObj["type"].type() == BSONType::String);
- tassert(7559702,
- str::stream() << "Expected 'crs' to contain a 'properties' object, got , " << crsObj,
- crsObj["properties"] && crsObj["properties"].type() == BSONType::Object);
- tassert(7559703,
- str::stream() << "Expected 'crs.properties' to contain a 'name' "
- "string field, got "
- << crsObj["properties"],
- crsObj["properties"].Obj()["name"] &&
- crsObj["properties"].Obj()["name"].type() == BSONType::String);
-
- // The CRS "type" and "properties.name" fields must be preserved for
- // kToRepresentativeParseableValue serialization policy so the query
- // shape can be re-parsed (and will be preserved for kUnchanged policy
- // as well).
- BSONObjBuilder crsObjBuilder(bob.subobjStart("crs"));
- if (opts.literalPolicy == LiteralSerializationPolicy::kToDebugTypeString) {
- opts.appendLiteral(&crsObjBuilder, crsObj["type"]);
- } else {
- crsObjBuilder.append(crsObj["type"]);
- }
- BSONObjBuilder crsPropBuilder(crsObjBuilder.subobjStart("properties"));
- if (opts.literalPolicy == LiteralSerializationPolicy::kToDebugTypeString) {
- opts.appendLiteral(&crsPropBuilder, crsObj["properties"].Obj()["name"]);
- } else {
- crsPropBuilder.append(crsObj["properties"].Obj()["name"]);
- }
- crsPropBuilder.doneFast();
- crsObjBuilder.doneFast();
-}
-
-// This function maps closely to GeoParser::parseFromGeoJSON. We serialize a GeoJSON object that
-// should have a type, coordinates, etc. This format may have been used as the RHS for a $geometry
-// obj, or implicitly as the RHS of a $geoNear.
-void appendGeoJSONObj(BSONObjBuilder& bob,
- const BSONObj& geometryObj,
- const SerializationOptions& opts) {
- auto typeElem = geometryObj["type"];
- if (typeElem) {
- bob.append(typeElem);
- }
- if (auto coordinatesElem = geometryObj["coordinates"]) {
- appendGeoJSONCoordinatesLiteral(bob, coordinatesElem, typeElem, opts);
- } else if (auto geometriesElem = geometryObj[GEOJSON_GEOMETRIES]) {
- // We have a collection of geometries rather than a single one. Recursively
- // serialize them and add to the output object.
- BSONArrayBuilder geometriesArrBuilder;
- for (const auto& geometry : geometriesElem.Array()) {
- BSONObjBuilder geometryBuilder;
- appendGeoJSONObj(geometryBuilder, geometry.Obj(), opts);
- geometriesArrBuilder.append(geometryBuilder.obj());
- }
- bob.append(GEOJSON_GEOMETRIES, geometriesArrBuilder.arr());
- }
-
- // 'crs' can be present if users want to use STRICT_SPHERE coordinate
- // system.
- if (auto crsElt = geometryObj["crs"]) {
- appendCRSObject(bob, crsElt, opts);
- }
-}
-
-/* appendGeometryOperator() implements the serialization of a $geometry, which could be either an
- * Array or Object. This code is closely tied to GeometryContainer parsing. During GeometryContainer
- * parsing, if the $geometry element is an array type, or if the first element of the object is a
- * number, we parse the element as a Point. Parsing a legacy point does not enforce specific field
- * names, the only constraint is that the coordinates be numeric. So you can essentially have a
- * $geometry: {"foo":1, "banana": 2} and it will interpret is as a point with x:1, y: 2.
- */
-void appendGeometryOperator(BSONObjBuilder& bob,
- const BSONElement& geometryElem,
- const SerializationOptions& opts) {
- if (geometryElem.type() == BSONType::Array) {
- // This would be like {$geometry: [0, 0]} which must be a point.
- auto asArray = geometryElem.Array();
- tassert(7539807,
- "Expected the point to have exactly 2 elements: an x and y.",
- asArray.size() == 2UL);
- bob.appendArray(
- "$geometry"_sd,
- BSON_ARRAY(opts.serializeLiteral(asArray[0]) << opts.serializeLiteral(asArray[1])));
- } else {
- const auto& geometryObj = geometryElem.Obj();
- BSONObjBuilder nestedSubObj = bob.subobjStart("$geometry"_sd);
- if (geometryObj.firstElement().isNumber()) {
- BSONElement x, y;
- auto status = GeoParser::parseFlatPointCoordinates(geometryElem, x, y);
- tassert(8548500,
- "Expected the point to have exactly 2 elements: an x and y.",
- status.isOK());
- opts.appendLiteral(&nestedSubObj, x);
- opts.appendLiteral(&nestedSubObj, y);
- } else {
- appendGeoJSONObj(nestedSubObj, geometryObj, opts);
- }
- nestedSubObj.doneFast();
- }
-}
-
-/**
- * Typically, geoNear expressions have a single embedded object under the top-level
- * geoNear operator, but there is an exception for syntax that allows geoJSON
- * coordinates without specifying $geometry (e.g., {$nearSphere: {type: 'Point',
- * coordinates: [1,2]}}). We're iterating outer_it and appending extra literals to
- * handle $minDistance and $maxDistance fields that could be included outside the
- * primary geo object in those edge cases (e.g., {$nearSphere: {type: 'Point',
- * coordinates: [1,2]}, $minDistance: 10}).
- */
-void appendGeoNearOperator(BSONObjBuilder& bob,
- StringData fieldName,
- const BSONElement& geoNearElem,
- const SerializationOptions& opts) {
- if (geoNearElem.type() == mongo::Array) {
- appendGeoNearLegacyArray(bob, geoNearElem, opts);
- } else {
- BSONObjBuilder subObj = BSONObjBuilder(bob.subobjStart(fieldName));
- auto geoNearObj = geoNearElem.Obj();
- BSONObjIterator embedded_it(geoNearObj);
- tassert(8548501, "Expected non-empty geometry object.", embedded_it.more());
-
- // If the first element of the embedded object is numeric, we could be dealing
- // with a legacy style embedded coordinate pair.
- if (geoNearObj.firstElement().isNumber()) {
- BSONElement x, y;
- auto status = GeoParser::parseFlatPointCoordinates(geoNearObj.firstElement(), x, y);
- // If we successfully parsed a legacy flat point as an embedded object, we
- // can return as we are done parsing the geometry.
- if (status.isOK()) {
- opts.appendLiteral(&subObj, x);
- opts.appendLiteral(&subObj, y);
- subObj.doneFast();
- return;
- }
- }
- // We couldn't parse a legacy coordinate pair expressed as an embedded object, so we
- // enumerate the embedded geometry obj and parse the internals. Typically we expect to find
- // $geometry, a GeoJSONPoint or $minDistance/$maxDistance/$uniqueDocs.
- while (embedded_it.more()) {
- auto embeddedElem = embedded_it.next();
- auto embeddedFieldName = embeddedElem.fieldNameStringData();
- if (embeddedFieldName == "$geometry"_sd) {
- appendGeometryOperator(subObj, embeddedElem, opts);
- } else if (embeddedFieldName == "crs" || embeddedFieldName == "type"_sd ||
- embeddedFieldName == "coordinates"_sd) {
- appendGeoJSONObj(subObj, geoNearObj, opts);
- } else {
- // $minDistance/$maxDistance/$uniqueDocs.
- opts.appendLiteral(&subObj, embeddedElem);
- }
- }
- subObj.doneFast();
- }
-}
-} // namespace
-
-/**
- * geoNearExpressionCustomSerialization implements the serialization of geoNear queries (i.e $near,
- * $geoNear, $nearSphere). GeoNear queries can be broken down into two styles of queries:
- * 1. Legacy Style (see GeoNearExpression::parseLegacyQuery()):
- * t.find({ loc : { $nearSphere: [0,0], $minDistance: 1, $maxDistance: 3 }})
- * t.find({ loc : { $nearSphere: [0,0] }})
- * t.find({ loc : { $near : [0, 0, 1] } });
- * t.find({ loc : { $near: { someGeoJSONPoint}})
- * t.find({ loc : { $geoNear: { someGeoJSONPoint}})
- * 2. New style queries:
- * t.find({loc: {$geoNear : { $geometry: {someGeoJSONPoint}, $maxDistance:3 }}})
- */
-void geoNearExpressionCustomSerialization(BSONObjBuilder& bob,
- const BSONObj& obj,
- const SerializationOptions& opts,
- bool includePath) {
- BSONObjIterator outer_it(obj);
- while (outer_it.more()) {
- auto elem = outer_it.next();
- if (elem.isABSONObj()) {
- StringData fieldName = elem.fieldNameStringData();
- if (fieldName == "$near"_sd || fieldName == "$geoNear"_sd ||
- fieldName == "$nearSphere"_sd) {
- appendGeoNearOperator(bob, fieldName, elem, opts);
- } else if (fieldName == "$geometry"_sd) {
- appendGeometryOperator(bob, elem, opts);
- } else {
- opts.appendLiteral(&bob, elem);
- }
- } else {
- // Here we only expect to see $minDistance/$maxDistance/$uniqueDocs
- opts.appendLiteral(&bob, elem);
- }
- }
-}
-
-/**
- * geoExpressionCustomSerialization() implements the serialization of geoExpressions ($within,
- * $geoWithin, $geoIntersects). Examples of such expressions are:
- * { $geoWithin : { $geometry : <GeoJSON> } }
- * { $geoIntersects : { $geometry : <GeoJSON> } }
- * { $geoWithin : { $box : [[x1, y1], [x2, y2]] } }
- * { $geoWithin : { $polygon : [[x1, y1], [x1, y2], [x2, y2], [x2, y1]] } }
- * { $geoWithin : { $center : [[x1, y1], r], } }
- * { $geoWithin : { $centerSphere : [[x, y], radius] } }
- * { $geoIntersects : { $geometry : [1, 2] } }
- */
-void geoExpressionCustomSerialization(BSONObjBuilder& bob,
- const BSONObj& obj,
- const SerializationOptions& opts,
- bool includePath) {
- BSONObjIterator outerIt(obj);
- BSONElement geoExprElem = outerIt.next();
- tassert(8548502, "Invalid extra fields in geo expression.", !outerIt.more());
- tassert(8548503, "Geo expression must be an object.", geoExprElem.type() == mongo::Object);
- auto fieldName = geoExprElem.fieldNameStringData();
-
- BSONObjBuilder subObj = BSONObjBuilder(bob.subobjStart(fieldName));
- auto geoObj = geoExprElem.Obj();
- BSONObjIterator embedded_it(geoObj);
- while (embedded_it.more()) {
- auto elem = embedded_it.next();
- fieldName = elem.fieldNameStringData();
- // $geoWithin/$geoIntersects can only have a $geometry or shape operators as operands
- // (i.e $polygon, $box, etc.)
- if (fieldName == "$geometry"_sd) {
- appendGeometryOperator(subObj, elem, opts);
- break;
- } else {
- appendShapeOperator(subObj, elem, opts);
- }
- }
-}
-} // namespace mongo
diff --git a/src/mongo/db/matcher/expression_geo_serializer.h b/src/mongo/db/matcher/expression_geo_serializer.h
deleted file mode 100644
index aac11318466..00000000000
--- a/src/mongo/db/matcher/expression_geo_serializer.h
+++ /dev/null
@@ -1,67 +0,0 @@
-/**
- * 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/bson/bsonelement.h"
-#include "mongo/bson/bsonobj.h"
-#include "mongo/db/query/query_shape/serialization_options.h"
-
-namespace mongo {
-
-/**
- * We rely on these custom serializers for geo expressions to handle serialization with
- * kToRepresentativeParseableValue and kToDebugTypeString policies since the original raw query
- * needs to be re-parsed in order to properly serialize.
- *
- * Ideally each sub operator ($minDistance, $maxDistance, $geometry, $box) would serialize itself,
- * rather than GeoExpression reparse the query during serialization. However, GeoExpression and
- * GeoNearExpression don't capture the nesting of the various sub-operators. Re-parsing is therefore
- * required to serialize GeoMatchExpression and GeoNearMatchExpression into BSON representative of
- * the correct original query.
- *
- * To further complicate the serialization, serializing with policy
- * kToRepresentativeParseableValue requires output that can again be
- * re-parsed, and the geoparser performs validation checking to make sure input coordinates apply to
- * the correct geo type. For example, a GeoJSON Polygon must have minimum four pairs of coordinates
- * in a closed loop. The default representative parseable array value used in const
- * SerializationOptions (an empty array) is not useful here since it won't pass geo validation
- * checks. As a workaround, this custom serializer determines a parseable value for each shape or
- * point type.
- */
-void geoNearExpressionCustomSerialization(BSONObjBuilder& bob,
- const BSONObj& obj,
- const SerializationOptions& opts = {},
- bool includePath = true);
-
-void geoExpressionCustomSerialization(BSONObjBuilder& bob,
- const BSONObj& obj,
- const SerializationOptions& opts = {},
- bool includePath = true);
-} // namespace mongo
diff --git a/src/mongo/db/matcher/expression_geo_test.cpp b/src/mongo/db/matcher/expression_geo_test.cpp
index 4c2d81cbfa8..4115285de18 100644
--- a/src/mongo/db/matcher/expression_geo_test.cpp
+++ b/src/mongo/db/matcher/expression_geo_test.cpp
@@ -33,13 +33,11 @@
#include <memory>
-#include "mongo/db/exec/document_value/document_value_test_util.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/json.h"
#include "mongo/db/matcher/expression.h"
#include "mongo/db/matcher/expression_geo.h"
-
namespace mongo {
TEST(ExpressionGeoTest, Geo1) {
@@ -155,143 +153,6 @@ TEST(ExpressionGeoTest, GeoNearEquivalent) {
}
}
-
-TEST(ExpressionGeoTest, SerializeGeoExpressions) {
- SerializationOptions opts = {};
- opts.transformIdentifiers = true;
- opts.literalPolicy = LiteralSerializationPolicy::kToDebugTypeString;
- {
- BSONObj query = fromjson("{$within: {$box: [{x: 4, y: 4}, [6, 6]]}}");
- std::unique_ptr<GeoMatchExpression> ge(makeGeoMatchExpression(query));
-
- ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
- R"({"$within":{"$box":"?array<>"}})",
- ge->getSerializedRightHandSide(opts));
- }
- {
- BSONObj query = fromjson(
- "{$geoWithin: {$geometry: {type: \"MultiPolygon\", coordinates: [[[[20.0, 70.0],[30.0, "
- "70.0],[30.0, 50.0],[20.0, 50.0],[20.0, 70.0]]]]}}}");
- std::unique_ptr<GeoMatchExpression> ge(makeGeoMatchExpression(query));
- ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
- R"({
- "$geoWithin": {
- "$geometry": {
- "type": "MultiPolygon",
- "coordinates": "?array<?array>"
- }
- }
- })",
- ge->getSerializedRightHandSide(opts));
- }
- {
- BSONObj query = fromjson(
- R"({
- "$geoIntersects": {
- "$geometry": {
- "type": "MultiPolygon",
- "coordinates": [[[
- [-20.0, -70.0],
- [-30.0, -70.0],
- [-30.0, -50.0],
- [-20.0, -50.0],
- [-20.0, -70.0]
- ]]]
- }
- }
- })");
- std::unique_ptr<GeoMatchExpression> ge(makeGeoMatchExpression(query));
- ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
- R"({
- "$geoIntersects": {
- "$geometry": {
- "type": "MultiPolygon",
- "coordinates": "?array<?array>"
- }
- }
- })",
- ge->getSerializedRightHandSide(opts));
- }
- {
- BSONObj query1 = fromjson(
- R"({$within: {
- $geometry: {
- type: 'Polygon',
- coordinates: [[[0, 0], [3, 6], [6, 1], [0, 0]]]
- }
- }})");
- std::unique_ptr<GeoMatchExpression> ge(makeGeoMatchExpression(query1));
- ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
- R"({"$within":{"$geometry":{"type":"Polygon","coordinates":"?array<?array>"}}})",
- ge->getSerializedRightHandSide(opts));
- }
- {
- BSONObj query = fromjson(
- "{$near: {$maxDistance: 100, "
- "$geometry: {type: 'Point', coordinates: [0, 0]}}}");
- std::unique_ptr<GeoNearMatchExpression> gne(makeGeoNearMatchExpression(query));
- ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
- R"({
- "$near": {
- "$maxDistance": "?number",
- "$geometry": {
- "type": "Point",
- "coordinates": "?array<?number>"
- }
- }
- })",
- gne->getSerializedRightHandSide(opts));
- }
- {
- BSONObj query = fromjson("{ $nearSphere: [0,0], $minDistance: 1, $maxDistance: 3 }");
- std::unique_ptr<GeoNearMatchExpression> gne(makeGeoNearMatchExpression(query));
- ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
- R"({
- "$nearSphere": "?array<?number>",
- "$minDistance": "?number",
- "$maxDistance": "?number"
- })",
- gne->getSerializedRightHandSide(opts));
- }
-
- {
- BSONObj query = fromjson("{$near : [0, 0, 1] }");
- std::unique_ptr<GeoNearMatchExpression> gne(makeGeoNearMatchExpression(query));
- ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
- R"({"$near":"?array<?number>"})",
- gne->getSerializedRightHandSide(opts));
- }
- {
- BSONObj query = fromjson("{$geoNear: [0, 0, 100]}");
- std::unique_ptr<GeoNearMatchExpression> gne(makeGeoNearMatchExpression(query));
- ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
- R"({"$geoNear":"?array<?number>"})",
- gne->getSerializedRightHandSide(opts));
- }
- {
- BSONObj query = fromjson("{$geoNear: [0, 10], $maxDistance: 80 }");
- std::unique_ptr<GeoNearMatchExpression> gne(makeGeoNearMatchExpression(query));
- ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
- R"({"$geoNear":"?array<?number>","$maxDistance":"?number"})",
- gne->getSerializedRightHandSide(opts));
- }
- {
- BSONObj query = fromjson("{$geoIntersects: {$geometry: [0, 0]}}");
- std::unique_ptr<GeoMatchExpression> ge(makeGeoMatchExpression(query));
- ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
- R"({"$geoIntersects":{"$geometry":["?number","?number"]}})",
- ge->getSerializedRightHandSide(opts));
- }
- {
- // Make sure we reject arrays with <2 or >2 elements.
- BSONObj query = fromjson("{$geoIntersects: {$geometry: [0, 0, 1]}}");
- std::unique_ptr<GeoExpression> gq(new GeoExpression);
- ASSERT_NOT_OK(gq->parseFrom(query));
- query = fromjson("{$geoIntersects: {$geometry: [0]}}");
- ASSERT_NOT_OK(gq->parseFrom(query));
- }
-}
-
/**
* A geo expression being not equivalent to another expression.
*/
@@ -321,280 +182,4 @@ TEST(ExpressionGeoTest, GeoNearNotEquivalent) {
gne2(makeGeoNearMatchExpression(query2));
ASSERT(!gne1->equivalent(gne2.get()));
}
-
-TEST(ExpressionGeoTest, SerializeWithCRSIFSpecifiedWithChangedOptions) {
- BSONObj query1 = fromjson(
- "{$within: {$geometry: {type: 'Polygon',"
- "coordinates: [[[0, 0], [3, 6], [6, 1], [0, 0]]],"
- "crs: {"
- "type: 'name',"
- "properties: { name: 'urn:x-mongodb:crs:strictwinding:EPSG:4326' }"
- "}}}}");
- std::unique_ptr<GeoMatchExpression> ge1(makeGeoMatchExpression(query1));
- auto opts = SerializationOptions{LiteralSerializationPolicy::kToRepresentativeParseableValue};
- auto serialized = ge1->getSerializedRightHandSide(opts);
- ASSERT_BSONOBJ_EQ_AUTO(
- R"({
- "$within": {
- "$geometry": {
- "type": "Polygon",
- "coordinates": [
- [
- [
- 0,
- 0
- ],
- [
- 0,
- 1
- ],
- [
- 1,
- 1
- ],
- [
- 0,
- 0
- ]
- ]
- ],
- "crs": {
- "type": "name",
- "properties": {
- "name": "urn:x-mongodb:crs:strictwinding:EPSG:4326"
- }
- }
- }
- }
- })",
- serialized);
- serialized = ge1->getSerializedRightHandSide(opts);
- ASSERT_BSONOBJ_EQ_AUTO(
- R"({
- "$within": {
- "$geometry": {
- "type": "Polygon",
- "coordinates": [
- [
- [
- 0,
- 0
- ],
- [
- 0,
- 1
- ],
- [
- 1,
- 1
- ],
- [
- 0,
- 0
- ]
- ]
- ],
- "crs": {
- "type": "name",
- "properties": {
- "name": "urn:x-mongodb:crs:strictwinding:EPSG:4326"
- }
- }
- }
- }
- })",
- serialized);
-}
-
-template <typename CreateFn>
-void assertRepresentativeShapeIsStable(BSONObj inputExpr,
- BSONObj expectedRepresentativeExpr,
- CreateFn createFn) {
- auto opts = SerializationOptions{LiteralSerializationPolicy::kToRepresentativeParseableValue};
- auto ge(createFn(inputExpr));
-
- auto serializedExpr = ge->getSerializedRightHandSide(opts);
- ASSERT_BSONOBJ_EQ(serializedExpr, expectedRepresentativeExpr);
-
- auto roundTripped = createFn(serializedExpr);
- ASSERT_BSONOBJ_EQ(roundTripped->getSerializedRightHandSide(opts), serializedExpr);
-}
-
-void assertRepresentativeGeoShapeIsStable(BSONObj inputExpr, BSONObj expectedRepresentativeExpr) {
- assertRepresentativeShapeIsStable(
- inputExpr, expectedRepresentativeExpr, [](const BSONObj& input) {
- return makeGeoMatchExpression(input);
- });
-}
-
-void assertRepresentativeGeoNearShapeIsStable(BSONObj inputExpr,
- BSONObj expectedRepresentativeExpr) {
- assertRepresentativeShapeIsStable(
- inputExpr, expectedRepresentativeExpr, [](const BSONObj& input) {
- return makeGeoNearMatchExpression(input);
- });
-}
-
-TEST(ExpressionGeoTest, RoundTripSerializeGeoExpressions) {
- assertRepresentativeGeoShapeIsStable(fromjson("{$within: {$box: [{x: 4, y: 4}, [6, 6]]}}"),
- fromjson("{$within: {$box: [[1, 1],[1, 1]]}}"));
-
- assertRepresentativeGeoShapeIsStable(
- fromjson(
- R"({$geoWithin: {$geometry: {type: "MultiPolygon", coordinates: [[[[20.0, 70.0],[30.0, 70.0],[30.0, 50.0],[20.0, 50.0],[20.0, 70.0]]]]}}})"),
- fromjson(
- R"({$geoWithin: {$geometry: {type: "MultiPolygon", coordinates: [[[[0, 0],[0, 1],[1, 1],[0, 0]]]]}}})"));
-
- assertRepresentativeGeoShapeIsStable(fromjson(R"({
- "$geoIntersects": {
- "$geometry": {
- "type": "MultiPolygon",
- "coordinates": [[[
- [-20.0, -70.0],
- [-30.0, -70.0],
- [-30.0, -50.0],
- [-20.0, -50.0],
- [-20.0, -70.0]
- ]]]
- }
- }
- })"),
- fromjson(R"({
- "$geoIntersects": {
- "$geometry": {
- "type": "MultiPolygon",
- "coordinates": [[[[0, 0],[0, 1],[1, 1],[0, 0]]]]
- }
- }
- })"));
-
- assertRepresentativeGeoShapeIsStable(fromjson(R"({$within: {
- $geometry: {
- type: 'Polygon',
- coordinates: [[[0, 0], [3, 6], [6, 1], [0, 0]]]
- }
- }})"),
- fromjson(R"({$within: {
- $geometry: {
- type: 'Polygon',
- coordinates: [[[0, 0],[0, 1],[1, 1],[0, 0]]]
- }
- }})"));
-
- assertRepresentativeGeoNearShapeIsStable(
- fromjson("{$near: {$maxDistance: 100, $geometry: {type: 'Point', coordinates: [0, 0]}}}"),
- fromjson("{$near: {$maxDistance: 1, $geometry: {type: 'Point', coordinates: [1, 1]}}}"));
-
- assertRepresentativeGeoNearShapeIsStable(
- fromjson("{$nearSphere: [0,0], $minDistance: 2, $maxDistance: 4 }"),
- fromjson("{$nearSphere: [1,1], $minDistance: 1, $maxDistance: 1 }"));
-
- assertRepresentativeGeoNearShapeIsStable(
- fromjson("{$minDistance: 2, $maxDistance: 4, $nearSphere: [0,0]}"),
- fromjson("{$minDistance: 1, $maxDistance: 1, $nearSphere: [1,1]}"));
-
- assertRepresentativeGeoNearShapeIsStable(fromjson("{$near: [0, 0, 1]}"),
- fromjson("{$near: [1, 1]}"));
-
- assertRepresentativeGeoNearShapeIsStable(fromjson("{$geoNear: [0, 0, 100]}"),
- fromjson("{$geoNear: [1, 1]}"));
-
- assertRepresentativeGeoNearShapeIsStable(fromjson("{$geoNear: [0, 10], $maxDistance: 80 }"),
- fromjson("{$geoNear: [1, 1], $maxDistance: 1}"));
-
- assertRepresentativeGeoShapeIsStable(fromjson("{$geoIntersects: {$geometry: [0, 0]}}"),
- fromjson("{$geoIntersects: {$geometry: [1, 1]}}"));
- // Test scenario with new $geometry query not specifying the geometry type.
- assertRepresentativeGeoNearShapeIsStable(
- fromjson("{$geoNear: { $geometry: {coordinates: [0, 10]}}}"),
- fromjson("{$geoNear: { $geometry: {coordinates: [1, 1]}}}"));
-
- // Test scenario with new $geometry query specifying invalid type.
- assertRepresentativeGeoNearShapeIsStable(
- fromjson("{$geoNear: { $geometry: { type: 'b.c', coordinates: [0, 10]}}}"),
- fromjson("{$geoNear: { $geometry: {type: 'b.c', coordinates: [1, 1]}}}"));
-
- // Test scenario with $nearSphere without $geometry and no type specified
- assertRepresentativeGeoNearShapeIsStable(fromjson(R"({"$nearSphere":{"coordinates":[0,0]}})"),
- fromjson(R"({"$nearSphere":{"coordinates":[1,1]}})"));
-
- // Test case with first field of $geometry as numeric field, arbitrary coordinate naming.
- assertRepresentativeGeoShapeIsStable(
- fromjson(R"({"$geoIntersects":{"$geometry":{"shardOptions":40,"y":5}}})"),
- fromjson(R"({"$geoIntersects":{"$geometry":{"shardOptions":1,"y":1}}})"));
-
- assertRepresentativeGeoShapeIsStable(fromjson(R"({
- "$geoIntersects": {
- "$geometry": {
- "type": "MultiLineString",
- "coordinates": [[
- [2, 0],
- [2, 2]
- ], [
- [0, 4],
- [1, 4]
- ]]
- }
- }
- })"),
- fromjson(R"({
- "$geoIntersects": {
- "$geometry": {
- "type": "MultiLineString",
- "coordinates": [[[0, 0], [1, 1]],[[0, 0], [1, 1]]]
- }
- }
- })"));
-
- assertRepresentativeGeoShapeIsStable(fromjson(R"({
- "$geoIntersects": {
- "$geometry": {
- "type": "LineString",
- "coordinates": [
- [2, 0],
- [2, 2]
- ]
- }
- }
- })"),
- fromjson(R"({
- "$geoIntersects": {
- "$geometry": {
- "type": "LineString",
- "coordinates": [[0, 0], [1, 1]]
- }
- }
- })"));
-
- assertRepresentativeGeoShapeIsStable(fromjson(R"({
- "$geoIntersects": {
- "$geometry": {
- "type": "GeometryCollection",
- "geometries": [{
- "type": "LineString",
- "coordinates": [
- [2, 0],
- [2, 2]
- ]
- }, {
- type: 'Point', coordinates: [2, 2]
- }]
- }
- }
- })"),
- fromjson(R"({
- "$geoIntersects": {
- "$geometry": {
- "type": "GeometryCollection",
- "geometries": [{
- "type": "LineString",
- "coordinates": [[0, 0], [1, 1]]
- }, {
- type: 'Point', coordinates: [1, 1]
- }]
- }
- }
- })"));
-}
-
} // namespace mongo
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 e09f142a770..5bf75f8218f 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, {});
+ serialize(&builder, true);
debug << builder.obj().toString() << "\n";
const auto* tag = getTag();
@@ -194,17 +194,13 @@ bool InternalBucketGeoWithinMatchExpression::_matchesBSONObj(const BSONObj& obj)
}
void InternalBucketGeoWithinMatchExpression::serialize(BSONObjBuilder* builder,
- const SerializationOptions& opts,
bool includePath) const {
BSONObjBuilder bob(builder->subobjStart(InternalBucketGeoWithinMatchExpression::kName));
- // Serialize the geometry shape.
BSONObjBuilder withinRegionBob(
bob.subobjStart(InternalBucketGeoWithinMatchExpression::kWithinRegion));
- opts.appendLiteral(&withinRegionBob, _geoContainer->getGeoElement());
+ withinRegionBob.append(_geoContainer->getGeoElement());
withinRegionBob.doneFast();
- // Serialize the field which is being searched over.
- bob.append(InternalBucketGeoWithinMatchExpression::kField,
- opts.serializeFieldPathFromString(_field));
+ bob.append(InternalBucketGeoWithinMatchExpression::kField, _field);
bob.doneFast();
}
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 0749e33adb4..b46b6c5076c 100644
--- a/src/mongo/db/matcher/expression_internal_bucket_geo_within.h
+++ b/src/mongo/db/matcher/expression_internal_bucket_geo_within.h
@@ -70,7 +70,6 @@ public:
: MatchExpression(MatchExpression::INTERNAL_BUCKET_GEO_WITHIN, std::move(annotation)),
_geoContainer(container),
_indexField("data." + field),
- _fieldRef(_indexField),
_field(std::move(field)) {}
void debugString(StringBuilder& debug, int indentationLevel) const final;
@@ -95,9 +94,7 @@ public:
return false;
}
- void serialize(BSONObjBuilder* builder,
- const SerializationOptions& opts = {},
- bool includePath = true) const final;
+ void serialize(BSONObjBuilder* builder, bool includePath) const final;
std::unique_ptr<MatchExpression> shallowClone() const final;
@@ -130,7 +127,8 @@ public:
}
const FieldRef* fieldRef() const final {
- return &_fieldRef;
+ MONGO_UNREACHABLE_TASSERT(5837104);
+ return nullptr;
}
void acceptVisitor(MatchExpressionMutableVisitor* visitor) final {
@@ -157,7 +155,6 @@ private:
std::shared_ptr<GeometryContainer> _geoContainer;
std::string _indexField;
- FieldRef _fieldRef;
std::string _field;
};
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 facc1f9f7a4..0de1fbec00b 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, {});
+ eq.serialize(&bob, true);
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 721b93c8da4..31157666e92 100644
--- a/src/mongo/db/matcher/expression_leaf.cpp
+++ b/src/mongo/db/matcher/expression_leaf.cpp
@@ -39,7 +39,6 @@
#include "mongo/bson/bsonmisc.h"
#include "mongo/bson/bsonobj.h"
#include "mongo/config.h"
-#include "mongo/db/exec/document_value/value.h"
#include "mongo/db/field_ref.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/matcher/expression_parser.h"
@@ -94,10 +93,8 @@ void ComparisonMatchExpressionBase::debugString(StringBuilder& debug, int indent
debug << "\n";
}
-void ComparisonMatchExpressionBase::appendSerializedRightHandSide(BSONObjBuilder* bob,
- const SerializationOptions& opts,
- bool includePath) const {
- opts.appendLiteral(bob, name(), _rhs);
+BSONObj ComparisonMatchExpressionBase::getSerializedRightHandSide() const {
+ return BSON(name() << _rhs);
}
ComparisonMatchExpression::ComparisonMatchExpression(MatchType type,
@@ -293,19 +290,15 @@ void RegexMatchExpression::debugString(StringBuilder& debug, int indentationLeve
debug << "\n";
}
-void RegexMatchExpression::appendSerializedRightHandSide(BSONObjBuilder* bob,
- const SerializationOptions& opts,
- bool includePath) const {
- // We need to be careful to generate a valid regex representative value, and the default string
- // "?" is not valid.
- opts.appendLiteral(bob, "$regex", _regex, Value("\\?"_sd));
+BSONObj RegexMatchExpression::getSerializedRightHandSide() const {
+ BSONObjBuilder regexBuilder;
+ regexBuilder.append("$regex", _regex);
if (!_flags.empty()) {
- // We need to make sure the $options value can be re-parsed as legal regex options, so
- // we'll set the representative value in this case to be the string "i" rather than
- // "?", which is the standard representative for string values.
- opts.appendLiteral(bob, "$options", _flags, Value("i"_sd));
+ regexBuilder.append("$options", _flags);
}
+
+ return regexBuilder.obj();
}
void RegexMatchExpression::serializeToBSONTypeRegex(BSONObjBuilder* out) const {
@@ -378,11 +371,8 @@ void ModMatchExpression::debugString(StringBuilder& debug, int indentationLevel)
debug << "\n";
}
-void ModMatchExpression::appendSerializedRightHandSide(BSONObjBuilder* bob,
- const SerializationOptions& opts,
- bool includePath) const {
- bob->append("$mod",
- BSON_ARRAY(opts.serializeLiteral(_divisor) << opts.serializeLiteral(_remainder)));
+BSONObj ModMatchExpression::getSerializedRightHandSide() const {
+ return BSON("$mod" << BSON_ARRAY(_divisor << _remainder));
}
bool ModMatchExpression::equivalent(const MatchExpression* other) const {
@@ -417,10 +407,8 @@ void ExistsMatchExpression::debugString(StringBuilder& debug, int indentationLev
debug << "\n";
}
-void ExistsMatchExpression::appendSerializedRightHandSide(BSONObjBuilder* bob,
- const SerializationOptions& opts,
- bool includePath) const {
- opts.appendLiteral(bob, "$exists", true);
+BSONObj ExistsMatchExpression::getSerializedRightHandSide() const {
+ return BSON("$exists" << true);
}
bool ExistsMatchExpression::equivalent(const MatchExpression* other) const {
@@ -446,7 +434,6 @@ std::unique_ptr<MatchExpression> InMatchExpression::shallowClone() const {
}
next->_hasNull = _hasNull;
next->_hasEmptyArray = _hasEmptyArray;
- next->_hasNonEmptyArrayOrObject = _hasNonEmptyArrayOrObject;
next->_equalitySet = _equalitySet;
next->_originalEqualityVector = _originalEqualityVector;
next->_equalityStorage = _equalityStorage;
@@ -502,45 +489,9 @@ void InMatchExpression::debugString(StringBuilder& debug, int indentationLevel)
debug << "\n";
}
-namespace {
-/**
- * Reduces the potentially large vector of elements to just the first of each "canonical" type.
- * Different types of numbers are not considered distinct.
- *
- * For example, collapses [2, 4, NumberInt(3), "string", "another", 3, 5] into just [2, "string"].
- */
-std::vector<Value> justFirstOfEachType(std::vector<BSONElement> elems) {
- stdx::unordered_set<int> seenTypes;
- std::vector<Value> result;
- for (auto&& elem : elems) {
- bool inserted = seenTypes.insert(canonicalizeBSONType(elem.type())).second;
- if (inserted) {
- // A new type.
- result.emplace_back(elem);
- }
- }
- return result;
-}
-} // namespace
-
-void InMatchExpression::serializeToShape(BSONObjBuilder* bob,
- const SerializationOptions& opts) const {
- std::vector<Value> firstOfEachType = justFirstOfEachType(_equalitySet);
- if (hasRegex()) {
- firstOfEachType.emplace_back(BSONRegEx());
- }
- opts.appendLiteral(bob, "$in", std::move(firstOfEachType));
-}
-
-void InMatchExpression::appendSerializedRightHandSide(BSONObjBuilder* bob,
- const SerializationOptions& opts,
- bool includePath) const {
- if (opts.literalPolicy != LiteralSerializationPolicy::kUnchanged) {
- serializeToShape(bob, opts);
- return;
- }
-
- BSONArrayBuilder arrBob(bob->subarrayStart("$in"));
+BSONObj InMatchExpression::getSerializedRightHandSide() const {
+ BSONObjBuilder inBob;
+ BSONArrayBuilder arrBob(inBob.subarrayStart("$in"));
for (auto&& _equality : _equalitySet) {
arrBob.append(_equality);
}
@@ -550,6 +501,7 @@ void InMatchExpression::appendSerializedRightHandSide(BSONObjBuilder* bob,
arrBob.append(regexBob.obj().firstElement());
}
arrBob.doneFast();
+ return inBob.obj();
}
bool InMatchExpression::equivalent(const MatchExpression* other) const {
@@ -625,8 +577,6 @@ Status InMatchExpression::setEqualities(std::vector<BSONElement> equalities) {
_hasNull = true;
} else if (equality.type() == BSONType::Array && equality.Obj().isEmpty()) {
_hasEmptyArray = true;
- } else if (equality.type() == BSONType::Array || equality.type() == BSONType::Object) {
- _hasNonEmptyArrayOrObject = true;
}
}
@@ -845,25 +795,6 @@ bool BitTestMatchExpression::matchesSingleElement(const BSONElement& e,
if (eDouble != static_cast<double>(static_cast<long long>(eDouble))) {
return false;
}
- } else if (e.type() == BSONType::NumberDecimal) {
- Decimal128 eDecimal = e.numberDecimal();
-
- // NaN NumberDecimals are rejected.
- if (eDecimal.isNaN()) {
- return false;
- }
-
- // NumberDecimals that are too large or small to be represented as a 64-bit signed
- // integer are treated as 0.
- if (eDecimal > Decimal128(std::numeric_limits<long long>::max()) ||
- eDecimal < Decimal128(std::numeric_limits<long long>::min())) {
- return false;
- }
-
- // This checks if e is an integral NumberDecimal.
- if (eDecimal != eDecimal.round(Decimal128::kRoundTowardZero)) {
- return false;
- }
}
long long eValue = e.numberLong();
@@ -908,9 +839,7 @@ void BitTestMatchExpression::debugString(StringBuilder& debug, int indentationLe
}
}
-void BitTestMatchExpression::appendSerializedRightHandSide(BSONObjBuilder* bob,
- const SerializationOptions& opts,
- bool includePath) const {
+BSONObj BitTestMatchExpression::getSerializedRightHandSide() const {
std::string opString = "";
switch (matchType()) {
@@ -935,10 +864,8 @@ void BitTestMatchExpression::appendSerializedRightHandSide(BSONObjBuilder* bob,
arrBob.append(static_cast<int32_t>(bitPosition));
}
arrBob.doneFast();
- // Unfortunately this cannot be done without copying the array into the BSONObjBuilder, since
- // `opts.appendLiteral` may choose to append this actual array, a representative empty array, or
- // a debug string.
- opts.appendLiteral(bob, opString, arrBob.arr());
+
+ return BSON(opString << arrBob.arr());
}
bool BitTestMatchExpression::equivalent(const MatchExpression* other) const {
diff --git a/src/mongo/db/matcher/expression_leaf.h b/src/mongo/db/matcher/expression_leaf.h
index 5f4f2666a94..46a80aa5e91 100644
--- a/src/mongo/db/matcher/expression_leaf.h
+++ b/src/mongo/db/matcher/expression_leaf.h
@@ -163,9 +163,7 @@ public:
virtual void debugString(StringBuilder& debug, int indentationLevel = 0) const;
- virtual void appendSerializedRightHandSide(BSONObjBuilder* bob,
- const SerializationOptions& opts = {},
- bool includePath = true) const;
+ BSONObj getSerializedRightHandSide() const final;
virtual bool equivalent(const MatchExpression* other) const;
@@ -508,9 +506,7 @@ public:
virtual void debugString(StringBuilder& debug, int indentationLevel) const;
- void appendSerializedRightHandSide(BSONObjBuilder* bob,
- const SerializationOptions& opts = {},
- bool includePath = true) const final;
+ BSONObj getSerializedRightHandSide() const final;
void serializeToBSONTypeRegex(BSONObjBuilder* out) const;
@@ -590,9 +586,7 @@ public:
virtual void debugString(StringBuilder& debug, int indentationLevel) const;
- void appendSerializedRightHandSide(BSONObjBuilder* bob,
- const SerializationOptions& opts = {},
- bool includePath = true) const final;
+ BSONObj getSerializedRightHandSide() const final;
virtual bool equivalent(const MatchExpression* other) const;
@@ -657,9 +651,7 @@ public:
virtual void debugString(StringBuilder& debug, int indentationLevel) const;
- void appendSerializedRightHandSide(BSONObjBuilder* bob,
- const SerializationOptions& opts = {},
- bool includePath = true) const final;
+ BSONObj getSerializedRightHandSide() const final;
virtual bool equivalent(const MatchExpression* other) const;
@@ -690,9 +682,7 @@ public:
virtual void debugString(StringBuilder& debug, int indentationLevel) const;
- void appendSerializedRightHandSide(BSONObjBuilder* bob,
- const SerializationOptions& opts = {},
- bool includePath = true) const final;
+ BSONObj getSerializedRightHandSide() const final;
virtual bool equivalent(const MatchExpression* other) const;
@@ -733,14 +723,6 @@ public:
return _hasEmptyArray;
}
- bool hasNonEmptyArrayOrObject() const {
- return _hasNonEmptyArrayOrObject;
- }
-
- bool hasNonScalarOrNonEmptyValues() const {
- return hasNonEmptyArrayOrObject() || hasNull() || hasRegex();
- }
-
void acceptVisitor(MatchExpressionMutableVisitor* visitor) final {
visitor->visit(this);
}
@@ -760,21 +742,12 @@ public:
private:
ExpressionOptimizerFunc getOptimizer() const final;
- /**
- * A helper to serialize to something like {$in: "?array<?number>"} or similar, depending on
- * 'opts' and whether we have a mixed-type $in or not.
- */
- void serializeToShape(BSONObjBuilder* bob, const SerializationOptions& opts) const;
-
// Whether or not '_equalities' has a jstNULL element in it.
bool _hasNull = false;
// Whether or not '_equalities' has an empty array element in it.
bool _hasEmptyArray = false;
- // Whether or not '_equalities' has a non-empty array or object element in it.
- bool _hasNonEmptyArrayOrObject = false;
-
// Collator used to construct '_eltCmp';
const CollatorInterface* _collator = nullptr;
@@ -834,9 +807,7 @@ public:
virtual void debugString(StringBuilder& debug, int indentationLevel) const;
- void appendSerializedRightHandSide(BSONObjBuilder* bob,
- const SerializationOptions& opts = {},
- bool includePath = true) const final;
+ BSONObj getSerializedRightHandSide() const final;
virtual bool equivalent(const MatchExpression* other) const;
diff --git a/src/mongo/db/matcher/expression_leaf_test.cpp b/src/mongo/db/matcher/expression_leaf_test.cpp
index cefae1506b9..99bedfa067f 100644
--- a/src/mongo/db/matcher/expression_leaf_test.cpp
+++ b/src/mongo/db/matcher/expression_leaf_test.cpp
@@ -1463,15 +1463,6 @@ TEST(BitTestMatchExpression, DoesNotMatchOther) {
BSONObj notMatch8 = fromjson("{a: 1e100}"); // Too-Large Double
BSONObj notMatch9 = fromjson("{a: ObjectId('000000000000000000000000')}"); // OID
BSONObj notMatch10 = fromjson("{a: Date(54)}"); // Date
- BSONObj notMatch11 = fromjson("{a: NumberDecimal(\"Infinity\")}"); // Infinity NumberDecimal
- BSONObj notMatch12 =
- fromjson("{a: NumberDecimal(\"-Infinity\")}"); // Negative infinity NumberDecimal
- BSONObj notMatch13 = fromjson("{a: NumberDecimal(\"NaN\")}"); // NaN NumberDecimal
- BSONObj notMatch14 = fromjson("{a: NumberDecimal(\"1e100\")}"); // Too-Large NumberDecimal
- BSONObj notMatch15 = fromjson("{a: NumberDecimal(\"-1e100\")}"); // Too-Small NumberDecimal
- BSONObj notMatch16 = fromjson("{a: NumberDecimal(\"5.5\")}"); // Non-integral NumberDecimal
- BSONObj notMatch17 =
- fromjson("{a: NumberDecimal(\"-5.5\")}"); // Negative-integral NumberDecimal
BitsAllSetMatchExpression balls("a", bitPositions);
BitsAllClearMatchExpression ballc("a", bitPositions);
@@ -1492,13 +1483,6 @@ TEST(BitTestMatchExpression, DoesNotMatchOther) {
ASSERT(!balls.matchesSingleElement(notMatch8["a"]));
ASSERT(!balls.matchesSingleElement(notMatch9["a"]));
ASSERT(!balls.matchesSingleElement(notMatch10["a"]));
- ASSERT(!balls.matchesSingleElement(notMatch11["a"]));
- ASSERT(!balls.matchesSingleElement(notMatch12["a"]));
- ASSERT(!balls.matchesSingleElement(notMatch13["a"]));
- ASSERT(!balls.matchesSingleElement(notMatch14["a"]));
- ASSERT(!balls.matchesSingleElement(notMatch15["a"]));
- ASSERT(!balls.matchesSingleElement(notMatch16["a"]));
- ASSERT(!balls.matchesSingleElement(notMatch17["a"]));
ASSERT(!ballc.matchesSingleElement(notMatch1["a"]));
ASSERT(!ballc.matchesSingleElement(notMatch2["a"]));
ASSERT(!ballc.matchesSingleElement(notMatch3["a"]));
@@ -1509,13 +1493,6 @@ TEST(BitTestMatchExpression, DoesNotMatchOther) {
ASSERT(!ballc.matchesSingleElement(notMatch8["a"]));
ASSERT(!ballc.matchesSingleElement(notMatch9["a"]));
ASSERT(!ballc.matchesSingleElement(notMatch10["a"]));
- ASSERT(!ballc.matchesSingleElement(notMatch11["a"]));
- ASSERT(!ballc.matchesSingleElement(notMatch12["a"]));
- ASSERT(!ballc.matchesSingleElement(notMatch13["a"]));
- ASSERT(!ballc.matchesSingleElement(notMatch14["a"]));
- ASSERT(!ballc.matchesSingleElement(notMatch15["a"]));
- ASSERT(!ballc.matchesSingleElement(notMatch16["a"]));
- ASSERT(!ballc.matchesSingleElement(notMatch17["a"]));
ASSERT(!banys.matchesSingleElement(notMatch1["a"]));
ASSERT(!banys.matchesSingleElement(notMatch2["a"]));
ASSERT(!banys.matchesSingleElement(notMatch3["a"]));
@@ -1526,13 +1503,6 @@ TEST(BitTestMatchExpression, DoesNotMatchOther) {
ASSERT(!banys.matchesSingleElement(notMatch8["a"]));
ASSERT(!banys.matchesSingleElement(notMatch9["a"]));
ASSERT(!banys.matchesSingleElement(notMatch10["a"]));
- ASSERT(!banys.matchesSingleElement(notMatch11["a"]));
- ASSERT(!banys.matchesSingleElement(notMatch12["a"]));
- ASSERT(!banys.matchesSingleElement(notMatch13["a"]));
- ASSERT(!banys.matchesSingleElement(notMatch14["a"]));
- ASSERT(!banys.matchesSingleElement(notMatch15["a"]));
- ASSERT(!banys.matchesSingleElement(notMatch16["a"]));
- ASSERT(!banys.matchesSingleElement(notMatch17["a"]));
ASSERT(!banyc.matchesSingleElement(notMatch1["a"]));
ASSERT(!banyc.matchesSingleElement(notMatch2["a"]));
ASSERT(!banyc.matchesSingleElement(notMatch3["a"]));
@@ -1543,13 +1513,6 @@ TEST(BitTestMatchExpression, DoesNotMatchOther) {
ASSERT(!banyc.matchesSingleElement(notMatch8["a"]));
ASSERT(!banyc.matchesSingleElement(notMatch9["a"]));
ASSERT(!banyc.matchesSingleElement(notMatch10["a"]));
- ASSERT(!banyc.matchesSingleElement(notMatch11["a"]));
- ASSERT(!banyc.matchesSingleElement(notMatch12["a"]));
- ASSERT(!banyc.matchesSingleElement(notMatch13["a"]));
- ASSERT(!banyc.matchesSingleElement(notMatch14["a"]));
- ASSERT(!banyc.matchesSingleElement(notMatch15["a"]));
- ASSERT(!banyc.matchesSingleElement(notMatch16["a"]));
- ASSERT(!banyc.matchesSingleElement(notMatch17["a"]));
}
TEST(BitTestMatchExpression, MatchBinaryWithLongBitMask) {
diff --git a/src/mongo/db/matcher/expression_optimize_test.cpp b/src/mongo/db/matcher/expression_optimize_test.cpp
index 763d80fa2cb..eaa606e06df 100644
--- a/src/mongo/db/matcher/expression_optimize_test.cpp
+++ b/src/mongo/db/matcher/expression_optimize_test.cpp
@@ -47,9 +47,9 @@ using unittest::assertGet;
/**
* Helper function to parse the given BSON object as a MatchExpression, checks the status,
- * and return the MatchExpression pointer.
+ * and return the MatchExpression*.
*/
-std::unique_ptr<MatchExpression> parseMatchExpression(const BSONObj& obj) {
+MatchExpression* parseMatchExpression(const BSONObj& obj) {
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
StatusWithMatchExpression status =
MatchExpressionParser::parse(obj,
@@ -63,7 +63,7 @@ std::unique_ptr<MatchExpression> parseMatchExpression(const BSONObj& obj) {
FAIL(ss);
}
- return std::move(status.getValue());
+ return status.getValue().release();
}
/**
@@ -75,8 +75,7 @@ Status isValid(const std::string& queryStr, const FindCommandRequest& findComman
BSONObj queryObj = fromjson(queryStr);
std::unique_ptr<MatchExpression> me(parseMatchExpression(queryObj));
me = MatchExpression::optimize(std::move(me));
- if (auto status = parsed_find_command::isValid(me.get(), findCommand).getStatus();
- !status.isOK()) {
+ if (auto status = CanonicalQuery::isValid(me.get(), findCommand).getStatus(); !status.isOK()) {
return status;
}
return CanonicalQuery::isValidNormalized(me.get());
@@ -351,14 +350,18 @@ 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));
- ASSERT_BSONOBJ_EQ(optimizedMatchExpression->serialize(), fromjson("{$alwaysFalse: 1}"));
+ BSONObjBuilder bob;
+ optimizedMatchExpression->serialize(&bob, true);
+ ASSERT_BSONOBJ_EQ(bob.obj(), 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));
- ASSERT_BSONOBJ_EQ(optimizedMatchExpression->serialize(), fromjson("{a: {$eq: 1}}"));
+ BSONObjBuilder bob;
+ optimizedMatchExpression->serialize(&bob, true);
+ ASSERT_BSONOBJ_EQ(bob.obj(), fromjson("{a: {$eq: 1}}"));
}
TEST(ExpressionOptimizeTest, AndWithSingleChildAlwaysTrueOptimizesToEmptyAnd) {
@@ -367,7 +370,9 @@ 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()));
- ASSERT_BSONOBJ_EQ(optimizedMatchExpression->serialize(), fromjson("{}"));
+ BSONObjBuilder bob;
+ optimizedMatchExpression->serialize(&bob, true);
+ ASSERT_BSONOBJ_EQ(bob.obj(), fromjson("{}"));
}
TEST(ExpressionOptimizeTest, AndWithEachChildAlwaysTrueOptimizesToEmptyAnd) {
@@ -376,28 +381,36 @@ 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()));
- ASSERT_BSONOBJ_EQ(optimizedMatchExpression->serialize(), fromjson("{}"));
+ BSONObjBuilder bob;
+ optimizedMatchExpression->serialize(&bob, true);
+ ASSERT_BSONOBJ_EQ(bob.obj(), 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));
- ASSERT_BSONOBJ_EQ(optimizedMatchExpression->serialize(), fromjson("{$alwaysFalse: 1}"));
+ BSONObjBuilder bob;
+ optimizedMatchExpression->serialize(&bob, true);
+ ASSERT_BSONOBJ_EQ(bob.obj(), 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));
- ASSERT_BSONOBJ_EQ(optimizedMatchExpression->serialize(), fromjson("{$alwaysTrue: 1}"));
+ BSONObjBuilder bob;
+ optimizedMatchExpression->serialize(&bob, true);
+ ASSERT_BSONOBJ_EQ(bob.obj(), 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));
- ASSERT_BSONOBJ_EQ(optimizedMatchExpression->serialize(), fromjson("{a: {$eq: 1}}"));
+ BSONObjBuilder bob;
+ optimizedMatchExpression->serialize(&bob, true);
+ ASSERT_BSONOBJ_EQ(bob.obj(), fromjson("{a: {$eq: 1}}"));
}
TEST(ExpressionOptimizeTest, OrPromotesSingleAlwaysFalseAfterOptimize) {
@@ -406,7 +419,9 @@ TEST(ExpressionOptimizeTest, OrPromotesSingleAlwaysFalseAfterOptimize) {
std::unique_ptr<MatchExpression> matchExpression(parseMatchExpression(obj));
auto optimizedMatchExpression = MatchExpression::optimize(std::move(matchExpression));
ASSERT_TRUE(dynamic_cast<AlwaysFalseMatchExpression*>(optimizedMatchExpression.get()));
- ASSERT_BSONOBJ_EQ(optimizedMatchExpression->serialize(), fromjson("{$alwaysFalse: 1}"));
+ BSONObjBuilder bob;
+ optimizedMatchExpression->serialize(&bob, true);
+ ASSERT_BSONOBJ_EQ(bob.obj(), fromjson("{$alwaysFalse: 1}"));
}
TEST(ExpressionOptimizeTest, OrPromotesSingleAlwaysFalse) {
@@ -414,7 +429,9 @@ TEST(ExpressionOptimizeTest, OrPromotesSingleAlwaysFalse) {
std::unique_ptr<MatchExpression> matchExpression(parseMatchExpression(obj));
auto optimizedMatchExpression = MatchExpression::optimize(std::move(matchExpression));
ASSERT_TRUE(dynamic_cast<AlwaysFalseMatchExpression*>(optimizedMatchExpression.get()));
- ASSERT_BSONOBJ_EQ(optimizedMatchExpression->serialize(), fromjson("{$alwaysFalse: 1}"));
+ BSONObjBuilder bob;
+ optimizedMatchExpression->serialize(&bob, true);
+ ASSERT_BSONOBJ_EQ(bob.obj(), fromjson("{$alwaysFalse: 1}"));
}
TEST(ExpressionOptimizeTest, OrPromotesMultipleAlwaysFalse) {
@@ -422,14 +439,18 @@ TEST(ExpressionOptimizeTest, OrPromotesMultipleAlwaysFalse) {
std::unique_ptr<MatchExpression> matchExpression(parseMatchExpression(obj));
auto optimizedMatchExpression = MatchExpression::optimize(std::move(matchExpression));
ASSERT_TRUE(dynamic_cast<AlwaysFalseMatchExpression*>(optimizedMatchExpression.get()));
- ASSERT_BSONOBJ_EQ(optimizedMatchExpression->serialize(), fromjson("{$alwaysFalse: 1}"));
+ BSONObjBuilder bob;
+ optimizedMatchExpression->serialize(&bob, true);
+ ASSERT_BSONOBJ_EQ(bob.obj(), 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));
- ASSERT_BSONOBJ_EQ(optimizedMatchExpression->serialize(), fromjson("{$alwaysTrue: 1}"));
+ BSONObjBuilder bob;
+ optimizedMatchExpression->serialize(&bob, true);
+ ASSERT_BSONOBJ_EQ(bob.obj(), fromjson("{$alwaysTrue: 1}"));
}
TEST(ExpressionOptimizeTest, OrRewrittenToIn) {
@@ -456,7 +477,9 @@ TEST(ExpressionOptimizeTest, OrRewrittenToIn) {
auto obj = fromjson(exprStr);
std::unique_ptr<MatchExpression> matchExpression(parseMatchExpression(obj));
auto optimizedMatchExpression = MatchExpression::optimize(std::move(matchExpression));
- return optimizedMatchExpression->serialize();
+ BSONObjBuilder bob;
+ optimizedMatchExpression->serialize(&bob, true);
+ return bob.obj();
};
ASSERT_BSONOBJ_EQ(optimizeExpr(queries[0].first), fromjson(queries[0].second));
@@ -472,33 +495,5 @@ TEST(ExpressionOptimizeTest, OrRewrittenToIn) {
ASSERT_BSONOBJ_EQ(optimizeExpr(queries[10].first), fromjson(queries[10].second));
}
-TEST(ExpressionOptimizeTest, PartialOrToInRewriteDoesNotGenerateDirectlyNestedOr) {
- BSONObj obj = fromjson("{$or: [{x: {$eq: 3}}, {x: {$eq: 4}}, {y: 5}, {z: 6}]}");
- auto optimizedMatchExpression = MatchExpression::optimize(parseMatchExpression(obj));
- ASSERT_BSONOBJ_EQ(optimizedMatchExpression->serialize(),
- fromjson("{$or: [{x: {$in: [3, 4]}}, {y: {$eq: 5}}, {z: {$eq: 6}}]}"));
-}
-
-TEST(ExpressionOptimizeTest, NorRemovesAlwaysFalseChildren) {
- BSONObj obj = fromjson("{$nor: [{a: 1}, {$alwaysFalse: 1}]}");
- std::unique_ptr<MatchExpression> matchExpression(parseMatchExpression(obj));
- auto optimizedMatchExpression = MatchExpression::optimize(std::move(matchExpression));
- ASSERT_BSONOBJ_EQ(optimizedMatchExpression->serialize(), fromjson("{a: {$not: {$eq: 1}}}"));
-}
-
-TEST(ExpressionOptimizeTest, NorWithoutChildrenOptimizesToEmptyAnd) {
- BSONObj obj = fromjson("{$nor: [{$alwaysFalse: 1}, {$alwaysFalse: 1}]}");
- std::unique_ptr<MatchExpression> matchExpression(parseMatchExpression(obj));
- auto optimizedMatchExpression = MatchExpression::optimize(std::move(matchExpression));
- ASSERT_TRUE(dynamic_cast<AndMatchExpression*>(optimizedMatchExpression.get()));
- ASSERT_BSONOBJ_EQ(optimizedMatchExpression->serialize(), fromjson("{}"));
-}
-
-TEST(ExpressionOptimizeTest, NorWithAlwaysTrueChildOptimizesToAlwaysFalse) {
- BSONObj obj = fromjson("{$nor: [{a: 1}, {$alwaysTrue: 1}]}");
- std::unique_ptr<MatchExpression> matchExpression(parseMatchExpression(obj));
- auto optimizedMatchExpression = MatchExpression::optimize(std::move(matchExpression));
- ASSERT_BSONOBJ_EQ(optimizedMatchExpression->serialize(), fromjson("{$alwaysFalse: 1}"));
-}
} // namespace
} // namespace mongo
diff --git a/src/mongo/db/matcher/expression_parser.cpp b/src/mongo/db/matcher/expression_parser.cpp
index e6529908910..0e86cd4e06b 100644
--- a/src/mongo/db/matcher/expression_parser.cpp
+++ b/src/mongo/db/matcher/expression_parser.cpp
@@ -2205,15 +2205,15 @@ MONGO_INITIALIZER_WITH_PREREQUISITES(MatchExpressionCounters,
if (name[0] == '_' || exceptionsSet.count(name) > 0) {
continue;
}
- operatorCountersMatchExpressions.addCounter("$" + name);
+ operatorCountersMatchExpressions.addMatchExprCounter("$" + name);
}
for (auto&& [name, fn] : *pathlessOperatorMap) {
if (name[0] == '_' || exceptionsSet.count(name) > 0) {
continue;
}
- operatorCountersMatchExpressions.addCounter("$" + name);
+ operatorCountersMatchExpressions.addMatchExprCounter("$" + name);
}
- operatorCountersMatchExpressions.addCounter("$not");
+ operatorCountersMatchExpressions.addMatchExprCounter("$not");
}
diff --git a/src/mongo/db/matcher/expression_path.h b/src/mongo/db/matcher/expression_path.h
index f167177bdb4..16182705a62 100644
--- a/src/mongo/db/matcher/expression_path.h
+++ b/src/mongo/db/matcher/expression_path.h
@@ -122,39 +122,21 @@ public:
}
}
- void serialize(BSONObjBuilder* out,
- const SerializationOptions& opts = {},
- bool includePath = true) const override {
+ void serialize(BSONObjBuilder* out, bool includePath) const override {
if (includePath) {
- BSONObjBuilder subObj(out->subobjStart(opts.serializeFieldPathFromString(path())));
- appendSerializedRightHandSide(&subObj, opts, includePath);
- subObj.doneFast();
+ out->append(path(), getSerializedRightHandSide());
} else {
- appendSerializedRightHandSide(out, opts, includePath);
+ out->appendElements(getSerializedRightHandSide());
}
}
/**
- * Constructs a BSONObj that represents the right-hand-side of a PathMatchExpression. Used for
+ * Returns a BSONObj that represents the right-hand-side of a PathMatchExpression. Used for
* 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.
- *
- * Serialization options should be respected for any descendent expressions. Eg, if the
- * 'literalPolicy' option is 'kToDebugTypeString', then any literal argument (like the number 1
- * in the example above), should be "shapified" (e.g. "?number"). 'literal' here is in contrast
- * to another expression, if that is possible syntactically.
*/
- virtual void appendSerializedRightHandSide(BSONObjBuilder* bob,
- const SerializationOptions& opts = {},
- bool includePath = true) const = 0;
-
- BSONObj getSerializedRightHandSide(const SerializationOptions& opts = {},
- bool includePath = true) const {
- BSONObjBuilder bob;
- appendSerializedRightHandSide(&bob, opts, includePath);
- return bob.obj();
- }
+ virtual BSONObj getSerializedRightHandSide() const = 0;
protected:
void _doAddDependencies(DepsTracker* deps) const final {
diff --git a/src/mongo/db/matcher/expression_serialization_test.cpp b/src/mongo/db/matcher/expression_serialization_test.cpp
index 3b25631cd0f..78517a52634 100644
--- a/src/mongo/db/matcher/expression_serialization_test.cpp
+++ b/src/mongo/db/matcher/expression_serialization_test.cpp
@@ -37,16 +37,7 @@
#include "mongo/db/matcher/expression_parser.h"
#include "mongo/db/matcher/extensions_callback_noop.h"
#include "mongo/db/matcher/matcher.h"
-#include "mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index.h"
-#include "mongo/db/matcher/schema/expression_internal_schema_cond.h"
-#include "mongo/db/matcher/schema/expression_internal_schema_eq.h"
-#include "mongo/db/matcher/schema/expression_internal_schema_fmod.h"
-#include "mongo/db/matcher/schema/expression_internal_schema_max_items.h"
#include "mongo/db/matcher/schema/expression_internal_schema_max_length.h"
-#include "mongo/db/matcher/schema/expression_internal_schema_max_properties.h"
-#include "mongo/db/matcher/schema/expression_internal_schema_min_items.h"
-#include "mongo/db/matcher/schema/expression_internal_schema_min_length.h"
-#include "mongo/db/matcher/schema/expression_internal_schema_min_properties.h"
#include "mongo/db/pipeline/expression_context_for_test.h"
#include "mongo/unittest/unittest.h"
@@ -58,7 +49,9 @@ using std::string;
using std::unique_ptr;
BSONObj serialize(MatchExpression* match) {
- return match->serialize();
+ BSONObjBuilder bob;
+ match->serialize(&bob, true);
+ return bob.obj();
}
TEST(SerializeBasic, AndExpressionWithOneChildSerializesCorrectly) {
@@ -362,6 +355,7 @@ TEST(SerializeBasic, ExpressionElemMatchValueWithTripleNotSerializesCorrectly) {
ASSERT_EQ(original.matches(obj), reserialized.matches(obj));
}
+
TEST(SerializeBasic, ExpressionSizeSerializesCorrectly) {
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
Matcher original(fromjson("{x: {$size: 2}}"),
@@ -636,32 +630,6 @@ TEST(SerializeBasic, ExpressionRegexWithValueAndOptionsSerializesCorrectly) {
ASSERT_EQ(original.matches(obj), reserialized.matches(obj));
}
-TEST(SerializeBasic, ExpressionRegexWithoutOptionsSerializesShapeCorrectly) {
- auto query = fromjson(R"({x: {$regex: ".*"}})");
- boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- auto objMatch = MatchExpressionParser::parse(query, expCtx);
- ASSERT_OK(objMatch.getStatus());
- SerializationOptions opts;
- opts.literalPolicy = LiteralSerializationPolicy::kToRepresentativeParseableValue;
- ASSERT_BSONOBJ_EQ(BSON("x" << BSON("$regex"
- << "\\?")),
- objMatch.getValue()->serialize(opts));
-}
-
-TEST(SerializeBasic, ExpressionRegexWithOptionsSerializesShapeCorrectly) {
- auto query = fromjson(R"({x: {$regex: ".*", $options: "m"}})");
- boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- auto objMatch = MatchExpressionParser::parse(query, expCtx);
- ASSERT_OK(objMatch.getStatus());
- SerializationOptions opts;
- opts.literalPolicy = LiteralSerializationPolicy::kToRepresentativeParseableValue;
- ASSERT_BSONOBJ_EQ(BSON("x" << BSON("$regex"
- << "\\?"
- << "$options"
- << "i")),
- objMatch.getValue()->serialize(opts));
-}
-
TEST(SerializeBasic, ExpressionRegexWithValueSerializesCorrectly) {
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
Matcher original(fromjson("{x: /a.b/}"),
@@ -1125,7 +1093,7 @@ TEST(SerializeBasic, ExpressionNotWithDirectPathExpSerializesCorrectly) {
// direct path expression child, instead creating a NOT -> AND -> path expression. This test
// manually constructs such an expression in case it ever turns up, since that should still be
// able to serialize.
- auto originalBSON = fromjson("{a: {$not: {$eq: 2}}}");
+ auto originalBSON = fromjson("{a: {$not: {$eq: 2}}}}");
auto equalityRHSElem = originalBSON["a"]["$not"]["$eq"];
auto equalityExpression = std::make_unique<EqualityMatchExpression>("a"_sd, equalityRHSElem);
@@ -1711,7 +1679,7 @@ TEST(SerializeInternalSchema, ExpressionInternalSchemaMaxLengthSerializesCorrect
TEST(SerializeInternalSchema, ExpressionInternalSchemaCondSerializesCorrectly) {
boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- Matcher original(fromjson("{$_internalSchemaCond: [{a: 1}, {b: 2}, {c: 3}]}"),
+ Matcher original(fromjson("{$_internalSchemaCond: [{a: 1}, {b: 2}, {c: 3}]}}"),
expCtx,
ExtensionsCallbackNoop(),
MatchExpressionParser::kAllowAllSpecialFeatures);
@@ -1722,7 +1690,7 @@ TEST(SerializeInternalSchema, ExpressionInternalSchemaCondSerializesCorrectly) {
BSONObjBuilder builder;
ASSERT_BSONOBJ_EQ(
*reserialized.getQuery(),
- fromjson("{$_internalSchemaCond: [{a: {$eq: 1}}, {b: {$eq: 2}}, {c: {$eq: 3}}]}"));
+ fromjson("{$_internalSchemaCond: [{a: {$eq: 1}}, {b: {$eq: 2}}, {c: {$eq: 3}}]}}"));
ASSERT_BSONOBJ_EQ(*reserialized.getQuery(), serialize(reserialized.getMatchExpression()));
}
@@ -1880,311 +1848,5 @@ TEST(SerializeInternalBinDataSubType, ExpressionBinDataSubTypeSerializesCorrectl
ASSERT_TRUE(original.matches(obj));
}
-TEST(SerializeInternalSchema, AllowedPropertiesRedactsCorrectly) {
-
- auto query = fromjson(
- "{$_internalSchemaAllowedProperties: {properties: ['a', 'b'],"
- "namePlaceholder: 'i', patternProperties: [], otherwise: {i: 0}}}");
- boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- auto objMatch = MatchExpressionParser::parse(query, expCtx);
- ASSERT_OK(objMatch.getStatus());
-
- SerializationOptions opts = SerializationOptions::kDebugShapeAndMarkIdentifiers_FOR_TEST;
-
- ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
- R"({
- "$_internalSchemaAllowedProperties": {
- "properties": "?array<?string>",
- "namePlaceholder": "i",
- "patternProperties": [],
- "otherwise": {
- "HASH<i>": {
- "$eq": "?number"
- }
- }
- }
- })",
- objMatch.getValue()->serialize(opts));
-}
-
-/**
- * Helper function for parsing and creating MatchExpressions.
- */
-std::unique_ptr<InternalSchemaCondMatchExpression> createCondMatchExpression(BSONObj condition,
- BSONObj thenBranch,
- BSONObj elseBranch) {
- boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- auto conditionExpr = MatchExpressionParser::parse(condition, expCtx);
- ASSERT_OK(conditionExpr.getStatus());
- auto thenBranchExpr = MatchExpressionParser::parse(thenBranch, expCtx);
- ASSERT_OK(thenBranchExpr.getStatus());
- auto elseBranchExpr = MatchExpressionParser::parse(elseBranch, expCtx);
-
- std::array<std::unique_ptr<MatchExpression>, 3> expressions = {
- {std::move(conditionExpr.getValue()),
- std::move(thenBranchExpr.getValue()),
- std::move(elseBranchExpr.getValue())}};
-
- auto cond = std::make_unique<InternalSchemaCondMatchExpression>(std::move(expressions));
-
- return cond;
-}
-
-TEST(SerializeInternalSchema, CondMatchRedactsCorrectly) {
- auto opts = SerializationOptions::kDebugShapeAndMarkIdentifiers_FOR_TEST;
- auto conditionQuery = BSON("age" << BSON("$lt" << 18));
- auto thenQuery = BSON("job"
- << "student");
- auto elseQuery = BSON("job"
- << "engineer");
- auto cond = createCondMatchExpression(conditionQuery, thenQuery, elseQuery);
- BSONObjBuilder bob;
- cond->serialize(&bob, opts);
- ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
- R"({
- "$_internalSchemaCond": [
- {
- "HASH<age>": {
- "$lt": "?number"
- }
- },
- {
- "HASH<job>": {
- "$eq": "?string"
- }
- },
- {
- "HASH<job>": {
- "$eq": "?string"
- }
- }
- ]
- })",
- bob.done());
-}
-
-TEST(SerializeInternalSchema, FmodMatchRedactsCorrectly) {
- InternalSchemaFmodMatchExpression m("a"_sd, Decimal128(1.7), Decimal128(2));
- auto opts = SerializationOptions{LiteralSerializationPolicy::kToDebugTypeString};
- BSONObjBuilder bob;
- m.serialize(&bob, opts);
- ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
- R"({"a":{"$_internalSchemaFmod":["?number","?number"]}})",
- bob.done());
-}
-
-TEST(SerializeInternalSchema, MatchArrayIndexRedactsCorrectly) {
- auto query = fromjson(
- "{foo: {$_internalSchemaMatchArrayIndex:"
- "{index: 0, namePlaceholder: 'i', expression: {i: {$type: 'number'}}}}}");
- boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- auto objMatch = MatchExpressionParser::parse(query, expCtx);
- ASSERT_OK(objMatch.getStatus());
-
- BSONObjBuilder bob;
- auto opts = SerializationOptions::kDebugShapeAndMarkIdentifiers_FOR_TEST;
- objMatch.getValue()->serialize(&bob, opts);
- ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
- R"({
- "HASH<foo>": {
- "$_internalSchemaMatchArrayIndex": {
- "index": "?number",
- "namePlaceholder": "HASH<i>",
- "expression": {
- "HASH<i>": {
- "$type": ['number']
- }
- }
- }
- }
- })",
- bob.done());
-}
-
-TEST(SerializeInternalSchema, MaxItemsRedactsCorrectly) {
- InternalSchemaMaxItemsMatchExpression maxItems("a.b"_sd, 2);
- auto opts = SerializationOptions::kDebugShapeAndMarkIdentifiers_FOR_TEST;
- ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
- R"({"$_internalSchemaMaxItems":"?number"})",
- maxItems.getSerializedRightHandSide(opts));
-}
-
-TEST(SerializeInternalSchema, MaxLengthRedactsCorrectly) {
- InternalSchemaMaxLengthMatchExpression maxLength("a"_sd, 2);
- auto opts = SerializationOptions::kDebugShapeAndMarkIdentifiers_FOR_TEST;
- ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
- R"({"$_internalSchemaMaxLength":"?number"})",
- maxLength.getSerializedRightHandSide(opts));
-}
-
-TEST(SerializeInternalSchema, MinItemsRedactsCorrectly) {
- InternalSchemaMinItemsMatchExpression minItems("a.b"_sd, 2);
- auto opts = SerializationOptions::kDebugShapeAndMarkIdentifiers_FOR_TEST;
-
- ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
- R"({"$_internalSchemaMinItems":"?number"})",
- minItems.getSerializedRightHandSide(opts));
-}
-
-TEST(SerializeInternalSchema, MinLengthRedactsCorrectly) {
- InternalSchemaMinLengthMatchExpression minLength("a"_sd, 2);
- auto opts = SerializationOptions{LiteralSerializationPolicy::kToDebugTypeString};
- ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
- R"({"$_internalSchemaMinLength":"?number"})",
- minLength.getSerializedRightHandSide(opts));
-}
-
-TEST(SerializeInternalSchema, MinPropertiesRedactsCorrectly) {
- InternalSchemaMinPropertiesMatchExpression minProperties(5);
- auto opts = SerializationOptions{LiteralSerializationPolicy::kToDebugTypeString};
-
- BSONObjBuilder bob;
- minProperties.serialize(&bob, opts);
- ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
- R"({"$_internalSchemaMinProperties":"?number"})",
- bob.done());
-}
-
-TEST(SerializeInternalSchema, ObjectMatchRedactsCorrectly) {
- auto opts = SerializationOptions::kDebugShapeAndMarkIdentifiers_FOR_TEST;
- auto query = fromjson(
- " {a: {$_internalSchemaObjectMatch: {"
- " c: {$eq: 3}"
- " }}}");
- boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- auto objMatch = MatchExpressionParser::parse(query, expCtx);
- ASSERT_OK(objMatch.getStatus());
-
- ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
- R"({"HASH<a>":{"$_internalSchemaObjectMatch":{"HASH<c>":{"$eq":"?number"}}}})",
- objMatch.getValue()->serialize(opts));
-}
-
-TEST(SerializeInternalSchema, RootDocEqRedactsCorrectly) {
- auto query = fromjson("{$_internalSchemaRootDocEq: {a:1, b: {c: 1, d: [1]}}}");
- boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- auto opts = SerializationOptions::kDebugShapeAndMarkIdentifiers_FOR_TEST;
- auto objMatch = MatchExpressionParser::parse(query, expCtx);
- ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
- R"({
- "$_internalSchemaRootDocEq": {
- "HASH<a>": "?number",
- "HASH<b>": {
- "HASH<c>": "?number",
- "HASH<d>": [
- "?number"
- ]
- }
- }
- })",
- objMatch.getValue()->serialize(opts));
-}
-
-TEST(SerializeInternalSchema, BinDataEncryptedTypeRedactsCorrectly) {
- MatcherTypeSet typeSet;
- typeSet.bsonTypes.insert(BSONType::String);
- typeSet.bsonTypes.insert(BSONType::Date);
- InternalSchemaBinDataEncryptedTypeExpression e("a"_sd, std::move(typeSet));
- auto opts = SerializationOptions{LiteralSerializationPolicy::kToDebugTypeString};
- ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
- R"({"$_internalSchemaBinDataEncryptedType":[2,9]})",
- e.getSerializedRightHandSide(opts));
-}
-
-TEST(SerializeInternalSchema, BinDataFLE2EncryptedTypeRedactsCorrectly) {
- InternalSchemaBinDataFLE2EncryptedTypeExpression e("ssn"_sd, BSONType::String);
- auto opts = SerializationOptions{LiteralSerializationPolicy::kToDebugTypeString};
- ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
- R"({"$_internalSchemaBinDataFLE2EncryptedType":[2]})",
- e.getSerializedRightHandSide(opts));
-}
-
-TEST(SerializesInternalSchema, MaxPropertiesRedactsCorrectly) {
- InternalSchemaMaxPropertiesMatchExpression maxProperties(5);
- auto opts = SerializationOptions{LiteralSerializationPolicy::kToDebugTypeString};
-
- BSONObjBuilder bob;
- maxProperties.serialize(&bob, opts);
- ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
- R"({"$_internalSchemaMaxProperties":"?number"})",
- bob.done());
-}
-
-TEST(SerializesInternalSchema, EqRedactsCorrectly) {
- auto opts = SerializationOptions::kDebugShapeAndMarkIdentifiers_FOR_TEST;
- auto query = fromjson("{$_internalSchemaEq: {a:1, b: {c: 1, d: [1]}}}");
- BSONObjBuilder bob;
- InternalSchemaEqMatchExpression e("a"_sd, query.firstElement());
- e.serialize(&bob, opts);
- ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
- R"({
- "HASH<a>": {
- "$_internalSchemaEq": {
- "HASH<a>": "?number",
- "HASH<b>": {
- "HASH<c>": "?number",
- "HASH<d>": [
- "?number"
- ]
- }
- }
- }
- })",
- bob.done());
-}
-
-TEST(InternalSchemaAllElemMatchFromIndexMatchExpression, RedactsExpressionCorrectly) {
- auto query = fromjson("{a: {$_internalSchemaAllElemMatchFromIndex: [2, {a: {$lt: 5}}]}}");
- boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- auto expr = MatchExpressionParser::parse(query, expCtx);
- ASSERT_OK(expr.getStatus());
- auto elemMatchExpr = dynamic_cast<const InternalSchemaAllElemMatchFromIndexMatchExpression*>(
- expr.getValue().get());
-
- auto opts = SerializationOptions::kDebugShapeAndMarkIdentifiers_FOR_TEST;
-
- ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
- R"({
- "$_internalSchemaAllElemMatchFromIndex": [
- "?number",
- {
- "HASH<a>": {
- "$lt": "?number"
- }
- }
- ]
- })",
- elemMatchExpr->getSerializedRightHandSide(opts));
-}
-
-TEST(SerializeBasic, SerializesNestedElemMatchCorrectly) {
- auto query = fromjson(R"({a: {$elemMatch: {$elemMatch: {b: {$lt: 6, $gt: 4}}}}})");
- boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
- auto objMatch = MatchExpressionParser::parse(query, expCtx);
- ASSERT_OK(objMatch.getStatus());
- SerializationOptions opts;
- opts.literalPolicy = LiteralSerializationPolicy::kToDebugTypeString;
- ASSERT_BSONOBJ_EQ_AUTO(
- R"({"a": {
- "$elemMatch": {
- "$elemMatch": {
- "$and": [
- {
- "b": {
- "$lt": "?number"
- }
- },
- {
- "b": {
- "$gt": "?number"
- }
- }
- ]
- }
- }
- }
- })",
- objMatch.getValue()->serialize(opts));
-}
} // namespace
} // namespace mongo
diff --git a/src/mongo/db/matcher/expression_text_base.cpp b/src/mongo/db/matcher/expression_text_base.cpp
index ce485de6fbc..56ad05af3d3 100644
--- a/src/mongo/db/matcher/expression_text_base.cpp
+++ b/src/mongo/db/matcher/expression_text_base.cpp
@@ -56,16 +56,12 @@ void TextMatchExpressionBase::debugString(StringBuilder& debug, int indentationL
debug << "\n";
}
-void TextMatchExpressionBase::serialize(BSONObjBuilder* out,
- const SerializationOptions& opts,
- bool includePath) const {
+void TextMatchExpressionBase::serialize(BSONObjBuilder* out, bool includePath) const {
const fts::FTSQuery& ftsQuery = getFTSQuery();
out->append("$text",
- BSON("$search" << opts.serializeLiteral(ftsQuery.getQuery()) << "$language"
- << opts.serializeLiteral(ftsQuery.getLanguage()) << "$caseSensitive"
- << opts.serializeLiteral(ftsQuery.getCaseSensitive())
- << "$diacriticSensitive"
- << opts.serializeLiteral(ftsQuery.getDiacriticSensitive())));
+ BSON("$search" << ftsQuery.getQuery() << "$language" << ftsQuery.getLanguage()
+ << "$caseSensitive" << ftsQuery.getCaseSensitive()
+ << "$diacriticSensitive" << ftsQuery.getDiacriticSensitive()));
}
bool TextMatchExpressionBase::equivalent(const MatchExpression* other) const {
diff --git a/src/mongo/db/matcher/expression_text_base.h b/src/mongo/db/matcher/expression_text_base.h
index 622438e1500..22fc581a2e2 100644
--- a/src/mongo/db/matcher/expression_text_base.h
+++ b/src/mongo/db/matcher/expression_text_base.h
@@ -60,9 +60,7 @@ public:
*/
virtual const fts::FTSQuery& getFTSQuery() const = 0;
- void appendSerializedRightHandSide(BSONObjBuilder* bob,
- const SerializationOptions& opts = {},
- bool includePath = true) const final {
+ BSONObj getSerializedRightHandSide() const final {
MONGO_UNREACHABLE;
}
@@ -72,9 +70,7 @@ public:
void debugString(StringBuilder& debug, int indentationLevel = 0) const final;
- void serialize(BSONObjBuilder* out,
- const SerializationOptions& opts = {},
- bool includePath = true) const final;
+ void serialize(BSONObjBuilder* out, bool includePath) 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 74765949ca5..df70ad278c3 100644
--- a/src/mongo/db/matcher/expression_tree.cpp
+++ b/src/mongo/db/matcher/expression_tree.cpp
@@ -40,106 +40,16 @@
#include "mongo/db/matcher/expression_text_base.h"
namespace mongo {
-namespace {
-
-PathMatchExpression* getEligiblePathMatchForNotSerialization(MatchExpression* expr) {
- // Returns a pointer to a PathMatchExpression if 'expr' is such a pointer, otherwise returns
- // nullptr.
- //
- // One exception: while TextMatchExpressionBase derives from PathMatchExpression, text match
- // expressions cannot be serialized in the same manner as other PathMatchExpression derivatives.
- // This is because the path for a TextMatchExpression is embedded within the $text object,
- // whereas for other PathMatchExpressions it is on the left-hand-side, for example {x: {$eq:
- // 1}}.
- //
- // Rather than the following dynamic_cast, we'll do a more performant, but also more verbose
- // check.
- // dynamic_cast<PathMatchExpression*>(expr) && !dynamic_cast<TextMatchExpressionBase*>(expr)
- //
- // This version below is less obviously exhaustive, but because this is just a legibility
- // optimization, and this function also gets called on the query shape stats recording hot path,
- // we think it is worth it.
- switch (expr->matchType()) {
- // leaf types
- case MatchExpression::EQ:
- case MatchExpression::LTE:
- case MatchExpression::LT:
- case MatchExpression::GT:
- case MatchExpression::GTE:
- case MatchExpression::REGEX:
- case MatchExpression::MOD:
- case MatchExpression::EXISTS:
- case MatchExpression::MATCH_IN:
- case MatchExpression::BITS_ALL_SET:
- case MatchExpression::BITS_ALL_CLEAR:
- case MatchExpression::BITS_ANY_SET:
- case MatchExpression::BITS_ANY_CLEAR:
- // array types
- case MatchExpression::ELEM_MATCH_OBJECT:
- case MatchExpression::ELEM_MATCH_VALUE:
- case MatchExpression::SIZE:
- // special types
- case MatchExpression::TYPE_OPERATOR:
- case MatchExpression::GEO:
- case MatchExpression::GEO_NEAR:
- // Internal subclasses of PathMatchExpression:
- case MatchExpression::INTERNAL_SCHEMA_ALL_ELEM_MATCH_FROM_INDEX:
- case MatchExpression::INTERNAL_SCHEMA_BIN_DATA_ENCRYPTED_TYPE:
- case MatchExpression::INTERNAL_SCHEMA_BIN_DATA_FLE2_ENCRYPTED_TYPE:
- case MatchExpression::INTERNAL_SCHEMA_BIN_DATA_SUBTYPE:
- case MatchExpression::INTERNAL_SCHEMA_MATCH_ARRAY_INDEX:
- case MatchExpression::INTERNAL_SCHEMA_MAX_ITEMS:
- case MatchExpression::INTERNAL_SCHEMA_MAX_LENGTH:
- case MatchExpression::INTERNAL_SCHEMA_MAX_PROPERTIES:
- case MatchExpression::INTERNAL_SCHEMA_MIN_ITEMS:
- case MatchExpression::INTERNAL_SCHEMA_MIN_LENGTH:
- case MatchExpression::INTERNAL_SCHEMA_TYPE:
- case MatchExpression::INTERNAL_SCHEMA_UNIQUE_ITEMS:
- return static_cast<PathMatchExpression*>(expr);
- // purposefully skip TEXT:
- case MatchExpression::TEXT:
- // Any other type is not considered a PathMatchExpression.
- case MatchExpression::AND:
- case MatchExpression::OR:
- case MatchExpression::NOT:
- case MatchExpression::NOR:
- case MatchExpression::WHERE:
- case MatchExpression::EXPRESSION:
- case MatchExpression::ALWAYS_FALSE:
- case MatchExpression::ALWAYS_TRUE:
- case MatchExpression::INTERNAL_2D_POINT_IN_ANNULUS:
- case MatchExpression::INTERNAL_BUCKET_GEO_WITHIN:
- case MatchExpression::INTERNAL_EXPR_EQ:
- case MatchExpression::INTERNAL_EXPR_GT:
- case MatchExpression::INTERNAL_EXPR_GTE:
- case MatchExpression::INTERNAL_EXPR_LT:
- case MatchExpression::INTERNAL_EXPR_LTE:
- case MatchExpression::INTERNAL_SCHEMA_ALLOWED_PROPERTIES:
- case MatchExpression::INTERNAL_SCHEMA_COND:
- case MatchExpression::INTERNAL_SCHEMA_EQ:
- case MatchExpression::INTERNAL_SCHEMA_FMOD:
- case MatchExpression::INTERNAL_SCHEMA_MIN_PROPERTIES:
- case MatchExpression::INTERNAL_SCHEMA_OBJECT_MATCH:
- case MatchExpression::INTERNAL_SCHEMA_ROOT_DOC_EQ:
- case MatchExpression::INTERNAL_SCHEMA_XOR:
- return nullptr;
- default:
- MONGO_UNREACHABLE_TASSERT(7800300);
- }
-};
-} // namespace
void ListOfMatchExpression::_debugList(StringBuilder& debug, int indentationLevel) const {
for (unsigned i = 0; i < _expressions.size(); i++)
_expressions[i]->debugString(debug, indentationLevel + 1);
}
-void ListOfMatchExpression::_listToBSON(BSONArrayBuilder* out,
- const SerializationOptions& opts,
- bool includePath) const {
+void ListOfMatchExpression::_listToBSON(BSONArrayBuilder* out, bool includePath) const {
for (unsigned i = 0; i < _expressions.size(); i++) {
BSONObjBuilder childBob(out->subobjStart());
- _expressions[i]->serialize(&childBob, opts, includePath);
+ _expressions[i]->serialize(&childBob, includePath);
}
out->doneFast();
}
@@ -187,13 +97,12 @@ MatchExpression::ExpressionOptimizerFunc ListOfMatchExpression::getOptimizer() c
std::back_inserter(children));
}
- // Remove all children of AND that are $alwaysTrue and all children of OR and NOR that are
+ // Remove all children of AND that are $alwaysTrue and all children of OR that are
// $alwaysFalse.
- if (matchType == AND || matchType == OR || matchType == NOR) {
+ if (matchType == AND || matchType == OR) {
for (auto& childExpression : children)
if ((childExpression->isTriviallyTrue() && matchType == MatchExpression::AND) ||
- (childExpression->isTriviallyFalse() && matchType == MatchExpression::OR) ||
- (childExpression->isTriviallyFalse() && matchType == MatchExpression::NOR))
+ (childExpression->isTriviallyFalse() && matchType == MatchExpression::OR))
childExpression = nullptr;
// We replaced each destroyed child expression with nullptr. Now we remove those
@@ -203,17 +112,13 @@ MatchExpression::ExpressionOptimizerFunc ListOfMatchExpression::getOptimizer() c
// Check if the above optimizations eliminated all children. An OR with no children is
// always false.
+ // TODO SERVER-34759 It is correct to replace this empty AND with an $alwaysTrue, but we
+ // need to make enhancements to the planner to make it understand an $alwaysTrue and an
+ // empty AND as the same thing. The planner can create inferior plans for $alwaysTrue which
+ // it would not produce for an AND with no children.
if (children.empty() && matchType == MatchExpression::OR) {
return std::make_unique<AlwaysFalseMatchExpression>();
}
- // An AND with no children is always true and we need to return an
- // EmptyExpression. This ensures that the empty $and[] will be returned that serializes to
- // {} (SERVER-34759). A NOR with no children is always true. We treat an empty $nor[]
- // similarly.
- if (children.empty() &&
- (matchType == MatchExpression::AND || matchType == MatchExpression::NOR)) {
- return std::make_unique<AndMatchExpression>();
- }
if (children.size() == 1) {
if ((matchType == AND || matchType == OR || matchType == INTERNAL_SCHEMA_XOR)) {
@@ -231,8 +136,7 @@ MatchExpression::ExpressionOptimizerFunc ListOfMatchExpression::getOptimizer() c
}
}
- if (matchType == MatchExpression::AND || matchType == MatchExpression::OR ||
- matchType == MatchExpression::NOR) {
+ if (matchType == MatchExpression::AND || matchType == MatchExpression::OR) {
for (auto& childExpression : children) {
// An AND containing an expression that always evaluates to false can be
// optimized to a single $alwaysFalse expression.
@@ -245,11 +149,6 @@ MatchExpression::ExpressionOptimizerFunc ListOfMatchExpression::getOptimizer() c
if (childExpression->isTriviallyTrue() && matchType == MatchExpression::OR) {
return std::make_unique<AlwaysTrueMatchExpression>();
}
- // A NOR containing an expression that always evaluates to true can be
- // optimized to a single $alwaysFalse expression.
- if (childExpression->isTriviallyTrue() && matchType == MatchExpression::NOR) {
- return std::make_unique<AlwaysFalseMatchExpression>();
- }
}
}
@@ -318,19 +217,19 @@ MatchExpression::ExpressionOptimizerFunc ListOfMatchExpression::getOptimizer() c
if (countEquivEqPaths > 1) {
tassert(3401202, "There must be a common path", childPath);
auto inExpression = std::make_unique<InMatchExpression>(StringData(*childPath));
- std::vector<std::unique_ptr<MatchExpression>> nonEquivOrChildren;
- nonEquivOrChildren.reserve(countNonEquivExpr);
+ auto nonEquivOrExpr =
+ (countNonEquivExpr > 0) ? std::make_unique<OrMatchExpression>() : nullptr;
BSONArrayBuilder bab;
for (auto& childExpression : children) {
if (*childPath != childExpression->path()) {
- nonEquivOrChildren.push_back(std::move(childExpression));
+ nonEquivOrExpr->add(std::move(childExpression));
} else if (childExpression->matchType() == MatchExpression::EQ) {
std::unique_ptr<EqualityMatchExpression> eqExpressionPtr{
static_cast<EqualityMatchExpression*>(childExpression.release())};
if (isRegEx(eqExpressionPtr->getData()) ||
eqExpressionPtr->getCollator() != eqCollator) {
- nonEquivOrChildren.push_back(std::move(eqExpressionPtr));
+ nonEquivOrExpr->add(std::move(eqExpressionPtr));
} else {
bab.append(eqExpressionPtr->getData());
}
@@ -345,13 +244,13 @@ MatchExpression::ExpressionOptimizerFunc ListOfMatchExpression::getOptimizer() c
"Conversion from OR to IN should always succeed",
status == Status::OK());
} else {
- nonEquivOrChildren.push_back(std::move(childExpression));
+ nonEquivOrExpr->add(std::move(childExpression));
}
}
children.clear();
tassert(3401204,
"Incorrect number of non-equivalent expressions",
- nonEquivOrChildren.size() == countNonEquivExpr);
+ !nonEquivOrExpr || nonEquivOrExpr->numChildren() == countNonEquivExpr);
auto backingArr = bab.arr();
std::vector<BSONElement> inEqualities;
@@ -375,13 +274,11 @@ MatchExpression::ExpressionOptimizerFunc ListOfMatchExpression::getOptimizer() c
if (countNonEquivExpr > 0) {
auto parentOrExpr = std::make_unique<OrMatchExpression>();
parentOrExpr->add(std::move(inExpression));
-
- // Move all of the non-equivalent children of the original $or so that they
- // become children of the newly constructed $or node.
- auto&& childVec = *parentOrExpr->getChildVector();
- std::move(std::make_move_iterator(nonEquivOrChildren.begin()),
- std::make_move_iterator(nonEquivOrChildren.end()),
- std::back_inserter(childVec));
+ if (countNonEquivExpr == 1) {
+ parentOrExpr->add(nonEquivOrExpr->releaseChild(0));
+ } else {
+ parentOrExpr->add(std::move(nonEquivOrExpr));
+ }
return parentOrExpr;
}
return inExpression;
@@ -444,9 +341,7 @@ void AndMatchExpression::debugString(StringBuilder& debug, int indentationLevel)
_debugList(debug, indentationLevel);
}
-void AndMatchExpression::serialize(BSONObjBuilder* out,
- const SerializationOptions& opts,
- bool includePath) const {
+void AndMatchExpression::serialize(BSONObjBuilder* out, bool includePath) const {
if (!numChildren()) {
// It is possible for an AndMatchExpression to have no children, resulting in the serialized
// expression {$and: []}, which is not a valid query object.
@@ -454,7 +349,7 @@ void AndMatchExpression::serialize(BSONObjBuilder* out,
}
BSONArrayBuilder arrBob(out->subarrayStart("$and"));
- _listToBSON(&arrBob, opts, includePath);
+ _listToBSON(&arrBob, includePath);
arrBob.doneFast();
}
@@ -490,9 +385,7 @@ void OrMatchExpression::debugString(StringBuilder& debug, int indentationLevel)
_debugList(debug, indentationLevel);
}
-void OrMatchExpression::serialize(BSONObjBuilder* out,
- const SerializationOptions& opts,
- bool includePath) const {
+void OrMatchExpression::serialize(BSONObjBuilder* out, bool includePath) const {
if (!numChildren()) {
// It is possible for an OrMatchExpression to have no children, resulting in the serialized
// expression {$or: []}, which is not a valid query object. An empty $or is logically
@@ -501,7 +394,7 @@ void OrMatchExpression::serialize(BSONObjBuilder* out,
return;
}
BSONArrayBuilder arrBob(out->subarrayStart("$or"));
- _listToBSON(&arrBob, opts, includePath);
+ _listToBSON(&arrBob, includePath);
}
bool OrMatchExpression::isTriviallyFalse() const {
@@ -534,11 +427,9 @@ void NorMatchExpression::debugString(StringBuilder& debug, int indentationLevel)
_debugList(debug, indentationLevel);
}
-void NorMatchExpression::serialize(BSONObjBuilder* out,
- const SerializationOptions& opts,
- bool includePath) const {
+void NorMatchExpression::serialize(BSONObjBuilder* out, bool includePath) const {
BSONArrayBuilder arrBob(out->subarrayStart("$nor"));
- _listToBSON(&arrBob, opts, includePath);
+ _listToBSON(&arrBob, includePath);
}
// -------
@@ -551,10 +442,9 @@ void NotMatchExpression::debugString(StringBuilder& debug, int indentationLevel)
void NotMatchExpression::serializeNotExpressionToNor(MatchExpression* exp,
BSONObjBuilder* out,
- const SerializationOptions& opts,
bool includePath) {
BSONObjBuilder childBob;
- exp->serialize(&childBob, opts, includePath);
+ exp->serialize(&childBob, includePath);
BSONObj tempObj = childBob.obj();
BSONArrayBuilder tBob(out->subarrayStart("$nor"));
@@ -562,11 +452,9 @@ void NotMatchExpression::serializeNotExpressionToNor(MatchExpression* exp,
tBob.doneFast();
}
-void NotMatchExpression::serialize(BSONObjBuilder* out,
- const SerializationOptions& opts,
- bool includePath) const {
+void NotMatchExpression::serialize(BSONObjBuilder* out, bool includePath) const {
if (_exp->matchType() == MatchType::AND && _exp->numChildren() == 0) {
- opts.appendLiteral(out, "$alwaysFalse", 1);
+ out->append("$alwaysFalse", 1);
return;
}
@@ -577,10 +465,10 @@ void NotMatchExpression::serialize(BSONObjBuilder* out,
// 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, opts, includePath);
+ _exp->getChild(x)->serialize(&notBob, includePath);
}
} else {
- _exp->serialize(&notBob, opts, includePath);
+ _exp->serialize(&notBob, includePath);
}
return;
}
@@ -593,15 +481,20 @@ void NotMatchExpression::serialize(BSONObjBuilder* out,
// It is generally easier to be correct if we just always serialize to a $nor, since this will
// delegate the path serialization to lower in the tree where we have the information on-hand.
// However, for legibility we preserve a $not with a single path-accepting child as a $not.
- if (auto pathMatch = getEligiblePathMatchForNotSerialization(expressionToNegate)) {
- auto append = [&](StringData path) {
- BSONObjBuilder pathBob(out->subobjStart(path));
- pathBob.append("$not", pathMatch->getSerializedRightHandSide(opts));
- };
- append(opts.serializeFieldPathFromString(pathMatch->path()));
+ //
+ // One exception: while TextMatchExpressionBase derives from PathMatchExpression, text match
+ // expressions cannot be serialized in the same manner as other PathMatchExpression derivatives.
+ // This is because the path for a TextMatchExpression is embedded within the $text object,
+ // whereas for other PathMatchExpressions it is on the left-hand-side, for example {x: {$eq:
+ // 1}}.
+ if (auto pathMatch = dynamic_cast<PathMatchExpression*>(expressionToNegate);
+ pathMatch && !dynamic_cast<TextMatchExpressionBase*>(expressionToNegate)) {
+ const auto path = pathMatch->path();
+ BSONObjBuilder pathBob(out->subobjStart(path));
+ pathBob.append("$not", pathMatch->getSerializedRightHandSide());
return;
}
- return serializeNotExpressionToNor(expressionToNegate, out, opts);
+ return serializeNotExpressionToNor(expressionToNegate, out, includePath);
}
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 b067c7b5f7e..f8b9179c003 100644
--- a/src/mongo/db/matcher/expression_tree.h
+++ b/src/mongo/db/matcher/expression_tree.h
@@ -98,9 +98,7 @@ public:
protected:
void _debugList(StringBuilder& debug, int indentationLevel) const;
- void _listToBSON(BSONArrayBuilder* out,
- const SerializationOptions& opts = {},
- bool includePath = true) const;
+ void _listToBSON(BSONArrayBuilder* out, bool includePath) const;
private:
ExpressionOptimizerFunc getOptimizer() const final;
@@ -139,9 +137,7 @@ public:
virtual void debugString(StringBuilder& debug, int indentationLevel = 0) const;
- virtual void serialize(BSONObjBuilder* out,
- const SerializationOptions& opts = {},
- bool includePath = true) const;
+ virtual void serialize(BSONObjBuilder* out, bool includePath) const;
bool isTriviallyTrue() const final;
@@ -185,9 +181,7 @@ public:
virtual void debugString(StringBuilder& debug, int indentationLevel = 0) const;
- virtual void serialize(BSONObjBuilder* out,
- const SerializationOptions& opts = {},
- bool includePath = true) const;
+ virtual void serialize(BSONObjBuilder* out, bool includePath) const;
bool isTriviallyFalse() const final;
@@ -231,9 +225,7 @@ public:
virtual void debugString(StringBuilder& debug, int indentationLevel = 0) const;
- virtual void serialize(BSONObjBuilder* out,
- const SerializationOptions& opts = {},
- bool includePath = true) const;
+ virtual void serialize(BSONObjBuilder* out, bool includePath) const;
void acceptVisitor(MatchExpressionMutableVisitor* visitor) final {
visitor->visit(this);
@@ -273,9 +265,7 @@ public:
virtual void debugString(StringBuilder& debug, int indentationLevel = 0) const;
- virtual void serialize(BSONObjBuilder* out,
- const SerializationOptions& opts = {},
- bool includePath = true) const;
+ virtual void serialize(BSONObjBuilder* out, bool includePath) const;
bool equivalent(const MatchExpression* other) const final;
@@ -316,8 +306,7 @@ public:
private:
static void serializeNotExpressionToNor(MatchExpression* exp,
BSONObjBuilder* out,
- const SerializationOptions& opts = {},
- bool includePath = true);
+ bool includePath);
ExpressionOptimizerFunc getOptimizer() const final;
diff --git a/src/mongo/db/matcher/expression_type.h b/src/mongo/db/matcher/expression_type.h
index 07e575245c9..c81aade1701 100644
--- a/src/mongo/db/matcher/expression_type.h
+++ b/src/mongo/db/matcher/expression_type.h
@@ -82,10 +82,12 @@ public:
debug << "\n";
}
- void appendSerializedRightHandSide(BSONObjBuilder* bob,
- const SerializationOptions& opts = {},
- bool includePath = true) const final {
- bob->appendArray(name(), _typeSet.toBSONArray());
+ BSONObj getSerializedRightHandSide() const final {
+ BSONObjBuilder subBuilder;
+ BSONArrayBuilder arrBuilder(subBuilder.subarrayStart(name()));
+ _typeSet.toBSONArray(&arrBuilder);
+ arrBuilder.doneFast();
+ return subBuilder.obj();
}
bool equivalent(const MatchExpression* other) const final {
@@ -254,19 +256,10 @@ public:
debug << "\n";
}
- void appendSerializedRightHandSide(BSONObjBuilder* bob,
- const SerializationOptions& opts = {},
- bool includePath = true) const final {
- if (opts.literalPolicy == LiteralSerializationPolicy::kUnchanged) {
- bob->append(name(), _binDataSubType);
- } else {
- // There is some fancy serialization logic to get the above BSONObjBuilder append to
- // work. We just want to make sure we're doing the same thing here.
- static_assert(BSONObjAppendFormat<decltype(_binDataSubType)>::value == NumberInt,
- "Expecting that the BinData sub type should be specified and serialized "
- "as an int.");
- opts.appendLiteral(bob, name(), static_cast<int>(_binDataSubType));
- }
+ BSONObj getSerializedRightHandSide() const final {
+ BSONObjBuilder bob;
+ bob.append(name(), _binDataSubType);
+ return bob.obj();
}
bool equivalent(const MatchExpression* other) const final {
diff --git a/src/mongo/db/matcher/expression_type_test.cpp b/src/mongo/db/matcher/expression_type_test.cpp
index 573ab77860f..bf7d40e836a 100644
--- a/src/mongo/db/matcher/expression_type_test.cpp
+++ b/src/mongo/db/matcher/expression_type_test.cpp
@@ -217,14 +217,6 @@ TEST(ExpressionTypeTest, InternalSchemaTypeExprWithMultipleTypesMatchesAllSuchTy
ASSERT_FALSE(expr.matchesBSON(fromjson("{a: ['str']}")));
}
-TEST(ExpressionTypeTest, RedactsTypesCorrectly) {
- TypeMatchExpression type(""_sd, String);
- auto opts = SerializationOptions{LiteralSerializationPolicy::kToDebugTypeString};
- ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
- R"({"$type":[2]})",
- type.getSerializedRightHandSide(opts));
-}
-
TEST(ExpressionBinDataSubTypeTest, MatchesBinDataGeneral) {
BSONObj match = BSON("a" << BSONBinData(nullptr, 0, BinDataType::BinDataGeneral));
BSONObj notMatch = BSON("a" << BSONBinData(nullptr, 0, BinDataType::bdtCustom));
@@ -273,14 +265,6 @@ TEST(ExpressionBinDataSubTypeTest, MatchesBinDataColumnType) {
ASSERT_FALSE(type.matchesSingleElement(notMatch["a"]));
}
-TEST(ExpressionBinDataSubTypeTest, MatchesBinDataSensitiveType) {
- BSONObj match = BSON("a" << BSONBinData(nullptr, 0, BinDataType::Sensitive));
- BSONObj notMatch = BSON("a" << BSONBinData(nullptr, 0, BinDataType::newUUID));
- InternalSchemaBinDataSubTypeExpression type(""_sd, BinDataType::Sensitive);
- ASSERT_TRUE(type.matchesSingleElement(match["a"]));
- ASSERT_FALSE(type.matchesSingleElement(notMatch["a"]));
-}
-
TEST(ExpressionBinDataSubTypeTest, MatchesBinDataBdtCustom) {
BSONObj match = BSON("a" << BSONBinData(nullptr, 0, BinDataType::bdtCustom));
BSONObj notMatch = BSON("a" << BSONBinData(nullptr, 0, BinDataType::Function));
@@ -316,14 +300,6 @@ TEST(ExpressionBinDataSubTypeTest, Equivalent) {
ASSERT(!e1.equivalent(&e3));
}
-TEST(ExpressionBinDataSubTypeTest, RedactsCorrectly) {
- InternalSchemaBinDataSubTypeExpression e("b"_sd, BinDataType::newUUID);
- auto opts = SerializationOptions{LiteralSerializationPolicy::kToDebugTypeString};
- ASSERT_BSONOBJ_EQ_AUTO( // NOLINT
- R"({"$_internalSchemaBinDataSubType":"?number"})",
- e.getSerializedRightHandSide(opts));
-}
-
TEST(InternalSchemaBinDataEncryptedTypeTest, DoesNotTraverseLeafArrays) {
MatcherTypeSet typeSet;
typeSet.bsonTypes.insert(BSONType::String);
diff --git a/src/mongo/db/matcher/expression_where_base.cpp b/src/mongo/db/matcher/expression_where_base.cpp
index b25a8febccf..30148f9a42b 100644
--- a/src/mongo/db/matcher/expression_where_base.cpp
+++ b/src/mongo/db/matcher/expression_where_base.cpp
@@ -46,10 +46,8 @@ void WhereMatchExpressionBase::debugString(StringBuilder& debug, int indentation
debug << "code: " << getCode() << "\n";
}
-void WhereMatchExpressionBase::serialize(BSONObjBuilder* out,
- const SerializationOptions& opts,
- bool includePath) const {
- opts.appendLiteral(out, "$where", BSONCode(getCode()));
+void WhereMatchExpressionBase::serialize(BSONObjBuilder* out, bool includePath) const {
+ out->appendCode("$where", getCode());
}
bool WhereMatchExpressionBase::equivalent(const MatchExpression* other) const {
diff --git a/src/mongo/db/matcher/expression_where_base.h b/src/mongo/db/matcher/expression_where_base.h
index 91b6d0985cd..550e94d1d37 100644
--- a/src/mongo/db/matcher/expression_where_base.h
+++ b/src/mongo/db/matcher/expression_where_base.h
@@ -69,9 +69,7 @@ public:
void debugString(StringBuilder& debug, int indentationLevel = 0) const final;
- void serialize(BSONObjBuilder* out,
- const SerializationOptions& opts = {},
- bool includePath = true) const final;
+ void serialize(BSONObjBuilder* out, bool includePath) 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
deleted file mode 100644
index 4c4d8a9dc43..00000000000
--- a/src/mongo/db/matcher/parsed_match_expression_for_test.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/**
- * 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,
- ExtensionsCallbackNoop(),
- MatchExpressionParser::kDefaultSpecialFeatures |
- MatchExpressionParser::AllowedFeatures::kJavascript);
- 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.cpp b/src/mongo/db/matcher/rewrite_expr.cpp
index 4f44cd269ef..e61ddeaab78 100644
--- a/src/mongo/db/matcher/rewrite_expr.cpp
+++ b/src/mongo/db/matcher/rewrite_expr.cpp
@@ -45,7 +45,7 @@ using CmpOp = ExpressionCompare::CmpOp;
RewriteExpr::RewriteResult RewriteExpr::rewrite(const boost::intrusive_ptr<Expression>& expression,
const CollatorInterface* collator) {
LOGV2_DEBUG(
- 20725, 5, "Expression prior to rewrite", "expression"_attr = expression->serialize());
+ 20725, 5, "Expression prior to rewrite", "expression"_attr = expression->serialize(false));
RewriteExpr rewriteExpr(collator);
std::unique_ptr<MatchExpression> matchExpression;
@@ -63,9 +63,7 @@ RewriteExpr::RewriteResult RewriteExpr::rewrite(const boost::intrusive_ptr<Expre
"expression"_attr = matchExpression->debugString());
}
- return {std::move(matchExpression),
- std::move(rewriteExpr._matchExprElemStorage),
- rewriteExpr._allSubExpressionsRewritten};
+ return {std::move(matchExpression), std::move(rewriteExpr._matchExprElemStorage)};
}
std::unique_ptr<MatchExpression> RewriteExpr::_rewriteExpression(
@@ -88,11 +86,8 @@ std::unique_ptr<MatchExpression> RewriteExpr::_rewriteAndExpression(
auto andMatch = std::make_unique<AndMatchExpression>();
for (auto&& child : currExprNode->getOperandList())
- if (auto childMatch = _rewriteExpression(child)) {
+ if (auto childMatch = _rewriteExpression(child))
andMatch->add(std::move(childMatch));
- } else {
- _allSubExpressionsRewritten = false;
- }
if (andMatch->numChildren() > 0)
return andMatch;
@@ -107,12 +102,10 @@ std::unique_ptr<MatchExpression> RewriteExpr::_rewriteOrExpression(
for (auto&& child : currExprNode->getOperandList())
if (auto childExpr = _rewriteExpression(child))
orMatch->add(std::move(childExpr));
- else {
+ else
// If any child cannot be rewritten to a MatchExpression then we must abandon adding
// this $or clause.
- _allSubExpressionsRewritten = false;
return nullptr;
- }
if (orMatch->numChildren() > 0)
return orMatch;
diff --git a/src/mongo/db/matcher/rewrite_expr.h b/src/mongo/db/matcher/rewrite_expr.h
index 08e7d82d23d..1752c9726b8 100644
--- a/src/mongo/db/matcher/rewrite_expr.h
+++ b/src/mongo/db/matcher/rewrite_expr.h
@@ -42,20 +42,14 @@ namespace mongo {
class RewriteExpr final {
public:
/**
- * Holds the result of an Expression rewrite operation. $expr expressions can't take advantage
- * of indexes. When we rewrite the expressions as a conjunction of internal match expressions,
- * the query planner can now use the internal match expressions to potentially generate an index
- * scan. We use internal match expressions that are non-type bracketed to match non-type
- * bracketed comparison operators inside $expr.
+ * Holds the result of an Expression rewrite operation.
*/
class RewriteResult final {
public:
RewriteResult(std::unique_ptr<MatchExpression> matchExpression,
- std::vector<BSONObj> matchExprElemStorage,
- bool allSubExpressionsRewritten)
+ std::vector<BSONObj> matchExprElemStorage)
: _matchExpression(std::move(matchExpression)),
- _matchExprElemStorage(std::move(matchExprElemStorage)),
- _allSubExpressionsRewritten(allSubExpressionsRewritten) {}
+ _matchExprElemStorage(std::move(matchExprElemStorage)) {}
MatchExpression* matchExpression() const {
return _matchExpression.get();
@@ -67,11 +61,7 @@ public:
RewriteResult clone() const {
auto clonedMatch = _matchExpression ? _matchExpression->shallowClone() : nullptr;
- return {std::move(clonedMatch), _matchExprElemStorage, _allSubExpressionsRewritten};
- }
-
- bool allSubExpressionsRewritten() {
- return _allSubExpressionsRewritten;
+ return {std::move(clonedMatch), _matchExprElemStorage};
}
private:
@@ -81,22 +71,12 @@ public:
// owned and expected to outlive the MatchExpression. '_matchExprElemStorage' holds the
// underlying BSONObj storage for these arguments.
std::vector<BSONObj> _matchExprElemStorage;
-
- // Defaults to true, is false if there is a child in an $or/$and expression that
- // contains children that cannot be rewritten to a MatchExpression.
- bool _allSubExpressionsRewritten = true;
};
/**
- * Attempts to construct a MatchExpression that will match against either an identical set
- * or a superset of the documents matched by 'expr'. Due to semantic differences the
- * rewritten MatchExpression might match more documents than the ExprMatchExpression. For
- * example,
- * $_internalExprEq in MatchExpression reaches into arrays, and $eq in ExprMatchExpression
- * does not. However, $_internalExprLt/$_internalExprGt are non-type bracketed for
- * MatchExpression, just like ExprMatchExpressions. Returns the MatchExpression as a
- * RewriteResult. If a rewrite is not possible, RewriteResult::matchExpression() will return
- * a nullptr.
+ * Attempts to construct a MatchExpression that will match against either an identical set or a
+ * superset of the documents matched by 'expr'. Returns the MatchExpression as a RewriteResult.
+ * If a rewrite is not possible, RewriteResult::matchExpression() will return a nullptr.
*/
static RewriteResult rewrite(const boost::intrusive_ptr<Expression>& expr,
const CollatorInterface* collator);
@@ -127,7 +107,6 @@ private:
std::vector<BSONObj> _matchExprElemStorage;
const CollatorInterface* _collator;
- bool _allSubExpressionsRewritten = true;
};
} // namespace mongo
diff --git a/src/mongo/db/matcher/rewrite_expr_test.cpp b/src/mongo/db/matcher/rewrite_expr_test.cpp
index c4f1a4b7619..948add02c26 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, {});
+ result.matchExpression()->serialize(&bob, true);
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 7f9b3d14292..e62b5e62e1d 100644
--- a/src/mongo/db/matcher/schema/assert_serializes_to.h
+++ b/src/mongo/db/matcher/schema/assert_serializes_to.h
@@ -34,9 +34,11 @@ namespace mongo {
/**
* Asserts that the given MatchExpression 'match' serializes to the BSONObj 'expected'.
*/
-#define ASSERT_SERIALIZES_TO(match, expected) \
- do { \
- ASSERT_BSONOBJ_EQ(match->serialize(), expected); \
+#define ASSERT_SERIALIZES_TO(match, expected) \
+ do { \
+ BSONObjBuilder bob; \
+ match->serialize(&bob, true); \
+ ASSERT_BSONOBJ_EQ(bob.obj(), 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 caf39e7fcc4..27b2adae68d 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,11 +76,17 @@ void InternalSchemaAllElemMatchFromIndexMatchExpression::debugString(StringBuild
_expression->getFilter()->debugString(debug, indentationLevel + 1);
}
-void InternalSchemaAllElemMatchFromIndexMatchExpression::appendSerializedRightHandSide(
- BSONObjBuilder* bob, const SerializationOptions& opts, bool includePath) const {
- bob->append(kName,
- BSON_ARRAY(opts.serializeLiteral(_index)
- << _expression->getFilter()->serialize(opts, includePath)));
+BSONObj InternalSchemaAllElemMatchFromIndexMatchExpression::getSerializedRightHandSide() const {
+ BSONObjBuilder allElemMatchBob;
+ BSONArrayBuilder subArray(allElemMatchBob.subarrayStart(kName));
+ subArray.append(_index);
+ {
+ BSONObjBuilder eBuilder(subArray.subobjStart());
+ _expression->getFilter()->serialize(&eBuilder, true);
+ eBuilder.doneFast();
+ }
+ subArray.doneFast();
+ return allElemMatchBob.obj();
}
MatchExpression::ExpressionOptimizerFunc
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 1d1ff14d6c0..0a613b23303 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
@@ -77,9 +77,7 @@ public:
void debugString(StringBuilder& debug, int indentationLevel) const final;
- void appendSerializedRightHandSide(BSONObjBuilder* bob,
- const SerializationOptions& opts = {},
- bool includePath = true) const final;
+ BSONObj getSerializedRightHandSide() const final;
bool equivalent(const MatchExpression* other) const final;
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index_test.cpp b/src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index_test.cpp
index b3b3234778d..49e1b3b0235 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index_test.cpp
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_all_elem_match_from_index_test.cpp
@@ -135,5 +135,6 @@ DEATH_TEST_REGEX(InternalSchemaAllElemMatchFromIndexMatchExpression,
objMatch.getValue()->getChild(1);
}
+
} // namespace
} // namespace mongo
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 5a6471e2155..64b34aafc3a 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
@@ -59,7 +59,7 @@ void InternalSchemaAllowedPropertiesMatchExpression::debugString(StringBuilder&
_debugAddSpace(debug, indentationLevel);
BSONObjBuilder builder;
- serialize(&builder, {});
+ serialize(&builder, true);
debug << builder.obj().toString() << "\n";
const auto* tag = getTag();
@@ -128,29 +128,29 @@ bool InternalSchemaAllowedPropertiesMatchExpression::_matchesBSONObj(const BSONO
}
void InternalSchemaAllowedPropertiesMatchExpression::serialize(BSONObjBuilder* builder,
- const SerializationOptions& opts,
bool includePath) const {
BSONObjBuilder expressionBuilder(
builder->subobjStart(InternalSchemaAllowedPropertiesMatchExpression::kName));
std::vector<StringData> sortedProperties(_properties.begin(), _properties.end());
std::sort(sortedProperties.begin(), sortedProperties.end());
- opts.appendLiteral(&expressionBuilder, "properties", sortedProperties);
- // This will be serialized to "i", which is the parser chosen namePlaceholder. Using this
- // unmodified will have a similar effect to serializing to "?", however it preserves round trip
- // parsing.
+ expressionBuilder.append("properties", sortedProperties);
+
expressionBuilder.append("namePlaceholder", _namePlaceholder);
BSONArrayBuilder patternPropertiesBuilder(expressionBuilder.subarrayStart("patternProperties"));
- for (auto&& [pattern, expression] : _patternProperties) {
- patternPropertiesBuilder << BSON(
- "regex" << opts.serializeLiteral(BSONRegEx(pattern.rawRegex)) << "expression"
- << expression->getFilter()->serialize(opts, includePath));
+ for (auto&& item : _patternProperties) {
+ BSONObjBuilder itemBuilder(patternPropertiesBuilder.subobjStart());
+ itemBuilder.appendRegex("regex", item.first.rawRegex);
+
+ BSONObjBuilder subexpressionBuilder(itemBuilder.subobjStart("expression"));
+ item.second->getFilter()->serialize(&subexpressionBuilder, includePath);
+ subexpressionBuilder.doneFast();
}
patternPropertiesBuilder.doneFast();
BSONObjBuilder otherwiseBuilder(expressionBuilder.subobjStart("otherwise"));
- _otherwise->getFilter()->serialize(&otherwiseBuilder, opts, includePath);
+ _otherwise->getFilter()->serialize(&otherwiseBuilder, includePath);
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 ae6de80a7f8..e95d0582d15 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,9 +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,
- const SerializationOptions& opts = {},
- bool includePath = true) const final;
+ void serialize(BSONObjBuilder* builder, bool includePath) 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 31f5812256f..263b23cc4e9 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_eq.cpp
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_eq.cpp
@@ -69,16 +69,10 @@ void InternalSchemaEqMatchExpression::debugString(StringBuilder& debug,
debug << "\n";
}
-void InternalSchemaEqMatchExpression::appendSerializedRightHandSide(
- BSONObjBuilder* bob, const SerializationOptions& opts, bool includePath) const {
- if (opts.literalPolicy != LiteralSerializationPolicy::kUnchanged && _rhsElem.isABSONObj()) {
- BSONObjBuilder exprSpec(bob->subobjStart(kName));
- opts.addHmacedObjToBuilder(&exprSpec, _rhsElem.Obj());
- exprSpec.doneFast();
- return;
- }
- // If the element is not an object it must be a literal.
- opts.appendLiteral(bob, kName, _rhsElem);
+BSONObj InternalSchemaEqMatchExpression::getSerializedRightHandSide() const {
+ BSONObjBuilder eqObj;
+ eqObj.appendAs(_rhsElem, kName);
+ return eqObj.obj();
}
bool InternalSchemaEqMatchExpression::equivalent(const MatchExpression* other) const {
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 5a24d5787d2..3f604294f60 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_eq.h
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_eq.h
@@ -59,9 +59,7 @@ public:
void debugString(StringBuilder& debug, int indentationLevel) const final;
- void appendSerializedRightHandSide(BSONObjBuilder* bob,
- const SerializationOptions& opts = {},
- bool includePath = true) const final;
+ BSONObj getSerializedRightHandSide() 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 fbd267bcbbf..e86e4d0240d 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_fmod.cpp
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_fmod.cpp
@@ -76,10 +76,13 @@ void InternalSchemaFmodMatchExpression::debugString(StringBuilder& debug,
debug << "\n";
}
-void InternalSchemaFmodMatchExpression::appendSerializedRightHandSide(
- BSONObjBuilder* bob, const SerializationOptions& opts, bool includePath) const {
- bob->append("$_internalSchemaFmod"_sd,
- BSON_ARRAY(opts.serializeLiteral(_divisor) << opts.serializeLiteral(_remainder)));
+BSONObj InternalSchemaFmodMatchExpression::getSerializedRightHandSide() const {
+ BSONObjBuilder objMatchBob;
+ BSONArrayBuilder arrBuilder(objMatchBob.subarrayStart("$_internalSchemaFmod"));
+ arrBuilder.append(_divisor);
+ arrBuilder.append(_remainder);
+ arrBuilder.doneFast();
+ return objMatchBob.obj();
}
bool InternalSchemaFmodMatchExpression::equivalent(const MatchExpression* other) const {
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 e808e079b27..ae147f3a4fe 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_fmod.h
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_fmod.h
@@ -58,9 +58,7 @@ public:
void debugString(StringBuilder& debug, int indentationLevel) const final;
- void appendSerializedRightHandSide(BSONObjBuilder* bob,
- const SerializationOptions& opts = {},
- bool includePath = true) const final;
+ BSONObj getSerializedRightHandSide() 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 8af85dd6436..a09ad27ff22 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, {});
+ serialize(&builder, true);
debug << builder.obj().toString() << "\n";
const auto* tag = getTag();
@@ -72,14 +72,20 @@ bool InternalSchemaMatchArrayIndexMatchExpression::equivalent(const MatchExpress
_expression->equivalent(other->_expression.get());
}
-void InternalSchemaMatchArrayIndexMatchExpression::appendSerializedRightHandSide(
- BSONObjBuilder* bob, const SerializationOptions& opts, bool includePath) const {
- bob->append(
- kName,
- BSON(
- "index" << opts.serializeLiteral(_index) << "namePlaceholder"
- << opts.serializeFieldPathFromString(_expression->getPlaceholder().value_or(""))
- << "expression" << _expression->getFilter()->serialize(opts, includePath)));
+BSONObj InternalSchemaMatchArrayIndexMatchExpression::getSerializedRightHandSide() const {
+ BSONObjBuilder objBuilder;
+ {
+ BSONObjBuilder matchArrayElemSubobj(objBuilder.subobjStart(kName));
+ matchArrayElemSubobj.append("index", _index);
+ matchArrayElemSubobj.append("namePlaceholder", _expression->getPlaceholder().value_or(""));
+ {
+ BSONObjBuilder subexprSubObj(matchArrayElemSubobj.subobjStart("expression"));
+ _expression->getFilter()->serialize(&subexprSubObj, true);
+ subexprSubObj.doneFast();
+ }
+ matchArrayElemSubobj.doneFast();
+ }
+ return objBuilder.obj();
}
std::unique_ptr<MatchExpression> InternalSchemaMatchArrayIndexMatchExpression::shallowClone()
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 cea0149a028..8fff1225bf3 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
@@ -73,9 +73,7 @@ public:
return _expression->matchesBSONElement(element, details);
}
- void appendSerializedRightHandSide(BSONObjBuilder* bob,
- const SerializationOptions& opts = {},
- bool includePath = true) const final;
+ BSONObj getSerializedRightHandSide() 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 c6d20f63409..6fde9a327cf 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,9 +56,10 @@ void InternalSchemaNumArrayItemsMatchExpression::debugString(StringBuilder& debu
debug << "\n";
}
-void InternalSchemaNumArrayItemsMatchExpression::appendSerializedRightHandSide(
- BSONObjBuilder* bob, const SerializationOptions& opts, bool includePath) const {
- opts.appendLiteral(bob, _name, _numItems);
+BSONObj InternalSchemaNumArrayItemsMatchExpression::getSerializedRightHandSide() const {
+ BSONObjBuilder objBuilder;
+ objBuilder.append(_name, _numItems);
+ return objBuilder.obj();
}
bool InternalSchemaNumArrayItemsMatchExpression::equivalent(const MatchExpression* other) const {
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 e566692b642..99ed79ec5d6 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,9 +52,7 @@ public:
void debugString(StringBuilder& debug, int indentationLevel) const final;
- void appendSerializedRightHandSide(BSONObjBuilder* bob,
- const SerializationOptions& opts = {},
- bool includePath = true) const final;
+ BSONObj getSerializedRightHandSide() 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 91b764dd8f5..e640a58ec71 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,14 +37,13 @@ void InternalSchemaNumPropertiesMatchExpression::debugString(StringBuilder& debu
int indentationLevel) const {
_debugAddSpace(debug, indentationLevel);
BSONObjBuilder builder;
- serialize(&builder, {});
+ serialize(&builder, true);
debug << builder.obj().toString() << "\n";
}
void InternalSchemaNumPropertiesMatchExpression::serialize(BSONObjBuilder* out,
- const SerializationOptions& opts,
bool includePath) const {
- opts.appendLiteral(out, _name, _numProperties);
+ out->append(_name, _numProperties);
}
bool InternalSchemaNumPropertiesMatchExpression::equivalent(const MatchExpression* other) const {
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 679aaaca609..29fd4fc6145 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,9 +71,7 @@ public:
void debugString(StringBuilder& debug, int indentationLevel) const final;
- void serialize(BSONObjBuilder* out,
- const SerializationOptions& opts = {},
- bool includePath = true) const final;
+ void serialize(BSONObjBuilder* out, bool includePath) 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 317c316374d..ec5a0943fad 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,9 +61,12 @@ void InternalSchemaObjectMatchExpression::debugString(StringBuilder& debug,
_sub->debugString(debug, indentationLevel + 1);
}
-void InternalSchemaObjectMatchExpression::appendSerializedRightHandSide(
- BSONObjBuilder* bob, const SerializationOptions& opts, bool includePath) const {
- bob->append(kName, _sub->serialize(opts, includePath));
+BSONObj InternalSchemaObjectMatchExpression::getSerializedRightHandSide() const {
+ BSONObjBuilder objMatchBob;
+ BSONObjBuilder subBob(objMatchBob.subobjStart(kName));
+ _sub->serialize(&subBob, true);
+ subBob.doneFast();
+ return objMatchBob.obj();
}
bool InternalSchemaObjectMatchExpression::equivalent(const MatchExpression* other) const {
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 a41190d9545..93b6e1b765e 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
@@ -49,9 +49,7 @@ public:
void debugString(StringBuilder& debug, int indentationLevel = 0) const final;
- void appendSerializedRightHandSide(BSONObjBuilder* bob,
- const SerializationOptions& opts = {},
- bool includePath = true) const final;
+ BSONObj getSerializedRightHandSide() 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 300d1a49109..c4b1e3f7aa2 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,11 +55,9 @@ void InternalSchemaRootDocEqMatchExpression::debugString(StringBuilder& debug,
}
void InternalSchemaRootDocEqMatchExpression::serialize(BSONObjBuilder* out,
- const SerializationOptions& opts,
bool includePath) const {
BSONObjBuilder subObj(out->subobjStart(kName));
- SerializationOptions options = opts;
- options.addHmacedObjToBuilder(&subObj, _rhsObj);
+ 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 4418e101d43..62dfb6d66a2 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,9 +72,7 @@ public:
void debugString(StringBuilder& debug, int indentationLevel = 0) const final;
- void serialize(BSONObjBuilder* out,
- const SerializationOptions& opts = {},
- bool includePath = true) const final;
+ void serialize(BSONObjBuilder* out, bool includePath) 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 bdf2f5c2c4c..87d6396fd0f 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,9 +56,10 @@ void InternalSchemaStrLengthMatchExpression::debugString(StringBuilder& debug,
debug << "\n";
}
-void InternalSchemaStrLengthMatchExpression::appendSerializedRightHandSide(
- BSONObjBuilder* bob, const SerializationOptions& opts, bool includePath) const {
- opts.appendLiteral(bob, _name, _strLen);
+BSONObj InternalSchemaStrLengthMatchExpression::getSerializedRightHandSide() const {
+ BSONObjBuilder objBuilder;
+ objBuilder.append(_name, _strLen);
+ return objBuilder.obj();
}
bool InternalSchemaStrLengthMatchExpression::equivalent(const MatchExpression* other) const {
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 6796c532972..43483be1ce3 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,9 +62,7 @@ public:
void debugString(StringBuilder& debug, int indentationLevel) const final;
- void appendSerializedRightHandSide(BSONObjBuilder* bob,
- const SerializationOptions& opts = {},
- bool includePath = true) const final;
+ BSONObj getSerializedRightHandSide() 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 afe8f8422a9..f78dca0c37f 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, {});
+ serialize(&builder, true);
debug << builder.obj().toString() << "\n";
const auto* tag = getTag();
@@ -59,9 +59,10 @@ bool InternalSchemaUniqueItemsMatchExpression::equivalent(const MatchExpression*
return path() == other->path();
}
-void InternalSchemaUniqueItemsMatchExpression::appendSerializedRightHandSide(
- BSONObjBuilder* bob, const SerializationOptions& opts, bool includePath) const {
- bob->append(kName, true);
+BSONObj InternalSchemaUniqueItemsMatchExpression::getSerializedRightHandSide() const {
+ BSONObjBuilder bob;
+ bob.append(kName, true);
+ return bob.obj();
}
std::unique_ptr<MatchExpression> InternalSchemaUniqueItemsMatchExpression::shallowClone() const {
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 dc6e76af59f..ddb86c89403 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,9 +86,7 @@ public:
bool equivalent(const MatchExpression* other) const final;
- void appendSerializedRightHandSide(BSONObjBuilder* bob,
- const SerializationOptions& opts = {},
- bool includePath = true) const final;
+ BSONObj getSerializedRightHandSide() 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 04c5bbb045a..ad265266f52 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_xor.cpp
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_xor.cpp
@@ -73,10 +73,8 @@ void InternalSchemaXorMatchExpression::debugString(StringBuilder& debug,
_debugList(debug, indentationLevel);
}
-void InternalSchemaXorMatchExpression::serialize(BSONObjBuilder* out,
- const SerializationOptions& opts,
- bool includePath) const {
+void InternalSchemaXorMatchExpression::serialize(BSONObjBuilder* out, bool includePath) const {
BSONArrayBuilder arrBob(out->subarrayStart(kName));
- _listToBSON(&arrBob, opts, includePath);
+ _listToBSON(&arrBob, includePath);
}
} // 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 57825bff060..928a13413f1 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_xor.h
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_xor.h
@@ -69,9 +69,7 @@ public:
void debugString(StringBuilder& debug, int indentationLevel = 0) const final;
- void serialize(BSONObjBuilder* out,
- const SerializationOptions& opts = {},
- bool includePath = true) const final;
+ void serialize(BSONObjBuilder* out, bool includePath) 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 468c0bb8cc9..73bb4b7d7dd 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, {});
+ MatchExpression::optimize(std::move(typeResult.getValue()))->serialize(&typeBuilder, true);
BSONObjBuilder bsonTypeBuilder;
MatchExpression::optimize(std::move(bsonTypeResult.getValue()))
- ->serialize(&bsonTypeBuilder, {});
+ ->serialize(&bsonTypeBuilder, true);
ASSERT_BSONOBJ_EQ(typeBuilder.obj(), bsonTypeBuilder.obj());
}