diff options
author | J. Rassi <rassi@10gen.com> | 2016-05-25 14:50:50 -0400 |
---|---|---|
committer | J. Rassi <rassi@10gen.com> | 2016-05-27 14:02:55 -0400 |
commit | f93ad728e94b6813c242faac43d4b57a07a3c319 (patch) | |
tree | b24ef5d0a74ba1c166c314d49d2054a5b0b37d45 | |
parent | 1898c4eac3124a6d32de1d2c13d7f6d05b1b538b (diff) | |
download | mongo-f93ad728e94b6813c242faac43d4b57a07a3c319.tar.gz |
SERVER-23611 Refactor InMatchExpression
Includes deletion of the ArrayFilterEntries class.
This is groundwork for implementing InMatchExpression::setCollator().
-rw-r--r-- | src/mongo/db/commands/list_collections.cpp | 5 | ||||
-rw-r--r-- | src/mongo/db/matcher/expression_algo.cpp | 9 | ||||
-rw-r--r-- | src/mongo/db/matcher/expression_leaf.cpp | 201 | ||||
-rw-r--r-- | src/mongo/db/matcher/expression_leaf.h | 100 | ||||
-rw-r--r-- | src/mongo/db/matcher/expression_leaf_test.cpp | 111 | ||||
-rw-r--r-- | src/mongo/db/matcher/expression_parser.cpp | 14 | ||||
-rw-r--r-- | src/mongo/db/matcher/expression_parser.h | 2 | ||||
-rw-r--r-- | src/mongo/db/query/canonical_query.cpp | 26 | ||||
-rw-r--r-- | src/mongo/db/query/canonical_query_test.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/query/index_bounds_builder.cpp | 14 | ||||
-rw-r--r-- | src/mongo/db/query/plan_cache_indexability.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/query/planner_ixselect.cpp | 8 |
12 files changed, 162 insertions, 332 deletions
diff --git a/src/mongo/db/commands/list_collections.cpp b/src/mongo/db/commands/list_collections.cpp index ae5dcbe5fea..a6e2174eebf 100644 --- a/src/mongo/db/commands/list_collections.cpp +++ b/src/mongo/db/commands/list_collections.cpp @@ -84,10 +84,9 @@ boost::optional<vector<StringData>> _getExactNameMatches(const MatchExpression* } } else if (matchType == MatchExpression::MATCH_IN) { auto matchIn = checked_cast<const InMatchExpression*>(matcher); - const ArrayFilterEntries& entries = matchIn->getData(); - if (matchIn->path() == "name" && entries.numRegexes() == 0) { + if (matchIn->path() == "name" && matchIn->getRegexes().empty()) { vector<StringData> exactMatches; - for (auto&& elem : entries.equalities()) { + for (auto&& elem : matchIn->getEqualities()) { if (elem.type() == String) { exactMatches.push_back(elem.valueStringData()); } diff --git a/src/mongo/db/matcher/expression_algo.cpp b/src/mongo/db/matcher/expression_algo.cpp index 9a875aa488f..02d25a5df9a 100644 --- a/src/mongo/db/matcher/expression_algo.cpp +++ b/src/mongo/db/matcher/expression_algo.cpp @@ -150,11 +150,10 @@ bool _isSubsetOf(const MatchExpression* lhs, const ComparisonMatchExpression* rh if (lhs->matchType() == MatchExpression::MATCH_IN) { const InMatchExpression* ime = static_cast<const InMatchExpression*>(lhs); - const ArrayFilterEntries& arrayEntries = ime->getData(); - if (arrayEntries.numRegexes() > 0) { + if (!ime->getRegexes().empty()) { return false; } - for (BSONElement elem : arrayEntries.equalities()) { + for (BSONElement elem : ime->getEqualities()) { // Each element in the $in-array represents an equality predicate. // TODO SERVER-23618: pass the appropriate collator to EqualityMatchExpression(). EqualityMatchExpression equality(nullptr); @@ -199,7 +198,7 @@ bool _isSubsetOf(const MatchExpression* lhs, const ExistsMatchExpression* rhs) { return true; case MatchExpression::MATCH_IN: { const InMatchExpression* ime = static_cast<const InMatchExpression*>(lhs); - return !ime->getData().hasNull(); + return !ime->hasNull(); } case MatchExpression::NOT: // An expression can only match a subset of the documents matched by another if they are @@ -217,7 +216,7 @@ bool _isSubsetOf(const MatchExpression* lhs, const ExistsMatchExpression* rhs) { case MatchExpression::MATCH_IN: { const InMatchExpression* ime = static_cast<const InMatchExpression*>(lhs->getChild(0)); - return ime->getData().hasNull(); + return ime->hasNull(); } default: return false; diff --git a/src/mongo/db/matcher/expression_leaf.cpp b/src/mongo/db/matcher/expression_leaf.cpp index 3e7e2a4b909..04529e40e48 100644 --- a/src/mongo/db/matcher/expression_leaf.cpp +++ b/src/mongo/db/matcher/expression_leaf.cpp @@ -530,134 +530,59 @@ bool TypeMatchExpression::equivalent(const MatchExpression* other) const { } -// -------- - -ArrayFilterEntries::ArrayFilterEntries(const CollatorInterface* collator) - : _hasNull(false), _hasEmptyArray(false), _equalities(collator), _collator(collator) {} - -ArrayFilterEntries::~ArrayFilterEntries() { - for (unsigned i = 0; i < _regexes.size(); i++) - delete _regexes[i]; - _regexes.clear(); -} - -Status ArrayFilterEntries::addEquality(const BSONElement& e) { - if (e.type() == RegEx) - return Status(ErrorCodes::BadValue, "ArrayFilterEntries equality cannot be a regex"); - - if (e.type() == Undefined) { - return Status(ErrorCodes::BadValue, "ArrayFilterEntries equality cannot be undefined"); - } - - if (e.type() == jstNULL) { - _hasNull = true; - } - - if (e.type() == Array && e.Obj().isEmpty()) - _hasEmptyArray = true; - - _equalities.insert(e); - return Status::OK(); -} - -Status ArrayFilterEntries::addRegex(RegexMatchExpression* expr) { - _regexes.push_back(expr); - return Status::OK(); -} - -bool ArrayFilterEntries::equivalent(const ArrayFilterEntries& other) const { - if (_hasNull != other._hasNull) - return false; - - if (_regexes.size() != other._regexes.size()) - return false; - for (unsigned i = 0; i < _regexes.size(); i++) - if (!_regexes[i]->equivalent(other._regexes[i])) - return false; - - if (!CollatorInterface::collatorsMatch(_collator, other._collator)) { - return false; - } - - return _equalities == other._equalities; -} - -void ArrayFilterEntries::copyTo(ArrayFilterEntries& toFillIn) const { - toFillIn._hasNull = _hasNull; - toFillIn._hasEmptyArray = _hasEmptyArray; - toFillIn._equalities = _equalities; - for (unsigned i = 0; i < _regexes.size(); i++) - toFillIn._regexes.push_back( - static_cast<RegexMatchExpression*>(_regexes[i]->shallowClone().release())); -} - -void ArrayFilterEntries::debugString(StringBuilder& debug) const { - debug << "[ "; - for (BSONElementSet::const_iterator it = _equalities.begin(); it != _equalities.end(); ++it) { - debug << it->toString(false) << " "; - } - for (size_t i = 0; i < _regexes.size(); ++i) { - _regexes[i]->shortDebugString(debug); - debug << " "; - } - debug << "]"; -} - -void ArrayFilterEntries::serialize(BSONArrayBuilder* out) const { - for (BSONElementSet::const_iterator it = _equalities.begin(); it != _equalities.end(); ++it) { - out->append(*it); - } - for (size_t i = 0; i < _regexes.size(); ++i) { - BSONObjBuilder regexBob; - _regexes[i]->serialize(®exBob); - out->append(regexBob.obj().firstElement()); - } - out->doneFast(); -} - // ----------- +InMatchExpression::InMatchExpression(const CollatorInterface* collator) + : LeafMatchExpression(MATCH_IN), _equalities(collator), _collator(collator) {} + Status InMatchExpression::init(StringData path) { return setPath(path); } -bool InMatchExpression::_matchesRealElement(const BSONElement& e) const { - if (_arrayEntries.contains(e)) - return true; - - for (unsigned i = 0; i < _arrayEntries.numRegexes(); i++) { - if (_arrayEntries.regex(i)->matchesSingleElement(e)) - return true; +std::unique_ptr<MatchExpression> InMatchExpression::shallowClone() const { + auto next = stdx::make_unique<InMatchExpression>(_collator); + next->init(path()); + if (getTag()) { + next->setTag(getTag()->clone()); } - - return false; + next->_hasNull = _hasNull; + next->_hasEmptyArray = _hasEmptyArray; + next->_equalities = _equalities; + for (auto&& regex : _regexes) { + std::unique_ptr<RegexMatchExpression> clonedRegex( + static_cast<RegexMatchExpression*>(regex->shallowClone().release())); + next->_regexes.push_back(std::move(clonedRegex)); + } + return std::move(next); } bool InMatchExpression::matchesSingleElement(const BSONElement& e) const { - if (_arrayEntries.hasNull() && e.eoo()) + if (_hasNull && e.eoo()) { return true; - - if (_matchesRealElement(e)) + } + if (_equalities.find(e) != _equalities.end()) { return true; - - /* - if ( e.type() == Array ) { - BSONObjIterator i( e.Obj() ); - while ( i.more() ) { - BSONElement sub = i.next(); - if ( _matchesRealElement( sub ) ) - return true; + } + for (auto&& regex : _regexes) { + if (regex->matchesSingleElement(e)) { + return true; } } - */ - return false; } void InMatchExpression::debugString(StringBuilder& debug, int level) const { _debugAddSpace(debug, level); debug << path() << " $in "; - _arrayEntries.debugString(debug); + debug << "[ "; + for (auto&& equality : _equalities) { + debug << equality.toString(false) << " "; + } + for (auto&& regex : _regexes) { + regex->shortDebugString(debug); + debug << " "; + } + debug << "]"; MatchExpression::TagData* td = getTag(); if (NULL != td) { debug << " "; @@ -669,30 +594,64 @@ void InMatchExpression::debugString(StringBuilder& debug, int level) const { void InMatchExpression::serialize(BSONObjBuilder* out) const { BSONObjBuilder inBob(out->subobjStart(path())); BSONArrayBuilder arrBob(inBob.subarrayStart("$in")); - _arrayEntries.serialize(&arrBob); + for (auto&& _equality : _equalities) { + arrBob.append(_equality); + } + for (auto&& _regex : _regexes) { + BSONObjBuilder regexBob; + _regex->serialize(®exBob); + arrBob.append(regexBob.obj().firstElement()); + } + arrBob.doneFast(); inBob.doneFast(); } bool InMatchExpression::equivalent(const MatchExpression* other) const { - if (matchType() != other->matchType()) + if (matchType() != other->matchType()) { return false; + } const InMatchExpression* realOther = static_cast<const InMatchExpression*>(other); - return path() == realOther->path() && _arrayEntries.equivalent(realOther->_arrayEntries); + if (path() != realOther->path()) { + return false; + } + if (_hasNull != realOther->_hasNull) { + return false; + } + if (_regexes.size() != realOther->_regexes.size()) { + return false; + } + for (unsigned i = 0; i < _regexes.size(); i++) { + if (!_regexes[i]->equivalent(realOther->_regexes[i].get())) { + return false; + } + } + if (!CollatorInterface::collatorsMatch(_collator, realOther->_collator)) { + return false; + } + return _equalities == realOther->_equalities; } -std::unique_ptr<MatchExpression> InMatchExpression::shallowClone() const { - std::unique_ptr<InMatchExpression> next = - stdx::make_unique<InMatchExpression>(_arrayEntries.getCollator()); - copyTo(next.get()); - if (getTag()) { - next->setTag(getTag()->clone()); +Status InMatchExpression::addEquality(const BSONElement& elt) { + if (elt.type() == BSONType::RegEx) { + return Status(ErrorCodes::BadValue, "InMatchExpression equality cannot be a regex"); } - return std::move(next); + if (elt.type() == BSONType::Undefined) { + return Status(ErrorCodes::BadValue, "InMatchExpression equality cannot be undefined"); + } + + if (elt.type() == BSONType::jstNULL) { + _hasNull = true; + } + if (elt.type() == BSONType::Array && elt.Obj().isEmpty()) { + _hasEmptyArray = true; + } + _equalities.insert(elt); + return Status::OK(); } -void InMatchExpression::copyTo(InMatchExpression* toFillIn) const { - toFillIn->init(path()); - _arrayEntries.copyTo(toFillIn->_arrayEntries); +Status InMatchExpression::addRegex(std::unique_ptr<RegexMatchExpression> expr) { + _regexes.push_back(std::move(expr)); + return Status::OK(); } // ----------- diff --git a/src/mongo/db/matcher/expression_leaf.h b/src/mongo/db/matcher/expression_leaf.h index b39e709d3e6..21d0b63365d 100644 --- a/src/mongo/db/matcher/expression_leaf.h +++ b/src/mongo/db/matcher/expression_leaf.h @@ -309,69 +309,6 @@ public: }; /** - * INTERNAL - * terrible name - * holds the entries of an $in or $all - * either scalars or regex - */ -class ArrayFilterEntries { - MONGO_DISALLOW_COPYING(ArrayFilterEntries); - -public: - ArrayFilterEntries(const CollatorInterface* collator); - ~ArrayFilterEntries(); - - Status addEquality(const BSONElement& e); - Status addRegex(RegexMatchExpression* expr); - - const BSONElementSet& equalities() const { - return _equalities; - } - bool contains(const BSONElement& elem) const { - return _equalities.count(elem) > 0; - } - - size_t numRegexes() const { - return _regexes.size(); - } - RegexMatchExpression* regex(int idx) const { - return _regexes[idx]; - } - - bool hasNull() const { - return _hasNull; - } - bool singleNull() const { - return size() == 1 && _hasNull; - } - bool hasEmptyArray() const { - return _hasEmptyArray; - } - int size() const { - return _equalities.size() + _regexes.size(); - } - - const CollatorInterface* getCollator() const { - return _collator; - } - - bool equivalent(const ArrayFilterEntries& other) const; - - void copyTo(ArrayFilterEntries& toFillIn) const; - - void debugString(StringBuilder& debug) const; - - void serialize(BSONArrayBuilder* out) const; - -private: - bool _hasNull; // if _equalities has a jstNULL element in it - bool _hasEmptyArray; - BSONElementSet _equalities; - std::vector<RegexMatchExpression*> _regexes; - const CollatorInterface* _collator; -}; - -/** * query operator: $in */ class InMatchExpression : public LeafMatchExpression { @@ -379,16 +316,12 @@ public: /** * 'collator' must outlive the InMatchExpression and any clones made of it. */ - InMatchExpression(const CollatorInterface* collator) - : LeafMatchExpression(MATCH_IN), _arrayEntries(collator) {} + InMatchExpression(const CollatorInterface* collator); + Status init(StringData path); virtual std::unique_ptr<MatchExpression> shallowClone() const; - ArrayFilterEntries* getArrayFilterEntries() { - return &_arrayEntries; - } - virtual bool matchesSingleElement(const BSONElement& e) const; virtual void debugString(StringBuilder& debug, int level) const; @@ -397,19 +330,36 @@ public: virtual bool equivalent(const MatchExpression* other) const; - void copyTo(InMatchExpression* toFillIn) const; + Status addEquality(const BSONElement& elt); + + Status addRegex(std::unique_ptr<RegexMatchExpression> expr); - const ArrayFilterEntries& getData() const { - return _arrayEntries; + const BSONElementSet& getEqualities() const { + return _equalities; + } + + const std::vector<std::unique_ptr<RegexMatchExpression>>& getRegexes() const { + return _regexes; } const CollatorInterface* getCollator() const { - return _arrayEntries.getCollator(); + return _collator; + } + + bool hasNull() const { + return _hasNull; + } + + bool hasEmptyArray() const { + return _hasEmptyArray; } private: - bool _matchesRealElement(const BSONElement& e) const; - ArrayFilterEntries _arrayEntries; + bool _hasNull = false; // Whether or not _equalities has a jstNULL element in it. + bool _hasEmptyArray = false; // Whether or not _equalities has an empty array element in it. + BSONElementSet _equalities; + std::vector<std::unique_ptr<RegexMatchExpression>> _regexes; + const CollatorInterface* _collator = nullptr; }; // diff --git a/src/mongo/db/matcher/expression_leaf_test.cpp b/src/mongo/db/matcher/expression_leaf_test.cpp index 20b7fd5b098..a1a1fe6cb7d 100644 --- a/src/mongo/db/matcher/expression_leaf_test.cpp +++ b/src/mongo/db/matcher/expression_leaf_test.cpp @@ -1553,7 +1553,7 @@ TEST(InMatchExpression, MatchesElementSingle) { BSONObj notMatch = BSON("a" << 2); const CollatorInterface* collator = nullptr; InMatchExpression in(collator); - in.getArrayFilterEntries()->addEquality(operand.firstElement()); + in.addEquality(operand.firstElement()); ASSERT(in.matchesSingleElement(match["a"])); ASSERT(!in.matchesSingleElement(notMatch["a"])); } @@ -1573,10 +1573,10 @@ TEST(InMatchExpression, MatchesElementMultiple) { BSONObj operand = BSON_ARRAY(1 << "r" << true << 1); const CollatorInterface* collator = nullptr; InMatchExpression in(collator); - in.getArrayFilterEntries()->addEquality(operand[0]); - in.getArrayFilterEntries()->addEquality(operand[1]); - in.getArrayFilterEntries()->addEquality(operand[2]); - in.getArrayFilterEntries()->addEquality(operand[3]); + in.addEquality(operand[0]); + in.addEquality(operand[1]); + in.addEquality(operand[2]); + in.addEquality(operand[3]); BSONObj matchFirst = BSON("a" << 1); BSONObj matchSecond = BSON("a" @@ -1595,7 +1595,7 @@ TEST(InMatchExpression, MatchesScalar) { const CollatorInterface* collator = nullptr; InMatchExpression in(collator); in.init("a"); - in.getArrayFilterEntries()->addEquality(operand.firstElement()); + in.addEquality(operand.firstElement()); ASSERT(in.matchesBSON(BSON("a" << 5.0), NULL)); ASSERT(!in.matchesBSON(BSON("a" << 4), NULL)); @@ -1606,7 +1606,7 @@ TEST(InMatchExpression, MatchesArrayValue) { const CollatorInterface* collator = nullptr; InMatchExpression in(collator); in.init("a"); - in.getArrayFilterEntries()->addEquality(operand.firstElement()); + in.addEquality(operand.firstElement()); ASSERT(in.matchesBSON(BSON("a" << BSON_ARRAY(5.0 << 6)), NULL)); ASSERT(!in.matchesBSON(BSON("a" << BSON_ARRAY(6 << 7)), NULL)); @@ -1619,7 +1619,7 @@ TEST(InMatchExpression, MatchesNull) { const CollatorInterface* collator = nullptr; InMatchExpression in(collator); in.init("a"); - in.getArrayFilterEntries()->addEquality(operand.firstElement()); + in.addEquality(operand.firstElement()); ASSERT(in.matchesBSON(BSONObj(), NULL)); ASSERT(in.matchesBSON(BSON("a" << BSONNULL), NULL)); @@ -1634,7 +1634,7 @@ TEST(InMatchExpression, MatchesUndefined) { const CollatorInterface* collator = nullptr; InMatchExpression in(collator); in.init("a"); - Status s = in.getArrayFilterEntries()->addEquality(operand.firstElement()); + Status s = in.addEquality(operand.firstElement()); ASSERT_NOT_OK(s); } @@ -1643,7 +1643,7 @@ TEST(InMatchExpression, MatchesMinKey) { const CollatorInterface* collator = nullptr; InMatchExpression in(collator); in.init("a"); - in.getArrayFilterEntries()->addEquality(operand.firstElement()); + in.addEquality(operand.firstElement()); ASSERT(in.matchesBSON(BSON("a" << MinKey), NULL)); ASSERT(!in.matchesBSON(BSON("a" << MaxKey), NULL)); @@ -1655,7 +1655,7 @@ TEST(InMatchExpression, MatchesMaxKey) { const CollatorInterface* collator = nullptr; InMatchExpression in(collator); in.init("a"); - in.getArrayFilterEntries()->addEquality(operand.firstElement()); + in.addEquality(operand.firstElement()); ASSERT(in.matchesBSON(BSON("a" << MaxKey), NULL)); ASSERT(!in.matchesBSON(BSON("a" << MinKey), NULL)); @@ -1667,9 +1667,9 @@ TEST(InMatchExpression, MatchesFullArray) { const CollatorInterface* collator = nullptr; InMatchExpression in(collator); in.init("a"); - in.getArrayFilterEntries()->addEquality(operand[0]); - in.getArrayFilterEntries()->addEquality(operand[1]); - in.getArrayFilterEntries()->addEquality(operand[2]); + in.addEquality(operand[0]); + in.addEquality(operand[1]); + in.addEquality(operand[2]); ASSERT(in.matchesBSON(BSON("a" << BSON_ARRAY(1 << 2)), NULL)); ASSERT(!in.matchesBSON(BSON("a" << BSON_ARRAY(1 << 2 << 3)), NULL)); @@ -1682,8 +1682,8 @@ TEST(InMatchExpression, ElemMatchKey) { const CollatorInterface* collator = nullptr; InMatchExpression in(collator); in.init("a"); - in.getArrayFilterEntries()->addEquality(operand[0]); - in.getArrayFilterEntries()->addEquality(operand[1]); + in.addEquality(operand[0]); + in.addEquality(operand[1]); MatchDetails details; details.requestElemMatchKey(); @@ -1718,7 +1718,7 @@ TEST(InMatchExpression, StringMatchingWithNullCollatorUsesBinaryComparison) { << "string2"); const CollatorInterface* collator = nullptr; InMatchExpression in(collator); - in.getArrayFilterEntries()->addEquality(operand.firstElement()); + in.addEquality(operand.firstElement()); ASSERT(!in.matchesSingleElement(notMatch["a"])); } @@ -1728,85 +1728,10 @@ TEST(InMatchExpression, StringMatchingRespectsCollation) { << "string2"); CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kAlwaysEqual); InMatchExpression in(&collator); - in.getArrayFilterEntries()->addEquality(operand.firstElement()); + in.addEquality(operand.firstElement()); ASSERT(in.matchesSingleElement(match["a"])); } -/** -TEST( InMatchExpression, MatchesIndexKeyScalar ) { - BSONObj operand = BSON( "$in" << BSON_ARRAY( 6 << 5 ) ); - InMatchExpression in; - ASSERT( in.init( "a", operand[ "$in" ] ).isOK() ); - IndexSpec indexSpec( BSON( "a" << 1 ) ); - ASSERT( MatchMatchExpression::PartialMatchResult_True == - in.matchesIndexKey( BSON( "" << 6 ), indexSpec ) ); - ASSERT( MatchMatchExpression::PartialMatchResult_True == - in.matchesIndexKey( BSON( "" << 5 ), indexSpec ) ); - ASSERT( MatchMatchExpression::PartialMatchResult_False == - in.matchesIndexKey( BSON( "" << 4 ), indexSpec ) ); - ASSERT( MatchMatchExpression::PartialMatchResult_False == - in.matchesIndexKey( BSON( "" << BSON_ARRAY( 6 ) ), indexSpec ) ); -} - -TEST( InMatchExpression, MatchesIndexKeyMissing ) { - BSONObj operand = BSON( "$in" << BSON_ARRAY( 6 ) ); - ComparisonMatchExpression eq - ASSERT( eq.init( "a", operand[ "$in" ] ).isOK() ); - IndexSpec indexSpec( BSON( "b" << 1 ) ); - ASSERT( MatchMatchExpression::PartialMatchResult_Unknown == - eq.matchesIndexKey( BSON( "" << 6 ), indexSpec ) ); - ASSERT( MatchMatchExpression::PartialMatchResult_Unknown == - eq.matchesIndexKey( BSON( "" << 4 ), indexSpec ) ); - ASSERT( MatchMatchExpression::PartialMatchResult_Unknown == - eq.matchesIndexKey( BSON( "" << BSON_ARRAY( 8 << 6 ) ), indexSpec ) ); -} - -TEST( InMatchExpression, MatchesIndexKeyArray ) { - BSONObj operand = BSON( "$in" << BSON_ARRAY( 4 << BSON_ARRAY( 5 ) ) ); - InMatchExpression in; - ASSERT( in.init( "a", operand[ "$in" ] ).isOK() ); - IndexSpec indexSpec( BSON( "a" << 1 ) ); - ASSERT( MatchMatchExpression::PartialMatchResult_Unknown == - in.matchesIndexKey( BSON( "" << 4 ), indexSpec ) ); - ASSERT( MatchMatchExpression::PartialMatchResult_Unknown == - in.matchesIndexKey( BSON( "" << 5 ), indexSpec ) ); -} - -TEST( InMatchExpression, MatchesIndexKeyArrayValue ) { - BSONObjBuilder inArray; - inArray.append( "0", 4 ).append( "1", 5 ).appendRegex( "2", "abc", "" ); - BSONObj operand = BSONObjBuilder().appendArray( "$in", inArray.obj() ).obj(); - InMatchExpression in; - ASSERT( in.init( "a", operand[ "$in" ] ).isOK() ); - IndexSpec indexSpec( BSON( "loc" << "mockarrayvalue" << "a" << 1 ) ); - ASSERT( MatchMatchExpression::PartialMatchResult_True == - in.matchesIndexKey( BSON( "" << "dummygeohash" << "" << 4 ), indexSpec ) ); - ASSERT( MatchMatchExpression::PartialMatchResult_False == - in.matchesIndexKey( BSON( "" << "dummygeohash" << "" << 6 ), indexSpec ) ); - ASSERT( MatchMatchExpression::PartialMatchResult_True == - in.matchesIndexKey( BSON( "" << "dummygeohash" << "" << "abcd" ), indexSpec ) ); - ASSERT( MatchMatchExpression::PartialMatchResult_True == - in.matchesIndexKey( BSONObjBuilder() - .append( "", "dummygeohash" ) - .appendRegex( "", "abc", "" ).obj(), - indexSpec ) ); - ASSERT( MatchMatchExpression::PartialMatchResult_False == - in.matchesIndexKey( BSON( "" << "dummygeohash" << "" << "ab" ), indexSpec ) ); - ASSERT( MatchMatchExpression::PartialMatchResult_True == - in.matchesIndexKey( BSON( "" << "dummygeohash" << - "" << BSON_ARRAY( 8 << 5 ) ), indexSpec ) ); - ASSERT( MatchMatchExpression::PartialMatchResult_False == - in.matchesIndexKey( BSON( "" << "dummygeohash" << - "" << BSON_ARRAY( 8 << 9 ) ), indexSpec ) ); - ASSERT( MatchMatchExpression::PartialMatchResult_True == - in.matchesIndexKey( BSON( "" << "dummygeohash" << - "" << BSON_ARRAY( 8 << "abc" ) ), indexSpec ) ); - ASSERT( MatchMatchExpression::PartialMatchResult_False == - in.matchesIndexKey( BSON( "" << "dummygeohash" << - "" << BSON_ARRAY( 8 << "ac" ) ), indexSpec ) ); -} -*/ - std::vector<uint32_t> bsonArrayToBitPositions(const BSONArray& ba) { std::vector<uint32_t> bitPositions; diff --git a/src/mongo/db/matcher/expression_parser.cpp b/src/mongo/db/matcher/expression_parser.cpp index 0c9e0187936..e0b3b86fd62 100644 --- a/src/mongo/db/matcher/expression_parser.cpp +++ b/src/mongo/db/matcher/expression_parser.cpp @@ -146,7 +146,7 @@ StatusWithMatchExpression MatchExpressionParser::_parseSubField(const BSONObj& c Status s = temp->init(name); if (!s.isOK()) return s; - s = _parseArrayFilterEntries(temp->getArrayFilterEntries(), e.Obj()); + s = _parseInExpression(temp.get(), e.Obj()); if (!s.isOK()) return s; return {std::move(temp)}; @@ -160,7 +160,7 @@ StatusWithMatchExpression MatchExpressionParser::_parseSubField(const BSONObj& c Status s = temp->init(name); if (!s.isOK()) return s; - s = _parseArrayFilterEntries(temp->getArrayFilterEntries(), e.Obj()); + s = _parseInExpression(temp.get(), e.Obj()); if (!s.isOK()) return s; @@ -594,13 +594,13 @@ StatusWithMatchExpression MatchExpressionParser::_parseRegexDocument(const char* return {std::move(temp)}; } -Status MatchExpressionParser::_parseArrayFilterEntries(ArrayFilterEntries* entries, - const BSONObj& theArray) { +Status MatchExpressionParser::_parseInExpression(InMatchExpression* inExpression, + const BSONObj& theArray) { BSONObjIterator i(theArray); while (i.more()) { BSONElement e = i.next(); - // allow DBRefs but reject all fields with names starting wiht $ + // Allow DBRefs, but reject all fields with names starting with $. if (_isExpressionDocument(e, false)) { return Status(ErrorCodes::BadValue, "cannot nest $ under $in"); } @@ -610,11 +610,11 @@ Status MatchExpressionParser::_parseArrayFilterEntries(ArrayFilterEntries* entri Status s = r->init("", e); if (!s.isOK()) return s; - s = entries->addRegex(r.release()); + s = inExpression->addRegex(std::move(r)); if (!s.isOK()) return s; } else { - Status s = entries->addEquality(e); + Status s = inExpression->addEquality(e); if (!s.isOK()) return s; } diff --git a/src/mongo/db/matcher/expression_parser.h b/src/mongo/db/matcher/expression_parser.h index 61a712b7d92..823412ecc73 100644 --- a/src/mongo/db/matcher/expression_parser.h +++ b/src/mongo/db/matcher/expression_parser.h @@ -120,7 +120,7 @@ private: StatusWithMatchExpression _parseRegexDocument(const char* name, const BSONObj& doc); - Status _parseArrayFilterEntries(ArrayFilterEntries* entries, const BSONObj& theArray); + Status _parseInExpression(InMatchExpression* entries, const BSONObj& theArray); StatusWithMatchExpression _parseType(const char* name, const BSONElement& elt); diff --git a/src/mongo/db/query/canonical_query.cpp b/src/mongo/db/query/canonical_query.cpp index f62e5309744..360e796708f 100644 --- a/src/mongo/db/query/canonical_query.cpp +++ b/src/mongo/db/query/canonical_query.cpp @@ -316,14 +316,9 @@ MatchExpression* CanonicalQuery::normalizeTree(MatchExpression* root) { } else if (MatchExpression::MATCH_IN == root->matchType()) { std::unique_ptr<InMatchExpression> in(static_cast<InMatchExpression*>(root)); - ArrayFilterEntries* inArrayEntries = in->getArrayFilterEntries(); - if (inArrayEntries->size() != 1) { - return in.release(); - } - - // We contain either a single $regex, or we are equivalent to a $eq. - if (inArrayEntries->numRegexes()) { - RegexMatchExpression* childRe = inArrayEntries->regex(0); + // IN of 1 regex is the regex. + if (in->getRegexes().size() == 1 && in->getEqualities().empty()) { + RegexMatchExpression* childRe = in->getRegexes().begin()->get(); invariant(!childRe->getTag()); // Create a new RegexMatchExpression, because 'childRe' does not have a path. @@ -335,12 +330,17 @@ MatchExpression* CanonicalQuery::normalizeTree(MatchExpression* root) { return normalizeTree(re.release()); } - auto eq = stdx::make_unique<EqualityMatchExpression>(in->getCollator()); - eq->init(in->path(), *(inArrayEntries->equalities().begin())); - if (in->getTag()) { - eq->setTag(in->getTag()->clone()); + // IN of 1 equality is the equality. + if (in->getEqualities().size() == 1 && in->getRegexes().empty()) { + auto eq = stdx::make_unique<EqualityMatchExpression>(in->getCollator()); + eq->init(in->path(), *(in->getEqualities().begin())); + if (in->getTag()) { + eq->setTag(in->getTag()->clone()); + } + return eq.release(); } - return eq.release(); + + return in.release(); } return root; diff --git a/src/mongo/db/query/canonical_query_test.cpp b/src/mongo/db/query/canonical_query_test.cpp index d32521b5a58..3dc4386f412 100644 --- a/src/mongo/db/query/canonical_query_test.cpp +++ b/src/mongo/db/query/canonical_query_test.cpp @@ -545,7 +545,7 @@ TEST(CanonicalQueryTest, NormalizeWithInPreservesCollator) { CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString); auto inMatchExpression = stdx::make_unique<InMatchExpression>(&collator); BSONObj obj = fromjson("{'': 'string'}"); - inMatchExpression->getArrayFilterEntries()->addEquality(obj.firstElement()); + inMatchExpression->addEquality(obj.firstElement()); unique_ptr<MatchExpression> matchExpression( CanonicalQuery::normalizeTree(inMatchExpression.release())); ASSERT(matchExpression->matchType() == MatchExpression::MatchType::EQ); diff --git a/src/mongo/db/query/index_bounds_builder.cpp b/src/mongo/db/query/index_bounds_builder.cpp index 40ea382c8f8..86d8a005229 100644 --- a/src/mongo/db/query/index_bounds_builder.cpp +++ b/src/mongo/db/query/index_bounds_builder.cpp @@ -551,36 +551,34 @@ void IndexBoundsBuilder::translate(const MatchExpression* expr, : IndexBoundsBuilder::INEXACT_FETCH; } else if (MatchExpression::MATCH_IN == expr->matchType()) { const InMatchExpression* ime = static_cast<const InMatchExpression*>(expr); - const ArrayFilterEntries& afr = ime->getData(); *tightnessOut = IndexBoundsBuilder::EXACT; // Create our various intervals. IndexBoundsBuilder::BoundsTightness tightness; - for (BSONElementSet::iterator it = afr.equalities().begin(); it != afr.equalities().end(); - ++it) { - translateEquality(*it, index, isHashed, oilOut, &tightness); + for (auto&& equality : ime->getEqualities()) { + translateEquality(equality, index, isHashed, oilOut, &tightness); if (tightness != IndexBoundsBuilder::EXACT) { *tightnessOut = tightness; } } - for (size_t i = 0; i < afr.numRegexes(); ++i) { - translateRegex(afr.regex(i), index, oilOut, &tightness); + for (auto&& regex : ime->getRegexes()) { + translateRegex(regex.get(), index, oilOut, &tightness); if (tightness != IndexBoundsBuilder::EXACT) { *tightnessOut = tightness; } } - if (afr.hasNull()) { + if (ime->hasNull()) { // A null index key does not always match a null query value so we must fetch the // doc and run a full comparison. See SERVER-4529. // TODO: Do we already set the tightnessOut by calling translateEquality? *tightnessOut = INEXACT_FETCH; } - if (afr.hasEmptyArray()) { + if (ime->hasEmptyArray()) { // Empty arrays are indexed as undefined. BSONObjBuilder undefinedBob; undefinedBob.appendUndefined(""); diff --git a/src/mongo/db/query/plan_cache_indexability.cpp b/src/mongo/db/query/plan_cache_indexability.cpp index a6e0fa32e3f..4e61e9ba595 100644 --- a/src/mongo/db/query/plan_cache_indexability.cpp +++ b/src/mongo/db/query/plan_cache_indexability.cpp @@ -51,7 +51,7 @@ void PlanCacheIndexabilityState::processSparseIndex(const BSONObj& keyPattern) { return !queryExprEquality->getData().isNull(); } else if (queryExpr->matchType() == MatchExpression::MATCH_IN) { const auto* queryExprIn = static_cast<const InMatchExpression*>(queryExpr); - return !queryExprIn->getData().hasNull(); + return !queryExprIn->hasNull(); } else { return true; } diff --git a/src/mongo/db/query/planner_ixselect.cpp b/src/mongo/db/query/planner_ixselect.cpp index d3323b63bec..8af46a211af 100644 --- a/src/mongo/db/query/planner_ixselect.cpp +++ b/src/mongo/db/query/planner_ixselect.cpp @@ -90,7 +90,7 @@ static bool boundsGeneratingNodeContainsComparisonToType(MatchExpression* node, if (node->matchType() == MatchExpression::MATCH_IN) { const InMatchExpression* expr = static_cast<const InMatchExpression*>(node); - for (auto const& equality : expr->getData().equalities()) { + for (const auto& equality : expr->getEqualities()) { if (equality.type() == type) { return true; } @@ -212,7 +212,7 @@ bool QueryPlannerIXSelect::compatible(const BSONElement& elt, // Can't check for $in w/ null element w/a sparse index. if (exprtype == MatchExpression::MATCH_IN && index.sparse) { const InMatchExpression* expr = static_cast<const InMatchExpression*>(node); - if (expr->getData().hasNull()) { + if (expr->hasNull()) { return false; } } @@ -247,7 +247,7 @@ bool QueryPlannerIXSelect::compatible(const BSONElement& elt, // If it's a negated $in, it can't have any REGEX's inside. if (MatchExpression::MATCH_IN == childtype) { InMatchExpression* ime = static_cast<InMatchExpression*>(node->getChild(0)); - if (ime->getData().numRegexes() != 0) { + if (!ime->getRegexes().empty()) { return false; } } @@ -300,7 +300,7 @@ bool QueryPlannerIXSelect::compatible(const BSONElement& elt, } if (exprtype == MatchExpression::MATCH_IN) { const InMatchExpression* expr = static_cast<const InMatchExpression*>(node); - return expr->getData().numRegexes() == 0; + return expr->getRegexes().empty(); } return false; } else if (IndexNames::GEO_2DSPHERE == indexedFieldType) { |