summaryrefslogtreecommitdiff
path: root/src/mongo/db/matcher
diff options
context:
space:
mode:
authorDavid Storch <david.storch@10gen.com>2017-08-01 15:40:36 -0400
committerDavid Storch <david.storch@10gen.com>2017-08-02 16:54:56 -0400
commit0334cb2bf602ec0123594b59504b9b3e0a099899 (patch)
tree432c8463e9e7273ec2f11978c5dcbc47e363ba84 /src/mongo/db/matcher
parentf38554ab9fd611bb798812e4eb1fa7e3d3bbb7e3 (diff)
downloadmongo-0334cb2bf602ec0123594b59504b9b3e0a099899.tar.gz
SERVER-30245 Make ArrayMatchingMatchExpression inherit from PathMatchExpression.
Diffstat (limited to 'src/mongo/db/matcher')
-rw-r--r--src/mongo/db/matcher/expression.h7
-rw-r--r--src/mongo/db/matcher/expression_always_boolean.h2
-rw-r--r--src/mongo/db/matcher/expression_array.cpp38
-rw-r--r--src/mongo/db/matcher/expression_array.h34
-rw-r--r--src/mongo/db/matcher/expression_array_test.cpp6
-rw-r--r--src/mongo/db/matcher/expression_geo.cpp8
-rw-r--r--src/mongo/db/matcher/expression_geo.h9
-rw-r--r--src/mongo/db/matcher/expression_leaf.cpp19
-rw-r--r--src/mongo/db/matcher/expression_leaf.h18
-rw-r--r--src/mongo/db/matcher/expression_path.h20
-rw-r--r--src/mongo/db/matcher/expression_text_base.h2
-rw-r--r--src/mongo/db/matcher/expression_tree.cpp12
-rw-r--r--src/mongo/db/matcher/expression_tree.h13
-rw-r--r--src/mongo/db/matcher/expression_where_base.h2
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_cond.cpp8
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_cond.h2
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_max_properties.h3
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_min_properties.h3
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_object_match.cpp3
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_object_match.h6
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_object_match_test.cpp23
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_str_length.h3
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_xor.cpp5
-rw-r--r--src/mongo/db/matcher/schema/expression_internal_schema_xor.h2
-rw-r--r--src/mongo/db/matcher/schema/expression_parser_schema_test.cpp4
25 files changed, 140 insertions, 112 deletions
diff --git a/src/mongo/db/matcher/expression.h b/src/mongo/db/matcher/expression.h
index ef70f5ab6ca..f0c1d1c4184 100644
--- a/src/mongo/db/matcher/expression.h
+++ b/src/mongo/db/matcher/expression.h
@@ -182,9 +182,9 @@ public:
// Determine if a document satisfies the tree-predicate.
//
- virtual bool matches(const MatchableDocument* doc, MatchDetails* details = 0) const = 0;
+ virtual bool matches(const MatchableDocument* doc, MatchDetails* details = nullptr) const = 0;
- virtual bool matchesBSON(const BSONObj& doc, MatchDetails* details = 0) const;
+ virtual bool matchesBSON(const BSONObj& doc, MatchDetails* details = nullptr) const;
/**
* Determines if 'elem' would satisfy the predicate if wrapped with the top-level field name of
@@ -199,7 +199,8 @@ public:
* Determines if the element satisfies the tree-predicate.
* Not valid for all expressions (e.g. $where); in those cases, returns false.
*/
- virtual bool matchesSingleElement(const BSONElement& e) const = 0;
+ virtual bool matchesSingleElement(const BSONElement& e,
+ MatchDetails* details = nullptr) const = 0;
//
// Tagging mechanism: Hang data off of the tree for retrieval later.
diff --git a/src/mongo/db/matcher/expression_always_boolean.h b/src/mongo/db/matcher/expression_always_boolean.h
index 0f5b7ac465c..d54e4ac424d 100644
--- a/src/mongo/db/matcher/expression_always_boolean.h
+++ b/src/mongo/db/matcher/expression_always_boolean.h
@@ -48,7 +48,7 @@ public:
return _value;
}
- bool matchesSingleElement(const BSONElement& e) const final {
+ bool matchesSingleElement(const BSONElement&, MatchDetails* details = nullptr) const final {
return _value;
}
diff --git a/src/mongo/db/matcher/expression_array.cpp b/src/mongo/db/matcher/expression_array.cpp
index 965ab75d119..b80e321b976 100644
--- a/src/mongo/db/matcher/expression_array.cpp
+++ b/src/mongo/db/matcher/expression_array.cpp
@@ -35,39 +35,13 @@
namespace mongo {
-Status ArrayMatchingMatchExpression::setPath(StringData path) {
- _path = path;
- Status s = _elementPath.init(_path);
- _elementPath.setTraverseLeafArray(false);
- return s;
-}
-
-bool ArrayMatchingMatchExpression::matches(const MatchableDocument* doc,
- MatchDetails* details) const {
- MatchableDocument::IteratorHolder cursor(doc, &_elementPath);
-
- while (cursor->more()) {
- ElementIterator::Context e = cursor->next();
- if (e.element().type() != Array)
- continue;
-
- bool amIRoot = e.arrayOffset().eoo();
-
- if (!matchesArray(e.element().Obj(), amIRoot ? details : NULL))
- continue;
-
- if (!amIRoot && details && details->needRecord() && !e.arrayOffset().eoo()) {
- details->setElemMatchKey(e.arrayOffset().fieldName());
- }
- return true;
+bool ArrayMatchingMatchExpression::matchesSingleElement(const BSONElement& elt,
+ MatchDetails* details) const {
+ if (elt.type() != BSONType::Array) {
+ return false;
}
- return false;
-}
-bool ArrayMatchingMatchExpression::matchesSingleElement(const BSONElement& e) const {
- if (e.type() != Array)
- return false;
- return matchesArray(e.Obj(), NULL);
+ return matchesArray(elt.embeddedObject(), details);
}
@@ -78,7 +52,7 @@ bool ArrayMatchingMatchExpression::equivalent(const MatchExpression* other) cons
const ArrayMatchingMatchExpression* realOther =
static_cast<const ArrayMatchingMatchExpression*>(other);
- if (_path != realOther->_path)
+ if (path() != realOther->path())
return false;
if (numChildren() != realOther->numChildren())
diff --git a/src/mongo/db/matcher/expression_array.h b/src/mongo/db/matcher/expression_array.h
index 931e6403e1e..0eb32b024d2 100644
--- a/src/mongo/db/matcher/expression_array.h
+++ b/src/mongo/db/matcher/expression_array.h
@@ -35,40 +35,38 @@
#include "mongo/base/status.h"
#include "mongo/bson/bsonmisc.h"
#include "mongo/bson/bsonobj.h"
-#include "mongo/db/matcher/expression.h"
-#include "mongo/db/matcher/expression_leaf.h"
+#include "mongo/db/matcher/expression_path.h"
namespace mongo {
-class ArrayMatchingMatchExpression : public MatchExpression {
+/**
+ * A path match expression which does not expand arrays at the end of the path, and which only
+ * matches if the path contains an array.
+ */
+class ArrayMatchingMatchExpression : public PathMatchExpression {
public:
- ArrayMatchingMatchExpression(MatchType matchType) : MatchExpression(matchType) {}
- virtual ~ArrayMatchingMatchExpression() {}
+ ArrayMatchingMatchExpression(MatchType matchType) : PathMatchExpression(matchType) {}
- Status setPath(StringData path);
-
- virtual bool matches(const MatchableDocument* doc, MatchDetails* details) const;
+ virtual ~ArrayMatchingMatchExpression() {}
/**
- * @param e - has to be an array. calls matchesArray with e as an array
+ * Returns whether or not the nested array, represented as the object 'anArray', matches.
+ *
+ * 'anArray' must be the nested array at this expression's path.
*/
- virtual bool matchesSingleElement(const BSONElement& e) const;
-
virtual bool matchesArray(const BSONObj& anArray, MatchDetails* details) const = 0;
- bool equivalent(const MatchExpression* other) const;
+ bool matchesSingleElement(const BSONElement&, MatchDetails* details = nullptr) const final;
+
+ bool equivalent(const MatchExpression* other) const override;
- const StringData path() const {
- return _path;
+ bool shouldExpandLeafArray() const final {
+ return false;
}
MatchCategory getCategory() const final {
return MatchCategory::kArrayMatching;
}
-
-private:
- StringData _path;
- ElementPath _elementPath;
};
class ElemMatchObjectMatchExpression : public ArrayMatchingMatchExpression {
diff --git a/src/mongo/db/matcher/expression_array_test.cpp b/src/mongo/db/matcher/expression_array_test.cpp
index 894fbbd9e49..6b7d0c9a851 100644
--- a/src/mongo/db/matcher/expression_array_test.cpp
+++ b/src/mongo/db/matcher/expression_array_test.cpp
@@ -26,16 +26,16 @@
* it in the license file.
*/
-/** Unit tests for MatchMatchExpression operator implementations in match_operators.{h,cpp}. */
-
-#include "mongo/unittest/unittest.h"
+#include "mongo/platform/basic.h"
#include "mongo/db/jsobj.h"
#include "mongo/db/json.h"
#include "mongo/db/matcher/expression.h"
#include "mongo/db/matcher/expression_array.h"
+#include "mongo/db/matcher/expression_leaf.h"
#include "mongo/db/matcher/expression_tree.h"
#include "mongo/db/query/collation/collator_interface_mock.h"
+#include "mongo/unittest/unittest.h"
namespace mongo {
diff --git a/src/mongo/db/matcher/expression_geo.cpp b/src/mongo/db/matcher/expression_geo.cpp
index c15d1f564a7..90ed78ecaa7 100644
--- a/src/mongo/db/matcher/expression_geo.cpp
+++ b/src/mongo/db/matcher/expression_geo.cpp
@@ -337,7 +337,7 @@ Status GeoMatchExpression::init(StringData path,
return setPath(path);
}
-bool GeoMatchExpression::matchesSingleElement(const BSONElement& e) const {
+bool GeoMatchExpression::matchesSingleElement(const BSONElement& e, MatchDetails* details) const {
if (!e.isABSONObj())
return false;
@@ -420,10 +420,8 @@ Status GeoNearMatchExpression::init(StringData path,
return setPath(path);
}
-bool GeoNearMatchExpression::matchesSingleElement(const BSONElement& e) const {
- // See ops/update.cpp.
- // This node is removed by the query planner. It's only ever called if we're getting an
- // elemMatchKey.
+bool GeoNearMatchExpression::matchesSingleElement(const BSONElement& e,
+ MatchDetails* details) const {
return true;
}
diff --git a/src/mongo/db/matcher/expression_geo.h b/src/mongo/db/matcher/expression_geo.h
index 60ae66f3cb6..875adedf616 100644
--- a/src/mongo/db/matcher/expression_geo.h
+++ b/src/mongo/db/matcher/expression_geo.h
@@ -87,7 +87,7 @@ public:
*/
Status init(StringData path, const GeoExpression* query, const BSONObj& rawObj);
- virtual bool matchesSingleElement(const BSONElement& e) const;
+ bool matchesSingleElement(const BSONElement&, MatchDetails* details = nullptr) const final;
virtual void debugString(StringBuilder& debug, int level = 0) const;
@@ -168,8 +168,11 @@ public:
Status init(StringData path, const GeoNearExpression* query, const BSONObj& rawObj);
- // This shouldn't be called and as such will crash. GeoNear always requires an index.
- virtual bool matchesSingleElement(const BSONElement& e) const;
+ /**
+ * Stub implementation that should never be called, since geoNear execution requires an
+ * appropriate geo index.
+ */
+ bool matchesSingleElement(const BSONElement&, MatchDetails* details = nullptr) const final;
virtual void debugString(StringBuilder& debug, int level = 0) const;
diff --git a/src/mongo/db/matcher/expression_leaf.cpp b/src/mongo/db/matcher/expression_leaf.cpp
index 482ca061bc8..a07c8ada9c5 100644
--- a/src/mongo/db/matcher/expression_leaf.cpp
+++ b/src/mongo/db/matcher/expression_leaf.cpp
@@ -89,7 +89,8 @@ Status ComparisonMatchExpression::init(StringData path, const BSONElement& rhs)
}
-bool ComparisonMatchExpression::matchesSingleElement(const BSONElement& e) const {
+bool ComparisonMatchExpression::matchesSingleElement(const BSONElement& e,
+ MatchDetails* details) const {
if (e.canonicalType() != _rhs.canonicalType()) {
// some special cases
@@ -268,7 +269,7 @@ Status RegexMatchExpression::init(StringData path, StringData regex, StringData
return setPath(path);
}
-bool RegexMatchExpression::matchesSingleElement(const BSONElement& e) const {
+bool RegexMatchExpression::matchesSingleElement(const BSONElement& e, MatchDetails* details) const {
switch (e.type()) {
case String:
case Symbol: {
@@ -326,7 +327,7 @@ Status ModMatchExpression::init(StringData path, int divisor, int remainder) {
return setPath(path);
}
-bool ModMatchExpression::matchesSingleElement(const BSONElement& e) const {
+bool ModMatchExpression::matchesSingleElement(const BSONElement& e, MatchDetails* details) const {
if (!e.isNumber())
return false;
return e.numberLong() % _divisor == _remainder;
@@ -363,7 +364,8 @@ Status ExistsMatchExpression::init(StringData path) {
return setPath(path);
}
-bool ExistsMatchExpression::matchesSingleElement(const BSONElement& e) const {
+bool ExistsMatchExpression::matchesSingleElement(const BSONElement& e,
+ MatchDetails* details) const {
return !e.eoo();
}
@@ -423,7 +425,7 @@ Status TypeMatchExpression::init(StringData path, Type type) {
return setPath(path);
}
-bool TypeMatchExpression::matchesSingleElement(const BSONElement& e) const {
+bool TypeMatchExpression::matchesSingleElement(const BSONElement& e, MatchDetails* details) const {
if (_type.allNumbers) {
return e.isNumber();
}
@@ -499,7 +501,7 @@ std::unique_ptr<MatchExpression> InMatchExpression::shallowClone() const {
return std::move(next);
}
-bool InMatchExpression::matchesSingleElement(const BSONElement& e) const {
+bool InMatchExpression::matchesSingleElement(const BSONElement& e, MatchDetails* details) const {
if (_hasNull && e.eoo()) {
return true;
}
@@ -507,7 +509,7 @@ bool InMatchExpression::matchesSingleElement(const BSONElement& e) const {
return true;
}
for (auto&& regex : _regexes) {
- if (regex->matchesSingleElement(e)) {
+ if (regex->matchesSingleElement(e, details)) {
return true;
}
}
@@ -736,7 +738,8 @@ bool BitTestMatchExpression::performBitTest(const char* eBinary, uint32_t eBinar
return mt == BITS_ALL_SET || mt == BITS_ALL_CLEAR;
}
-bool BitTestMatchExpression::matchesSingleElement(const BSONElement& e) const {
+bool BitTestMatchExpression::matchesSingleElement(const BSONElement& e,
+ MatchDetails* details) const {
// Validate 'e' is a number or a BinData.
if (!e.isNumber() && e.type() != BSONType::BinData) {
return false;
diff --git a/src/mongo/db/matcher/expression_leaf.h b/src/mongo/db/matcher/expression_leaf.h
index 559dcf1f63b..c61fdd25ddc 100644
--- a/src/mongo/db/matcher/expression_leaf.h
+++ b/src/mongo/db/matcher/expression_leaf.h
@@ -49,6 +49,10 @@ public:
virtual ~LeafMatchExpression() {}
+ bool shouldExpandLeafArray() const final {
+ return true;
+ }
+
MatchCategory getCategory() const final {
return MatchCategory::kLeaf;
}
@@ -65,7 +69,7 @@ public:
virtual ~ComparisonMatchExpression() {}
- virtual bool matchesSingleElement(const BSONElement& e) const;
+ bool matchesSingleElement(const BSONElement&, MatchDetails* details = nullptr) const final;
virtual void debugString(StringBuilder& debug, int level = 0) const;
@@ -205,7 +209,7 @@ public:
return std::move(e);
}
- virtual bool matchesSingleElement(const BSONElement& e) const;
+ bool matchesSingleElement(const BSONElement&, MatchDetails* details = nullptr) const final;
virtual void debugString(StringBuilder& debug, int level) const;
@@ -245,7 +249,7 @@ public:
return std::move(m);
}
- virtual bool matchesSingleElement(const BSONElement& e) const;
+ bool matchesSingleElement(const BSONElement&, MatchDetails* details = nullptr) const final;
virtual void debugString(StringBuilder& debug, int level) const;
@@ -280,7 +284,7 @@ public:
return std::move(e);
}
- virtual bool matchesSingleElement(const BSONElement& e) const;
+ bool matchesSingleElement(const BSONElement&, MatchDetails* details = nullptr) const final;
virtual void debugString(StringBuilder& debug, int level) const;
@@ -300,7 +304,7 @@ public:
virtual std::unique_ptr<MatchExpression> shallowClone() const;
- virtual bool matchesSingleElement(const BSONElement& e) const;
+ bool matchesSingleElement(const BSONElement&, MatchDetails* details = nullptr) const final;
virtual void debugString(StringBuilder& debug, int level) const;
@@ -392,7 +396,7 @@ public:
return std::move(e);
}
- bool matchesSingleElement(const BSONElement& e) const override;
+ bool matchesSingleElement(const BSONElement&, MatchDetails* details = nullptr) const final;
void debugString(StringBuilder& debug, int level) const override;
@@ -438,7 +442,7 @@ public:
Status init(StringData path, uint64_t bitMask);
Status init(StringData path, const char* bitMaskBinary, uint32_t bitMaskLen);
- virtual bool matchesSingleElement(const BSONElement& e) const;
+ bool matchesSingleElement(const BSONElement&, MatchDetails* details = nullptr) const final;
virtual void debugString(StringBuilder& debug, int level) const;
diff --git a/src/mongo/db/matcher/expression_path.h b/src/mongo/db/matcher/expression_path.h
index 540bc76b52d..41c8a0addf7 100644
--- a/src/mongo/db/matcher/expression_path.h
+++ b/src/mongo/db/matcher/expression_path.h
@@ -45,11 +45,21 @@ public:
virtual ~PathMatchExpression() {}
+ /**
+ * Returns whether or not this expression should match against each element of an array (in
+ * addition to the array as a whole).
+ *
+ * For example, returns true if a path match expression on "f" should match against 1, 2, and
+ * [1, 2] for document {f: [1, 2]}. Returns false if this expression should only match against
+ * [1, 2].
+ */
+ virtual bool shouldExpandLeafArray() const = 0;
+
bool matches(const MatchableDocument* doc, MatchDetails* details = nullptr) const final {
MatchableDocument::IteratorHolder cursor(doc, &_elementPath);
while (cursor->more()) {
ElementIterator::Context e = cursor->next();
- if (!matchesSingleElement(e.element())) {
+ if (!matchesSingleElement(e.element(), details)) {
continue;
}
if (details && details->needRecord() && !e.arrayOffset().eoo()) {
@@ -66,7 +76,13 @@ public:
Status setPath(StringData path) {
_path = path;
- return _elementPath.init(_path);
+ auto status = _elementPath.init(_path);
+ if (!status.isOK()) {
+ return status;
+ }
+
+ _elementPath.setTraverseLeafArray(shouldExpandLeafArray());
+ return Status::OK();
}
private:
diff --git a/src/mongo/db/matcher/expression_text_base.h b/src/mongo/db/matcher/expression_text_base.h
index 06a23a010bf..0f75e40b412 100644
--- a/src/mongo/db/matcher/expression_text_base.h
+++ b/src/mongo/db/matcher/expression_text_base.h
@@ -62,7 +62,7 @@ public:
// Methods inherited from MatchExpression.
//
- bool matchesSingleElement(const BSONElement& e) const final {
+ bool matchesSingleElement(const BSONElement& e, MatchDetails* details = nullptr) const final {
// Text match expressions force the selection of the text index and always generate EXACT
// index bounds (which causes the MatchExpression node to be trimmed), so we don't currently
// implement any explicit text matching logic here. SERVER-17648 tracks the work to
diff --git a/src/mongo/db/matcher/expression_tree.cpp b/src/mongo/db/matcher/expression_tree.cpp
index 7cd2f8b2d60..e1dd45ee0e7 100644
--- a/src/mongo/db/matcher/expression_tree.cpp
+++ b/src/mongo/db/matcher/expression_tree.cpp
@@ -91,9 +91,9 @@ bool AndMatchExpression::matches(const MatchableDocument* doc, MatchDetails* det
return true;
}
-bool AndMatchExpression::matchesSingleElement(const BSONElement& e) const {
+bool AndMatchExpression::matchesSingleElement(const BSONElement& e, MatchDetails* details) const {
for (size_t i = 0; i < numChildren(); i++) {
- if (!getChild(i)->matchesSingleElement(e)) {
+ if (!getChild(i)->matchesSingleElement(e, details)) {
return false;
}
}
@@ -130,9 +130,9 @@ bool OrMatchExpression::matches(const MatchableDocument* doc, MatchDetails* deta
return false;
}
-bool OrMatchExpression::matchesSingleElement(const BSONElement& e) const {
+bool OrMatchExpression::matchesSingleElement(const BSONElement& e, MatchDetails* details) const {
for (size_t i = 0; i < numChildren(); i++) {
- if (getChild(i)->matchesSingleElement(e)) {
+ if (getChild(i)->matchesSingleElement(e, details)) {
return true;
}
}
@@ -162,9 +162,9 @@ bool NorMatchExpression::matches(const MatchableDocument* doc, MatchDetails* det
return true;
}
-bool NorMatchExpression::matchesSingleElement(const BSONElement& e) const {
+bool NorMatchExpression::matchesSingleElement(const BSONElement& e, MatchDetails* details) const {
for (size_t i = 0; i < numChildren(); i++) {
- if (getChild(i)->matchesSingleElement(e)) {
+ if (getChild(i)->matchesSingleElement(e, details)) {
return false;
}
}
diff --git a/src/mongo/db/matcher/expression_tree.h b/src/mongo/db/matcher/expression_tree.h
index 0d759eed65e..3c2925ccc1e 100644
--- a/src/mongo/db/matcher/expression_tree.h
+++ b/src/mongo/db/matcher/expression_tree.h
@@ -108,7 +108,8 @@ public:
virtual ~AndMatchExpression() {}
virtual bool matches(const MatchableDocument* doc, MatchDetails* details = 0) const;
- virtual bool matchesSingleElement(const BSONElement& e) const;
+
+ bool matchesSingleElement(const BSONElement&, MatchDetails* details = nullptr) const final;
virtual std::unique_ptr<MatchExpression> shallowClone() const {
std::unique_ptr<AndMatchExpression> self = stdx::make_unique<AndMatchExpression>();
@@ -132,7 +133,8 @@ public:
virtual ~OrMatchExpression() {}
virtual bool matches(const MatchableDocument* doc, MatchDetails* details = 0) const;
- virtual bool matchesSingleElement(const BSONElement& e) const;
+
+ bool matchesSingleElement(const BSONElement&, MatchDetails* details = nullptr) const final;
virtual std::unique_ptr<MatchExpression> shallowClone() const {
std::unique_ptr<OrMatchExpression> self = stdx::make_unique<OrMatchExpression>();
@@ -156,7 +158,8 @@ public:
virtual ~NorMatchExpression() {}
virtual bool matches(const MatchableDocument* doc, MatchDetails* details = 0) const;
- virtual bool matchesSingleElement(const BSONElement& e) const;
+
+ bool matchesSingleElement(const BSONElement&, MatchDetails* details = nullptr) const final;
virtual std::unique_ptr<MatchExpression> shallowClone() const {
std::unique_ptr<NorMatchExpression> self = stdx::make_unique<NorMatchExpression>();
@@ -199,8 +202,8 @@ public:
return !_exp->matches(doc, NULL);
}
- virtual bool matchesSingleElement(const BSONElement& e) const {
- return !_exp->matchesSingleElement(e);
+ bool matchesSingleElement(const BSONElement& elt, MatchDetails* details = nullptr) const final {
+ return !_exp->matchesSingleElement(elt, details);
}
virtual void debugString(StringBuilder& debug, int level = 0) const;
diff --git a/src/mongo/db/matcher/expression_where_base.h b/src/mongo/db/matcher/expression_where_base.h
index 62e8f1ac8f1..10204ec07cb 100644
--- a/src/mongo/db/matcher/expression_where_base.h
+++ b/src/mongo/db/matcher/expression_where_base.h
@@ -48,7 +48,7 @@ public:
// Methods inherited from MatchExpression.
//
- bool matchesSingleElement(const BSONElement& e) const final {
+ bool matchesSingleElement(const BSONElement& e, MatchDetails* details = nullptr) const final {
return false;
}
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_cond.cpp b/src/mongo/db/matcher/schema/expression_internal_schema_cond.cpp
index 1929491034d..b05de7d8d69 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_cond.cpp
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_cond.cpp
@@ -39,8 +39,10 @@ bool InternalSchemaCondMatchExpression::matches(const MatchableDocument* doc,
: elseBranch()->matches(doc, details);
}
-bool InternalSchemaCondMatchExpression::matchesSingleElement(const BSONElement& elem) const {
- return condition()->matchesSingleElement(elem) ? thenBranch()->matchesSingleElement(elem)
- : elseBranch()->matchesSingleElement(elem);
+bool InternalSchemaCondMatchExpression::matchesSingleElement(const BSONElement& elem,
+ MatchDetails* details) const {
+ return condition()->matchesSingleElement(elem, details)
+ ? thenBranch()->matchesSingleElement(elem, details)
+ : elseBranch()->matchesSingleElement(elem, details);
}
} // namespace mongo
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_cond.h b/src/mongo/db/matcher/schema/expression_internal_schema_cond.h
index 2af7c6b4036..94da4bc3781 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_cond.h
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_cond.h
@@ -69,7 +69,7 @@ public:
* 'thenBranch'. Otherwise, returns the result of matching it against 'elseBranch'.
*/
bool matches(const MatchableDocument* doc, MatchDetails* details = nullptr) const final;
- bool matchesSingleElement(const BSONElement& elem) const final;
+ bool matchesSingleElement(const BSONElement& elem, MatchDetails* details = nullptr) const final;
};
} // namespace mongo
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_max_properties.h b/src/mongo/db/matcher/schema/expression_internal_schema_max_properties.h
index e4ef7d72c9b..5736ca08017 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_max_properties.h
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_max_properties.h
@@ -48,7 +48,8 @@ public:
return (obj.nFields() <= numProperties());
}
- bool matchesSingleElement(const BSONElement& elem) const final {
+ bool matchesSingleElement(const BSONElement& elem,
+ MatchDetails* details = nullptr) const final {
if (elem.type() != BSONType::Object) {
return false;
}
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_min_properties.h b/src/mongo/db/matcher/schema/expression_internal_schema_min_properties.h
index 73d9b748bda..b97506fd882 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_min_properties.h
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_min_properties.h
@@ -48,7 +48,8 @@ public:
return (obj.nFields() >= numProperties());
}
- bool matchesSingleElement(const BSONElement& elem) const final {
+ bool matchesSingleElement(const BSONElement& elem,
+ MatchDetails* details = nullptr) const final {
if (elem.type() != BSONType::Object) {
return false;
}
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 72990694bfc..82d9cb85247 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
@@ -34,7 +34,8 @@ namespace mongo {
constexpr StringData InternalSchemaObjectMatchExpression::kName;
-bool InternalSchemaObjectMatchExpression::matchesSingleElement(const BSONElement& elem) const {
+bool InternalSchemaObjectMatchExpression::matchesSingleElement(const BSONElement& elem,
+ MatchDetails* details) const {
if (elem.type() != BSONType::Object) {
return false;
}
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 b0939635cb7..4257b5b05a5 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
@@ -43,7 +43,7 @@ public:
return setPath(path);
}
- bool matchesSingleElement(const BSONElement& elem) const final;
+ bool matchesSingleElement(const BSONElement& elem, MatchDetails* details = nullptr) const final;
std::unique_ptr<MatchExpression> shallowClone() const final;
@@ -63,6 +63,10 @@ public:
return MatchCategory::kOther;
}
+ bool shouldExpandLeafArray() const final {
+ return false;
+ }
+
private:
std::unique_ptr<MatchExpression> _sub;
};
diff --git a/src/mongo/db/matcher/schema/expression_internal_schema_object_match_test.cpp b/src/mongo/db/matcher/schema/expression_internal_schema_object_match_test.cpp
index 54ebe2ff749..89a95e7eff3 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_object_match_test.cpp
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_object_match_test.cpp
@@ -79,8 +79,13 @@ TEST(InternalSchemaObjectMatchExpression, AcceptsObjectsThatMatch) {
ASSERT_TRUE(objMatch.matchesBSON(BSON("a" << BSON("b"
<< "string"))));
- ASSERT_TRUE(objMatch.matchesBSON(BSON("a" << BSON_ARRAY(BSON("b" << 1) << BSON("b"
- << "string")))));
+ ASSERT_TRUE(objMatch.matchesBSON(BSON("a" << BSON("b"
+ << "string"
+ << "c"
+ << 1))));
+ ASSERT_FALSE(
+ objMatch.matchesBSON(BSON("a" << BSON_ARRAY(BSON("b" << 1) << BSON("b"
+ << "string")))));
ASSERT_TRUE(objMatch.matchesBSON(BSON("a" << BSON("b" << BSON_ARRAY("string")))));
}
@@ -115,7 +120,7 @@ TEST(InternalSchemaObjectMatchExpression, EmptyMatchAcceptsAllObjects) {
ASSERT_TRUE(objMatch.matchesBSON(BSON("a" << BSONObj())));
ASSERT_TRUE(objMatch.matchesBSON(BSON("a" << BSON("b"
<< "string"))));
- ASSERT_TRUE(objMatch.matchesBSON(BSON("a" << BSON_ARRAY(BSONObj()))));
+ ASSERT_FALSE(objMatch.matchesBSON(BSON("a" << BSON_ARRAY(BSONObj()))));
}
TEST(InternalSchemaObjectMatchExpression, NestedObjectMatchReturnsCorrectPath) {
@@ -188,5 +193,17 @@ TEST(InternalSchemaObjectMatchExpression, SubExpressionRespectsCollator) {
ASSERT_TRUE(objectMatch.getValue()->matchesBSON(fromjson("{a: {b: 'foo'}}")));
}
+TEST(InternalSchemaObjectMatchExpression, RejectsArraysContainingMatchingSubObject) {
+ auto query = fromjson("{a: {$_internalSchemaObjectMatch: {b: 1}}}");
+ auto objMatch =
+ MatchExpressionParser::parse(query, ExtensionsCallbackDisallowExtensions(), nullptr);
+ ASSERT_OK(objMatch.getStatus());
+
+ ASSERT_FALSE(objMatch.getValue()->matchesBSON(fromjson("{a: 1}")));
+ ASSERT_TRUE(objMatch.getValue()->matchesBSON(fromjson("{a: {b: 1}}")));
+ ASSERT_FALSE(objMatch.getValue()->matchesBSON(fromjson("{a: [{b: 1}]}")));
+ ASSERT_FALSE(objMatch.getValue()->matchesBSON(fromjson("{a: [{b: 1}, {b: 2}]}")));
+}
+
} // namespace
} // namespace mongo
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 0d9f1cf448f..5ff2ff7f52d 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
@@ -50,7 +50,8 @@ public:
virtual Validator getComparator() const = 0;
- bool matchesSingleElement(const BSONElement& elem) const final {
+ bool matchesSingleElement(const BSONElement& elem,
+ MatchDetails* details = nullptr) const final {
if (elem.type() != BSONType::String) {
return false;
}
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 a03ce13349a..21fd5b6a7fc 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_xor.cpp
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_xor.cpp
@@ -51,10 +51,11 @@ bool InternalSchemaXorMatchExpression::matches(const MatchableDocument* doc,
return found;
}
-bool InternalSchemaXorMatchExpression::matchesSingleElement(const BSONElement& element) const {
+bool InternalSchemaXorMatchExpression::matchesSingleElement(const BSONElement& element,
+ MatchDetails* details) const {
bool found = false;
for (size_t i = 0; i < numChildren(); i++) {
- if (getChild(i)->matchesSingleElement(element)) {
+ if (getChild(i)->matchesSingleElement(element, details)) {
if (found) {
return false;
}
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 f865a480f8f..53fb21c0631 100644
--- a/src/mongo/db/matcher/schema/expression_internal_schema_xor.h
+++ b/src/mongo/db/matcher/schema/expression_internal_schema_xor.h
@@ -44,7 +44,7 @@ public:
bool matches(const MatchableDocument* doc, MatchDetails* details = nullptr) const final;
- bool matchesSingleElement(const BSONElement& element) const final;
+ bool matchesSingleElement(const BSONElement&, MatchDetails* details = nullptr) const final;
virtual std::unique_ptr<MatchExpression> shallowClone() const {
auto xorCopy = stdx::make_unique<InternalSchemaXorMatchExpression>();
diff --git a/src/mongo/db/matcher/schema/expression_parser_schema_test.cpp b/src/mongo/db/matcher/schema/expression_parser_schema_test.cpp
index 78dee6b6626..0d7dddfbebd 100644
--- a/src/mongo/db/matcher/schema/expression_parser_schema_test.cpp
+++ b/src/mongo/db/matcher/schema/expression_parser_schema_test.cpp
@@ -214,7 +214,7 @@ TEST(MatchExpressionParserSchemaTest, ObjectMatchCorrectlyParsesObjects) {
ASSERT_FALSE(result.getValue()->matchesBSON(fromjson("{a: {b: 'string'}}")));
ASSERT_FALSE(result.getValue()->matchesBSON(fromjson("{a: {b: -1}}")));
ASSERT_TRUE(result.getValue()->matchesBSON(fromjson("{a: {b: 1}}")));
- ASSERT_TRUE(result.getValue()->matchesBSON(fromjson("{a: [{b: 0}]}")));
+ ASSERT_FALSE(result.getValue()->matchesBSON(fromjson("{a: [{b: 0}]}")));
}
TEST(MatchExpressionParserSchemaTest, ObjectMatchCorrectlyParsesNestedObjectMatch) {
@@ -233,7 +233,7 @@ TEST(MatchExpressionParserSchemaTest, ObjectMatchCorrectlyParsesNestedObjectMatc
ASSERT_FALSE(result.getValue()->matchesBSON(fromjson("{a: {b: {c: 0}}}")));
ASSERT_TRUE(result.getValue()->matchesBSON(fromjson("{a: {b: {c: 'string'}}}")));
ASSERT_TRUE(result.getValue()->matchesBSON(fromjson("{a: {b: {c: 1}}}")));
- ASSERT_TRUE(
+ ASSERT_FALSE(
result.getValue()->matchesBSON(fromjson("{a: [{b: 0}, {b: [{c: 0}, {c: 'string'}]}]}")));
}