diff options
Diffstat (limited to 'src')
26 files changed, 141 insertions, 113 deletions
diff --git a/src/mongo/db/exec/geo_near.cpp b/src/mongo/db/exec/geo_near.cpp index 49ee2feb6f9..5349bfaa3e5 100644 --- a/src/mongo/db/exec/geo_near.cpp +++ b/src/mongo/db/exec/geo_near.cpp @@ -513,7 +513,7 @@ public: out->append("TwoDPtInAnnulusExpression", true); } - bool matchesSingleElement(const BSONElement& e) const final { + bool matchesSingleElement(const BSONElement& e, MatchDetails* details = nullptr) const final { if (!e.isABSONObj()) return false; 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'}]}]}"))); } |