summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJ. Rassi <rassi@10gen.com>2016-05-25 14:50:50 -0400
committerJ. Rassi <rassi@10gen.com>2016-05-27 14:02:55 -0400
commitf93ad728e94b6813c242faac43d4b57a07a3c319 (patch)
treeb24ef5d0a74ba1c166c314d49d2054a5b0b37d45
parent1898c4eac3124a6d32de1d2c13d7f6d05b1b538b (diff)
downloadmongo-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.cpp5
-rw-r--r--src/mongo/db/matcher/expression_algo.cpp9
-rw-r--r--src/mongo/db/matcher/expression_leaf.cpp201
-rw-r--r--src/mongo/db/matcher/expression_leaf.h100
-rw-r--r--src/mongo/db/matcher/expression_leaf_test.cpp111
-rw-r--r--src/mongo/db/matcher/expression_parser.cpp14
-rw-r--r--src/mongo/db/matcher/expression_parser.h2
-rw-r--r--src/mongo/db/query/canonical_query.cpp26
-rw-r--r--src/mongo/db/query/canonical_query_test.cpp2
-rw-r--r--src/mongo/db/query/index_bounds_builder.cpp14
-rw-r--r--src/mongo/db/query/plan_cache_indexability.cpp2
-rw-r--r--src/mongo/db/query/planner_ixselect.cpp8
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(&regexBob);
- 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(&regexBob);
+ 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) {