diff options
author | Siyuan Zhou <siyuan.zhou@mongodb.com> | 2014-05-01 16:19:01 -0400 |
---|---|---|
committer | Siyuan Zhou <siyuan.zhou@mongodb.com> | 2014-05-05 18:15:47 -0400 |
commit | 9ba412717035fa34502c89e5886fa3a3ae3e05d5 (patch) | |
tree | 865a784a4cc7f6e683776a609a21c720e03f8402 | |
parent | 9aeb3d4c5a70f8fb3616c5aea22c15db86b0d6e1 (diff) | |
download | mongo-9ba412717035fa34502c89e5886fa3a3ae3e05d5.tar.gz |
SERVER-13807 Remove old query framework related to shard targeting
-rw-r--r-- | src/mongo/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/db/commands/find_and_modify.cpp | 3 | ||||
-rw-r--r-- | src/mongo/db/keypattern.cpp | 125 | ||||
-rw-r--r-- | src/mongo/db/keypattern.h | 40 | ||||
-rw-r--r-- | src/mongo/db/ops/update.cpp | 1 | ||||
-rw-r--r-- | src/mongo/db/queryutil-inl.h | 125 | ||||
-rw-r--r-- | src/mongo/db/queryutil.cpp | 1894 | ||||
-rw-r--r-- | src/mongo/db/queryutil.h | 729 | ||||
-rw-r--r-- | src/mongo/dbtests/namespacetests.cpp | 1 | ||||
-rw-r--r-- | src/mongo/dbtests/queryutiltests.cpp | 2963 | ||||
-rw-r--r-- | src/mongo/dbtests/repltests.cpp | 1 | ||||
-rw-r--r-- | src/mongo/s/chunk.cpp | 1 | ||||
-rw-r--r-- | src/mongo/s/commands_public.cpp | 25 | ||||
-rw-r--r-- | src/mongo/s/shardkey.h | 12 |
14 files changed, 29 insertions, 5892 deletions
diff --git a/src/mongo/SConscript b/src/mongo/SConscript index ae76cff908d..e4d27a05125 100644 --- a/src/mongo/SConscript +++ b/src/mongo/SConscript @@ -456,7 +456,6 @@ env.Library("coredb", [ "db/pipeline/field_path.cpp", "db/pipeline/value.cpp", "db/projection.cpp", - "db/queryutil.cpp", "db/stats/timer_stats.cpp", "s/shardconnection.cpp", ] diff --git a/src/mongo/db/commands/find_and_modify.cpp b/src/mongo/db/commands/find_and_modify.cpp index e6b92512064..9d9e8d189a5 100644 --- a/src/mongo/db/commands/find_and_modify.cpp +++ b/src/mongo/db/commands/find_and_modify.cpp @@ -40,7 +40,6 @@ #include "mongo/db/ops/delete.h" #include "mongo/db/ops/update.h" #include "mongo/db/ops/update_lifecycle_impl.h" -#include "mongo/db/queryutil.h" #include "mongo/db/query/get_runner.h" #include "mongo/db/storage/mmap_v1/dur_transaction.h" @@ -155,7 +154,7 @@ namespace mongo { } BSONObj queryModified = queryOriginal; - if ( found && doc["_id"].type() && ! isSimpleIdQuery( queryOriginal ) ) { + if ( found && doc["_id"].type() && ! CanonicalQuery::isSimpleIdQuery( queryOriginal ) ) { // we're going to re-write the query to be more efficient // we have to be a little careful because of positional operators // maybe we can pass this all through eventually, but right now isn't an easy way diff --git a/src/mongo/db/keypattern.cpp b/src/mongo/db/keypattern.cpp index ecd44b448c4..222253e4c4d 100644 --- a/src/mongo/db/keypattern.cpp +++ b/src/mongo/db/keypattern.cpp @@ -31,7 +31,6 @@ #include "mongo/db/keypattern.h" #include "mongo/db/hasher.h" -#include "mongo/db/queryutil.h" #include "mongo/util/mongoutils/str.h" using namespace mongoutils; @@ -136,87 +135,6 @@ namespace mongo { return newBound.obj(); } - typedef vector<pair<BSONObj,BSONObj> >::const_iterator BoundListIter; - - BoundList KeyPattern::keyBounds( const FieldRangeSet& queryConstraints ) const { - // To construct our bounds we will generate intervals based on constraints for - // the first field, then compound intervals based on constraints for the first - // 2 fields, then compound intervals for the first 3 fields, etc. - // As we loop through the fields, we start generating new intervals that will later - // get extended in another iteration of the loop. We define these partially constructed - // intervals using pairs of BSONObjBuilders (shared_ptrs, since after one iteration of the - // loop they still must exist outside their scope). - typedef vector< pair< shared_ptr<BSONObjBuilder> , - shared_ptr<BSONObjBuilder> > > BoundBuilders; - BoundBuilders builders; - builders.push_back( make_pair( shared_ptr<BSONObjBuilder>( new BSONObjBuilder() ), - shared_ptr<BSONObjBuilder>( new BSONObjBuilder() ) ) ); - BSONObjIterator i( _pattern ); - // until equalityOnly is false, we are just dealing with equality (no range or $in queries). - bool equalityOnly = true; - while( i.more() ) { - BSONElement e = i.next(); - - // get the relevant intervals for this field, but we may have to transform the - // list of what's relevant according to the expression for this field - const FieldRange &fr = queryConstraints.range( e.fieldName() ); - const vector<FieldInterval> &oldIntervals = fr.intervals(); - BoundList fieldBounds = _transformFieldBounds( oldIntervals , e ); - - if ( equalityOnly ) { - if ( fieldBounds.size() == 1 && - ( fieldBounds.front().first == fieldBounds.front().second ) ){ - // this field is only a single point-interval - BoundBuilders::const_iterator j; - for( j = builders.begin(); j != builders.end(); ++j ) { - j->first->appendElements( fieldBounds.front().first ); - j->second->appendElements( fieldBounds.front().first ); - } - } - else { - // This clause is the first to generate more than a single point. - // We only execute this clause once. After that, we simplify the bound - // extensions to prevent combinatorial explosion. - equalityOnly = false; - - BoundBuilders newBuilders; - BoundBuilders::const_iterator i; - for( i = builders.begin(); i != builders.end(); ++i ) { - BSONObj first = i->first->obj(); - BSONObj second = i->second->obj(); - - for(BoundListIter j = fieldBounds.begin(); j != fieldBounds.end(); ++j ) { - uassert( 16452, - "combinatorial limit of $in partitioning of results exceeded" , - newBuilders.size() < MAX_IN_COMBINATIONS ); - newBuilders.push_back( - make_pair( shared_ptr<BSONObjBuilder>( new BSONObjBuilder() ), - shared_ptr<BSONObjBuilder>( new BSONObjBuilder()))); - newBuilders.back().first->appendElements( first ); - newBuilders.back().second->appendElements( second ); - newBuilders.back().first->appendElements( j->first ); - newBuilders.back().second->appendElements( j->second ); - } - } - builders = newBuilders; - } - } - else { - // if we've already generated a range or multiple point-intervals - // just extend what we've generated with min/max bounds for this field - BoundBuilders::const_iterator j; - for( j = builders.begin(); j != builders.end(); ++j ) { - j->first->appendElements( fieldBounds.front().first ); - j->second->appendElements( fieldBounds.back().second ); - } - } - } - BoundList ret; - for( BoundBuilders::const_iterator i = builders.begin(); i != builders.end(); ++i ) - ret.push_back( make_pair( i->first->obj(), i->second->obj() ) ); - return ret; - } - BoundList KeyPattern::keyBounds( const BSONObj& keyPattern, const IndexBounds& indexBounds ) { invariant(indexBounds.fields.size() == (size_t)keyPattern.nFields()); @@ -308,47 +226,4 @@ namespace mongo { return ret; } - BoundList KeyPattern::_transformFieldBounds( const vector<FieldInterval>& oldIntervals , - const BSONElement& field ) const { - - BoundList ret; - vector<FieldInterval>::const_iterator i; - for( i = oldIntervals.begin(); i != oldIntervals.end(); ++i ) { - if ( isAscending( field ) ){ - // straightforward map [a,b] --> [a,b] - ret.push_back( make_pair( BSON( field.fieldName() << i->_lower._bound ) , - BSON( field.fieldName() << i->_upper._bound ) ) ); - } else if ( isDescending( field ) ) { - // reverse [a,b] --> [b,a] - ret.push_back( make_pair( BSON( field.fieldName() << i->_upper._bound ) , - BSON( field.fieldName() << i->_lower._bound ) ) ); - } else if ( isHashed( field ) ){ - if ( i->equality() ) { - // hash [a,a] --> [hash(a),hash(a)] - long long int h = BSONElementHasher::hash64( i->_lower._bound , - BSONElementHasher::DEFAULT_HASH_SEED ); - ret.push_back( make_pair( BSON( field.fieldName() << h ) , - BSON( field.fieldName() << h ) ) ); - } else { - // if it's a range interval and this field is hashed, just generate one - // big interval from MinKey to MaxKey, since these vals could lie anywhere - ret.clear(); - ret.push_back( make_pair( BSON( field.fieldName() << MINKEY ) , - BSON( field.fieldName() << MAXKEY ) ) ); - break; - } - } - } - - if ( isDescending( field ) ) { - // now order is [ [2,1], [4,3] , [6,5]....[n,n-1] ]. Reverse to get decreasing order. - reverse( ret.begin() , ret.end() ); - } else if ( isHashed( field ) ){ - // [ hash(a) , hash(b) , hash(c) ...] no longer in order, so sort before returning - sort( ret.begin() , ret.end() ); - } - - return ret; - } - } // namespace mongo diff --git a/src/mongo/db/keypattern.h b/src/mongo/db/keypattern.h index 3e99a1c9d85..3e0eebfb613 100644 --- a/src/mongo/db/keypattern.h +++ b/src/mongo/db/keypattern.h @@ -38,9 +38,6 @@ namespace mongo { - struct FieldInterval; - class FieldRangeSet; - /** * A BoundList contains intervals specified by inclusive start * and end bounds. The intervals should be nonoverlapping and occur in @@ -61,6 +58,10 @@ namespace mongo { */ class KeyPattern { public: + + //maximum number of intervals produced by $in queries. + static const unsigned MAX_IN_COMBINATIONS = 4000000; + /* * We are allowing implicit conversion from BSON */ @@ -148,32 +149,6 @@ namespace mongo { */ BSONObj extractSingleKey( const BSONObj& doc ) const; - /**@param queryConstraints a FieldRangeSet, usually formed from parsing a query - * @return an ordered list of bounds generated using this KeyPattern and the - * constraints from the FieldRangeSet. This function is used in sharding to - * determine where to route queries according to the shard key pattern. - * - * Examples: - * If this KeyPattern is { a : 1 } - * FieldRangeSet( {a : 5 } ) --> returns [{a : 5}, {a : 5 } ] - * FieldRangeSet( {a : {$gt : 3}} ) --> returns [({a : 3} , { a : MaxInt})] - * - * If this KeyPattern is { a : "hashed } - * FieldRangeSet( {a : 5 } --> returns [ ({ a : hash(5) }, {a : hash(5) }) ] - * - * The bounds returned by this function may be a superset of those defined - * by the constraints. For instance, if this KeyPattern is {a : 1} - * FieldRanget( { a : {$in : [1,2]} , b : {$in : [3,4,5]} } ) - * --> returns [({a : 1 , b : 3} , {a : 1 , b : 5}]), - * [({a : 2 , b : 3} , {a : 2 , b : 5}]) - * - * The queryConstraints should be defined for all the fields in this keypattern - * (i.e. the value of frsp->matchPossibleForSingleKeyFRS(_pattern) should be true, - * otherwise this function could throw). - * - */ - BoundList keyBounds( const FieldRangeSet& queryConstraints ) const; - /** * Return an ordered list of bounds generated using this KeyPattern and the * bounds from the IndexBounds. This function is used in sharding to @@ -228,13 +203,6 @@ namespace mongo { return ( fieldExpression.isNumber() && fieldExpression.numberInt() == -1 ); } - /* Takes a list of intervals corresponding to constraints on a given field - * in this keypattern, and transforms them into a list of bounds - * based on the expression for 'field' - */ - BoundList _transformFieldBounds( const vector<FieldInterval>& oldIntervals , - const BSONElement& field ) const; - }; } // namespace mongo diff --git a/src/mongo/db/ops/update.cpp b/src/mongo/db/ops/update.cpp index 33e082f8d32..8b0d7a506fe 100644 --- a/src/mongo/db/ops/update.cpp +++ b/src/mongo/db/ops/update.cpp @@ -48,7 +48,6 @@ #include "mongo/db/query/lite_parsed_query.h" #include "mongo/db/query/query_planner_common.h" #include "mongo/db/query/runner_yield_policy.h" -#include "mongo/db/queryutil.h" #include "mongo/db/repl/is_master.h" #include "mongo/db/repl/oplog.h" #include "mongo/db/storage/mmap_v1/dur_transaction.h" diff --git a/src/mongo/db/queryutil-inl.h b/src/mongo/db/queryutil-inl.h deleted file mode 100644 index 6d1b7a29d77..00000000000 --- a/src/mongo/db/queryutil-inl.h +++ /dev/null @@ -1,125 +0,0 @@ -// @file queryutil-inl.h - Inline definitions for frequently called queryutil.h functions - -/* Copyright 2011 10gen Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -namespace mongo { - - inline bool FieldInterval::equality() const { - if ( _cachedEquality == -1 ) { - _cachedEquality = ( _lower._inclusive && _upper._inclusive && _lower._bound.woCompare( _upper._bound, false ) == 0 ); - } - return _cachedEquality != 0; - } - - inline bool FieldRange::equality() const { - return - !empty() && - min().woCompare( max(), false ) == 0 && - maxInclusive() && - minInclusive(); - } - - inline const FieldRange &FieldRangeSet::range( const char *fieldName ) const { - map<string,FieldRange>::const_iterator f = _ranges.find( fieldName ); - if ( f == _ranges.end() ) - return universalRange(); - return f->second; - } - - inline FieldRange &FieldRangeSet::range( const char *fieldName ) { - map<string,FieldRange>::iterator f = _ranges.find( fieldName ); - if ( f == _ranges.end() ) { - _ranges.insert( make_pair( string( fieldName ), universalRange() ) ); - return _ranges.find( fieldName )->second; - } - return f->second; - } - - inline bool FieldRangeSet::matchPossible() const { - for( map<string,FieldRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i ) { - if ( i->second.empty() ) { - return false; - } - } - return true; - } - - inline bool FieldRangeSet::matchPossibleForIndex( const BSONObj &keyPattern ) const { - if ( !_singleKey ) { - return matchPossible(); - } - BSONObjIterator i( keyPattern ); - while( i.more() ) { - BSONElement e = i.next(); - if ( e.fieldName() == string( "$natural" ) ) { - return true; - } - if ( range( e.fieldName() ).empty() ) { - return false; - } - } - return true; - } - - inline unsigned FieldRangeVector::size() const { - unsigned ret = 1; - for( vector<FieldRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i ) { - unsigned long long product = - (unsigned long long)ret * (unsigned long long)i->intervals().size(); - // Check for overflow SERVER-5415. - verify( ( product >> 32 ) == 0 ); - ret = static_cast<unsigned>( product ); - } - return ret; - } - - inline void FieldRangeVectorIterator::CompoundRangeCounter::set( int i, int newVal ) { - resetIntervalCount(); - _i[ i ] = newVal; - } - - inline void FieldRangeVectorIterator::CompoundRangeCounter::inc( int i ) { - resetIntervalCount(); - ++_i[ i ]; - } - - inline void FieldRangeVectorIterator::CompoundRangeCounter::setZeroes( int i ) { - resetIntervalCount(); - for( int j = i; j < (int)_i.size(); ++j ) _i[ j ] = 0; - } - - inline void FieldRangeVectorIterator::CompoundRangeCounter::setUnknowns( int i ) { - resetIntervalCount(); - for( int j = i; j < (int)_i.size(); ++j ) _i[ j ] = -1; - } - - inline FieldRangeSetPair *OrRangeGenerator::topFrsp() const { - FieldRangeSetPair *ret = new FieldRangeSetPair( _baseSet ); - if (_orSets.size()) { - *ret &= _orSets.front(); - } - return ret; - } - - inline FieldRangeSetPair *OrRangeGenerator::topFrspOriginal() const { - FieldRangeSetPair *ret = new FieldRangeSetPair( _baseSet ); - if (_originalOrSets.size()) { - *ret &= _originalOrSets.front(); - } - return ret; - } - -} // namespace mongo diff --git a/src/mongo/db/queryutil.cpp b/src/mongo/db/queryutil.cpp deleted file mode 100644 index d93e3f3d88d..00000000000 --- a/src/mongo/db/queryutil.cpp +++ /dev/null @@ -1,1894 +0,0 @@ -/* Copyright 2009 10gen Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "mongo/pch.h" - -#include "mongo/db/queryutil.h" - -#include "mongo/db/index_names.h" -#include "mongo/db/matcher.h" -#include "mongo/util/mongoutils/str.h" - -namespace mongo { - - using namespace mongoutils; - using namespace mongoutils::str; - - extern BSONObj staticNull; - extern BSONObj staticUndefined; - - /** returns a string that when used as a matcher, would match a super set of regex() - returns "" for complex regular expressions - used to optimize queries in some simple regex cases that start with '^' - - if purePrefix != NULL, sets it to whether the regex can be converted to a range query - */ - string simpleRegex(const char* regex, const char* flags, bool* purePrefix) { - string r = ""; - - if (purePrefix) *purePrefix = false; - - bool multilineOK; - if ( regex[0] == '\\' && regex[1] == 'A') { - multilineOK = true; - regex += 2; - } - else if (regex[0] == '^') { - multilineOK = false; - regex += 1; - } - else { - return r; - } - - bool extended = false; - while (*flags) { - switch (*(flags++)) { - case 'm': // multiline - if (multilineOK) - continue; - else - return r; - case 'x': // extended - extended = true; - break; - default: - return r; // cant use index - } - } - - stringstream ss; - - while(*regex) { - char c = *(regex++); - if ( c == '*' || c == '?' ) { - // These are the only two symbols that make the last char optional - r = ss.str(); - r = r.substr( 0 , r.size() - 1 ); - return r; //breaking here fails with /^a?/ - } - else if (c == '|') { - // whole match so far is optional. Nothing we can do here. - return string(); - } - else if (c == '\\') { - c = *(regex++); - if (c == 'Q'){ - // \Q...\E quotes everything inside - while (*regex) { - c = (*regex++); - if (c == '\\' && (*regex == 'E')){ - regex++; //skip the 'E' - break; // go back to start of outer loop - } - else { - ss << c; // character should match itself - } - } - } - else if ((c >= 'A' && c <= 'Z') || - (c >= 'a' && c <= 'z') || - (c >= '0' && c <= '0') || - (c == '\0')) { - // don't know what to do with these - r = ss.str(); - break; - } - else { - // slash followed by non-alphanumeric represents the following char - ss << c; - } - } - else if (strchr("^$.[()+{", c)) { - // list of "metacharacters" from man pcrepattern - r = ss.str(); - break; - } - else if (extended && c == '#') { - // comment - r = ss.str(); - break; - } - else if (extended && isspace(c)) { - continue; - } - else { - // self-matching char - ss << c; - } - } - - if ( r.empty() && *regex == 0 ) { - r = ss.str(); - if (purePrefix) *purePrefix = !r.empty(); - } - - return r; - } - inline string simpleRegex(const BSONElement& e) { - switch(e.type()) { - case RegEx: - return simpleRegex(e.regex(), e.regexFlags()); - case Object: { - BSONObj o = e.embeddedObject(); - return simpleRegex(o["$regex"].valuestrsafe(), o["$options"].valuestrsafe()); - } - default: verify(false); return ""; //return squashes compiler warning - } - } - - string simpleRegexEnd( string regex ) { - ++regex[ regex.length() - 1 ]; - return regex; - } - - - FieldRange::FieldRange( const BSONElement &e, bool isNot, bool optimize ) : - _exactMatchRepresentation() { - int op = e.getGtLtOp(); - - // NOTE with $not, we could potentially form a complementary set of intervals. - if ( !isNot && !e.eoo() && e.type() != RegEx && op == BSONObj::opIN ) { - bool exactMatchesOnly = true; - set<BSONElement,element_lt> vals; - vector<FieldRange> regexes; - uassert( 12580 , "invalid query" , e.isABSONObj() ); - BSONObjIterator i( e.embeddedObject() ); - while( i.more() ) { - BSONElement ie = i.next(); - uassert( 15881, "$elemMatch not allowed within $in", - ie.type() != Object || - ie.embeddedObject().firstElement().getGtLtOp() != BSONObj::opELEM_MATCH ); - if ( ie.type() == RegEx ) { - exactMatchesOnly = false; - regexes.push_back( FieldRange( ie, false, optimize ) ); - } - else { - // A document array may be indexed by its first element, by undefined - // if it is empty, or as a full array if it is embedded within another - // array. - vals.insert( ie ); - if ( ie.type() == Array ) { - exactMatchesOnly = false; - BSONElement temp = ie.embeddedObject().firstElement(); - if ( temp.eoo() ) { - temp = staticUndefined.firstElement(); - } - vals.insert( temp ); - } - if ( ie.isNull() ) { - // A null index key will not always match a null query value (eg - // SERVER-4529). As a result, a field range containing null cannot be an - // exact match representation. - exactMatchesOnly = false; - } - } - } - - _exactMatchRepresentation = exactMatchesOnly; - for( set<BSONElement,element_lt>::const_iterator i = vals.begin(); i != vals.end(); ++i ) - _intervals.push_back( FieldInterval(*i) ); - - for( vector<FieldRange>::const_iterator i = regexes.begin(); i != regexes.end(); ++i ) - *this |= *i; - - return; - } - - // A document array may be indexed by its first element, by undefined - // if it is empty, or as a full array if it is embedded within another - // array. - if ( e.type() == Array && op == BSONObj::Equality ) { - - _intervals.push_back( FieldInterval(e) ); - BSONElement temp = e.embeddedObject().firstElement(); - if ( temp.eoo() ) { - temp = staticUndefined.firstElement(); - } - if ( temp < e ) { - _intervals.insert( _intervals.begin() , temp ); - } - else { - _intervals.push_back( FieldInterval(temp) ); - } - - return; - } - - _intervals.push_back( FieldInterval() ); - FieldInterval &initial = _intervals[ 0 ]; - BSONElement &lower = initial._lower._bound; - bool &lowerInclusive = initial._lower._inclusive; - BSONElement &upper = initial._upper._bound; - bool &upperInclusive = initial._upper._inclusive; - lower = minKey.firstElement(); - lowerInclusive = true; - upper = maxKey.firstElement(); - upperInclusive = true; - - if ( e.eoo() ) { - _exactMatchRepresentation = true; - return; - } - - bool existsSpec = false; - if ( op == BSONObj::opEXISTS ) { - existsSpec = e.trueValue(); - } - - if ( e.type() == RegEx - || (e.type() == Object && !e.embeddedObject()["$regex"].eoo()) - ) { - uassert( 13454, "invalid regular expression operator", op == BSONObj::Equality || op == BSONObj::opREGEX ); - if ( !isNot ) { // no optimization for negated regex - we could consider creating 2 intervals comprising all nonmatching prefixes - const string r = simpleRegex(e); - if ( r.size() ) { - lower = addObj( BSON( "" << r ) ).firstElement(); - upper = addObj( BSON( "" << simpleRegexEnd( r ) ) ).firstElement(); - upperInclusive = false; - } - else { - BSONObjBuilder b1(32), b2(32); - b1.appendMinForType( "" , String ); - lower = addObj( b1.obj() ).firstElement(); - - b2.appendMaxForType( "" , String ); - upper = addObj( b2.obj() ).firstElement(); - upperInclusive = false; //MaxForType String is an empty Object - } - - // regex matches self - regex type > string type - if (e.type() == RegEx) { - BSONElement re = addObj( BSON( "" << e ) ).firstElement(); - _intervals.push_back( FieldInterval(re) ); - } - else { - BSONObj orig = e.embeddedObject(); - BSONObjBuilder b; - b.appendRegex("", orig["$regex"].valuestrsafe(), orig["$options"].valuestrsafe()); - BSONElement re = addObj( b.obj() ).firstElement(); - _intervals.push_back( FieldInterval(re) ); - } - - } - return; - } - - // Identify simple cases where this FieldRange represents the exact set of BSONElement - // values matching the query expression element used to construct the FieldRange. - - if ( // If type bracketing is enabled (see 'optimize' case at the end of this function) ... - optimize && - // ... and the operator isn't within a $not clause ... - !isNot && - // ... and the operand is of a type that implements exact type bracketing and will be - // exactly represented in an index key (eg is not null or an array) ... - e.isSimpleType() ) { - switch( op ) { - // ... and the operator is one for which this constructor will determine exact - // bounds on the values that match ... - case BSONObj::Equality: - case BSONObj::LT: - case BSONObj::LTE: - case BSONObj::GT: - case BSONObj::GTE: - // ... then this FieldRange exactly characterizes those documents that match the - // operator. - _exactMatchRepresentation = true; - default: - break; - } - } - - if ( isNot ) { - switch( op ) { - case BSONObj::Equality: - return; -// op = BSONObj::NE; -// break; - case BSONObj::opALL: - case BSONObj::opMOD: // NOTE for mod and type, we could consider having 1-2 intervals comprising the complementary types (multiple intervals already possible with $in) - case BSONObj::opTYPE: - // no bound calculation - return; - case BSONObj::NE: - op = BSONObj::Equality; - break; - case BSONObj::LT: - op = BSONObj::GTE; - break; - case BSONObj::LTE: - op = BSONObj::GT; - break; - case BSONObj::GT: - op = BSONObj::LTE; - break; - case BSONObj::GTE: - op = BSONObj::LT; - break; - case BSONObj::opEXISTS: - existsSpec = !existsSpec; - break; - default: // otherwise doesn't matter - break; - } - } - switch( op ) { - case BSONObj::Equality: - lower = upper = e; - break; - case BSONObj::NE: { - // this will invalidate the upper/lower references above - _intervals.push_back( FieldInterval() ); - // optimize doesn't make sense for negative ranges - _intervals[ 0 ]._upper._bound = e; - _intervals[ 0 ]._upper._inclusive = false; - _intervals[ 1 ]._lower._bound = e; - _intervals[ 1 ]._lower._inclusive = false; - _intervals[ 1 ]._upper._bound = maxKey.firstElement(); - _intervals[ 1 ]._upper._inclusive = true; - optimize = false; // don't run optimize code below - break; - } - case BSONObj::LT: - upperInclusive = false; - case BSONObj::LTE: - upper = e; - break; - case BSONObj::GT: - lowerInclusive = false; - case BSONObj::GTE: - lower = e; - break; - case BSONObj::opALL: { - uassert( 10370 , "$all requires array", e.type() == Array ); - BSONObjIterator i( e.embeddedObject() ); - bool bound = false; - while ( i.more() ) { - BSONElement x = i.next(); - if ( x.type() == Object && x.embeddedObject().firstElement().getGtLtOp() == BSONObj::opELEM_MATCH ) { - // taken care of elsewhere - } - else if ( x.type() != RegEx ) { - lower = upper = x; - bound = true; - break; - } - } - if ( !bound ) { // if no good non regex bound found, try regex bounds - BSONObjIterator i( e.embeddedObject() ); - while( i.more() ) { - BSONElement x = i.next(); - if ( x.type() != RegEx ) - continue; - string simple = simpleRegex( x.regex(), x.regexFlags() ); - if ( !simple.empty() ) { - lower = addObj( BSON( "" << simple ) ).firstElement(); - upper = addObj( BSON( "" << simpleRegexEnd( simple ) ) ).firstElement(); - break; - } - } - } - break; - } - case BSONObj::opMOD: { - { - BSONObjBuilder b; - b.appendMinForType( "" , NumberDouble ); - lower = addObj( b.obj() ).firstElement(); - } - { - BSONObjBuilder b; - b.appendMaxForType( "" , NumberDouble ); - upper = addObj( b.obj() ).firstElement(); - } - break; - } - case BSONObj::opTYPE: { - BSONType t = (BSONType)e.numberInt(); - { - BSONObjBuilder b; - b.appendMinForType( "" , t ); - lower = addObj( b.obj() ).firstElement(); - } - { - BSONObjBuilder b; - b.appendMaxForType( "" , t ); - upper = addObj( b.obj() ).firstElement(); - } - - break; - } - case BSONObj::opREGEX: - case BSONObj::opOPTIONS: - // These operators are handled via their encapsulating object, so no bounds are - // generated for them individually. - break; - case BSONObj::opELEM_MATCH: { - log() << "warning: shouldn't get here?" << endl; - break; - } - case BSONObj::opWITHIN: - _special.add(IndexNames::GEO_2D, SpecialIndices::NO_INDEX_REQUIRED); - _special.add(IndexNames::GEO_2DSPHERE, SpecialIndices::NO_INDEX_REQUIRED); - break; - case BSONObj::opNEAR: - _special.add(IndexNames::GEO_2D, SpecialIndices::INDEX_REQUIRED); - _special.add(IndexNames::GEO_2DSPHERE, SpecialIndices::INDEX_REQUIRED); - break; - case BSONObj::opGEO_INTERSECTS: - _special.add(IndexNames::GEO_2DSPHERE, SpecialIndices::NO_INDEX_REQUIRED); - break; - case BSONObj::opEXISTS: { - if ( !existsSpec ) { - lower = upper = staticNull.firstElement(); - } - optimize = false; - break; - } - default: - break; - } - - // If 'optimize' is set, then bracket the field range by bson type. For example, if this - // FieldRange is constructed with the operator { $gt:5 }, then the lower bound will be 5 - // at this point but the upper bound will be MaxKey. If 'optimize' is true, the upper bound - // is bracketed to the highest possible bson numeric value. This is consistent with the - // Matcher's $gt implementation. - - if ( optimize ) { - if ( lower.type() != MinKey && upper.type() == MaxKey && lower.isSimpleType() ) { // TODO: get rid of isSimpleType - BSONObjBuilder b; - b.appendMaxForType( lower.fieldName() , lower.type() ); - upper = addObj( b.obj() ).firstElement(); - if ( upper.canonicalType() != lower.canonicalType() ) { - // _exactMatchRepresentation will be set if lower.isSimpleType(), requiring that - // this field range exactly describe the values matching its query operator. If - // lower's max for type is not of the same canonical type as lower, it is - // assumed to be the lowest value of the next canonical type meaning the upper - // bound should be exclusive. - upperInclusive = false; - } - } - else if ( lower.type() == MinKey && upper.type() != MaxKey && upper.isSimpleType() ) { // TODO: get rid of isSimpleType - BSONObjBuilder b; - b.appendMinForType( upper.fieldName() , upper.type() ); - lower = addObj( b.obj() ).firstElement(); - if ( lower.canonicalType() != upper.canonicalType() ) { - // _exactMatchRepresentation will be set if upper.isSimpleType(), requiring that - // this field range exactly describe the values matching its query operator. If - // upper's min for type is not of the same canonical type as upper, it is - // assumed to be the highest value of the previous canonical type meaning the - // lower bound should be exclusive. - lowerInclusive = false; - } - } - } - - } - - void FieldRange::finishOperation( const vector<FieldInterval> &newIntervals, - const FieldRange &other, bool exactMatchRepresentation ) { - _intervals = newIntervals; - for( vector<BSONObj>::const_iterator i = other._objData.begin(); i != other._objData.end(); ++i ) - _objData.push_back( *i ); - if (_special.empty() && !other._special.empty()) - _special = other._special; - _exactMatchRepresentation = exactMatchRepresentation; - // A manipulated FieldRange may no longer be valid within a parent context. - _elemMatchContext = BSONElement(); - } - - /** @return the maximum of two lower bounds, considering inclusivity. */ - static FieldBound maxLowerBound( const FieldBound& a, const FieldBound& b ) { - int cmp = a._bound.woCompare( b._bound, false ); - if ( ( cmp == 0 && !b._inclusive ) || cmp < 0 ) - return b; - return a; - } - - /** @return the minimum of two upper bounds, considering inclusivity. */ - static FieldBound minUpperBound( const FieldBound& a, const FieldBound& b ) { - int cmp = a._bound.woCompare( b._bound, false ); - if ( ( cmp == 0 && !b._inclusive ) || cmp > 0 ) - return b; - return a; - } - - /** - * @return true when the overlap of two intervals is a valid interval. - * @param one, @param two - The intervals. - * @param result - The resulting overlap. - */ - static bool fieldIntervalOverlap( const FieldInterval& one, const FieldInterval& two, - FieldInterval& result ) { - result._lower = maxLowerBound( one._lower, two._lower ); - result._upper = minUpperBound( one._upper, two._upper ); - return result.isStrictValid(); - } - - const FieldRange &FieldRange::intersect( const FieldRange &other, bool singleKey ) { - // If 'this' FieldRange is universal(), intersect by copying the 'other' range into 'this'. - if ( universal() ) { - SpecialIndices intersectSpecial = _special.combineWith(other._special); - *this = other; - _special = intersectSpecial; - return *this; - } - // Range intersections are not taken for multikey indexes. See SERVER-958. - if ( !singleKey && !universal() ) { - SpecialIndices intersectSpecial = _special.combineWith(other._special); - // Pick 'other' range if it is smaller than or equal to 'this'. - if ( other <= *this ) { - *this = other; - } - _exactMatchRepresentation = false; - _special = intersectSpecial; - return *this; - } - vector<FieldInterval> newIntervals; - vector<FieldInterval>::const_iterator i = _intervals.begin(); - vector<FieldInterval>::const_iterator j = other._intervals.begin(); - while( i != _intervals.end() && j != other._intervals.end() ) { - FieldInterval overlap; - if ( fieldIntervalOverlap( *i, *j, overlap ) ) { - // If the two intervals overlap, add the overlap to the result. - newIntervals.push_back( overlap ); - } - // Increment the iterator with the current interval having the lower upper bound. The - // next interval of this iterator may overlap with the current interval of the other - // iterator. - if ( i->_upper == minUpperBound( i->_upper, j->_upper ) ) { - ++i; - } - else { - ++j; - } - } - finishOperation( newIntervals, other, - mustBeExactMatchRepresentation() && - other.mustBeExactMatchRepresentation() ); - return *this; - } - - /** Helper class for assembling a union of FieldRange objects. */ - class RangeUnionBuilder : boost::noncopyable { - public: - RangeUnionBuilder() : _initial( true ) {} - /** @param next: Supply next ordered interval, ordered by _lower FieldBound. */ - void nextOrderedInterval( const FieldInterval &next ) { - if ( _initial ) { - _tail = next; - _initial = false; - return; - } - if ( !handleDisjoint( next ) ) { - handleExtend( next ); - } - } - void done() { - if ( !_initial ) { - _unionIntervals.push_back( _tail ); - } - } - const vector<FieldInterval> &unionIntervals() const { return _unionIntervals; } - private: - /** If _tail and next are disjoint, next becomes the new _tail. */ - bool handleDisjoint( const FieldInterval &next ) { - int cmp = _tail._upper._bound.woCompare( next._lower._bound, false ); - if ( ( cmp < 0 ) || - ( cmp == 0 && !_tail._upper._inclusive && !next._lower._inclusive ) ) { - _unionIntervals.push_back( _tail ); - _tail = next; - return true; - } - return false; - } - /** Extend _tail to upper bound of next if necessary. */ - void handleExtend( const FieldInterval &next ) { - int cmp = _tail._upper._bound.woCompare( next._upper._bound, false ); - if ( ( cmp < 0 ) || - ( cmp == 0 && !_tail._upper._inclusive && next._upper._inclusive ) ) { - _tail._upper = next._upper; - } - } - bool _initial; - FieldInterval _tail; - vector<FieldInterval> _unionIntervals; - }; - - const FieldRange &FieldRange::operator|=( const FieldRange &other ) { - RangeUnionBuilder b; - vector<FieldInterval>::const_iterator i = _intervals.begin(); - vector<FieldInterval>::const_iterator j = other._intervals.begin(); - while( i != _intervals.end() && j != other._intervals.end() ) { - int cmp = i->_lower._bound.woCompare( j->_lower._bound, false ); - if ( cmp < 0 || ( cmp == 0 && i->_lower._inclusive ) ) { - b.nextOrderedInterval( *i++ ); - } - else { - b.nextOrderedInterval( *j++ ); - } - } - while( i != _intervals.end() ) { - b.nextOrderedInterval( *i++ ); - } - while( j != other._intervals.end() ) { - b.nextOrderedInterval( *j++ ); - } - b.done(); - finishOperation( b.unionIntervals(), other, false ); - return *this; - } - - const FieldRange &FieldRange::operator-=( const FieldRange &other ) { - vector<FieldInterval> newIntervals; - vector<FieldInterval>::iterator i = _intervals.begin(); - vector<FieldInterval>::const_iterator j = other._intervals.begin(); - while( i != _intervals.end() && j != other._intervals.end() ) { - int cmp = i->_lower._bound.woCompare( j->_lower._bound, false ); - if ( cmp < 0 || - ( cmp == 0 && i->_lower._inclusive && !j->_lower._inclusive ) ) { - int cmp2 = i->_upper._bound.woCompare( j->_lower._bound, false ); - if ( cmp2 < 0 ) { - newIntervals.push_back( *i ); - ++i; - } - else if ( cmp2 == 0 ) { - newIntervals.push_back( *i ); - if ( newIntervals.back()._upper._inclusive && j->_lower._inclusive ) { - newIntervals.back()._upper._inclusive = false; - } - ++i; - } - else { - newIntervals.push_back( *i ); - newIntervals.back()._upper = j->_lower; - newIntervals.back()._upper.flipInclusive(); - int cmp3 = i->_upper._bound.woCompare( j->_upper._bound, false ); - if ( cmp3 < 0 || - ( cmp3 == 0 && ( !i->_upper._inclusive || j->_upper._inclusive ) ) ) { - ++i; - } - else { - i->_lower = j->_upper; - i->_lower.flipInclusive(); - ++j; - } - } - } - else { - int cmp2 = i->_lower._bound.woCompare( j->_upper._bound, false ); - if ( cmp2 > 0 || - ( cmp2 == 0 && ( !i->_lower._inclusive || !j->_upper._inclusive ) ) ) { - ++j; - } - else { - int cmp3 = i->_upper._bound.woCompare( j->_upper._bound, false ); - if ( cmp3 < 0 || - ( cmp3 == 0 && ( !i->_upper._inclusive || j->_upper._inclusive ) ) ) { - ++i; - } - else { - i->_lower = j->_upper; - i->_lower.flipInclusive(); - ++j; - } - } - } - } - while( i != _intervals.end() ) { - newIntervals.push_back( *i ); - ++i; - } - finishOperation( newIntervals, other, false ); - return *this; - } - - // TODO write a proper implementation that doesn't do a full copy - bool FieldRange::operator<=( const FieldRange &other ) const { - FieldRange temp = *this; - temp -= other; - return temp.empty(); - } - - bool FieldRange::universal() const { - if ( empty() ) { - return false; - } - if ( minKey.firstElement().woCompare( min(), false ) != 0 ) { - return false; - } - if ( maxKey.firstElement().woCompare( max(), false ) != 0 ) { - return false; - } - // TODO ensure that adjacent intervals are not possible (the two intervals should be - // merged), and just determine if the range is universal by testing _intervals.size() == 1. - for ( unsigned i = 1; i < _intervals.size(); ++i ) { - const FieldBound &prev = _intervals[ i-1 ]._upper; - const FieldBound &curr = _intervals[ i ]._lower; - if ( !prev._inclusive && !curr._inclusive ) { - return false; - } - if ( prev._bound.woCompare( curr._bound ) < 0 ) { - return false; - } - } - return true; - } - - bool FieldRange::isPointIntervalSet() const { - if ( _intervals.empty() ) { - return false; - } - for( vector<FieldInterval>::const_iterator i = _intervals.begin(); i != _intervals.end(); - ++i ) { - if ( !i->equality() ) { - return false; - } - } - return true; - } - - void FieldRange::setExclusiveBounds() { - for( vector<FieldInterval>::iterator i = _intervals.begin(); i != _intervals.end(); ++i ) { - i->_lower._inclusive = false; - i->_upper._inclusive = false; - } - } - - void FieldRange::reverse( FieldRange &ret ) const { - verify( _special.empty() ); - ret._intervals.clear(); - ret._objData = _objData; - for( vector<FieldInterval>::const_reverse_iterator i = _intervals.rbegin(); i != _intervals.rend(); ++i ) { - FieldInterval fi; - fi._lower = i->_upper; - fi._upper = i->_lower; - ret._intervals.push_back( fi ); - } - } - - BSONObj FieldRange::addObj( const BSONObj &o ) { - _objData.push_back( o ); - return o; - } - - string FieldInterval::toString() const { - StringBuilder buf; - buf << ( _lower._inclusive ? "[" : "(" ) << " "; - buf << _lower._bound.toString( false ); - buf << " , "; - buf << _upper._bound.toString( false ); - buf << " " << ( _upper._inclusive ? "]" : ")" ); - return buf.str(); - } - - - string FieldRange::toString() const { - StringBuilder buf; - buf << "(FieldRange special: { " << _special.toString() << "} intervals: "; - for (vector<FieldInterval>::const_iterator i = _intervals.begin(); i != _intervals.end(); ++i) { - buf << i->toString() << " "; - } - buf << ")"; - return buf.str(); - } - - SpecialIndices FieldRangeSet::getSpecial() const { - for (map<string, FieldRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); i++) { - if (!i->second.getSpecial().empty()) { - return i->second.getSpecial(); - } - } - return SpecialIndices(); - } - - /** - * Btree scanning for a multidimentional key range will yield a - * multidimensional box. The idea here is that if an 'other' - * multidimensional box contains the current box we don't have to scan - * the current box. If the 'other' box contains the current box in - * all dimensions but one, we can safely subtract the values of 'other' - * along that one dimension from the values for the current box on the - * same dimension. In other situations, subtracting the 'other' - * box from the current box yields a result that is not a box (but - * rather can be expressed as a union of boxes). We don't support - * such splitting currently in calculating index ranges. Note that - * where I have said 'box' above, I actually mean sets of boxes because - * a field range can consist of multiple intervals. - */ - const FieldRangeSet &FieldRangeSet::operator-=( const FieldRangeSet &other ) { - int nUnincluded = 0; - string unincludedKey; - map<string,FieldRange>::const_iterator i = _ranges.begin(); - map<string,FieldRange>::const_iterator j = other._ranges.begin(); - while( nUnincluded < 2 && i != _ranges.end() && j != other._ranges.end() ) { - int cmp = i->first.compare( j->first ); - if ( cmp == 0 ) { - if ( i->second <= j->second ) { - // nothing - } - else { - ++nUnincluded; - unincludedKey = i->first; - } - ++i; - ++j; - } - else if ( cmp < 0 ) { - ++i; - } - else { - // other has a bound we don't, nothing can be done - return *this; - } - } - if ( j != other._ranges.end() ) { - // other has a bound we don't, nothing can be done - return *this; - } - if ( nUnincluded > 1 ) { - return *this; - } - if ( nUnincluded == 0 ) { - makeEmpty(); - return *this; - } - // nUnincluded == 1 - range( unincludedKey.c_str() ) -= other.range( unincludedKey.c_str() ); - appendQueries( other ); - return *this; - } - - const FieldRangeSet &FieldRangeSet::operator&=( const FieldRangeSet &other ) { - map<string,FieldRange>::iterator i = _ranges.begin(); - map<string,FieldRange>::const_iterator j = other._ranges.begin(); - while( i != _ranges.end() && j != other._ranges.end() ) { - int cmp = i->first.compare( j->first ); - if ( cmp == 0 ) { - // Same field name, so find range intersection. - i->second.intersect( j->second, _singleKey ); - ++i; - ++j; - } - else if ( cmp < 0 ) { - // Field present in *this. - ++i; - } - else { - // Field not present in *this, so add it. - range( j->first.c_str() ) = j->second; - ++j; - } - } - while( j != other._ranges.end() ) { - // Field not present in *this, add it. - range( j->first.c_str() ) = j->second; - ++j; - } - appendQueries( other ); - return *this; - } - - void FieldRangeSet::appendQueries( const FieldRangeSet &other ) { - for( vector<BSONObj>::const_iterator i = other._queries.begin(); i != other._queries.end(); ++i ) { - _queries.push_back( *i ); - } - } - - void FieldRangeSet::makeEmpty() { - for( map<string,FieldRange>::iterator i = _ranges.begin(); i != _ranges.end(); ++i ) { - i->second.makeEmpty(); - } - } - - void FieldRangeSet::handleOp( const char* matchFieldName, const BSONElement& op, bool isNot, - bool optimize ) { - int opType = op.getGtLtOp(); - - // If the first $all element's first op is an $elemMatch, generate bounds for it - // and ignore the remaining $all elements. SERVER-664 - if ( opType == BSONObj::opALL ) { - uassert( 13050, "$all requires array", op.type() == Array ); - BSONElement firstAllClause = op.embeddedObject().firstElement(); - if ( firstAllClause.type() == Object ) { - BSONElement firstAllClauseOp = firstAllClause.embeddedObject().firstElement(); - if ( firstAllClauseOp.getGtLtOp() == BSONObj::opELEM_MATCH ) { - handleElemMatch( matchFieldName, firstAllClauseOp, isNot, optimize ); - return; - } - } - } - - if ( opType == BSONObj::opELEM_MATCH ) { - handleElemMatch( matchFieldName, op, isNot, optimize ); - } - else { - intersectMatchField( matchFieldName, op, isNot, optimize ); - } - } - - void FieldRangeSet::handleNotOp( const char* matchFieldName, const BSONElement& notOp, - bool optimize ) { - switch( notOp.type() ) { - case Object: { - BSONObjIterator notOpIterator( notOp.embeddedObject() ); - while( notOpIterator.more() ) { - BSONElement opToNegate = notOpIterator.next(); - uassert( 13034, "invalid use of $not", - opToNegate.getGtLtOp() != BSONObj::Equality ); - handleOp( matchFieldName, opToNegate, true, optimize ); - } - break; - } - case RegEx: - handleOp( matchFieldName, notOp, true, optimize ); - break; - default: - uassert( 13041, "invalid use of $not", false ); - } - } - - void FieldRangeSet::handleElemMatch( const char* matchFieldName, const BSONElement& elemMatch, - bool isNot, bool optimize ) { - adjustMatchField(); - if ( !_boundElemMatch ) { - return; - } - if ( isNot ) { - // SERVER-5740 $elemMatch queries may depend on multiple fields, so $not:$elemMatch - // cannot in general generate range constraints for a single field. - return; - } - BSONObj elemMatchObj = elemMatch.embeddedObjectUserCheck(); - BSONElement firstElemMatchElement = elemMatchObj.firstElement(); - if ( firstElemMatchElement.getGtLtOp() != BSONObj::Equality || - str::equals( firstElemMatchElement.fieldName(), "$not" ) ) { - // Handle $elemMatch applied to top level elements (where $elemMatch fields are - // operators). SERVER-1264 - BSONObj namedMatchExpression = elemMatch.wrap( matchFieldName ); - FieldRangeSet elemMatchRanges( _ns.c_str(), - namedMatchExpression, - true, // SERVER-4180 Generate single key index bounds. - optimize, - false // Prevent computing FieldRanges for nested - // $elemMatch expressions, which the index key - // generation implementation does not support. - ); - *this &= elemMatchRanges; - } - else { - // Handle $elemMatch applied to nested elements ($elemMatch fields are not operators, - // but full match specifications). - - FieldRangeSet elemMatchRanges( _ns.c_str(), elemMatchObj, _singleKey, optimize ); - // Set the parent $elemMatch context on direct $elemMatch children (those with undotted - // field names). - for( map<string,FieldRange>::iterator i = elemMatchRanges._ranges.begin(); - i != elemMatchRanges._ranges.end(); ++i ) { - if ( !str::contains( i->first, '.' ) ) { - i->second.setElemMatchContext( elemMatch ); - } - } - scoped_ptr<FieldRangeSet> prefixedRanges - ( elemMatchRanges.prefixed( matchFieldName ) ); - *this &= *prefixedRanges; - } - } - - void FieldRangeSet::handleConjunctionClauses( const BSONObj& clauses, bool optimize ) { - BSONObjIterator clausesIterator( clauses ); - while( clausesIterator.more() ) { - BSONElement clause = clausesIterator.next(); - uassert( 14817, "$and/$or elements must be objects", clause.type() == Object ); - BSONObjIterator clauseIterator( clause.embeddedObject() ); - while( clauseIterator.more() ) { - BSONElement matchElement = clauseIterator.next(); - handleMatchField( matchElement, optimize ); - } - } - } - - void FieldRangeSet::handleMatchField( const BSONElement& matchElement, bool optimize ) { - const char* matchFieldName = matchElement.fieldName(); - if ( matchFieldName[ 0 ] == '$' ) { - if ( str::equals( matchFieldName, "$and" ) ) { - uassert( 14816, "$and expression must be a nonempty array", - matchElement.type() == Array && - matchElement.embeddedObject().nFields() > 0 ); - handleConjunctionClauses( matchElement.embeddedObject(), optimize ); - return; - } - - adjustMatchField(); - - if ( str::equals( matchFieldName, "$or" ) ) { - // Check for a singleton $or expression. - if ( matchElement.type() == Array && - matchElement.embeddedObject().nFields() == 1 ) { - // Compute field bounds for a singleton $or expression as if it is a $and - // expression. With only one clause, the matching semantics are the same. - // SERVER-6416 - handleConjunctionClauses( matchElement.embeddedObject(), optimize ); - } - return; - } - - if ( str::equals( matchFieldName, "$nor" ) ) { - return; - } - - if ( str::equals( matchFieldName, "$where" ) ) { - return; - } - - if ( str::equals( matchFieldName, "$atomic" ) || - str::equals( matchFieldName, "$isolated" ) ) { - return; - } - } - - bool equality = - // Check for a parsable '$' operator within a match element, indicating the object - // should not be matched as is but parsed. - // NOTE This only checks for a '$' prefix in the first embedded field whereas Matcher - // checks all embedded fields. - ( getGtLtOp( matchElement ) == BSONObj::Equality ) && - // Similarly check for the $not meta operator. - !( matchElement.type() == Object && - str::equals( matchElement.embeddedObject().firstElementFieldName(), "$not" ) ); - - if ( equality ) { - intersectMatchField( matchFieldName, matchElement, false, optimize ); - return; - } - - bool untypedRegex = - ( matchElement.type() == Object ) && - matchElement.embeddedObject().hasField( "$regex" ); - - if ( untypedRegex ) { - // $regex/$options pairs must be handled together and so are passed via the - // element encapsulating them. - intersectMatchField( matchFieldName, matchElement, false, optimize ); - // Other elements may remain to be handled, below. - } - - BSONObjIterator matchExpressionIterator( matchElement.embeddedObject() ); - while( matchExpressionIterator.more() ) { - BSONElement opElement = matchExpressionIterator.next(); - if ( str::equals( opElement.fieldName(), "$not" ) ) { - handleNotOp( matchFieldName, opElement, optimize ); - } - else { - handleOp( matchFieldName, opElement, false, optimize ); - } - } - } - - FieldRangeSet::FieldRangeSet( const char *ns, const BSONObj &query, bool singleKey, - bool optimize ) : - _ns( ns ), - _queries( 1, query.getOwned() ), - _singleKey( singleKey ), - _exactMatchRepresentation( true ), - _boundElemMatch( true ) { - init( optimize ); - } - - FieldRangeSet::FieldRangeSet( const char* ns, const BSONObj& query, bool singleKey, - bool optimize, bool boundElemMatch ) : - _ns( ns ), - _queries( 1, query.getOwned() ), - _singleKey( singleKey ), - _exactMatchRepresentation( true ), - _boundElemMatch( boundElemMatch ) { - init( optimize ); - } - - void FieldRangeSet::init( bool optimize ) { - BSONObjIterator i( _queries[ 0 ] ); - while( i.more() ) { - handleMatchField( i.next(), optimize ); - } - } - - /** - * TODO When operators are refactored to a standard interface, a version of this should be - * part of that interface. - */ - void FieldRangeSet::adjustMatchField() { - _exactMatchRepresentation = false; - } - - void FieldRangeSet::intersectMatchField( const char *fieldName, const BSONElement &matchElement, - bool isNot, bool optimize ) { - FieldRange &selectedRange = range( fieldName ); - selectedRange.intersect( FieldRange( matchElement, isNot, optimize ), _singleKey ); - if ( !selectedRange.mustBeExactMatchRepresentation() ) { - _exactMatchRepresentation = false; - } - } - - /** - * @return true if @param range is universal or can be easily identified as a reverse universal - * range (see FieldRange::reverse()). - */ - static bool universalOrReverseUniversalRange( const FieldRange& range ) { - if ( range.universal() ) { - return true; - } - if ( range.intervals().size() != 1 ) { - return false; - } - if ( !range.min().valuesEqual( maxKey.firstElement() ) ) { - return false; - } - if ( !range.max().valuesEqual( minKey.firstElement() ) ) { - return false; - } - return true; - } - - FieldRangeVector::FieldRangeVector( const FieldRangeSet &frs, BSONObj keyPattern, - int direction ) : - _keyPattern(keyPattern), - _direction( direction >= 0 ? 1 : -1 ), - _hasAllIndexedRanges( true ) { - verify( frs.matchPossibleForIndex( keyPattern)); - _queries = frs._queries; - - // For key generation - BSONObjIterator it(_keyPattern); - while (it.more()) { - BSONElement elt = it.next(); - _fieldNames.push_back(elt.fieldName()); - _fixed.push_back(BSONElement()); - } - - _keyGenerator.reset(new BtreeKeyGeneratorV1(_fieldNames, _fixed, false)); - - map<string,BSONElement> topFieldElemMatchContexts; - BSONObjIterator i(keyPattern); - while( i.more() ) { - BSONElement e = i.next(); - const FieldRange *range = &frs.range( e.fieldName() ); - verify( !range->empty() ); - - if ( !frs.singleKey() ) { - // Some constraints across different fields cannot be correctly intersected on a - // multikey index. SERVER-958 - // - // Given a multikey index { 'a.b':1, 'a.c':1 } and query { 'a.b':3, 'a.c':3 } only - // the index field 'a.b' is constrained to the range [3, 3], while the index - // field 'a.c' is just constrained to be within minkey and maxkey. This - // implementation ensures that the document { a:[ { b:3 }, { c:3 } ] }, which - // generates index keys { 'a.b':3, 'a.c':null } and { 'a.b':null and 'a.c':3 } will - // be retrieved for the query. - // - // However, if the query is instead { a:{ $elemMatch:{ b:3, c:3 } } } then the - // document { a:[ { b:3 }, { c:3 } ] } should not match the query and both index - // fields 'a.b' and 'a.c' are constrained to the range [3, 3]. - - // If no other field with the same topField has been constrained ... - string topField = str::before( e.fieldName(), '.' ); - if ( topFieldElemMatchContexts.count( topField ) == 0 ) { - // ... and this field is constrained ... - if ( !range->universal() ) { - // ... record this field's elemMatchContext for its topField. - topFieldElemMatchContexts[ topField ] = range->elemMatchContext(); - } - } - - // Else if an already constrained field is not an $elemMatch sibling of this field - // (does not share its elemMatchContext) ... - else if ( range->elemMatchContext().eoo() || - range->elemMatchContext().rawdata() != - topFieldElemMatchContexts[ topField ].rawdata() ) { - // ... this field's parsed range cannot be used. - range = &frs.universalRange(); - _hasAllIndexedRanges = false; - } - } - - int number = (int) e.number(); // returns 0.0 if not numeric - bool forward = ( ( number >= 0 ? 1 : -1 ) * ( direction >= 0 ? 1 : -1 ) > 0 ); - if ( forward ) { - _ranges.push_back( *range ); - } - else { - _ranges.push_back( FieldRange( BSONObj().firstElement(), false, true ) ); - range->reverse( _ranges.back() ); - } - verify( !_ranges.back().empty() ); - } - uassert( 13385, "combinatorial limit of $in partitioning of result set exceeded", - size() < MAX_IN_COMBINATIONS ); - } - - bool FieldRangeVector::isSingleInterval() const { - size_t i = 0; - - // Skip all equality ranges. - while( i < _ranges.size() && _ranges[ i ].equality() ) { - ++i; - } - - // If there are no ranges left ... - if ( i >= _ranges.size() ) { - // ... then all ranges are equalities. - return true; - } - - // If the first non equality range does not consist of a single interval ... - if ( _ranges[ i ].intervals().size() != 1 ) { - // ... then the full FieldRangeVector does not consist of a single interval. - return false; - } - ++i; - - while( i < _ranges.size() ) { - // If a range after the first non equality is not universal ... - if ( !universalOrReverseUniversalRange( _ranges[ i ] ) ) { - // ... then the full FieldRangeVector does not consist of a single interval. - return false; - } - ++i; - } - - // The FieldRangeVector consists of zero or more equalities, then zero or one inequality - // with a single interval, then zero or more universal ranges. - return true; - } - - BSONObj FieldRangeVector::startKey() const { - BSONObjBuilder b; - BSONObjIterator keys(_keyPattern); - vector<FieldRange>::const_iterator i = _ranges.begin(); - for( ; i != _ranges.end(); ++i, ++keys ) { - // Append lower bounds until an exclusive bound is found. - const FieldInterval &fi = i->intervals().front(); - b.appendAs( fi._lower._bound, "" ); - if ( !fi._lower._inclusive ) { - ++i; - ++keys; - break; - } - } - for( ; i != _ranges.end(); ++i, ++keys ) { - // After the first exclusive bound is found, use extreme values for subsequent fields. - // For example, on index { a:1, b:1 } with query { a:{ $gt: 5 } } the start key is - // { '':5, '':MaxKey }. - bool forward = ( ( (*keys).number() >= 0 ? 1 : -1 ) * _direction > 0 ); - if ( forward ) { - b.appendMaxKey( "" ); - } - else { - b.appendMinKey( "" ); - } - } - return b.obj(); - } - - bool FieldRangeVector::startKeyInclusive() const { - for( vector<FieldRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i ) { - if( !i->intervals().front()._lower._inclusive ) { - return false; - } - } - return true; - } - - BSONObj FieldRangeVector::endKey() const { - BSONObjBuilder b; - BSONObjIterator keys(_keyPattern); - vector<FieldRange>::const_iterator i = _ranges.begin(); - for( ; i != _ranges.end(); ++i, ++keys ) { - // Append upper bounds until an exclusive bound is found. - const FieldInterval &fi = i->intervals().back(); - b.appendAs( fi._upper._bound, "" ); - if ( !fi._upper._inclusive ) { - ++i; - ++keys; - break; - } - } - for( ; i != _ranges.end(); ++i, ++keys ) { - // After the first exclusive bound is found, use extreme values for subsequent fields. - // For example, on index { a:1, b:1 } with query { a:{ $lt: 5 } } the end key is - // { '':5, '':MinKey }. - bool forward = ( ( (*keys).number() >= 0 ? 1 : -1 ) * _direction > 0 ); - if ( forward ) { - b.appendMinKey( "" ); - } - else { - b.appendMaxKey( "" ); - } - } - return b.obj(); - } - - bool FieldRangeVector::endKeyInclusive() const { - for( vector<FieldRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i ) { - if( !i->intervals().front()._upper._inclusive ) { - return false; - } - } - return true; - } - - BSONObj FieldRangeVector::obj() const { - BSONObjBuilder b; - BSONObjIterator k(_keyPattern); - for( int i = 0; i < (int)_ranges.size(); ++i ) { - BSONArrayBuilder a( b.subarrayStart( k.next().fieldName() ) ); - for( vector<FieldInterval>::const_iterator j = _ranges[ i ].intervals().begin(); - j != _ranges[ i ].intervals().end(); ++j ) { - a << BSONArray( BSON_ARRAY( j->_lower._bound << j->_upper._bound ).clientReadable() ); - } - a.done(); - } - return b.obj(); - } - - FieldRange *FieldRangeSet::__universalRange = 0; - const FieldRange &FieldRangeSet::universalRange() const { - FieldRange *&ret = __universalRange; - if ( ret == 0 ) { - // TODO: SERVER-5112 - ret = new FieldRange( BSONObj().firstElement(), false, true ); - } - return *ret; - } - - bool FieldRangeSet::isPointIntervalSet( const string& fieldname ) const { - - const vector<FieldInterval>& intervals = range( fieldname.c_str() ).intervals(); - - if ( intervals.empty() ) { - return false; - } - - vector<FieldInterval>::const_iterator i; - for(i = intervals.begin() ; i != intervals.end(); ++i){ - if (! i->equality() ) { - return false; - } - } - return true; - } - - int FieldRangeSet::numNonUniversalRanges() const { - int count = 0; - for( map<string,FieldRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i ) { - if ( !i->second.universal() ) - ++count; - } - return count; - } - - FieldRangeSet *FieldRangeSet::subset( const BSONObj &fields ) const { - FieldRangeSet *ret = new FieldRangeSet( ns(), BSONObj(), _singleKey, true ); - BSONObjIterator i( fields ); - while( i.more() ) { - BSONElement e = i.next(); - if ( !range( e.fieldName() ).universal() ) { - ret->range( e.fieldName() ) = range( e.fieldName() ); - } - } - ret->_queries = _queries; - return ret; - } - - FieldRangeSet* FieldRangeSet::prefixed( const string& prefix ) const { - auto_ptr<FieldRangeSet> ret( new FieldRangeSet( ns(), BSONObj(), _singleKey, true ) ); - for( map<string,FieldRange>::const_iterator i = ranges().begin(); i != ranges().end(); - ++i ) { - string prefixedFieldName = prefix + "." + i->first; - ret->range( prefixedFieldName.c_str() ) = i->second; - } - ret->_queries = _queries; - return ret.release(); - } - - string FieldRangeSet::toString() const { - BSONObjBuilder bob; - for( map<string,FieldRange>::const_iterator i = _ranges.begin(); i != _ranges.end(); ++i ) { - bob << i->first << i->second.toString(); - } - return bob.obj().jsonString(); - } - - bool FieldRangeSetPair::noNonUniversalRanges() const { - return _singleKey.numNonUniversalRanges() == 0 && _multiKey.numNonUniversalRanges() == 0; - } - - FieldRangeSetPair &FieldRangeSetPair::operator&=( const FieldRangeSetPair &other ) { - _singleKey &= other._singleKey; - _multiKey &= other._multiKey; - return *this; - } - - FieldRangeSetPair &FieldRangeSetPair::operator-=( const FieldRangeSet &scanned ) { - _singleKey -= scanned; - _multiKey -= scanned; - return *this; - } - - string FieldRangeSetPair::toString() const { - return BSON( - "singleKey" << _singleKey.toString() << - "multiKey" << _multiKey.toString() - ).jsonString(); - } - - bool FieldRangeVector::matchesElement( const BSONElement &e, int i, bool forward ) const { - bool eq; - int l = matchingLowElement( e, i, forward, eq ); - return ( l % 2 == 0 ); // if we're inside an interval - } - - // binary search for interval containing the specified element - // an even return value indicates that the element is contained within a valid interval - int FieldRangeVector::matchingLowElement( const BSONElement &e, int i, bool forward, bool &lowEquality ) const { - lowEquality = false; - int l = -1; - int h = _ranges[ i ].intervals().size() * 2; - while( l + 1 < h ) { - int m = ( l + h ) / 2; - BSONElement toCmp; - bool toCmpInclusive; - const FieldInterval &interval = _ranges[ i ].intervals()[ m / 2 ]; - if ( m % 2 == 0 ) { - toCmp = interval._lower._bound; - toCmpInclusive = interval._lower._inclusive; - } - else { - toCmp = interval._upper._bound; - toCmpInclusive = interval._upper._inclusive; - } - int cmp = toCmp.woCompare( e, false ); - if ( !forward ) { - cmp = -cmp; - } - if ( cmp < 0 ) { - l = m; - } - else if ( cmp > 0 ) { - h = m; - } - else { - if ( m % 2 == 0 ) { - lowEquality = true; - } - int ret = m; - // if left match and inclusive, all good - // if left match and not inclusive, return right before left bound - // if right match and inclusive, return left bound - // if right match and not inclusive, return right bound - if ( ( m % 2 == 0 && !toCmpInclusive ) || ( m % 2 == 1 && toCmpInclusive ) ) { - --ret; - } - return ret; - } - } - verify( l + 1 == h ); - return l; - } - - bool FieldRangeVector::matchesKey( const BSONObj &key ) const { - BSONObjIterator j( key ); - BSONObjIterator k(_keyPattern); - for( int l = 0; l < (int)_ranges.size(); ++l ) { - int number = (int) k.next().number(); - bool forward = ( number >= 0 ? 1 : -1 ) * ( _direction >= 0 ? 1 : -1 ) > 0; - if ( !matchesElement( j.next(), l, forward ) ) { - return false; - } - } - return true; - } - - bool FieldRangeVector::matches( const BSONObj &obj ) const { - bool ok = false; - - BSONObjSet keys; - - /** - * Key generation by design is behind the index interface. There is an exception here - * because $or uses key generation to dedup its results. When $or is fixed to not require - * this, key generation will be removed from here. - */ - _keyGenerator->getKeys(obj, &keys); - - // TODO The representation of matching keys could potentially be optimized - // more for the case at hand. (For example, we can potentially consider - // fields individually instead of constructing several bson objects using - // multikey arrays.) But getKeys() canonically defines the key set for a - // given object and for now we are using it as is. - for( BSONObjSet::const_iterator i = keys.begin(); i != keys.end(); ++i ) { - if ( matchesKey( *i ) ) { - ok = true; - break; - } - } - - LOG(5) << "FieldRangeVector::matches() returns " << ok << endl; - - return ok; - } - - BSONObj FieldRangeVector::firstMatch( const BSONObj &obj ) const { - // NOTE Only works in forward direction. - verify( _direction >= 0 ); - BSONObjCmp oc(_keyPattern); - BSONObjSet keys(oc); - // See FieldRangeVector::matches for comment on key generation. - _keyGenerator->getKeys(obj, &keys); - for( BSONObjSet::const_iterator i = keys.begin(); i != keys.end(); ++i ) { - if ( matchesKey( *i ) ) { - return *i; - } - } - return BSONObj(); - } - - string FieldRangeVector::toString() const { - BSONObjBuilder bob; - BSONObjIterator i(_keyPattern); - for( vector<FieldRange>::const_iterator r = _ranges.begin(); - r != _ranges.end() && i.more(); ++r ) { - BSONElement e = i.next(); - bob << e.fieldName() << r->toString(); - } - return bob.obj().jsonString(); - } - - FieldRangeVectorIterator::FieldRangeVectorIterator( const FieldRangeVector &v, - int singleIntervalLimit ) : - _v( v ), - _i( _v._ranges.size(), singleIntervalLimit ), - _cmp( _v._ranges.size(), 0 ), - _inc( _v._ranges.size(), false ), - _after(), - _endNonUniversalRanges( endNonUniversalRanges() ) { - } - - // TODO optimize more SERVER-5450. - int FieldRangeVectorIterator::advance( const BSONObj &curr ) { - BSONObjIterator j( curr ); - BSONObjIterator o( _v._keyPattern); - // track first field for which we are not at the end of the valid values, - // since we may need to advance from the key prefix ending with this field - int latestNonEndpoint = -1; - // iterate over fields to determine appropriate advance method - for( int i = 0; i < _endNonUniversalRanges; ++i ) { - if ( i > 0 && !_v._ranges[ i - 1 ].intervals()[ _i.get( i - 1 ) ].equality() ) { - // if last bound was inequality, we don't know anything about where we are for this field - // TODO if possible avoid this certain cases when value in previous field of the previous - // key is the same as value of previous field in current key - _i.setUnknowns( i ); - } - BSONElement oo = o.next(); - bool reverse = ( ( oo.number() < 0 ) ^ ( _v._direction < 0 ) ); - BSONElement jj = j.next(); - if ( _i.get( i ) == -1 ) { // unknown position for this field, do binary search - bool lowEquality; - int l = _v.matchingLowElement( jj, i, !reverse, lowEquality ); - if ( l % 2 == 0 ) { // we are in a valid range for this field - _i.set( i, l / 2 ); - int diff = (int)_v._ranges[ i ].intervals().size() - _i.get( i ); - if ( diff > 1 ) { - latestNonEndpoint = i; - } - else if ( diff == 1 ) { - int x = _v._ranges[ i ].intervals()[ _i.get( i ) ]._upper._bound.woCompare( jj, false ); - if ( x != 0 ) { - latestNonEndpoint = i; - } - } - continue; - } - else { // not in a valid range for this field - determine if and how to advance - // check if we're after the last interval for this field - if ( l == (int)_v._ranges[ i ].intervals().size() * 2 - 1 ) { - if ( latestNonEndpoint == -1 ) { - return -2; - } - return advancePastZeroed( latestNonEndpoint + 1 ); - } - _i.set( i, ( l + 1 ) / 2 ); - if ( lowEquality ) { - return advancePast( i + 1 ); - } - return advanceToLowerBound( i ); - } - } - bool first = true; - bool eq = false; - // _i.get( i ) != -1, so we have a starting interval for this field - // which serves as a lower/equal bound on the first iteration - - // we advance from this interval to find a matching interval - while( _i.get( i ) < (int)_v._ranges[ i ].intervals().size() ) { - - int advanceMethod = validateCurrentInterval( i, jj, reverse, first, eq ); - if ( advanceMethod >= 0 ) { - return advanceMethod; - } - if ( advanceMethod == -1 && !hasReachedLimitForLastInterval( i ) ) { - break; - } - // advance to next interval and reset remaining fields - _i.inc( i ); - _i.setZeroes( i + 1 ); - first = false; - } - int diff = (int)_v._ranges[ i ].intervals().size() - _i.get( i ); - if ( diff > 1 || ( !eq && diff == 1 ) ) { - // check if we're not at the end of valid values for this field - latestNonEndpoint = i; - } - else if ( diff == 0 ) { // check if we're past the last interval for this field - if ( latestNonEndpoint == -1 ) { - return -2; - } - // more values possible, skip... - return advancePastZeroed( latestNonEndpoint + 1 ); - } - } - _i.incSingleIntervalCount(); - return -1; - } - - void FieldRangeVectorIterator::prepDive() { - for( int j = 0; j < _i.size(); ++j ) { - _cmp[ j ] = &_v._ranges[ j ].intervals().front()._lower._bound; - _inc[ j ] = _v._ranges[ j ].intervals().front()._lower._inclusive; - } - _i.resetIntervalCount(); - } - - int FieldRangeVectorIterator::validateCurrentInterval( int intervalIdx, - const BSONElement &currElt, - bool reverse, bool first, - bool &eqInclusiveUpperBound ) { - eqInclusiveUpperBound = false; - FieldIntervalMatcher matcher - ( _v._ranges[ intervalIdx ].intervals()[ _i.get( intervalIdx ) ], currElt, - reverse ); - - if ( matcher.isEqInclusiveUpperBound() ) { - eqInclusiveUpperBound = true; - return -1; - } - if ( matcher.isGteUpperBound() ) { - return -2; - } - - // below the upper bound - - if ( intervalIdx == 0 && first ) { - // the value of 1st field won't go backward, so don't check lower bound - // TODO maybe we can check 'first' only? - return -1; - } - - if ( matcher.isEqExclusiveLowerBound() ) { - return advancePastZeroed( intervalIdx + 1 ); - } - if ( matcher.isLtLowerBound() ) { - _i.setZeroes( intervalIdx + 1 ); - return advanceToLowerBound( intervalIdx ); - } - - return -1; - } - - int FieldRangeVectorIterator::advanceToLowerBound( int i ) { - _cmp[ i ] = &_v._ranges[ i ].intervals()[ _i.get( i ) ]._lower._bound; - _inc[ i ] = _v._ranges[ i ].intervals()[ _i.get( i ) ]._lower._inclusive; - for( int j = i + 1; j < _i.size(); ++j ) { - _cmp[ j ] = &_v._ranges[ j ].intervals().front()._lower._bound; - _inc[ j ] = _v._ranges[ j ].intervals().front()._lower._inclusive; - } - _after = false; - return i; - } - - int FieldRangeVectorIterator::advancePast( int i ) { - _after = true; - return i; - } - - int FieldRangeVectorIterator::advancePastZeroed( int i ) { - _i.setZeroes( i ); - return advancePast( i ); - } - - int FieldRangeVectorIterator::endNonUniversalRanges() const { - int i = _v._ranges.size() - 1; - while( i > -1 && universalOrReverseUniversalRange( _v._ranges[ i ] ) ) { - --i; - } - return i + 1; - } - - FieldRangeVectorIterator::CompoundRangeCounter::CompoundRangeCounter( int size, - int singleIntervalLimit ) : - _i( size, -1 ), - _singleIntervalCount(), - _singleIntervalLimit( singleIntervalLimit ) { - } - - string FieldRangeVectorIterator::CompoundRangeCounter::toString() const { - BSONArrayBuilder bab; - for( vector<int>::const_iterator i = _i.begin(); i != _i.end(); ++i ) { - bab << *i; - } - return BSON( "i" << bab.arr() << - "count" << _singleIntervalCount << - "limit" << _singleIntervalLimit - ).jsonString(); - } - - FieldRangeVectorIterator::FieldIntervalMatcher::FieldIntervalMatcher - ( const FieldInterval &interval, const BSONElement &element, bool reverse ) : - _interval( interval ), - _element( element ), - _reverse( reverse ) { - } - - int FieldRangeVectorIterator::FieldIntervalMatcher::lowerCmp() const { - if ( !_lowerCmp._valid ) { - setCmp( _lowerCmp, _interval._lower._bound ); - } - return _lowerCmp._cmp; - } - - int FieldRangeVectorIterator::FieldIntervalMatcher::upperCmp() const { - if ( !_upperCmp._valid ) { - setCmp( _upperCmp, _interval._upper._bound ); - if ( _interval.equality() ) { - _lowerCmp = _upperCmp; - } - } - return _upperCmp._cmp; - } - - OrRangeGenerator::OrRangeGenerator(const char *ns, const BSONObj &query, bool optimize) - : _baseSet(ns, query, optimize), _orFound() { - BSONObjIterator i( _baseSet.originalQuery() ); - - while (i.more()) { - BSONElement e = i.next(); - if (strcmp(e.fieldName(), "$or") == 0) { - uassert( 13262, "$or requires nonempty array", e.type() == Array && e.embeddedObject().nFields() > 0 ); - BSONObjIterator j( e.embeddedObject() ); - while( j.more() ) { - BSONElement f = j.next(); - uassert( 13263, "$or array must contain objects", f.type() == Object ); - _orSets.push_back( FieldRangeSetPair( ns, f.embeddedObject(), optimize ) ); - uassert( 13291, "$or may not contain 'special' query", _orSets.back().getSpecial().empty() ); - _originalOrSets.push_back( _orSets.back() ); - } - _orFound = true; - continue; - } - } - } - - void OrRangeGenerator::assertMayPopOrClause() { - massert( 13274, "no or clause to pop", _orFound && !orRangesExhausted() ); - } - - void OrRangeGenerator::popOrClauseSingleKey() { - assertMayPopOrClause(); - FieldRangeSet *toDiff = &_originalOrSets.front()._singleKey; - _popOrClause( toDiff, -1, BSONObj() ); - } - - void OrRangeGenerator::_popOrClause( const FieldRangeSet *toDiff, int idxNo, const BSONObj &keyPattern ) { - list<FieldRangeSetPair>::iterator i = _orSets.begin(); - list<FieldRangeSetPair>::iterator j = _originalOrSets.begin(); - ++i; - ++j; - while( i != _orSets.end() ) { - *i -= *toDiff; - // Check if match is possible at all, and if it is possible for the recently scanned index. - if( !i->matchPossible() ) { - i = _orSets.erase( i ); - j = _originalOrSets.erase( j ); - } - else { - ++i; - ++j; - } - } - _oldOrSets.push_front( _orSets.front() ); - _orSets.pop_front(); - _originalOrSets.pop_front(); - } - - - long long applySkipLimit( long long num , const BSONObj& cmd ) { - BSONElement s = cmd["skip"]; - BSONElement l = cmd["limit"]; - - if ( s.isNumber() ) { - num = num - s.numberLong(); - if ( num < 0 ) { - num = 0; - } - } - - if ( l.isNumber() ) { - long long limit = l.numberLong(); - if( limit < 0 ){ - limit = -limit; - } - - if ( limit < num && limit != 0 ) { // 0 limit means no limit - num = limit; - } - } - - return num; - } - - bool isSimpleIdQuery( const BSONObj& query ) { - BSONObjIterator i(query); - - if( !i.more() ) - return false; - - BSONElement e = i.next(); - - if( i.more() ) - return false; - - if( strcmp("_id", e.fieldName()) != 0 ) - return false; - - if ( e.isSimpleType() ) // e.g. not something like { _id : { $gt : ... - return true; - - if ( e.type() == Object ) - return e.Obj().firstElementFieldName()[0] != '$'; - - return false; - } - -} // namespace mongo diff --git a/src/mongo/db/queryutil.h b/src/mongo/db/queryutil.h deleted file mode 100644 index 23ab584f11e..00000000000 --- a/src/mongo/db/queryutil.h +++ /dev/null @@ -1,729 +0,0 @@ -/* Copyright 2009 10gen Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "jsobj.h" -#include "mongo/db/index/btree_key_generator.h" - -namespace mongo { - - //maximum number of intervals produced by $in queries. - static const unsigned MAX_IN_COMBINATIONS = 4000000; - - /** - * One side of an interval of BSONElements, defined by a value and a boolean indicating if the - * interval includes the value. - */ - struct FieldBound { - BSONElement _bound; - bool _inclusive; - bool operator==( const FieldBound &other ) const { - return _bound.woCompare( other._bound ) == 0 && - _inclusive == other._inclusive; - } - void flipInclusive() { _inclusive = !_inclusive; } - }; - - // Keep track of what special indices we're using. This can be nontrivial - // because an index could be required by one operator but not by another. - struct SpecialIndices { - // Unlike true/false, this is readable. :) - enum IndexRequired { - INDEX_REQUIRED, - NO_INDEX_REQUIRED, - }; - map<string, bool> _indexRequired; - - bool has(const string& name) const { - return _indexRequired.end() != _indexRequired.find(name); - } - - SpecialIndices combineWith(const SpecialIndices& other) { - SpecialIndices ret = *this; - for (map<string, bool>::const_iterator it = other._indexRequired.begin(); - it != other._indexRequired.end(); ++it) { - ret._indexRequired[it->first] = ret._indexRequired[it->first] || it->second; - } - return ret; - } - - - void add(const string& name, IndexRequired req) { - _indexRequired[name] = _indexRequired[name] || (req == INDEX_REQUIRED); - } - - bool allRequireIndex() const { - for (map<string, bool>::const_iterator it = _indexRequired.begin(); - it != _indexRequired.end(); ++it) { - if (!it->second) { return false; } - } - return true; - } - - bool empty() const { return _indexRequired.empty(); } - string toString() const { - stringstream ss; - for (map<string, bool>::const_iterator it = _indexRequired.begin(); - it != _indexRequired.end(); ++it) { - ss << it->first; - ss << (it->second ? " (needs index)" : " (no index needed)"); - ss << ", "; - } - return ss.str(); - } - }; - - /** An interval defined between a lower and an upper FieldBound. */ - struct FieldInterval { - FieldInterval() : _cachedEquality( -1 ) {} - FieldInterval( const BSONElement& e ) : _cachedEquality( -1 ) { - _lower._bound = _upper._bound = e; - _lower._inclusive = _upper._inclusive = true; - } - FieldBound _lower; - FieldBound _upper; - /** - * @return true when the interval can contain one or more values. - * NOTE May also return true in certain 'empty' discrete cases like x > false && x < true. - */ - bool isStrictValid() const { - int cmp = _lower._bound.woCompare( _upper._bound, false ); - return ( cmp < 0 || ( cmp == 0 && _lower._inclusive && _upper._inclusive ) ); - } - /** @return true if the interval is an equality constraint. */ - bool equality() const; - mutable int _cachedEquality; - - string toString() const; - }; - - /** - * An ordered list of FieldIntervals expressing constraints on valid - * BSONElement values for a field. - */ - class FieldRange { - public: - /** - * Creates a FieldRange representing a superset of the BSONElement values matching a query - * expression element. - * @param e - The query expression element. - * @param isNot - Indicates that 'e' appears within a query $not clause and its matching - * semantics are inverted. - * @param optimize - If true, the range may be bracketed by 'e''s data type. - * TODO It is unclear why 'optimize' is optional, see SERVER-5165. - */ - FieldRange( const BSONElement &e , bool isNot, bool optimize ); - - void setElemMatchContext( const BSONElement& elemMatchContext ) { - _elemMatchContext = elemMatchContext; - } - - /** - * @return Range intersection with 'other'. - * @param singleKey - Indicate whether intersection will be performed in a single value or - * multi value context. - */ - const FieldRange &intersect( const FieldRange &other, bool singleKey ); - /** @return Range union with 'other'. */ - const FieldRange &operator|=( const FieldRange &other ); - /** @return Range of elements elements included in 'this' but not 'other'. */ - const FieldRange &operator-=( const FieldRange &other ); - /** @return true iff this range is a subset of 'other'. */ - bool operator<=( const FieldRange &other ) const; - - /** - * If there are any valid values for this range, the extreme values can - * be extracted. - */ - - BSONElement min() const { verify( !empty() ); return _intervals[ 0 ]._lower._bound; } - BSONElement max() const { verify( !empty() ); return _intervals[ _intervals.size() - 1 ]._upper._bound; } - bool minInclusive() const { verify( !empty() ); return _intervals[ 0 ]._lower._inclusive; } - bool maxInclusive() const { verify( !empty() ); return _intervals[ _intervals.size() - 1 ]._upper._inclusive; } - - /** @return true iff this range expresses a single equality interval. */ - bool equality() const; - /** - * @return true iff this range includes all BSONElements - * (the range is the universal set of BSONElements). - */ - bool universal() const; - /** @return true iff this range includes no BSONElements. */ - bool empty() const { return _intervals.empty(); } - /** - * @return true in many cases when this FieldRange represents the exact set of BSONElement - * values matching the query expression element used to construct the FieldRange. This - * attribute is used to implement higher level optimizations and is computed with a simple - * implementation that identifies common (but not all) cases of this property and may return - * false negatives. - */ - bool mustBeExactMatchRepresentation() const { return _exactMatchRepresentation; } - /* Checks whether this FieldRange is a non-empty union of point-intervals. - * Examples: - * FieldRange( { a:3 } ), isPointIntervalSet() -> true - * FieldRange( { a:{ $in:[ 1, 2 ] } } ), isPointIntervalSet() -> true - * FieldRange( { a:{ $gt:5 } } ), isPointIntervalSet() -> false - * FieldRange( {} ), isPointIntervalSet() -> false - */ - bool isPointIntervalSet() const; - const BSONElement& elemMatchContext() const { return _elemMatchContext; } - - /** Empty the range so it includes no BSONElements. */ - void makeEmpty() { _intervals.clear(); } - const vector<FieldInterval> &intervals() const { return _intervals; } - const SpecialIndices& getSpecial() const { return _special; } - /** Make component intervals noninclusive. */ - void setExclusiveBounds(); - /** - * Constructs a range where all FieldIntervals and FieldBounds are in - * the opposite order of the current range. - * NOTE the resulting intervals might not be strictValid(). - */ - void reverse( FieldRange &ret ) const; - - string toString() const; - private: - BSONObj addObj( const BSONObj &o ); - void finishOperation( const vector<FieldInterval> &newIntervals, const FieldRange &other, - bool exactMatchRepresentation ); - vector<FieldInterval> _intervals; - // Owns memory for our BSONElements. - vector<BSONObj> _objData; - SpecialIndices _special; // Index type name of a non standard (eg '2d') index required by a - // parsed query operator (eg '$near'). Could be >1. - bool _exactMatchRepresentation; - BSONElement _elemMatchContext; // Parent $elemMatch object of the field constraint that - // generated this FieldRange. For example if the query is - // { a:{ $elemMatch:{ b:1, c:1 } } }, then the - // _elemMatchContext for the FieldRange on 'a.b' is the query - // element having field name '$elemMatch'. - }; - - /** - * A set of FieldRanges determined from constraints on the fields of a query, - * that may be used to determine index bounds. - */ - class FieldRangeSet { - public: - friend class OrRangeGenerator; - friend class FieldRangeVector; - /** - * Creates a FieldRangeSet representing a superset of the documents matching a query. - * @param ns - The query's namespace. - * @param query - The query. - * @param singleKey - Indicates that document fields contain single values (there are no - * multiply valued fields). - * @param optimize - If true, each field's value range may be bracketed by data type. - * TODO It is unclear why 'optimize' is optional, see SERVER-5165. - */ - FieldRangeSet( const char *ns, const BSONObj &query , bool singleKey , bool optimize ); - - /** @return range for the given field. */ - const FieldRange &range( const char *fieldName ) const; - /** @return range for the given field. Public for testing. */ - FieldRange &range( const char *fieldName ); - /** @return the number of non universal ranges. */ - int numNonUniversalRanges() const; - /** @return the field ranges comprising this set. */ - const map<string,FieldRange> &ranges() const { return _ranges; } - /** - * @return true if a match could be possible on every field. Generally this - * is not useful information for a single key FieldRangeSet and - * matchPossibleForIndex() should be used instead. - */ - bool matchPossible() const; - /** - * @return true if a match could be possible given the value of _singleKey - * and index key 'keyPattern'. - * @param keyPattern May be {} or {$natural:1} for a non index scan. - */ - bool matchPossibleForIndex( const BSONObj &keyPattern ) const; - /** - * @return true in many cases when this FieldRangeSet represents the exact set of BSONObjs - * matching the query expression used to construct the FieldRangeSet. This attribute is - * used to implement higher level optimizations and is computed with a simple implementation - * that identifies common (but not all) cases of this property and may return false - * negatives. - */ - bool mustBeExactMatchRepresentation() const { return _exactMatchRepresentation; } - - /* Checks whether this FieldRangeSet is a non-empty union of point-intervals - * on a given field. - * Examples: - * FieldRangeSet({a : 3}), isPointIntervalSet("a") -> true - * FieldRangeSet({a : {$in : [1 , 2]}}), isPointIntervalSet("a") -> true - * FieldRangeSet({}), isPointIntervalSet("a") -> false - * FieldRangeSet({b : 1}), isPointIntervalSet("a") -> false - * - * Used in determining "suitability" for hashedindexes, and also in - * sharding for determining the relevant shards for a query. - * - * TODO: move this into FieldRange instead of FieldRangeSet - */ - bool isPointIntervalSet( const string& fieldname ) const; - - const char *ns() const { return _ns.c_str(); } - - // QueryPattern pattern( const BSONObj &sort = BSONObj() ) const; - SpecialIndices getSpecial() const; - - /** - * @return a FieldRangeSet approximation of the documents in 'this' but - * not in 'other'. The approximation will be a superset of the documents - * in 'this' but not 'other'. - */ - const FieldRangeSet &operator-=( const FieldRangeSet &other ); - /** @return intersection of 'this' with 'other'. */ - const FieldRangeSet &operator&=( const FieldRangeSet &other ); - - /** - * @return - A new FieldRangeSet based on this FieldRangeSet, but with only - * a subset of the fields. - * @param fields - Only fields which are represented as field names in this object - * will be included in the returned FieldRangeSet. - */ - FieldRangeSet *subset( const BSONObj &fields ) const; - - /** - * @return A new FieldRangeSet based on this FieldRangeSet, but with all field names - * prefixed by the specified @param prefix field name. - */ - FieldRangeSet* prefixed( const string& prefix ) const; - - bool singleKey() const { return _singleKey; } - - BSONObj originalQuery() const { return _queries[ 0 ]; } - - string toString() const; - - private: - /** - * Private constructor for implementation specific delegate objects. - * @param boundElemMatch - If false, FieldRanges will not be computed for $elemMatch - * expressions. - */ - FieldRangeSet( const char* ns, const BSONObj& query, bool singleKey, bool optimize, - bool boundElemMatch ); - /** Initializer shared by the constructors. */ - void init( bool optimize ); - - void appendQueries( const FieldRangeSet &other ); - void makeEmpty(); - - /** - * Query parsing routines. - * TODO integrate these with an external query parser shared by the matcher. SERVER-1009 - */ - void handleMatchField( const BSONElement& matchElement, bool optimize ); - void handleConjunctionClauses( const BSONObj& clauses, bool optimize ); - void handleOp( const char* matchFieldName, const BSONElement& op, bool isNot, - bool optimize ); - void handleNotOp( const char* matchFieldName, const BSONElement& notOp, bool optimize ); - void handleElemMatch( const char* matchFieldName, const BSONElement& elemMatch, bool isNot, - bool optimize ); - - /** Must be called when a match element is skipped or modified to generate a FieldRange. */ - void adjustMatchField(); - void intersectMatchField( const char *fieldName, const BSONElement &matchElement, - bool isNot, bool optimize ); - static FieldRange *__universalRange; - const FieldRange &universalRange() const; - map<string,FieldRange> _ranges; - string _ns; - // Owns memory for FieldRange BSONElements. - vector<BSONObj> _queries; - bool _singleKey; - bool _exactMatchRepresentation; - bool _boundElemMatch; - }; - - /** - * A pair of FieldRangeSets, one representing constraints for single key - * indexes and the other representing constraints for multi key indexes and - * unindexed scans. In several member functions the caller is asked to - * supply an index so that the implementation may utilize the proper - * FieldRangeSet and return results that are appropriate with respect to that - * supplied index. - */ - class FieldRangeSetPair { - public: - FieldRangeSetPair( const char *ns, const BSONObj &query, bool optimize=true ) - :_singleKey( ns, query, true, optimize ), _multiKey( ns, query, false, optimize ) {} - - /** @return a field range in the single key FieldRangeSet. */ - const FieldRange &shardKeyRange( const char *fieldName ) const { - return _singleKey.range( fieldName ); - } - /** @return true if the range limits are equivalent to an empty query. */ - bool noNonUniversalRanges() const; - /** @return false if a match is impossible regardless of index. */ - bool matchPossible() const { return _multiKey.matchPossible(); } - /** - * @return false if a match is impossible on the specified index. - * @param idxNo -1 for non index scan. - */ - const char *ns() const { return _singleKey.ns(); } - - SpecialIndices getSpecial() const { return _singleKey.getSpecial(); } - - /** Intersect with another FieldRangeSetPair. */ - FieldRangeSetPair &operator&=( const FieldRangeSetPair &other ); - /** - * Subtract a FieldRangeSet, generally one expressing a range that has - * already been scanned. - */ - FieldRangeSetPair &operator-=( const FieldRangeSet &scanned ); - - bool matchPossibleForSingleKeyFRS( const BSONObj &keyPattern ) const { - return _singleKey.matchPossibleForIndex( keyPattern ); - } - - BSONObj originalQuery() const { return _singleKey.originalQuery(); } - - const FieldRangeSet getSingleKeyFRS() const { return _singleKey; } - const FieldRangeSet getMultiKeyFRS() const { return _singleKey; } - - string toString() const; - private: - FieldRangeSetPair( const FieldRangeSet &singleKey, const FieldRangeSet &multiKey ) - :_singleKey( singleKey ), _multiKey( multiKey ) {} - /** matchPossibleForIndex() must be true. */ - FieldRangeSet _singleKey; - FieldRangeSet _multiKey; - friend class OrRangeGenerator; - friend struct QueryUtilIndexed; - }; - - /** - * An ordered list of fields and their FieldRanges, corresponding to valid - * index keys for a given index spec. - */ - class FieldRangeVector { - public: - /** - * @param frs The valid ranges for all fields, as defined by the query spec. None of the - * fields in indexSpec may be empty() ranges of frs. - * @param indexSpec The index spec (key pattern and info) - * @param direction The direction of index traversal - */ - FieldRangeVector( const FieldRangeSet &frs, BSONObj keyPattern, int direction ); - - /** - * Methods for identifying compound start and end btree bounds describing this field range - * vector. - * - * A FieldRangeVector contains the FieldRange bounds for every field of an index. A - * FieldRangeVectorIterator may be used to efficiently search for btree keys within these - * bounds. Alternatively, a single compound field interval of the btree may be scanned, - * between a compound field start point and end point. If isSingleInterval() is true then - * the interval between the start and end points will be an exact description of this - * FieldRangeVector, otherwise the start/end interval will be a superset of this - * FieldRangeVector. For example: - * - * index { a:1 }, query { a:{ $gt:2, $lte:4 } } - * -> frv ( 2, 4 ] - * -> start/end bounds ( { '':2 }, { '':4 } ] - * - * index { a:1, b:1 }, query { a:2, b:{ $gte:7, $lt:9 } } - * -> frv [ 2, 2 ], [ 7, 9 ) - * -> start/end bounds [ { '':2, '':7 }, { '':2, '':9 } ) - * - * index { a:1, b:-1 }, query { a:2, b:{ $gte:7, $lt:9 } } - * -> frv [ 2, 2 ], ( 9, 7 ] - * -> start/end bounds ( { '':2, '':9 }, { '':2, '':7 } ] - * - * index { a:1, b:1 }, query { a:{ $gte:7, $lt:9 } } - * -> frv [ 7, 9 ) - * -> start/end bounds [ { '':7, '':MinKey }, { '':9, '':MinKey } ) - * - * index { a:1, b:1 }, query { a:{ $gte:2, $lte:5 }, b:{ $gte:7, $lte:9 } } - * -> frv [ 2, 5 ], [ 7, 9 ] - * -> start/end bounds [ { '':2, '':7 }, { '':5, '':9 } ] - * (isSingleInterval() == false) - */ - - /** - * @return true if this FieldRangeVector represents a single interval within a btree, - * comprised of all keys between a single start point and a single end point. - */ - bool isSingleInterval() const; - - /** - * @return a starting point for an index traversal, a lower bound on the ranges represented - * by this FieldRangeVector according to the btree's native ordering. - */ - BSONObj startKey() const; - - /** @return true if the startKey() bound is inclusive. */ - bool startKeyInclusive() const; - - /** - * @return an end point for an index traversal, an upper bound on the ranges represented - * by this FieldRangeVector according to the btree's native ordering. - */ - BSONObj endKey() const; - - /** @return true if the endKey() bound is inclusive. */ - bool endKeyInclusive() const; - - /** @return the number of index ranges represented by 'this' */ - unsigned size() const; - - /** @return a client readable representation of 'this' */ - BSONObj obj() const; - - /** - * @return true iff the provided document matches valid ranges on all - * of this FieldRangeVector's fields, which is the case iff this document - * would be returned while scanning the index corresponding to this - * FieldRangeVector. This function is used for $or clause deduping. - */ - bool matches( const BSONObj &obj ) const; - - /** - * @return true if all values in the provided index key are contained within the field - * ranges of their respective fields in this FieldRangeVector. - * - * For example, given a query { a:3, b:4 } and index { a:1, b:1 }, the FieldRangeVector is - * [ [[ 3, 3 ]], [[ 4, 4 ]] ], consisting of field range [[ 3, 3 ]] on field 'a' and - * [[ 4, 4 ]] on field 'b'. The index key { '':3, '':4 } matches, but the index key - * { '':3, '':5 } does not match because the value 5 in the second field is not contained in - * the field range [[ 4, 4 ]] for field 'b'. - */ - bool matchesKey( const BSONObj& key ) const; - - /** - * @return first key of 'obj' that would be encountered by a forward - * index scan using this FieldRangeVector, BSONObj() if no such key. - */ - BSONObj firstMatch( const BSONObj &obj ) const; - - /** - * @return true if all ranges within the field range set on fields of this index are - * represented in this field range vector. May be false in certain multikey index cases - * when intervals on two fields cannot both be used, see comments related to SERVER-958 in - * FieldRangeVector(). - */ - bool hasAllIndexedRanges() const { return _hasAllIndexedRanges; } - - string toString() const; - - private: - int matchingLowElement( const BSONElement &e, int i, bool direction, bool &lowEquality ) const; - bool matchesElement( const BSONElement &e, int i, bool direction ) const; - vector<FieldRange> _ranges; - BSONObj _keyPattern; - int _direction; - vector<BSONObj> _queries; // make sure mem owned - bool _hasAllIndexedRanges; - friend class FieldRangeVectorIterator; - - vector<const char*> _fieldNames; - vector<BSONElement> _fixed; - // See FieldRangeVector::matches for comment on key generation. - scoped_ptr<BtreeKeyGenerator> _keyGenerator; - }; - - /** - * Helper class for iterating through an ordered representation of keys - * to find those keys that match a specified FieldRangeVector. - */ - class FieldRangeVectorIterator { - public: - /** - * @param v - a FieldRangeVector representing matching keys. - * @param singleIntervalLimit - The maximum number of keys to match a single (compound) - * interval before advancing to the next interval. Limit checking is disabled if 0. - */ - FieldRangeVectorIterator( const FieldRangeVector &v, int singleIntervalLimit ); - - /** - * @return Suggested advance method through an ordered list of keys with lookup support - * (generally a btree). - * -2 Iteration is complete, no need to advance further. - * -1 Advance to the next ordered key, without skipping. - * >=0 Skip parameter, let's call it 'r'. If after() is true, skip past the key prefix - * comprised of the first r elements of curr. For example, if curr is {a:1,b:1}, the - * index is {a:1,b:1}, the direction is 1, and r == 1, skip past {a:1,b:MaxKey}. If - * after() is false, skip to the key comprised of the first r elements of curr followed - * by the (r+1)th and greater elements of cmp() (with inclusivity specified by the - * (r+1)th and greater elements of inc()). For example, if curr is {a:1,b:1}, the - * index is {a:1,b:1}, the direction is 1, r == 1, cmp()[1] == b:4, and inc()[1] == - * true, then skip to {a:1,b:4}. Note that the element field names in curr and cmp() - * should generally be ignored when performing index key comparisons. - * @param curr The key at the current position in the list of keys. Values of curr must be - * supplied in order. - */ - int advance( const BSONObj &curr ); - const vector<const BSONElement *> &cmp() const { return _cmp; } - const vector<bool> &inc() const { return _inc; } - bool after() const { return _after; } - void prepDive(); - - /** - * Helper class representing a position within a vector of ranges. Public for testing. - */ - class CompoundRangeCounter { - public: - CompoundRangeCounter( int size, int singleIntervalLimit ); - int size() const { return (int)_i.size(); } - int get( int i ) const { return _i[ i ]; } - void set( int i, int newVal ); - void inc( int i ); - void setZeroes( int i ); - void setUnknowns( int i ); - void incSingleIntervalCount() { - if ( isTrackingIntervalCounts() ) ++_singleIntervalCount; - } - bool hasSingleIntervalCountReachedLimit() const { - return isTrackingIntervalCounts() && _singleIntervalCount >= _singleIntervalLimit; - } - void resetIntervalCount() { _singleIntervalCount = 0; } - string toString() const; - private: - bool isTrackingIntervalCounts() const { return _singleIntervalLimit > 0; } - vector<int> _i; - int _singleIntervalCount; - int _singleIntervalLimit; - }; - - /** - * Helper class for matching a BSONElement with the bounds of a FieldInterval. Some - * internal comparison results are cached. Public for testing. - */ - class FieldIntervalMatcher { - public: - FieldIntervalMatcher( const FieldInterval &interval, const BSONElement &element, - bool reverse ); - bool isEqInclusiveUpperBound() const { - return upperCmp() == 0 && _interval._upper._inclusive; - } - bool isGteUpperBound() const { return upperCmp() >= 0; } - bool isEqExclusiveLowerBound() const { - return lowerCmp() == 0 && !_interval._lower._inclusive; - } - bool isLtLowerBound() const { return lowerCmp() < 0; } - private: - struct BoundCmp { - BoundCmp() : _cmp(), _valid() {} - void set( int cmp ) { _cmp = cmp; _valid = true; } - int _cmp; - bool _valid; - }; - int mayReverse( int val ) const { return _reverse ? -val : val; } - int cmp( const BSONElement &bound ) const { - return mayReverse( _element.woCompare( bound, false ) ); - } - void setCmp( BoundCmp &boundCmp, const BSONElement &bound ) const { - boundCmp.set( cmp( bound ) ); - } - int lowerCmp() const; - int upperCmp() const; - const FieldInterval &_interval; - const BSONElement &_element; - bool _reverse; - mutable BoundCmp _lowerCmp; - mutable BoundCmp _upperCmp; - }; - - private: - /** - * @return values similar to advance() - * -2 Iteration is complete for the current interval. - * -1 Iteration is not complete for the current interval. - * >=0 Return value to be forwarded by advance(). - */ - int validateCurrentInterval( int intervalIdx, const BSONElement &currElt, - bool reverse, bool first, bool &eqInclusiveUpperBound ); - - /** Skip to curr / i / nextbounds. */ - int advanceToLowerBound( int i ); - /** Skip to curr / i / superlative. */ - int advancePast( int i ); - /** Skip to curr / i / superlative and reset following interval positions. */ - int advancePastZeroed( int i ); - - bool hasReachedLimitForLastInterval( int intervalIdx ) const { - return - _i.hasSingleIntervalCountReachedLimit() && - ( intervalIdx + 1 == _endNonUniversalRanges ); - } - - /** @return the index of the last non universal range + 1. */ - int endNonUniversalRanges() const; - - const FieldRangeVector &_v; - CompoundRangeCounter _i; - vector<const BSONElement*> _cmp; - vector<bool> _inc; - bool _after; - int _endNonUniversalRanges; - }; - - /** - * As we iterate through $or clauses this class generates a FieldRangeSetPair - * for the current $or clause, in some cases by excluding ranges that were - * included in a previous clause. - */ - class OrRangeGenerator { - public: - OrRangeGenerator( const char *ns, const BSONObj &query , bool optimize=true ); - - /** @return true iff we are done scanning $or clauses, or if there are no $or clauses. */ - bool orRangesExhausted() const { return _orSets.empty(); } - /** Iterates to the next $or clause by removing the current $or clause. */ - void popOrClauseSingleKey(); - /** @return FieldRangeSetPair for the current $or clause. */ - FieldRangeSetPair *topFrsp() const; - /** - * @return original FieldRangeSetPair for the current $or clause. While the - * original bounds are looser, they are composed of fewer ranges and it - * is faster to do operations with them; when they can be used instead of - * more precise bounds, they should. - */ - FieldRangeSetPair *topFrspOriginal() const; - - SpecialIndices getSpecial() const { return _baseSet.getSpecial(); } - private: - void assertMayPopOrClause(); - void _popOrClause( const FieldRangeSet *toDiff, int idxNo, const BSONObj &keyPattern ); - FieldRangeSetPair _baseSet; - list<FieldRangeSetPair> _orSets; - list<FieldRangeSetPair> _originalOrSets; - // ensure memory is owned - list<FieldRangeSetPair> _oldOrSets; - bool _orFound; - friend struct QueryUtilIndexed; - }; - - /** returns a string that when used as a matcher, would match a super set of regex() - returns "" for complex regular expressions - used to optimize queries in some simple regex cases that start with '^' - - if purePrefix != NULL, sets it to whether the regex can be converted to a range query - */ - string simpleRegex(const char* regex, const char* flags, bool* purePrefix=NULL); - - /** returns the upper bound of a query that matches prefix */ - string simpleRegexEnd( string prefix ); - - long long applySkipLimit( long long num , const BSONObj& cmd ); - - bool isSimpleIdQuery( const BSONObj& query ); - -} // namespace mongo - -#include "queryutil-inl.h" diff --git a/src/mongo/dbtests/namespacetests.cpp b/src/mongo/dbtests/namespacetests.cpp index 485855131fb..d0536b0bf8c 100644 --- a/src/mongo/dbtests/namespacetests.cpp +++ b/src/mongo/dbtests/namespacetests.cpp @@ -38,7 +38,6 @@ #include "mongo/db/index_names.h" #include "mongo/db/json.h" #include "mongo/db/query/internal_plans.h" -#include "mongo/db/queryutil.h" #include "mongo/db/storage/extent.h" #include "mongo/db/storage/extent_manager.h" #include "mongo/db/storage/mmap_v1/dur_transaction.h" diff --git a/src/mongo/dbtests/queryutiltests.cpp b/src/mongo/dbtests/queryutiltests.cpp deleted file mode 100644 index 0c78145fd77..00000000000 --- a/src/mongo/dbtests/queryutiltests.cpp +++ /dev/null @@ -1,2963 +0,0 @@ -// queryutiltests.cpp : query utility unit tests -// - -/** - * Copyright (C) 2009 10gen Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - * As a special exception, the copyright holders give permission to link the - * code of portions of this program with the OpenSSL library under certain - * conditions as described in each individual source file and distribute - * linked combinations including the program with the OpenSSL library. You - * must comply with the GNU Affero General Public License in all respects - * for all of the code used other than as permitted herein. If you modify - * file(s) with this exception, you may extend this exception to your - * version of the file(s), but you are not obligated to do so. If you do not - * wish to do so, delete this exception statement from your version. If you - * delete this exception statement from all source files in the program, - * then also delete it in the license file. - */ - -#include "mongo/pch.h" - -#include "mongo/db/catalog/collection.h" -#include "mongo/db/catalog/database.h" -#include "mongo/db/index/index_descriptor.h" -#include "mongo/db/instance.h" -#include "mongo/db/json.h" -#include "mongo/db/pdfile.h" -#include "mongo/db/queryutil.h" -#include "mongo/db/storage/mmap_v1/dur_transaction.h" -#include "mongo/db/structure/catalog/namespace_details.h" -#include "mongo/dbtests/dbtests.h" - -namespace QueryUtilTests { - - namespace FieldIntervalTests { - class ToString { - public: - void run() { - BSONObj obj = BSON( "a" << 1 ); - FieldInterval fieldInterval( obj.firstElement() ); - fieldInterval.toString(); // Just test that we don't crash. - } - }; - } // namespace FieldIntervalTests - - // - // TODO: Test cases inside namespace FieldRangeTests are testing - // code that will soon be fully deprecated and should eventually - // be deleted. - // - // Analogous tests for the new query system can be found in the - // index bounds builder test (db/query/index_bounds_builder_test.cpp). - // - namespace FieldRangeTests { - class ToString { - public: - void run() { - BSONObj obj = BSON( "a" << 1 ); - FieldRange fieldRange( obj.firstElement(), false, true ); - fieldRange.toString(); // Just test that we don't crash. - } - }; - - class Base { - public: - virtual ~Base() {} - void run() { - const FieldRangeSet s( "ns", query(), true, true ); - checkElt( lower(), s.range( "a" ).min() ); - checkElt( upper(), s.range( "a" ).max() ); - ASSERT_EQUALS( lowerInclusive(), s.range( "a" ).minInclusive() ); - ASSERT_EQUALS( upperInclusive(), s.range( "a" ).maxInclusive() ); - ASSERT_EQUALS( mustBeExactMatchRepresentation(), - s.range( "a" ).mustBeExactMatchRepresentation() ); - ASSERT_EQUALS( isPointIntervalSet(), - s.range( "a" ).isPointIntervalSet() ); - } - protected: - virtual BSONObj query() = 0; - virtual BSONElement lower() { return minKey.firstElement(); } - virtual bool lowerInclusive() { return true; } - virtual BSONElement upper() { return maxKey.firstElement(); } - virtual bool upperInclusive() { return true; } - virtual bool mustBeExactMatchRepresentation() { return false; } - virtual bool isPointIntervalSet() { return false; } - static void checkElt( BSONElement expected, BSONElement actual ) { - if ( expected.woCompare( actual, false ) ) { - mongo::unittest::log() << "expected: " << expected << ", got: " << actual; - ASSERT( false ); - } - } - }; - - - class NumericBase : public Base { - public: - NumericBase() { - o = BSON( "min" << -numeric_limits<double>::max() << "max" << numeric_limits<double>::max() ); - } - - virtual BSONElement lower() { return o["min"]; } - virtual BSONElement upper() { return o["max"]; } - private: - BSONObj o; - }; - - class EmptyQuery : public Base { - virtual BSONObj query() { return BSONObj(); } - virtual bool mustBeExactMatchRepresentation() { return true; } - }; - - class Eq : public Base { - public: - Eq() : o_( BSON( "a" << 1 ) ) {} - virtual BSONObj query() { return o_; } - virtual BSONElement lower() { return o_.firstElement(); } - virtual BSONElement upper() { return o_.firstElement(); } - virtual bool mustBeExactMatchRepresentation() { return true; } - virtual bool isPointIntervalSet() { return true; } - BSONObj o_; - }; - - class DupEq : public Eq { - public: - virtual BSONObj query() { return BSON( "a" << 1 << "b" << 2 << "a" << 1 ); } - }; - - class Lt : public NumericBase { - public: - Lt() : o_( BSON( "-" << 1 ) ) {} - virtual BSONObj query() { return BSON( "a" << LT << 1 ); } - virtual BSONElement upper() { return o_.firstElement(); } - virtual bool upperInclusive() { return false; } - virtual bool mustBeExactMatchRepresentation() { return true; } - BSONObj o_; - }; - - class Lte : public Lt { - virtual BSONObj query() { return BSON( "a" << LTE << 1 ); } - virtual bool upperInclusive() { return true; } - virtual bool mustBeExactMatchRepresentation() { return true; } - }; - - class LtDate : public Base { - public: - LtDate() : - _o( BSON( "" << Date_t( 5000 ) ) ), - _o2( BSON( "" << true ) ) { - } - virtual BSONObj query() { return BSON( "a" << LT << _o.firstElement() ); } - virtual BSONElement lower() { - // $lt:date is bounded from below by 'true', the highest value of the next lowest - // canonical type. - return _o2.firstElement(); - } - virtual bool lowerInclusive() { - // 'true' should not match $lt:date, so the bound is exclusive. - return false; - } - virtual BSONElement upper() { return _o.firstElement(); } - virtual bool upperInclusive() { return false; } - virtual bool mustBeExactMatchRepresentation() { return true; } - private: - BSONObj _o, _o2; - }; - - class Gt : public NumericBase { - public: - Gt() : o_( BSON( "-" << 1 ) ) {} - virtual BSONObj query() { return BSON( "a" << GT << 1 ); } - virtual BSONElement lower() { return o_.firstElement(); } - virtual bool lowerInclusive() { return false; } - virtual bool mustBeExactMatchRepresentation() { return true; } - BSONObj o_; - }; - - class Gte : public Gt { - virtual BSONObj query() { return BSON( "a" << GTE << 1 ); } - virtual bool mustBeExactMatchRepresentation() { return true; } - virtual bool lowerInclusive() { return true; } - }; - - class GtString : public Base { - public: - GtString() : - _o( BSON( "" << "abc" ) ), - _o2( BSON( "" << BSONObj() ) ) { - } - virtual BSONObj query() { return BSON( "a" << GT << _o.firstElement() ); } - virtual BSONElement lower() { return _o.firstElement(); } - virtual bool lowerInclusive() { return false; } - virtual BSONElement upper() { - // $gt:string is bounded from above by '{}', the lowest value of the next highest - // canonical type. - return _o2.firstElement(); - } - virtual bool upperInclusive() { - // '{}' should not match $gt:string, so the bound is exclusive. - return false; - } - virtual bool mustBeExactMatchRepresentation() { return true; } - private: - BSONObj _o, _o2; - }; - - class TwoLt : public Lt { - virtual BSONObj query() { return BSON( "a" << LT << 1 << LT << 5 ); } - }; - - class TwoGt : public Gt { - virtual BSONObj query() { return BSON( "a" << GT << 0 << GT << 1 ); } - }; - - class EqGte : public Eq { - virtual BSONObj query() { return BSON( "a" << 1 << "a" << GTE << 1 ); } - }; - - class EqGteInvalid { - public: - void run() { - FieldRangeSet frs( "ns", BSON( "a" << 1 << "a" << GTE << 2 ), true, true ); - ASSERT( !frs.matchPossible() ); - } - }; - - struct RegexBase : Base { - void run() { //need to only look at first interval - FieldRangeSet s( "ns", query(), true, true ); - checkElt( lower(), s.range( "a" ).intervals()[0]._lower._bound ); - checkElt( upper(), s.range( "a" ).intervals()[0]._upper._bound ); - ASSERT_EQUALS( lowerInclusive(), s.range( "a" ).intervals()[0]._lower._inclusive ); - ASSERT_EQUALS( upperInclusive(), s.range( "a" ).intervals()[0]._upper._inclusive ); - } - }; - - class Regex : public RegexBase { - public: - Regex() : o1_( BSON( "" << "abc" ) ), o2_( BSON( "" << "abd" ) ) {} - virtual BSONObj query() { - BSONObjBuilder b; - b.appendRegex( "a", "^abc" ); - return b.obj(); - } - virtual BSONElement lower() { return o1_.firstElement(); } - virtual BSONElement upper() { return o2_.firstElement(); } - virtual bool upperInclusive() { return false; } - BSONObj o1_, o2_; - }; - - class RegexObj : public RegexBase { - public: - RegexObj() : o1_( BSON( "" << "abc" ) ), o2_( BSON( "" << "abd" ) ) {} - virtual BSONObj query() { return BSON("a" << BSON("$regex" << "^abc")); } - virtual BSONElement lower() { return o1_.firstElement(); } - virtual BSONElement upper() { return o2_.firstElement(); } - virtual bool upperInclusive() { return false; } - BSONObj o1_, o2_; - }; - - class UnhelpfulRegex : public RegexBase { - public: - UnhelpfulRegex() { - BSONObjBuilder b; - b.appendMinForType("lower", String); - b.appendMaxForType("upper", String); - limits = b.obj(); - } - - virtual BSONObj query() { - BSONObjBuilder b; - b.appendRegex( "a", "abc" ); - return b.obj(); - } - virtual BSONElement lower() { return limits["lower"]; } - virtual BSONElement upper() { return limits["upper"]; } - virtual bool upperInclusive() { return false; } - BSONObj limits; - }; - - class In : public Base { - public: - In() : o1_( BSON( "-" << -3 ) ), o2_( BSON( "-" << 44 ) ) {} - virtual BSONObj query() { - vector< int > vals; - vals.push_back( 4 ); - vals.push_back( 8 ); - vals.push_back( 44 ); - vals.push_back( -1 ); - vals.push_back( -3 ); - vals.push_back( 0 ); - BSONObjBuilder bb; - bb.append( "$in", vals ); - BSONObjBuilder b; - b.append( "a", bb.done() ); - return b.obj(); - } - virtual BSONElement lower() { return o1_.firstElement(); } - virtual BSONElement upper() { return o2_.firstElement(); } - virtual bool mustBeExactMatchRepresentation() { return true; } - virtual bool isPointIntervalSet() { return true; } - BSONObj o1_, o2_; - }; - - class And : public Base { - public: - And() : _o1( BSON( "-" << 0 ) ), _o2( BSON( "-" << 10 ) ) {} - void run() { - Base::run(); - const FieldRangeSet s( "ns", query(), true, true ); - // There should not be an index constraint recorded for the $and field. - ASSERT( s.range( "$and" ).universal() ); - } - private: - virtual BSONObj query() { - return BSON( "$and" << - BSON_ARRAY( BSON( "a" << GT << 0 ) << BSON( "a" << LTE << 10 ) ) ); - } - virtual bool mustBeExactMatchRepresentation() { return true; } - virtual BSONElement lower() { return _o1.firstElement(); } - virtual bool lowerInclusive() { return false; } - virtual BSONElement upper() { return _o2.firstElement(); } - BSONObj _o1, _o2; - }; - - /** - * Field bounds of a query clause within a singleton $or expression are intersected with - * those of the overall query. SERVER-6416 - */ - class SingletonOr : public Base { - public: - SingletonOr() : - _obj( BSON( "" << 5 ) ) { - } - void run() { - Base::run(); - const FieldRangeSet s( "ns", query(), true, true ); - // There should not be an index constraint recorded for the $or field. - ASSERT( s.range( "$or" ).universal() ); - } - private: - virtual BSONObj query() { - // There is a single $or clause. - return fromjson( "{$or:[{a:5}]}" ); - } - virtual bool mustBeExactMatchRepresentation() { - // The 'a' FieldRange is an exact match representation (it is an equality), though - // not the overall FieldRangeSet. - return true; - } - virtual bool isPointIntervalSet() { return true; } - virtual BSONElement lower() { return _obj.firstElement(); } - virtual BSONElement upper() { return _obj.firstElement(); } - BSONObj _obj; - }; - - /** - * Field bounds of a query clause within a nested singleton $or expression are intersected - * with those of the overall query. SERVER-6416 - */ - class NestedSingletonOr : public SingletonOr { - virtual BSONObj query() { - // There is a single $or clause. - return fromjson( "{$or:[{$or:[{a:5}]}]}" ); - } - }; - - /** - * Field bounds of a query clause within a non singleton $or expression are not intersected - * with those of the overall query. - */ - class NonSingletonOr : public Base { - public: - void run() { - Base::run(); - const FieldRangeSet s( "ns", query(), true, true ); - // There should not be an index constraint recorded for the $or field. - ASSERT( s.range( "$or" ).universal() ); - } - private: - virtual BSONObj query() { - // There is more than one $or clause. - return fromjson( "{$or:[{a:5},{a:6}]}" ); - } - virtual bool mustBeExactMatchRepresentation() { - // The 'a' FieldRange is an exact match representation (the default universal range - // matches anything), though not the overall FieldRangeSet. - return true; - } - }; - - class Empty { - public: - void run() { - FieldRangeSet s( "ns", BSON( "a" << GT << 5 << LT << 5 ), true, true ); - const FieldRange &r = s.range( "a" ); - ASSERT( r.empty() ); - ASSERT( !r.equality() ); - ASSERT( !r.universal() ); - ASSERT( r.mustBeExactMatchRepresentation() ); - } - }; - - class Equality { - public: - void run() { - FieldRangeSet s( "ns", BSON( "a" << 1 ), true, true ); - ASSERT( s.range( "a" ).equality() ); - FieldRangeSet s2( "ns", BSON( "a" << GTE << 1 << LTE << 1 ), true, true ); - ASSERT( s2.range( "a" ).equality() ); - FieldRangeSet s3( "ns", BSON( "a" << GT << 1 << LTE << 1 ), true, true ); - ASSERT( !s3.range( "a" ).equality() ); - FieldRangeSet s4( "ns", BSON( "a" << GTE << 1 << LT << 1 ), true, true ); - ASSERT( !s4.range( "a" ).equality() ); - FieldRangeSet s5( "ns", BSON( "a" << GTE << 1 << LTE << 1 << GT << 1 ), true, - true ); - ASSERT( !s5.range( "a" ).equality() ); - FieldRangeSet s6( "ns", BSON( "a" << GTE << 1 << LTE << 1 << LT << 1 ), true, - true ); - ASSERT( !s6.range( "a" ).equality() ); - } - }; - - class NoWhere { - public: - void run() { - ASSERT_EQUALS( 0, FieldRangeSet( "ns", BSON( "$where" << 1 ), true, true ) - .numNonUniversalRanges() ); - } - }; - - class Numeric { - public: - void run() { - FieldRangeSet f( "", BSON( "a" << 1 ), true, true ); - ASSERT( f.range( "a" ).min().woCompare( BSON( "a" << 2.0 ).firstElement() ) < 0 ); - ASSERT( f.range( "a" ).min().woCompare( BSON( "a" << 0.0 ).firstElement() ) > 0 ); - } - }; - - class InLowerBound { - public: - void run() { - FieldRangeSet f( "", fromjson( "{a:{$gt:4,$in:[1,2,3,4,5,6]}}" ), true, true ); - ASSERT( f.range( "a" ).min().woCompare( BSON( "a" << 5.0 ).firstElement(), false ) == 0 ); - ASSERT( f.range( "a" ).max().woCompare( BSON( "a" << 6.0 ).firstElement(), false ) == 0 ); - } - }; - - class InUpperBound { - public: - void run() { - FieldRangeSet f( "", fromjson( "{a:{$lt:4,$in:[1,2,3,4,5,6]}}" ), true, true ); - ASSERT( f.range( "a" ).min().woCompare( BSON( "a" << 1.0 ).firstElement(), false ) == 0 ); - ASSERT( f.range( "a" ).max().woCompare( BSON( "a" << 3.0 ).firstElement(), false ) == 0 ); - } - }; - - /** Check union of two non overlapping ranges. */ - class BoundUnion { - public: - void run() { - FieldRangeSet frs( "", fromjson( "{a:{$gt:1,$lt:9},b:{$gt:9,$lt:12}}" ), true, - true ); - FieldRange ret = frs.range( "a" ); - ret |= frs.range( "b" ); - ASSERT_EQUALS( 2U, ret.intervals().size() ); - } - }; - - /** Check union of two ranges where one includes another. */ - class BoundUnionFullyContained { - public: - void run() { - FieldRangeSet frs( "", - fromjson( "{a:{$gt:1,$lte:9},b:{$gt:2,$lt:8}," - "c:{$gt:2,$lt:9},d:{$gt:2,$lte:9}}" ), true, true ); - FieldRange u = frs.range( "a" ); - u |= frs.range( "b" ); - ASSERT_EQUALS( 1U, u.intervals().size() ); - ASSERT_EQUALS( frs.range( "a" ).toString(), u.toString() ); - u |= frs.range( "c" ); - ASSERT_EQUALS( 1U, u.intervals().size() ); - ASSERT_EQUALS( frs.range( "a" ).toString(), u.toString() ); - u |= frs.range( "d" ); - ASSERT_EQUALS( 1U, u.intervals().size() ); - ASSERT_EQUALS( frs.range( "a" ).toString(), u.toString() ); - } - }; - - /** - * Check union of two ranges where one does not include another because of an inclusive - * bound. - */ - class BoundUnionOverlapWithInclusivity { - public: - void run() { - FieldRangeSet frs( "", fromjson( "{a:{$gt:1,$lt:9},b:{$gt:2,$lte:9}}" ), true, - true ); - FieldRange u = frs.range( "a" ); - u |= frs.range( "b" ); - ASSERT_EQUALS( 1U, u.intervals().size() ); - FieldRangeSet expected( "", fromjson( "{a:{$gt:1,$lte:9}}" ), true, true ); - ASSERT_EQUALS( expected.range( "a" ).toString(), u.toString() ); - } - }; - - /** Check union of two empty ranges. */ - class BoundUnionEmpty { - public: - void run() { - FieldRangeSet frs( "", fromjson( "{a:{$in:[]},b:{$in:[]}}" ), true, true ); - FieldRange a = frs.range( "a" ); - a |= frs.range( "b" ); - ASSERT( a.empty() ); - ASSERT_EQUALS( 0U, a.intervals().size() ); - } - }; - - class MultiBound { - public: - void run() { - FieldRangeSet frs1( "", fromjson( "{a:{$in:[1,3,5,7,9]}}" ), true, true ); - FieldRangeSet frs2( "", fromjson( "{a:{$in:[2,3,5,8,9]}}" ), true, true ); - FieldRange fr1 = frs1.range( "a" ); - FieldRange fr2 = frs2.range( "a" ); - fr1.intersect( fr2, true ); - ASSERT( fr1.min().woCompare( BSON( "a" << 3.0 ).firstElement(), false ) == 0 ); - ASSERT( fr1.max().woCompare( BSON( "a" << 9.0 ).firstElement(), false ) == 0 ); - vector< FieldInterval > intervals = fr1.intervals(); - vector< FieldInterval >::const_iterator j = intervals.begin(); - double expected[] = { 3, 5, 9 }; - for( int i = 0; i < 3; ++i, ++j ) { - ASSERT_EQUALS( expected[ i ], j->_lower._bound.number() ); - ASSERT( j->_lower._inclusive ); - ASSERT( j->_lower == j->_upper ); - } - ASSERT( j == intervals.end() ); - } - }; - - class DiffBase { - public: - virtual ~DiffBase() {} - void run() { - FieldRangeSet frs( "", fromjson( obj().toString() ), true, true ); - FieldRange ret = frs.range( "a" ); - ret -= frs.range( "b" ); - check( ret ); - } - protected: - void check( const FieldRange &fr ) { - vector< FieldInterval > fi = fr.intervals(); - ASSERT_EQUALS( len(), fi.size() ); - int i = 0; - for( vector< FieldInterval >::const_iterator j = fi.begin(); j != fi.end(); ++j ) { - ASSERT_EQUALS( nums()[ i ], j->_lower._bound.numberInt() ); - ASSERT_EQUALS( incs()[ i ], j->_lower._inclusive ); - ++i; - ASSERT_EQUALS( nums()[ i ], j->_upper._bound.numberInt() ); - ASSERT_EQUALS( incs()[ i ], j->_upper._inclusive ); - ++i; - } - } - virtual unsigned len() const = 0; - virtual const int *nums() const = 0; - virtual const bool *incs() const = 0; - virtual BSONObj obj() const = 0; - }; - - class TwoRangeBase : public DiffBase { - public: - TwoRangeBase( string obj, int low, int high, bool lowI, bool highI ) - : _obj( obj ) { - _n[ 0 ] = low; - _n[ 1 ] = high; - _b[ 0 ] = lowI; - _b[ 1 ] = highI; - } - private: - virtual unsigned len() const { return 1; } - virtual const int *nums() const { return _n; } - virtual const bool *incs() const { return _b; } - virtual BSONObj obj() const { return fromjson( _obj ); } - string _obj; - int _n[ 2 ]; - bool _b[ 2 ]; - }; - - struct Diff1 : public TwoRangeBase { - Diff1() : TwoRangeBase( "{a:{$gt:1,$lt:2},b:{$gt:3,$lt:4}}", 1, 2, false, false ) {} - }; - - struct Diff2 : public TwoRangeBase { - Diff2() : TwoRangeBase( "{a:{$gt:1,$lt:2},b:{$gt:2,$lt:4}}", 1, 2, false, false ) {} - }; - - struct Diff3 : public TwoRangeBase { - Diff3() : TwoRangeBase( "{a:{$gt:1,$lte:2},b:{$gt:2,$lt:4}}", 1, 2, false, true ) {} - }; - - struct Diff4 : public TwoRangeBase { - Diff4() : TwoRangeBase( "{a:{$gt:1,$lt:2},b:{$gte:2,$lt:4}}", 1, 2, false, false) {} - }; - - struct Diff5 : public TwoRangeBase { - Diff5() : TwoRangeBase( "{a:{$gt:1,$lte:2},b:{$gte:2,$lt:4}}", 1, 2, false, false) {} - }; - - struct Diff6 : public TwoRangeBase { - Diff6() : TwoRangeBase( "{a:{$gt:1,$lte:3},b:{$gte:2,$lt:4}}", 1, 2, false, false) {} - }; - - struct Diff7 : public TwoRangeBase { - Diff7() : TwoRangeBase( "{a:{$gt:1,$lte:3},b:{$gt:2,$lt:4}}", 1, 2, false, true) {} - }; - - struct Diff8 : public TwoRangeBase { - Diff8() : TwoRangeBase( "{a:{$gt:1,$lt:4},b:{$gt:2,$lt:4}}", 1, 2, false, true) {} - }; - - struct Diff9 : public TwoRangeBase { - Diff9() : TwoRangeBase( "{a:{$gt:1,$lt:4},b:{$gt:2,$lte:4}}", 1, 2, false, true) {} - }; - - struct Diff10 : public TwoRangeBase { - Diff10() : TwoRangeBase( "{a:{$gt:1,$lte:4},b:{$gt:2,$lte:4}}", 1, 2, false, true) {} - }; - - class SplitRangeBase : public DiffBase { - public: - SplitRangeBase( string obj, int low1, bool low1I, int high1, bool high1I, int low2, bool low2I, int high2, bool high2I ) - : _obj( obj ) { - _n[ 0 ] = low1; - _n[ 1 ] = high1; - _n[ 2 ] = low2; - _n[ 3 ] = high2; - _b[ 0 ] = low1I; - _b[ 1 ] = high1I; - _b[ 2 ] = low2I; - _b[ 3 ] = high2I; - } - private: - virtual unsigned len() const { return 2; } - virtual const int *nums() const { return _n; } - virtual const bool *incs() const { return _b; } - virtual BSONObj obj() const { return fromjson( _obj ); } - string _obj; - int _n[ 4 ]; - bool _b[ 4 ]; - }; - - struct Diff11 : public SplitRangeBase { - Diff11() : SplitRangeBase( "{a:{$gt:1,$lte:4},b:{$gt:2,$lt:4}}", 1, false, 2, true, 4, true, 4, true) {} - }; - - struct Diff12 : public SplitRangeBase { - Diff12() : SplitRangeBase( "{a:{$gt:1,$lt:5},b:{$gt:2,$lt:4}}", 1, false, 2, true, 4, true, 5, false) {} - }; - - struct Diff13 : public TwoRangeBase { - Diff13() : TwoRangeBase( "{a:{$gt:1,$lt:5},b:{$gt:1,$lt:4}}", 4, 5, true, false) {} - }; - - struct Diff14 : public SplitRangeBase { - Diff14() : SplitRangeBase( "{a:{$gte:1,$lt:5},b:{$gt:1,$lt:4}}", 1, true, 1, true, 4, true, 5, false) {} - }; - - struct Diff15 : public TwoRangeBase { - Diff15() : TwoRangeBase( "{a:{$gt:1,$lt:5},b:{$gte:1,$lt:4}}", 4, 5, true, false) {} - }; - - struct Diff16 : public TwoRangeBase { - Diff16() : TwoRangeBase( "{a:{$gte:1,$lt:5},b:{$gte:1,$lt:4}}", 4, 5, true, false) {} - }; - - struct Diff17 : public TwoRangeBase { - Diff17() : TwoRangeBase( "{a:{$gt:1,$lt:5},b:{$gt:0,$lt:4}}", 4, 5, true, false) {} - }; - - struct Diff18 : public TwoRangeBase { - Diff18() : TwoRangeBase( "{a:{$gt:1,$lt:5},b:{$gt:0,$lte:4}}", 4, 5, false, false) {} - }; - - struct Diff19 : public TwoRangeBase { - Diff19() : TwoRangeBase( "{a:{$gte:1,$lte:5},b:{$gte:0,$lte:1}}", 1, 5, false, true) {} - }; - - struct Diff20 : public TwoRangeBase { - Diff20() : TwoRangeBase( "{a:{$gt:1,$lte:5},b:{$gte:0,$lte:1}}", 1, 5, false, true) {} - }; - - struct Diff21 : public TwoRangeBase { - Diff21() : TwoRangeBase( "{a:{$gte:1,$lte:5},b:{$gte:0,$lt:1}}", 1, 5, true, true) {} - }; - - struct Diff22 : public TwoRangeBase { - Diff22() : TwoRangeBase( "{a:{$gt:1,$lte:5},b:{$gte:0,$lt:1}}", 1, 5, false, true) {} - }; - - struct Diff23 : public TwoRangeBase { - Diff23() : TwoRangeBase( "{a:{$gt:1,$lte:5},b:{$gte:0,$lt:0.5}}", 1, 5, false, true) {} - }; - - struct Diff24 : public TwoRangeBase { - Diff24() : TwoRangeBase( "{a:{$gt:1,$lte:5},b:0}", 1, 5, false, true) {} - }; - - struct Diff25 : public TwoRangeBase { - Diff25() : TwoRangeBase( "{a:{$gte:1,$lte:5},b:0}", 1, 5, true, true) {} - }; - - struct Diff26 : public TwoRangeBase { - Diff26() : TwoRangeBase( "{a:{$gt:1,$lte:5},b:1}", 1, 5, false, true) {} - }; - - struct Diff27 : public TwoRangeBase { - Diff27() : TwoRangeBase( "{a:{$gte:1,$lte:5},b:1}", 1, 5, false, true) {} - }; - - struct Diff28 : public SplitRangeBase { - Diff28() : SplitRangeBase( "{a:{$gte:1,$lte:5},b:3}", 1, true, 3, false, 3, false, 5, true) {} - }; - - struct Diff29 : public TwoRangeBase { - Diff29() : TwoRangeBase( "{a:{$gte:1,$lte:5},b:5}", 1, 5, true, false) {} - }; - - struct Diff30 : public TwoRangeBase { - Diff30() : TwoRangeBase( "{a:{$gte:1,$lt:5},b:5}", 1, 5, true, false) {} - }; - - struct Diff31 : public TwoRangeBase { - Diff31() : TwoRangeBase( "{a:{$gte:1,$lt:5},b:6}", 1, 5, true, false) {} - }; - - struct Diff32 : public TwoRangeBase { - Diff32() : TwoRangeBase( "{a:{$gte:1,$lte:5},b:6}", 1, 5, true, true) {} - }; - - class EmptyBase : public DiffBase { - public: - EmptyBase( string obj ) - : _obj( obj ) {} - private: - virtual unsigned len() const { return 0; } - virtual const int *nums() const { return 0; } - virtual const bool *incs() const { return 0; } - virtual BSONObj obj() const { return fromjson( _obj ); } - string _obj; - }; - - struct Diff33 : public EmptyBase { - Diff33() : EmptyBase( "{a:{$gte:1,$lte:5},b:{$gt:0,$lt:6}}" ) {} - }; - - struct Diff34 : public EmptyBase { - Diff34() : EmptyBase( "{a:{$gte:1,$lte:5},b:{$gte:1,$lt:6}}" ) {} - }; - - struct Diff35 : public EmptyBase { - Diff35() : EmptyBase( "{a:{$gt:1,$lte:5},b:{$gte:1,$lt:6}}" ) {} - }; - - struct Diff36 : public EmptyBase { - Diff36() : EmptyBase( "{a:{$gt:1,$lte:5},b:{$gt:1,$lt:6}}" ) {} - }; - - struct Diff37 : public TwoRangeBase { - Diff37() : TwoRangeBase( "{a:{$gte:1,$lte:5},b:{$gt:1,$lt:6}}", 1, 1, true, true ) {} - }; - - struct Diff38 : public EmptyBase { - Diff38() : EmptyBase( "{a:{$gt:1,$lt:5},b:{$gt:0,$lt:5}}" ) {} - }; - - struct Diff39 : public EmptyBase { - Diff39() : EmptyBase( "{a:{$gt:1,$lt:5},b:{$gt:0,$lte:5}}" ) {} - }; - - struct Diff40 : public EmptyBase { - Diff40() : EmptyBase( "{a:{$gt:1,$lte:5},b:{$gt:0,$lte:5}}" ) {} - }; - - struct Diff41 : public TwoRangeBase { - Diff41() : TwoRangeBase( "{a:{$gte:1,$lte:5},b:{$gt:0,$lt:5}}", 5, 5, true, true ) {} - }; - - struct Diff42 : public EmptyBase { - Diff42() : EmptyBase( "{a:{$gt:1,$lt:5},b:{$gt:1,$lt:5}}" ) {} - }; - - struct Diff43 : public EmptyBase { - Diff43() : EmptyBase( "{a:{$gt:1,$lt:5},b:{$gt:1,$lte:5}}" ) {} - }; - - struct Diff44 : public EmptyBase { - Diff44() : EmptyBase( "{a:{$gt:1,$lt:5},b:{$gte:1,$lt:5}}" ) {} - }; - - struct Diff45 : public EmptyBase { - Diff45() : EmptyBase( "{a:{$gt:1,$lt:5},b:{$gte:1,$lte:5}}" ) {} - }; - - struct Diff46 : public TwoRangeBase { - Diff46() : TwoRangeBase( "{a:{$gt:1,$lte:5},b:{$gt:1,$lt:5}}", 5, 5, true, true ) {} - }; - - struct Diff47 : public EmptyBase { - Diff47() : EmptyBase( "{a:{$gt:1,$lte:5},b:{$gt:1,$lte:5}}" ) {} - }; - - struct Diff48 : public TwoRangeBase { - Diff48() : TwoRangeBase( "{a:{$gt:1,$lte:5},b:{$gte:1,$lt:5}}", 5, 5, true, true ) {} - }; - - struct Diff49 : public EmptyBase { - Diff49() : EmptyBase( "{a:{$gt:1,$lte:5},b:{$gte:1,$lte:5}}" ) {} - }; - - struct Diff50 : public TwoRangeBase { - Diff50() : TwoRangeBase( "{a:{$gte:1,$lt:5},b:{$gt:1,$lt:5}}", 1, 1, true, true ) {} - }; - - struct Diff51 : public TwoRangeBase { - Diff51() : TwoRangeBase( "{a:{$gte:1,$lt:5},b:{$gt:1,$lte:5}}", 1, 1, true, true ) {} - }; - - struct Diff52 : public EmptyBase { - Diff52() : EmptyBase( "{a:{$gte:1,$lt:5},b:{$gte:1,$lt:5}}" ) {} - }; - - struct Diff53 : public EmptyBase { - Diff53() : EmptyBase( "{a:{$gte:1,$lt:5},b:{$gte:1,$lte:5}}" ) {} - }; - - struct Diff54 : public SplitRangeBase { - Diff54() : SplitRangeBase( "{a:{$gte:1,$lte:5},b:{$gt:1,$lt:5}}", 1, true, 1, true, 5, true, 5, true ) {} - }; - - struct Diff55 : public TwoRangeBase { - Diff55() : TwoRangeBase( "{a:{$gte:1,$lte:5},b:{$gt:1,$lte:5}}", 1, 1, true, true ) {} - }; - - struct Diff56 : public TwoRangeBase { - Diff56() : TwoRangeBase( "{a:{$gte:1,$lte:5},b:{$gte:1,$lt:5}}", 5, 5, true, true ) {} - }; - - struct Diff57 : public EmptyBase { - Diff57() : EmptyBase( "{a:{$gte:1,$lte:5},b:{$gte:1,$lte:5}}" ) {} - }; - - struct Diff58 : public TwoRangeBase { - Diff58() : TwoRangeBase( "{a:1,b:{$gt:1,$lt:5}}", 1, 1, true, true ) {} - }; - - struct Diff59 : public EmptyBase { - Diff59() : EmptyBase( "{a:1,b:{$gte:1,$lt:5}}" ) {} - }; - - struct Diff60 : public EmptyBase { - Diff60() : EmptyBase( "{a:2,b:{$gte:1,$lt:5}}" ) {} - }; - - struct Diff61 : public EmptyBase { - Diff61() : EmptyBase( "{a:5,b:{$gte:1,$lte:5}}" ) {} - }; - - struct Diff62 : public TwoRangeBase { - Diff62() : TwoRangeBase( "{a:5,b:{$gt:1,$lt:5}}", 5, 5, true, true ) {} - }; - - struct Diff63 : public EmptyBase { - Diff63() : EmptyBase( "{a:5,b:5}" ) {} - }; - - struct Diff64 : public TwoRangeBase { - Diff64() : TwoRangeBase( "{a:{$gte:1,$lte:2},b:{$gt:0,$lte:1}}", 1, 2, false, true ) {} - }; - - class DiffMulti1 : public DiffBase { - public: - void run() { - FieldRangeSet frs( "", fromjson( "{a:{$gt:1,$lt:9},b:{$gt:0,$lt:2}," - "c:3,d:{$gt:4,$lt:5},e:{$gt:7,$lt:10}}" ), true, - true ); - FieldRange ret = frs.range( "a" ); - FieldRange other = frs.range( "b" ); - other |= frs.range( "c" ); - other |= frs.range( "d" ); - other |= frs.range( "e" ); - ret -= other; - check( ret ); - } - protected: - virtual unsigned len() const { return 3; } - virtual const int *nums() const { static int n[] = { 2, 3, 3, 4, 5, 7 }; return n; } - virtual const bool *incs() const { static bool b[] = { true, false, false, true, true, true }; return b; } - virtual BSONObj obj() const { return BSONObj(); } - }; - - class DiffMulti2 : public DiffBase { - public: - void run() { - FieldRangeSet frs( "", fromjson( "{a:{$gt:1,$lt:9},b:{$gt:0,$lt:2}," - "c:3,d:{$gt:4,$lt:5},e:{$gt:7,$lt:10}}" ), true, - true ); - FieldRange mask = frs.range( "a" ); - FieldRange ret = frs.range( "b" ); - ret |= frs.range( "c" ); - ret |= frs.range( "d" ); - ret |= frs.range( "e" ); - ret -= mask; - check( ret ); - } - protected: - virtual unsigned len() const { return 2; } - virtual const int *nums() const { static int n[] = { 0, 1, 9, 10 }; return n; } - virtual const bool *incs() const { static bool b[] = { false, true, true, false }; return b; } - virtual BSONObj obj() const { return BSONObj(); } - }; - - class Universal { - public: - void run() { - FieldRangeSet frs1( "", BSON( "a" << 1 ), true, true ); - FieldRange f1 = frs1.range( "a" ); - ASSERT( !f1.universal() ); - ASSERT( frs1.range( "b" ).universal() ); - - FieldRangeSet frs2( "", BSON( "a" << GT << 1 ), true, true ); - FieldRange f2 = frs2.range( "a" ); - ASSERT( !f2.universal() ); - - FieldRangeSet frs3( "", BSON( "a" << LT << 1 ), true, true ); - FieldRange f3 = frs3.range( "a" ); - ASSERT( !frs3.range( "a" ).universal() ); - - FieldRangeSet frs4( "", BSON( "a" << NE << 1 ), true, true ); - FieldRange f4 = frs4.range( "a" ); - ASSERT( !f4.universal() ); - - f1 |= f4; - ASSERT( f1.universal() ); - f1.intersect( f2, true ); - ASSERT( !f1.universal() ); - - FieldRangeSet frs5( "", BSON( "a" << GT << 1 << LTE << 2 ), true, true ); - FieldRange f5 = frs5.range( "a" ); - ASSERT( !f5.universal() ); - - FieldRangeSet frs6( "", BSONObj(), true, true ); - FieldRange f6 = frs6.range( "a" ); - ASSERT( f6.universal() ); - - f6 -= f5; - ASSERT( !f6.universal() ); - } - }; - - /** Field range calculation for an untyped $regex within a $elemMatch. */ - class ElemMatchRegex { - public: - void run() { - FieldRangeSet frs( "", BSON( "a" << BSON( "$elemMatch" << BSON("$regex" << "^x") ) ), true, true ); - ASSERT( !frs.range( "a" ).universal() ); - ASSERT_EQUALS( "x", frs.range( "a" ).min().String() ); - } - }; - - /** - * The elemMatchContext is preserved when two FieldRanges are intersected, singleKey == - * false, and the resulting FieldRange is an unmodified copy of one of the original two - * FieldRanges. For example, if FieldRange a == [[1, 10]] is intersected with FieldRange b - * == [[5, 5]], a will be replaced with b. In this case, because a becomes an exact copy of - * b, b's elemMatchContext will be copied to a. - */ - class PreserveNonUniversalElemMatchContext { - public: - void run() { - BSONObj query = fromjson( "{a:{$elemMatch:{b:1}},c:{$lt:5},d:1}" ); - _expectedElemMatchContext = query.firstElement().Obj().firstElement(); - - // The elemMatchContext is set properly for the 'a.b' field. - FieldRangeSet frs( "", query, true, true ); - assertElemMatchContext( frs.range( "a.b" ) ); - - // The elemMatchContext is preserved after intersecting with a superset range. - frs.range( "a.b" ).intersect( frs.range( "c" ), false ); - assertElemMatchContext( frs.range( "a.b" ) ); - - // The elemMatchContext is forwarded after intersecting with a subset range. - frs.range( "c" ).intersect( frs.range( "a.b" ), false ); - assertElemMatchContext( frs.range( "c" ) ); - - // The elemMatchContext is cleared after a _single key_ intersection. - frs.range( "a.b" ).intersect( frs.range( "d" ), true /* singleKey */ ); - ASSERT( frs.range( "a.b" ).elemMatchContext().eoo() ); - } - private: - void assertElemMatchContext( const FieldRange& range ) { - ASSERT_EQUALS( _expectedElemMatchContext.rawdata(), - range.elemMatchContext().rawdata() ); - } - BSONElement _expectedElemMatchContext; - }; - - /** Intersect two universal ranges, one special. */ - class IntersectSpecial { - public: - void run() { - FieldRangeSet frs( "", fromjson( "{loc:{$near:[0,0],$maxDistance:5}}" ), true, - true ); - // The intersection is special. - ASSERT( !frs.range( "loc" ).getSpecial().empty() ); - } - }; - - namespace ExactMatchRepresentation { - - class NotExactMatchRepresentation { - public: - NotExactMatchRepresentation( const BSONObj &query ) : _query( query ) {} - virtual ~NotExactMatchRepresentation() {} - void run() { - ASSERT( !FieldRangeSet( "", _query, !multikey(), - true ).range( "a" ).mustBeExactMatchRepresentation() ); - } - protected: - virtual bool multikey() const { return false; } - private: - BSONObj _query; - }; - - struct EqualArray : public NotExactMatchRepresentation { - EqualArray() : NotExactMatchRepresentation( BSON( "a" << BSON_ARRAY( "1" ) ) ) {} - }; - - struct EqualEmptyArray : public NotExactMatchRepresentation { - EqualEmptyArray() : NotExactMatchRepresentation( fromjson( "{a:[]}" ) ) {} - }; - - struct EqualNull : public NotExactMatchRepresentation { - EqualNull() : NotExactMatchRepresentation( fromjson( "{a:null}" ) ) {} - }; - - struct InArray : public NotExactMatchRepresentation { - InArray() : NotExactMatchRepresentation( fromjson( "{a:{$in:[[1]]}}" ) ) {} - }; - - struct InRegex : public NotExactMatchRepresentation { - InRegex() : NotExactMatchRepresentation( fromjson( "{a:{$in:[/^a/]}}" ) ) {} - }; - - struct InNull : public NotExactMatchRepresentation { - InNull() : NotExactMatchRepresentation( fromjson( "{a:{$in:[null]}}" ) ) {} - }; - - struct Exists : public NotExactMatchRepresentation { - Exists() : NotExactMatchRepresentation( fromjson( "{a:{$exists:false}}" ) ) {} - }; - - struct UntypedRegex : public NotExactMatchRepresentation { - UntypedRegex() : NotExactMatchRepresentation( BSON( "a" << BSONObjBuilder().appendRegex("$regex", "^a", "").obj() ) ) {} - }; - - struct UntypedRegexString : public NotExactMatchRepresentation { - UntypedRegexString() : NotExactMatchRepresentation( BSON( "a" << BSON( "$regex" << "^a" ) ) ) {} - }; - - struct NotIn : public NotExactMatchRepresentation { - NotIn() : NotExactMatchRepresentation( fromjson( "{a:{$not:{$in:[0]}}}" ) ) {} - }; - - struct NotGt : public NotExactMatchRepresentation { - NotGt() : NotExactMatchRepresentation( fromjson( "{a:{$not:{$gt:0}}}" ) ) {} - }; - - struct GtArray : public NotExactMatchRepresentation { - GtArray() : NotExactMatchRepresentation( fromjson( "{a:{$gt:[0]}}" ) ) {} - }; - - struct GtNull : public NotExactMatchRepresentation { - GtNull() : NotExactMatchRepresentation( fromjson( "{a:{$gt:null}}" ) ) {} - }; - - struct LtObject : public NotExactMatchRepresentation { - LtObject() : NotExactMatchRepresentation( fromjson( "{a:{$lt:{}}}" ) ) {} - }; - - /** Descriptive test - behavior could potentially be different. */ - struct NotNe : public NotExactMatchRepresentation { - NotNe() : NotExactMatchRepresentation( fromjson( "{a:{$not:{$ne:4}}}" ) ) {} - }; - - class MultikeyIntersection : public NotExactMatchRepresentation { - public: - MultikeyIntersection() : NotExactMatchRepresentation - ( BSON( "a" << GTE << 0 << "a" << 0 ) ) { - } - private: - virtual bool multikey() const { return true; } - }; - - class Intersection { - public: - void run() { - FieldRangeSet set( "", BSON( "a" << 1 << "b" << GT << 2 << "c" << NE << 10 ), - true, true ); - FieldRange &a = set.range( "a" ); - FieldRange &b = set.range( "b" ); - FieldRange &c = set.range( "c" ); - FieldRange &missing = set.range( "missing" ); - ASSERT( a.mustBeExactMatchRepresentation() ); - ASSERT( b.mustBeExactMatchRepresentation() ); - ASSERT( !c.mustBeExactMatchRepresentation() ); - ASSERT( missing.mustBeExactMatchRepresentation() ); - - // Intersecting two exact matches preserves the mustBeExactMatchRepresentation - // property. - a.intersect( missing, true ); - ASSERT( a.mustBeExactMatchRepresentation() ); - a.intersect( b, true ); - ASSERT( a.mustBeExactMatchRepresentation() ); - - // Other operations clear the mustBeExactMatchRepresentation property. - b.intersect( c, true ); - ASSERT( !b.mustBeExactMatchRepresentation() ); - } - }; - - class Union { - public: - void run() { - FieldRangeSet set( "", BSON( "left" << 1 << "right" << GT << 2 ), true, true ); - FieldRange &left = set.range( "left" ); - FieldRange &right = set.range( "right" ); - left |= right; - ASSERT( !left.mustBeExactMatchRepresentation() ); - } - }; - - class Difference { - public: - void run() { - FieldRangeSet set( "", BSON( "left" << 1 << "right" << GT << 2 ), true, true ); - FieldRange &left = set.range( "left" ); - FieldRange &right = set.range( "right" ); - left -= right; - ASSERT( !left.mustBeExactMatchRepresentation() ); - } - }; - - } // namespace ExactMatchRepresentation - - } // namespace FieldRangeTests - - namespace FieldRangeSetTests { - - class Basics { - public: - void run() { - { - BSONObj obj = BSON( "a" << 1 ); - FieldRangeSet fieldRangeSet( "", obj, true, true ); - fieldRangeSet.toString(); // Just test that we don't crash. - } - - { - boost::shared_ptr<FieldRangeSet> frs; - { - string ns = str::stream() << "foo"; - frs.reset( new FieldRangeSet( ns.c_str(), BSONObj(), true, true ) ); - } - ASSERT_EQUALS( string( "foo" ), frs->ns() ); - } - } - }; - - class Intersect { - public: - void run() { - FieldRangeSet frs1( "", fromjson( "{b:{$in:[5,6]},c:7,d:{$in:[8,9]}}" ), true, - true ); - FieldRangeSet frs2( "", fromjson( "{a:1,b:5,c:{$in:[7,8]},d:{$in:[8,9]},e:10}" ), - true, true ); - frs1 &= frs2; - FieldRangeSet expectedResult( "", fromjson( "{a:1,b:5,c:7,d:{$in:[8,9]},e:10}" ), - true, true ); - ASSERT_EQUALS( frs1.toString(), expectedResult.toString() ); - } - }; - - class MultiKeyIntersect { - public: - void run() { - FieldRangeSet frs1( "", BSONObj(), false, true ); - FieldRangeSet frs2( "", BSON( "a" << GT << 4 ), false, true ); - FieldRangeSet frs3( "", BSON( "a" << LT << 6 ), false, true ); - // An intersection with a universal range is allowed. - frs1 &= frs2; - ASSERT_EQUALS( frs2.toString(), frs1.toString() ); - // An intersection with non universal range is not allowed, as it might prevent a - // valid multikey match. - frs1 &= frs3; - ASSERT_EQUALS( frs2.toString(), frs1.toString() ); - // Now intersect with a fully contained range. - FieldRangeSet frs4( "", BSON( "a" << GT << 6 ), false, true ); - frs1 &= frs4; - ASSERT_EQUALS( frs4.toString(), frs1.toString() ); - } - }; - - /* Intersecting an empty multikey range with another range produces an empty range. */ - class EmptyMultiKeyIntersect { - public: - void run() { - FieldRangeSet frs1( "", BSON( "a" << BSON( "$in" << BSONArray() ) ), false, true ); - FieldRangeSet frs2( "", BSON( "a" << 5 ), false, true ); - ASSERT( frs1.range( "a" ).empty() ); - frs1 &= frs2; - ASSERT( frs1.range( "a" ).empty() ); - } - }; - - class MultiKeyDiff { - public: - void run() { - FieldRangeSet frs1( "", BSON( "a" << GT << 4 ), false, true ); - FieldRangeSet frs2( "", BSON( "a" << GT << 6 ), false, true ); - // Range subtraction is no different for multikey ranges. - frs1 -= frs2; - FieldRangeSet expectedResult( "", BSON( "a" << GT << 4 << LTE << 6 ), true, true ); - ASSERT_EQUALS( frs1.toString(), expectedResult.toString() ); - } - }; - - class MatchPossible { - public: - void run() { - FieldRangeSet frs1( "", BSON( "a" << GT << 4 ), true, true ); - ASSERT( frs1.matchPossible() ); - // Conflicting constraints invalid for a single key set. - FieldRangeSet frs2( "", BSON( "a" << GT << 4 << LT << 2 ), true, true ); - ASSERT( !frs2.matchPossible() ); - // Conflicting constraints not possible for a multi key set. - FieldRangeSet frs3( "", BSON( "a" << GT << 4 << LT << 2 ), false, true ); - ASSERT( frs3.matchPossible() ); - } - }; - - class MatchPossibleForIndex { - public: - void run() { - // Conflicting constraints not possible for a multi key set. - FieldRangeSet frs1( "", BSON( "a" << GT << 4 << LT << 2 ), false, true ); - ASSERT( frs1.matchPossibleForIndex( BSON( "a" << 1 ) ) ); - // Conflicting constraints for a multi key set. - FieldRangeSet frs2( "", BSON( "a" << GT << 4 << LT << 2 ), true, true ); - ASSERT( !frs2.matchPossibleForIndex( BSON( "a" << 1 ) ) ); - // If the index doesn't include the key, it is not single key invalid. - ASSERT( frs2.matchPossibleForIndex( BSON( "b" << 1 ) ) ); - // If the index key is not an index, the set is not single key invalid. - ASSERT( frs2.matchPossibleForIndex( BSON( "$natural" << 1 ) ) ); - ASSERT( frs2.matchPossibleForIndex( BSONObj() ) ); - } - }; - - class Subset { - public: - void run() { - _frs1.reset - ( new FieldRangeSet - ( "", BSON( "a" << GT << 4 << LT << 4 << "b" << 5 << "c" << 6 ), true, true ) ); - _frs2.reset( _frs1->subset( BSON( "a" << 1 << "b" << 1 << "d" << 1 ) ) ); - - // An empty range should be copied. - ASSERT( _frs1->range( "a" ).empty() ); - ASSERT( _frs2->range( "a" ).empty() ); - assertRangeCopied( "a" ); - - assertRangeCopied( "b" ); - assertRangeNotCopied( "c" ); - assertRangeNotCopied( "d" ); - } - private: - void assertRangeCopied( const string &fieldName ) { - ASSERT_EQUALS( _frs1->range( fieldName.c_str() ).toString(), - _frs2->range( fieldName.c_str() ).toString() ); - } - void assertRangeNotCopied( const string &fieldName ) { - ASSERT_EQUALS( _frs1->range( "qqqqq" ).toString(), // Missing field, universal range - _frs2->range( fieldName.c_str() ).toString() ); - } - auto_ptr<FieldRangeSet> _frs1; - auto_ptr<FieldRangeSet> _frs2; - }; - - /** Duplicate a FieldRangeSet, but with prefixed field names. */ - class Prefixed { - public: - void run() { - FieldRangeSet original( "", BSON( "a" << 1 ), true, true ); - scoped_ptr<FieldRangeSet> prefixed( original.prefixed( "prefix" ) ); - ASSERT( prefixed->singleKey() ); - ASSERT( prefixed->range( "a" ).universal() ); - ASSERT( prefixed->range( "prefix.a" ).equality() ); - } - }; - - /** No field range is generated for an $atomic field. SERVER-5354 */ - class Atomic { - public: - void run() { - FieldRangeSet ranges( "", BSON( "a" << 1 << "$atomic" << 1 ), true, true ); - // No range is computed for the '$atomic' field. - ASSERT( ranges.range( "$atomic" ).universal() ); - // A standard equality range is computed for the 'a' field. - ASSERT( ranges.range( "a" ).equality() ); - } - }; - - namespace ElemMatch { - - /** Field ranges generated for the $elemMatch operator. */ - class Ranges { - public: - void run() { - FieldRangeSet set( "", fromjson( "{ a:{ $elemMatch:{ b:1, c:2 } } }" ), true, - true ); - ASSERT( set.range( "a.b" ).equality() ); - ASSERT( set.range( "a.c" ).equality() ); - ASSERT( !set.range( "a.d" ).equality() ); - ASSERT( !set.range( "a" ).equality() ); - } - }; - - /** - * Field ranges generated when the $elemMatch operator is applied to top level - * elements. - */ - class TopLevelElements { - public: - void run() { - FieldRangeSet set( "", fromjson( "{ a:{ $elemMatch:{ $gte:5, $lte:5 } } }" ), - true, true ); - // No constraints exist for operator based field names like "a.$gte". - ASSERT( set.range( "a.$gte" ).universal() ); - ASSERT( set.range( "a.$lte" ).universal() ); - // The index ranges for the $elemMatch subexpressions are intersected in a - // single value context, generating an equality index bound. SERVER-4180 - ASSERT( set.range( "a" ).equality() ); - } - }; - - /** - * Field ranges generated when the $elemMatch operator is applied to a top level - * $not meta operator. - */ - class TopLevelNotElement { - public: - void run() { - FieldRangeSet set( "", fromjson( "{ a:{ $elemMatch:{ $not:{ $ne:5 } } } }" ), - true, true ); - // No constraints exist for operator based field names like "a.$not". - ASSERT( set.range( "a.$not" ).universal() ); - ASSERT( set.range( "a.$not.$ne" ).universal() ); - ASSERT( set.range( "a" ).equality() ); - } - }; - - /** Field ranges generated for $elemMatch nested within a top level $elemMatch. */ - class TopLevelNested { - public: - void run() { - FieldRangeSet set( "", fromjson( "{ a:{ $elemMatch:{ $elemMatch:{ $in:[ 5 ] " - "} } } }" ), - true, true ); - ASSERT_EQUALS( 0, set.numNonUniversalRanges() ); - FieldRangeSet set2( "", fromjson( "{ a:{ $elemMatch:{ $elemMatch:{ b:{ " - "$in:[ 5 ] } } } } }" ), - true, true ); - ASSERT_EQUALS( 0, set2.numNonUniversalRanges() ); - } - }; - - /** Field ranges generated for $not:$elemMatch queries. */ - class Not { - public: - void run() { - FieldRangeSet set( "", - fromjson( "{ a:{ $not:{ $elemMatch:{ b:{ $ne:1 } } } } }" ), - true, true ); - ASSERT( !set.range( "a.b" ).equality() ); - ASSERT( set.range( "a.b" ).universal() ); - } - }; - - /** Field ranges generated for nested $elemMatch expressions. */ - class Nested { - public: - void run() { - FieldRangeSet set( "", - fromjson( "{ a:{ $elemMatch:{ b:{ $elemMatch:{ c:1" - " } } } } }" ), - true, true ); - // No constraints are generated for the following fields. - BSONArray universalFields = BSON_ARRAY( "a" << "a.b" << "b" << "b.c" << "c" ); - BSONObjIterator i( universalFields ); - while( i.more() ) { - ASSERT( set.range( i.next().String().c_str() ).universal() ); - } - // A correct constraint is generated for a nested $elemMatch field. - ASSERT( set.range( "a.b.c" ).equality() ); - ASSERT_EQUALS( 1, set.range( "a.b.c" ).min().number() ); - } - }; - - /** Field ranges generated for an $elemMatch expression nested within $all. */ - class AllNested { - public: - void run() { - FieldRangeSet set( "", - fromjson( "{ a:{ $elemMatch:{ b:{ $all:" - "[ { $elemMatch:{ c:1 } }, { $elemMatch:{ d:2 } } ]" - " } } } }" ), - true, true ); - // No constraints are generated for the following fields. - BSONArray universalFields = BSON_ARRAY( "a" << "a.b" << "b" << "b.c" << "c" - << "b.d" << "d" << "a.b.d" ); - BSONObjIterator i( universalFields ); - while( i.more() ) { - ASSERT( set.range( i.next().String().c_str() ).universal() ); - } - // A correct constraint is generated for the first nested $elemMatch field. - ASSERT( set.range( "a.b.c" ).equality() ); - ASSERT_EQUALS( 1, set.range( "a.b.c" ).min().number() ); - } - }; - - } // namespace ElemMatch - - namespace ExactMatchRepresentation { - - class ExactMatchRepresentation { - public: - ExactMatchRepresentation( const BSONObj &query ) : _query( query ) {} - void run() { - ASSERT( FieldRangeSet( "", _query, true, true ) - .mustBeExactMatchRepresentation() ); - } - private: - BSONObj _query; - }; - - class NotExactMatchRepresentation { - public: - NotExactMatchRepresentation( const BSONObj &query ) : _query( query ) {} - void run() { - ASSERT( !FieldRangeSet( "", _query, true, true ) - .mustBeExactMatchRepresentation() ); - } - private: - BSONObj _query; - }; - - struct EmptyQuery : public ExactMatchRepresentation { - EmptyQuery() : ExactMatchRepresentation( BSONObj() ) {} - }; - - struct Equal : public ExactMatchRepresentation { - Equal() : ExactMatchRepresentation( BSON( "a" << 0 ) ) {} - }; - - struct In : public ExactMatchRepresentation { - In() : ExactMatchRepresentation( fromjson( "{a:{$in:[0,1]}}" ) ) {} - }; - - struct Where : public NotExactMatchRepresentation { - Where() : NotExactMatchRepresentation( BSON( "a" << 0 << "$where" << "foo" ) ) {} - }; - - struct Not : public NotExactMatchRepresentation { - Not() : NotExactMatchRepresentation( fromjson( "{a:{$not:{$in:[0]}}}" ) ) {} - }; - - struct Regex : public NotExactMatchRepresentation { - Regex() : NotExactMatchRepresentation( fromjson( "{a:/^a/}" ) ) {} - }; - - struct UntypedRegex : public NotExactMatchRepresentation { - UntypedRegex() : NotExactMatchRepresentation( BSON( "a" << BSONObjBuilder().appendRegex("$regex", "^a", "").obj() ) ) {} - }; - - struct UntypedRegexString : public NotExactMatchRepresentation { - UntypedRegexString() : NotExactMatchRepresentation( BSON( "a" << BSON( "$regex" << "^a" ) ) ) {} - }; - - struct And : public ExactMatchRepresentation { - And() : ExactMatchRepresentation( fromjson( "{$and:[{a:{$in:[0,1]}}]}" ) ) {} - }; - - struct Or : public NotExactMatchRepresentation { - Or() : NotExactMatchRepresentation( fromjson( "{$or:[{a:{$in:[0,1]}}]}" ) ) {} - }; - - struct All : public NotExactMatchRepresentation { - All() : NotExactMatchRepresentation( fromjson( "{a:{$all:[0]}}" ) ) {} - }; - - struct ElemMatch : public NotExactMatchRepresentation { - ElemMatch() : NotExactMatchRepresentation( fromjson( "{a:{$elemMatch:{b:1}}}" ) ) {} - }; - - struct AllElemMatch : public NotExactMatchRepresentation { - AllElemMatch() : - NotExactMatchRepresentation( fromjson( "{a:{$all:[{$elemMatch:{b:1}}]}}" ) ) {} - }; - - struct NotSecondField : public NotExactMatchRepresentation { - NotSecondField() : - NotExactMatchRepresentation( fromjson( "{a:{$in:[1],$not:{$in:[0]}}}" ) ) {} - }; - - } // namespace ExactMatchRepresentation - - } // namespace FieldRangeSetTests - - namespace FieldRangeSetPairTests { - - class ToString { - public: - void run() { - BSONObj obj = BSON( "a" << 1 ); - FieldRangeSetPair FieldRangeSetPair( "", obj ); - FieldRangeSetPair.toString(); // Just test that we don't crash. - } - }; - - class NoNonUniversalRanges { - public: - void run() { - FieldRangeSetPair frsp1( "", BSONObj() ); - ASSERT( frsp1.noNonUniversalRanges() ); - FieldRangeSetPair frsp2( "", BSON( "a" << 1 ) ); - ASSERT( !frsp2.noNonUniversalRanges() ); - FieldRangeSetPair frsp3( "", BSON( "a" << GT << 1 ) ); - ASSERT( !frsp3.noNonUniversalRanges() ); - // A single key invalid constraint is not universal. - FieldRangeSetPair frsp4( "", BSON( "a" << GT << 1 << LT << 0 ) ); - ASSERT( !frsp4.noNonUniversalRanges() ); - // Still not universal if multikey invalid constraint. - FieldRangeSetPair frsp5( "", BSON( "a" << BSON( "$in" << BSONArray() ) ) ); - ASSERT( !frsp5.noNonUniversalRanges() ); - } - }; - - class MatchPossible { - public: - void run() { - // Match possible for simple query. - FieldRangeSetPair frsp1( "", BSON( "a" << 1 ) ); - ASSERT( frsp1.matchPossible() ); - // Match possible for single key invalid query. - FieldRangeSetPair frsp2( "", BSON( "a" << GT << 1 << LT << 0 ) ); - ASSERT( frsp2.matchPossible() ); - } - }; - - class IndexBase { - Lock::DBWrite _lk; - Client::Context _ctx; - DurTransaction _txn; - public: - IndexBase() : _lk(ns()), _ctx( ns() ) , indexNum_( 0 ) { - userCreateNS( &_txn, _ctx.db(), ns(), BSONObj(), false ); - } - ~IndexBase() { - if ( !nsd() ) - return; - _ctx.db()->dropCollection( &_txn, ns() ); - } - protected: - static const char *ns() { return "unittests.FieldRangeSetPairTests"; } - Client::Context* ctx() { return &_ctx; } - Database* db() { return _ctx.db(); } - Collection* collection() { return db()->getCollection( ns() ); } - NamespaceDetails *nsd() { return collection()->detailsWritable(); } - - int indexno( const BSONObj &key ) { - stringstream ss; - ss << indexNum_++; - string name = ss.str(); - client_.resetIndexCache(); - client_.ensureIndex( ns(), key, false, name.c_str() ); - - IndexCatalog* catalog = collection()->getIndexCatalog(); - IndexDescriptor* desc = catalog->findIndexByKeyPattern( key ); - invariant( desc ); - - int x = nsd()->_catalogFindIndexByName(collection(), desc->indexName(), false); - invariant( x >= 0 ); - - return x; - } - - static DBDirectClient client_; - private: - int indexNum_; - }; - DBDirectClient IndexBase::client_; - - } // namespace FieldRangeSetPairTests - - namespace FieldRangeVectorTests { - - class ToString { - public: - void run() { - BSONObj obj = BSON( "a" << 1 ); - FieldRangeSet fieldRangeSet( "", obj, true, true ); - BSONObj indexSpec( BSON( "a" << 1 ) ); - FieldRangeVector fieldRangeVector( fieldRangeSet, indexSpec, 1 ); - fieldRangeVector.toString(); // Just test that we don't crash. - } - }; - - /** - * Check FieldRangeVector::hasAllIndexedRanges(), indicating when all indexed field ranges - * in a field range set are represented in a field range vector (and none are excluded due - * to multikey index field name conflicts). - */ - class HasAllIndexedRanges { - public: - void run() { - // Single key index. - ASSERT( rangesRepresented( BSON( "a" << 1 ), true, BSON( "a" << 1 ) ) ); - // Multikey index, but no unrepresented ranges. - ASSERT( rangesRepresented( BSON( "a" << 1 ), false, BSON( "a" << 1 ) ) ); - // Multikey index, but no unrepresented ranges in the index. - ASSERT( rangesRepresented( BSON( "a" << 1 ), false, - BSON( "a" << 1 << "b" << 2 ) ) ); - // Compound multikey index with no unrepresented ranges. - ASSERT( rangesRepresented( BSON( "a" << 1 << "b" << 1 ), false, - BSON( "a" << 2 << "b" << 3 ) ) ); - // Compound multikey index with range 'a.c' unrepresented because of a conflict - // with range 'a.b', hence 'false' expected. - ASSERT( !rangesRepresented( BSON( "a.b" << 1 << "a.c" << 1 ), false, - BSON( "a.b" << 2 << "a.c" << 3 ) ) ); - // Compound multikey index without conflicts due to use of the $elemMatch operator. - ASSERT( rangesRepresented( BSON( "a.b" << 1 << "a.c" << 1 ), false, - BSON( "a" << BSON( "$elemMatch" << - BSON( "b" << 2 << "c" << 3 ) ) ) ) ); - // Single key index. - ASSERT( rangesRepresented( BSON( "a.b" << 1 << "a.c" << 1 ), true, - BSON( "a.b" << 2 << "a.c" << 3 ) ) ); - } - private: - bool rangesRepresented( const BSONObj& index, bool singleKey, const BSONObj& query ) { - FieldRangeSet fieldRangeSet( "", query, singleKey, true ); - BSONObj indexSpec( index ); - FieldRangeVector fieldRangeVector( fieldRangeSet, indexSpec, 1 ); - return fieldRangeVector.hasAllIndexedRanges(); - } - }; - - /** Detecting cases where a FieldRangeVector describes a single btree interval. */ - class SingleInterval { - public: - void run() { - // Equality on a single field is a single interval. - FieldRangeVector frv1( FieldRangeSet( "dummy", BSON( "a" << 5 ), true, true ), - ( BSON( "a" << 1 ) ), - 1 ); - ASSERT( frv1.isSingleInterval() ); - // Single interval on a single field is a single interval. - FieldRangeVector frv2( FieldRangeSet( "dummy", BSON( "a" << GT << 5 ), true, true ), - ( BSON( "a" << 1 ) ), - 1 ); - ASSERT( frv2.isSingleInterval() ); - // Multiple intervals on a single field is not a single interval. - FieldRangeVector frv3( FieldRangeSet( "dummy", - fromjson( "{a:{$in:[4,5]}}" ), - true, - true ), - ( BSON( "a" << 1 ) ), - 1 ); - ASSERT( !frv3.isSingleInterval() ); - - // Equality on two fields is a compound single interval. - FieldRangeVector frv4( FieldRangeSet( "dummy", - BSON( "a" << 5 << "b" << 6 ), - true, - true ), - ( BSON( "a" << 1 << "b" << 1 ) ), - 1 ); - ASSERT( frv4.isSingleInterval() ); - // Equality on first field and single interval on second field is a compound - // single interval. - FieldRangeVector frv5( FieldRangeSet( "dummy", - BSON( "a" << 5 << "b" << GT << 6 ), - true, - true ), - ( BSON( "a" << 1 << "b" << 1 ) ), - 1 ); - ASSERT( frv5.isSingleInterval() ); - // Single interval on first field and single interval on second field is not a - // compound single interval. - FieldRangeVector frv6( FieldRangeSet( "dummy", - BSON( "a" << LT << 5 << "b" << GT << 6 ), - true, - true ), - ( BSON( "a" << 1 << "b" << 1 ) ), - 1 ); - ASSERT( !frv6.isSingleInterval() ); - // Multiple intervals on two fields is not a compound single interval. - FieldRangeVector frv7( FieldRangeSet( "dummy", - fromjson( "{a:{$in:[4,5]},b:{$in:[7,8]}}" ), - true, - true ), - ( BSON( "a" << 1 << "b" << 1 ) ), - 1 ); - ASSERT( !frv7.isSingleInterval() ); - - // With missing second field is still a single compound interval. - FieldRangeVector frv8( FieldRangeSet( "dummy", - BSON( "a" << 5 ), - true, - true ), - ( BSON( "a" << 1 << "b" << 1 ) ), - 1 ); - ASSERT( frv8.isSingleInterval() ); - // With missing second field is still a single compound interval. - FieldRangeVector frv9( FieldRangeSet( "dummy", - BSON( "b" << 5 ), - true, - true ), - ( BSON( "a" << 1 << "b" << 1 ) ), - 1 ); - ASSERT( !frv9.isSingleInterval() ); - - // Equality on first two fields and single interval on third field is a compound - // single interval. - FieldRangeVector frv10( FieldRangeSet( "dummy", - fromjson( "{a:5,b:6,c:{$gt:7}}" ), - true, - true ), - ( BSON( "a" << 1 << "b" << 1 << "c" << 1 ) ), - 1 ); - ASSERT( frv10.isSingleInterval() ); - - // Equality, then single interval, then missing is a compound single interval. - FieldRangeVector frv11( FieldRangeSet( "dummy", - fromjson( "{a:5,b:{$gt:7}}" ), - true, - true ), - ( BSON( "a" << 1 << "b" << 1 << "c" << 1 ) ), - 1 ); - ASSERT( frv11.isSingleInterval() ); - // Equality, then single interval, then missing, then missing is a compound single - // interval. - FieldRangeVector frv12( FieldRangeSet( "dummy", - fromjson( "{a:5,b:{$gt:7}}" ), - true, - true ), - ( BSON( "a" << 1 << - "b" << 1 << - "c" << 1 << - "d" << 1 ) ), - 1 ); - ASSERT( frv12.isSingleInterval() ); - // Equality, then single interval, then missing, then missing, with mixed order - // fields is a compound single interval. - FieldRangeVector frv13( FieldRangeSet( "dummy", - fromjson( "{a:5,b:{$gt:7}}" ), - true, - true ), - ( BSON( "a" << 1 << - "b" << 1 << - "c" << 1 << - "d" << -1 ) ), - 1 ); - ASSERT( frv13.isSingleInterval() ); - // Equality, then single interval, then missing, then single interval is not a - // compound single interval. - FieldRangeVector frv14( FieldRangeSet( "dummy", - fromjson( "{a:5,b:{$gt:7},d:{$gt:1}}" ), - true, - true ), - ( BSON( "a" << 1 << - "b" << 1 << - "c" << 1 << - "d" << 1 ) ), - 1 ); - ASSERT( !frv14.isSingleInterval() ); - } - }; - - /** Check start and end key values. */ - class StartEndKey { - public: - void run() { - // Equality on a single field. - FieldRangeVector frv1( FieldRangeSet( "dummy", BSON( "a" << 5 ), true, true ), - ( BSON( "a" << 1 ) ), - 1 ); - ASSERT_EQUALS( BSON( "" << 5 ), frv1.startKey() ); - ASSERT( frv1.startKeyInclusive() ); - ASSERT_EQUALS( BSON( "" << 5 ), frv1.endKey() ); - ASSERT( frv1.endKeyInclusive() ); - // Single interval on a single field. - FieldRangeVector frv2( FieldRangeSet( "dummy", BSON( "a" << GT << 5 ), true, true ), - ( BSON( "a" << 1 ) ), - 1 ); - ASSERT_EQUALS( BSON( "" << 5 ), frv2.startKey() ); - ASSERT( !frv2.startKeyInclusive() ); - ASSERT_EQUALS( BSON( "" << numeric_limits<double>::max() ), frv2.endKey() ); - ASSERT( frv2.endKeyInclusive() ); - - // Equality on two fields. - FieldRangeVector frv3( FieldRangeSet( "dummy", - BSON( "a" << 5 << "b" << 6 ), - true, - true ), - ( BSON( "a" << 1 << "b" << 1 ) ), - 1 ); - ASSERT_EQUALS( BSON( "" << 5 << "" << 6 ), frv3.startKey() ); - ASSERT( frv3.startKeyInclusive() ); - ASSERT_EQUALS( BSON( "" << 5 << "" << 6 ), frv3.endKey() ); - ASSERT( frv3.endKeyInclusive() ); - // Equality on first field and single interval on second field. - FieldRangeVector frv4( FieldRangeSet( "dummy", - BSON( "a" << 5 << "b" << LT << 6 ), - true, - true ), - ( BSON( "a" << 1 << "b" << 1 ) ), - 1 ); - ASSERT_EQUALS( BSON( "" << 5 << "" << -numeric_limits<double>::max() ), - frv4.startKey() ); - ASSERT( frv4.startKeyInclusive() ); - ASSERT_EQUALS( BSON( "" << 5 << "" << 6 ), - frv4.endKey() ); - ASSERT( !frv4.endKeyInclusive() ); - - // With missing second field. - FieldRangeVector frv5( FieldRangeSet( "dummy", - BSON( "a" << 5 ), - true, - true ), - ( BSON( "a" << 1 << "b" << 1 ) ), - 1 ); - ASSERT_EQUALS( BSON( "" << 5 << "" << MINKEY ), frv5.startKey() ); - ASSERT( frv5.startKeyInclusive() ); - ASSERT_EQUALS( BSON( "" << 5 << "" << MAXKEY ), frv5.endKey() ); - ASSERT( frv5.endKeyInclusive() ); - // Equality, then single interval, then missing. - FieldRangeVector frv6( FieldRangeSet( "dummy", - fromjson( "{a:5,b:{$gt:7}}" ), - true, - true ), - ( BSON( "a" << 1 << "b" << 1 << "c" << 1 ) ), - 1 ); - ASSERT_EQUALS( BSON( "" << 5 << "" << 7 << "" << MAXKEY ), frv6.startKey() ); - ASSERT( !frv6.startKeyInclusive() ); - ASSERT_EQUALS( BSON( "" << 5 << - "" << numeric_limits<double>::max() << - "" << MAXKEY ), - frv6.endKey() ); - ASSERT( frv6.endKeyInclusive() ); - // Equality, then single interval, then missing, then missing. - FieldRangeVector frv7( FieldRangeSet( "dummy", - fromjson( "{a:5,b:{$gt:7}}" ), - true, - true ), - ( BSON( "a" << 1 << - "b" << 1 << - "c" << 1 << - "d" << 1 ) ), - 1 ); - ASSERT_EQUALS( BSON( "" << 5 << "" << 7 << "" << MAXKEY << "" << MAXKEY ), - frv7.startKey() ); - ASSERT( !frv7.startKeyInclusive() ); - ASSERT_EQUALS( BSON( "" << 5 << - "" << numeric_limits<double>::max() << - "" << MAXKEY << - "" << MAXKEY ), - frv7.endKey() ); - ASSERT( frv7.endKeyInclusive() ); - - // Equality, then single exclusive interval on both ends, then missing, then - // missing with mixed direction index ordering. - FieldRangeVector frv8( FieldRangeSet( "dummy", - fromjson( "{a:5,b:{$gt:7,$lt:10}}" ), - true, - true ), - ( BSON( "a" << 1 << - "b" << 1 << - "c" << 1 << - "d" << -1 ) ), - 1 ); - ASSERT_EQUALS( BSON( "" << 5 << "" << 7 << "" << MAXKEY << "" << MINKEY ), - frv8.startKey() ); - ASSERT( !frv8.startKeyInclusive() ); - ASSERT_EQUALS( BSON( "" << 5 << "" << 10 << "" << MINKEY << "" << MAXKEY ), - frv8.endKey() ); - ASSERT( !frv8.endKeyInclusive() ); - // Equality, then single exclusive interval on both ends, then missing, then - // missing with mixed direction index ordering and reverse direction traversal. - FieldRangeVector frv9( FieldRangeSet( "dummy", - fromjson( "{a:5,b:{$gt:7,$lt:10}}" ), - true, - true ), - ( BSON( "a" << 1 << - "b" << 1 << - "c" << 1 << - "d" << -1 ) ), - -1 ); - ASSERT_EQUALS( BSON( "" << 5 << "" << 10 << "" << MINKEY << "" << MAXKEY ), - frv9.startKey() ); - ASSERT( !frv9.startKeyInclusive() ); - ASSERT_EQUALS( BSON( "" << 5 << "" << 7 << "" << MAXKEY << "" << MINKEY ), - frv9.endKey() ); - ASSERT( !frv9.endKeyInclusive() ); - } - }; - - } // namespace FieldRangeVectorTests - - // These are currently descriptive, not normative tests. SERVER-5450 - namespace FieldRangeVectorIteratorTests { - - class Base { - public: - virtual ~Base() {} - void run() { - FieldRangeSet fieldRangeSet( "", query(), true, true ); - FieldRangeVector fieldRangeVector( fieldRangeSet, index(), 1 ); - _iterator.reset( new FieldRangeVectorIterator( fieldRangeVector, - singleIntervalLimit() ) ); - _iterator->advance( fieldRangeVector.startKey() ); - _iterator->prepDive(); - check(); - } - protected: - virtual BSONObj query() = 0; - virtual BSONObj index() = 0; - virtual int singleIntervalLimit() { return 0; } - virtual void check() = 0; - void assertAdvanceToNext( const BSONObj ¤t ) { - ASSERT_EQUALS( -1, advance( current ) ); - } - void assertAdvanceTo( const BSONObj ¤t, const BSONObj &target, - const BSONObj &inclusive = BSONObj() ) { - int partition = advance( current ); - ASSERT( !iterator().after() ); - BSONObjBuilder advanceToBuilder; - advanceToBuilder.appendElements( currentPrefix( current, partition ) ); - for( int i = partition; i < (int)iterator().cmp().size(); ++i ) { - advanceToBuilder << *iterator().cmp()[ i ]; - } - assertEqualWithoutFieldNames( target, advanceToBuilder.obj() ); - if ( !inclusive.isEmpty() ) { - BSONObjIterator inc( inclusive ); - for( int i = 0; i < partition; ++i ) inc.next(); - for( int i = partition; i < (int)iterator().inc().size(); ++i ) { - ASSERT_EQUALS( inc.next().Bool(), iterator().inc()[ i ] ); - } - } - } - void assertAdvanceToAfter( const BSONObj ¤t, const BSONObj &target ) { - int partition = advance( current ); - ASSERT( iterator().after() ); - assertEqualWithoutFieldNames( target, currentPrefix( current, partition ) ); - } - void assertDoneAdvancing( const BSONObj ¤t ) { - ASSERT_EQUALS( -2, advance( current ) ); - } - BSONElement minElt() const { return minKey.firstElement(); } - BSONElement maxElt() const { return maxKey.firstElement(); } - private: - static bool equalWithoutFieldNames( const BSONObj &one, const BSONObj &two ) { - return one.woCompare( two, BSONObj(), false ) == 0; - } - static void assertEqualWithoutFieldNames( const BSONObj &one, const BSONObj &two ) { - if ( !equalWithoutFieldNames( one, two ) ) { - mongo::unittest::log() << one << " != " << two << endl; - ASSERT( equalWithoutFieldNames( one, two ) ); - } - } - BSONObj currentPrefix( const BSONObj ¤t, int partition ) { - ASSERT( partition >= 0 ); - BSONObjIterator currentIter( current ); - BSONObjBuilder prefixBuilder; - for( int i = 0; i < partition; ++i ) { - prefixBuilder << currentIter.next(); - } - return prefixBuilder.obj(); - } - FieldRangeVectorIterator &iterator() { return *_iterator; } - int advance( const BSONObj ¤t ) { - return iterator().advance( current ); - } - scoped_ptr<FieldRangeVectorIterator> _iterator; - }; - - class AdvanceToNextIntervalEquality : public Base { - BSONObj query() { return fromjson( "{a:{$in:[0,1]}}" ); } - BSONObj index() { return BSON( "a" << 1 ); } - void check() { - assertAdvanceToNext( BSON( "a" << 0 ) ); - assertAdvanceTo( BSON( "a" << 0.5 ), BSON( "a" << 1 ) ); - } - }; - - class AdvanceToNextIntervalExclusiveInequality : public Base { - BSONObj query() { return fromjson( "{a:{$in:['a',/^q/,'z']}}" ); } - BSONObj index() { return BSON( "a" << 1 ); } - void check() { - assertAdvanceToNext( BSON( "a" << "a" ) ); - assertAdvanceToNext( BSON( "a" << "q" ) ); - assertAdvanceTo( BSON( "a" << "r" ), BSON( "a" << "z" ) ); - assertAdvanceToNext( BSON( "a" << "z" ) ); - } - }; - - class AdvanceToNextIntervalEqualityReverse : public Base { - BSONObj query() { return fromjson( "{a:{$in:[0,1]}}" ); } - BSONObj index() { return BSON( "a" << -1 ); } - void check() { - assertAdvanceToNext( BSON( "a" << 1 ) ); - assertAdvanceTo( BSON( "a" << 0.5 ), BSON( "a" << 0 ) ); - } - }; - - class AdvanceToNextIntervalEqualityCompound : public Base { - BSONObj query() { return fromjson( "{a:{$in:[0,1]},b:{$in:[4,5]}}" ); } - BSONObj index() { return BSON( "a" << 1 << "b" << 1 ); } - void check() { - assertAdvanceToNext( BSON( "a" << 0 << "b" << 5 ) ); - assertAdvanceToAfter( BSON( "a" << 0 << "b" << 6 ), BSON( "a" << 0 ) ); - assertAdvanceTo( BSON( "a" << 1 << "b" << 2 ), BSON( "a" << 1 << "b" << 4 ) ); - assertAdvanceToNext( BSON( "a" << 1 << "b" << 4 ) ); - assertDoneAdvancing( BSON( "a" << 1 << "b" << 5.1 ) ); - } - }; - - class AdvanceToNextIntervalIntermediateEqualityCompound : public Base { - BSONObj query() { return fromjson( "{a:{$in:[0,1]},b:{$in:[4,5]}}" ); } - BSONObj index() { return BSON( "a" << 1 << "b" << 1 ); } - void check() { - assertAdvanceToNext( BSON( "a" << 0 << "b" << 5 ) ); - assertAdvanceTo( BSON( "a" << 0.5 << "b" << 6 ), BSON( "a" << 1 << "b" << 4 ) ); - assertAdvanceToNext( BSON( "a" << 1 << "b" << 4 ) ); - } - }; - - class AdvanceToNextIntervalIntermediateInMixed : public Base { - BSONObj query() { return fromjson( "{a:{$in:[0,1]},b:{$in:[/^a/,'c'],$ne:'a'}}" ); } - BSONObj index() { return BSON( "a" << 1 << "b" << 1 ); } - void check() { - assertAdvanceTo( BSON( "a" << 0 << "b" << "bb" ), BSON( "a" << 0 << "b" << "c" ), - BSON( "a" << 0 << "b" << true ) ); - assertAdvanceTo( BSON( "a" << 0.5 << "b" << "a" ), BSON( "a" << 1 << "b" << "a" ), - BSON( "a" << true << "b" << false ) ); - } - }; - - class BeforeLowerBound : public Base { - BSONObj query() { return fromjson( "{a:{$in:[0,1]},b:{$in:[4,5]}}" ); } - BSONObj index() { return BSON( "a" << 1 << "b" << 1 ); } - void check() { - assertAdvanceTo( BSON( "a" << 0 << "b" << 1 ), BSON( "a" << 0 << "b" << 4 ) ); - assertAdvanceToNext( BSON( "a" << 0 << "b" << 4 ) ); - } - }; - - class BeforeLowerBoundMixed : public Base { - BSONObj query() { return fromjson( "{a:{$in:[0,1]},b:{$in:[/^a/,'c'],$ne:'a'}}" ); } - BSONObj index() { return BSON( "a" << 1 << "b" << 1 ); } - void check() { - assertAdvanceTo( BSON( "a" << 0 << "b" << "" ), BSON( "a" << 0 << "b" << "a" ), - BSON( "a" << 0 << "b" << false ) ); - assertAdvanceTo( BSON( "a" << 1 << "b" << "bb" ), BSON( "a" << 1 << "b" << "c" ), - BSON( "a" << 0 << "b" << true ) ); - } - }; - - class AdvanceToNextExclusiveIntervalCompound : public Base { - BSONObj query() { return fromjson( "{a:{$in:['x',/^y/],$ne:'y'},b:{$in:[0,1]}}" ); } - BSONObj index() { return fromjson( "{a:1,b:1}" ); } - void check() { - assertAdvanceToNext( BSON( "a" << "x" << "b" << 1 ) ); - assertAdvanceToAfter( BSON( "a" << "y" << "b" << 0 ), BSON( "a" << "y" ) ); - assertAdvanceToNext( BSON( "a" << "yy" << "b" << 0 ) ); - } - }; - - class AdvanceRange : public Base { - BSONObj query() { return fromjson( "{a:{$gt:2,$lt:8}}" ); } - BSONObj index() { return fromjson( "{a:1}" ); } - void check() { - assertAdvanceToNext( BSON( "a" << 2 ) ); - assertAdvanceToNext( BSON( "a" << 4 ) ); - assertAdvanceToNext( BSON( "a" << 5 ) ); - assertDoneAdvancing( BSON( "a" << 9 ) ); - } - }; - - class AdvanceInRange : public Base { - BSONObj query() { return fromjson( "{a:{$in:[0,1]},b:{$gt:2,$lt:8}}" ); } - BSONObj index() { return fromjson( "{a:1,b:1}" ); } - void check() { - assertAdvanceToNext( BSON( "a" << 0 << "b" << 5 ) ); - assertAdvanceToNext( BSON( "a" << 0 << "b" << 5 ) ); - } - }; - - class AdvanceRangeIn : public Base { - BSONObj query() { return fromjson( "{a:{$gt:2,$lt:8},b:{$in:[0,1]}}" ); } - BSONObj index() { return fromjson( "{a:1,b:1}" ); } - void check() { - assertAdvanceToNext( BSON( "a" << 4 << "b" << 1 ) ); - assertAdvanceToNext( BSON( "a" << 5 << "b" << 0 ) ); - } - }; - - class AdvanceRangeRange : public Base { - BSONObj query() { return fromjson( "{a:{$gt:2,$lt:8},b:{$gt:1,$lt:4}}" ); } - BSONObj index() { return fromjson( "{a:1,b:1}" ); } - void check() { - assertAdvanceTo( BSON( "a" << 4 << "b" << 0 ), BSON( "a" << 4 << "b" << 1 ) ); - assertAdvanceToAfter( BSON( "a" << 4 << "b" << 6 ), BSON( "a" << 4 ) ); - } - }; - - class AdvanceRangeRangeMultiple : public Base { - BSONObj query() { return fromjson( "{a:{$gt:2,$lt:8},b:{$gt:1,$lt:4}}" ); } - BSONObj index() { return fromjson( "{a:1,b:1}" ); } - void check() { - assertAdvanceToNext( BSON( "a" << 4 << "b" << 3 ) ); - assertAdvanceToNext( BSON( "a" << 4 << "b" << 3 ) ); - } - }; - - class AdvanceRangeRangeIn : public Base { - BSONObj query() { return fromjson( "{a:{$gt:2,$lt:8},b:{$gt:1,$lt:4},c:{$in:[6,7]}}" ); } - BSONObj index() { return fromjson( "{a:1,b:1,c:1}" ); } - void check() { - assertAdvanceToNext( BSON( "a" << 4 << "b" << 3 << "c" << 7 ) ); - assertAdvanceToAfter( BSON( "a" << 4 << "b" << 6 << "c" << 7 ), BSON( "a" << 4 ) ); - assertAdvanceToNext( BSON( "a" << 5 << "b" << 3 << "c" << 6 ) ); - } - }; - - class AdvanceRangeMixed : public Base { - BSONObj query() { - return fromjson( "{a:{$gt:2,$lt:8},b:{$in:['a',/^b/],$ne:'b'}}" ); - } - BSONObj index() { return fromjson( "{a:1,b:1}" ); } - void check() { - assertAdvanceToNext( BSON( "a" << 4 << "b" << "a" ) ); - assertAdvanceToAfter( fromjson( "{a:4,b:'b'}" ), BSON( "a" << 4 << "b" << "b" ) ); - } - }; - - class AdvanceRangeMixed2 : public Base { - BSONObj query() { - return fromjson( "{a:{$gt:2,$lt:8},b:{$in:['a',/^b/],$ne:'b'}}" ); - } - BSONObj index() { return fromjson( "{a:1,b:1}" ); } - void check() { - assertAdvanceToNext( BSON( "a" << 4 << "b" << "a" ) ); - assertAdvanceTo( BSON( "a" << 4 << "b" << "aa" ), BSON( "a" << 4 << "b" << "b" ), - BSON( "a" << 0 << "b" << false ) ); - } - }; - - class AdvanceRangeMixedIn : public Base { - BSONObj query() { - return fromjson( "{a:{$gt:2,$lt:8},b:{$in:[/^a/,'c']},c:{$in:[6,7]}}" ); - } - BSONObj index() { return fromjson( "{a:1,b:1,c:1}" ); } - void check() { - assertAdvanceToNext( BSON( "a" << 4 << "b" << "aa" << "c" << 7 ) ); - assertAdvanceToAfter( fromjson( "{a:4,b:/^q/,c:7}" ), BSON( "a" << 4 ) ); - assertAdvanceToNext( BSON( "a" << 5 << "b" << "c" << "c" << 6 ) ); - } - }; - - class AdvanceRangeMixedMixed : public Base { - BSONObj query() { - return fromjson( "{a:{$gt:2,$lt:8},b:{$in:['a',/^b/],$ne:'b'}," - "c:{$in:[/^a/,'c'],$ne:'a'}}" ); - } - BSONObj index() { return fromjson( "{a:1,b:1,c:1}" ); } - void check() { - assertAdvanceTo( BSON( "a" << 4 << "b" << "a" << "c" << "bb" ), - BSON( "a" << 4 << "b" << "a" << "c" << "c" ) ); - assertAdvanceTo( BSON( "a" << 4 << "b" << "aa" << "c" << "b" ), - BSON( "a" << 4 << "b" << "b" << "c" << "a" ), - BSON( "a" << 0 << "b" << false << "c" << false ) ); - } - }; - - class AdvanceMixedMixedIn : public Base { - BSONObj query() { - return fromjson( "{a:{$in:[/^a/,'c']},b:{$in:[/^a/,'c']},c:{$in:[6,7]}}" ); - } - BSONObj index() { return fromjson( "{a:1,b:1,c:1}" ); } - void check() { - assertAdvanceToNext( BSON( "a" << "a" << "b" << "aa" << "c" << 7 ) ); - assertAdvanceToAfter( fromjson( "{a:'a',b:/^q/,c:7}" ), BSON( "a" << "a" ) ); - assertAdvanceToNext( BSON( "a" << "c" << "b" << "c" << "c" << 6 ) ); - } - }; - - namespace CompoundRangeCounter { - - class RangeTracking { - public: - RangeTracking() : _counter( 2, 0 ) {} - void run() { - ASSERT_EQUALS( 2, _counter.size() ); - checkValues( -1, -1 ); - - _counter.setZeroes( 0 ); - checkValues( 0, 0 ); - - _counter.setUnknowns( 1 ); - checkValues( 0, -1 ); - - _counter.setZeroes( 1 ); - checkValues( 0, 0 ); - - _counter.setUnknowns( 0 ); - checkValues( -1, -1 ); - - _counter.set( 0, 3 ); - checkValues( 3, -1 ); - - _counter.inc( 0 ); - checkValues( 4, -1 ); - - _counter.set( 1, 5 ); - checkValues( 4, 5 ); - - _counter.inc( 1 ); - checkValues( 4, 6 ); - } - private: - void checkValues( int first, int second ) { - ASSERT_EQUALS( first, _counter.get( 0 ) ); - ASSERT_EQUALS( second, _counter.get( 1 ) ); - } - FieldRangeVectorIterator::CompoundRangeCounter _counter; - }; - - class SingleIntervalCount { - public: - SingleIntervalCount() : _counter( 2, 2 ) {} - void run() { - assertLimitNotReached(); - - _counter.incSingleIntervalCount(); - assertLimitNotReached(); - - _counter.incSingleIntervalCount(); - assertLimitReached(); - - _counter.set( 1, 1 ); - assertLimitNotReached(); - } - private: - void assertLimitReached() const { - ASSERT( _counter.hasSingleIntervalCountReachedLimit() ); - } - void assertLimitNotReached() const { - ASSERT( !_counter.hasSingleIntervalCountReachedLimit() ); - } - FieldRangeVectorIterator::CompoundRangeCounter _counter; - }; - - class SingleIntervalCountUpdateBase { - public: - SingleIntervalCountUpdateBase() : _counter( 2, 1 ) {} - virtual ~SingleIntervalCountUpdateBase() {} - void run() { - _counter.incSingleIntervalCount(); - ASSERT( _counter.hasSingleIntervalCountReachedLimit() ); - applyUpdate(); - ASSERT( !_counter.hasSingleIntervalCountReachedLimit() ); - } - protected: - virtual void applyUpdate() = 0; - FieldRangeVectorIterator::CompoundRangeCounter &counter() { return _counter; } - private: - FieldRangeVectorIterator::CompoundRangeCounter _counter; - }; - - class Set : public SingleIntervalCountUpdateBase { - void applyUpdate() { counter().set( 0, 1 ); } - }; - - class Inc : public SingleIntervalCountUpdateBase { - void applyUpdate() { counter().inc( 1 ); } - }; - - class SetZeroes : public SingleIntervalCountUpdateBase { - void applyUpdate() { counter().setZeroes( 0 ); } - }; - - class SetUnknowns : public SingleIntervalCountUpdateBase { - void applyUpdate() { counter().setUnknowns( 1 ); } - }; - - } // namespace CompoundRangeCounter - - namespace FieldIntervalMatcher { - - class IsEqInclusiveUpperBound { - public: - void run() { - BSONObj exclusiveInterval = BSON( "$lt" << 5 ); - for ( int i = 4; i <= 6; ++i ) { - for ( int reverse = 0; reverse < 2; ++reverse ) { - ASSERT( !isEqInclusiveUpperBound( exclusiveInterval, BSON( "" << i ), - reverse ) ); - } - } - BSONObj inclusiveInterval = BSON( "$lte" << 4 ); - for ( int i = 3; i <= 5; i += 2 ) { - for ( int reverse = 0; reverse < 2; ++reverse ) { - ASSERT( !isEqInclusiveUpperBound( inclusiveInterval, BSON( "" << i ), - reverse ) ); - } - } - ASSERT( isEqInclusiveUpperBound( inclusiveInterval, BSON( "" << 4 ), true ) ); - ASSERT( isEqInclusiveUpperBound( inclusiveInterval, BSON( "" << 4 ), false ) ); - } - private: - bool isEqInclusiveUpperBound( const BSONObj &intervalSpec, - const BSONObj &elementSpec, - bool reverse ) { - FieldRange range( intervalSpec.firstElement(), false, true ); - BSONElement element = elementSpec.firstElement(); - FieldRangeVectorIterator::FieldIntervalMatcher matcher - ( range.intervals()[ 0 ], element, reverse ); - return matcher.isEqInclusiveUpperBound(); - } - }; - - class IsGteUpperBound { - public: - void run() { - BSONObj exclusiveInterval = BSON( "$lt" << 5 ); - ASSERT( !isGteUpperBound( exclusiveInterval, BSON( "" << 4 ), false ) ); - ASSERT( isGteUpperBound( exclusiveInterval, BSON( "" << 5 ), false ) ); - ASSERT( isGteUpperBound( exclusiveInterval, BSON( "" << 6 ), false ) ); - ASSERT( isGteUpperBound( exclusiveInterval, BSON( "" << 4 ), true ) ); - ASSERT( isGteUpperBound( exclusiveInterval, BSON( "" << 5 ), true ) ); - ASSERT( !isGteUpperBound( exclusiveInterval, BSON( "" << 6 ), true ) ); - BSONObj inclusiveInterval = BSON( "$lte" << 4 ); - ASSERT( !isGteUpperBound( inclusiveInterval, BSON( "" << 3 ), false ) ); - ASSERT( isGteUpperBound( inclusiveInterval, BSON( "" << 4 ), false ) ); - ASSERT( isGteUpperBound( inclusiveInterval, BSON( "" << 5 ), false ) ); - ASSERT( isGteUpperBound( inclusiveInterval, BSON( "" << 3 ), true ) ); - ASSERT( isGteUpperBound( inclusiveInterval, BSON( "" << 4 ), true ) ); - ASSERT( !isGteUpperBound( inclusiveInterval, BSON( "" << 5 ), true ) ); - } - private: - bool isGteUpperBound( const BSONObj &intervalSpec, const BSONObj &elementSpec, - bool reverse ) { - FieldRange range( intervalSpec.firstElement(), false, true ); - BSONElement element = elementSpec.firstElement(); - FieldRangeVectorIterator::FieldIntervalMatcher matcher - ( range.intervals()[ 0 ], element, reverse ); - return matcher.isGteUpperBound(); - } - }; - - class IsEqExclusiveLowerBound { - public: - void run() { - BSONObj exclusiveInterval = BSON( "$gt" << 5 ); - for ( int i = 4; i <= 6; i += 2 ) { - for ( int reverse = 0; reverse < 2; ++reverse ) { - ASSERT( !isEqExclusiveLowerBound( exclusiveInterval, BSON( "" << i ), - reverse ) ); - } - } - ASSERT( isEqExclusiveLowerBound( exclusiveInterval, BSON( "" << 5 ), true ) ); - ASSERT( isEqExclusiveLowerBound( exclusiveInterval, BSON( "" << 5 ), false ) ); - BSONObj inclusiveInterval = BSON( "$gte" << 4 ); - for ( int i = 3; i <= 5; ++i ) { - for ( int reverse = 0; reverse < 2; ++reverse ) { - ASSERT( !isEqExclusiveLowerBound( inclusiveInterval, BSON( "" << i ), - reverse ) ); - } - } - } - private: - bool isEqExclusiveLowerBound( const BSONObj &intervalSpec, - const BSONObj &elementSpec, - bool reverse ) { - FieldRange range( intervalSpec.firstElement(), false, true ); - BSONElement element = elementSpec.firstElement(); - FieldRangeVectorIterator::FieldIntervalMatcher matcher - ( range.intervals()[ 0 ], element, reverse ); - return matcher.isEqExclusiveLowerBound(); - } - }; - - class IsLtLowerBound { - public: - void run() { - BSONObj exclusiveInterval = BSON( "$gt" << 5 ); - ASSERT( isLtLowerBound( exclusiveInterval, BSON( "" << 4 ), false ) ); - ASSERT( !isLtLowerBound( exclusiveInterval, BSON( "" << 5 ), false ) ); - ASSERT( !isLtLowerBound( exclusiveInterval, BSON( "" << 6 ), false ) ); - ASSERT( !isLtLowerBound( exclusiveInterval, BSON( "" << 4 ), true ) ); - ASSERT( !isLtLowerBound( exclusiveInterval, BSON( "" << 5 ), true ) ); - ASSERT( isLtLowerBound( exclusiveInterval, BSON( "" << 6 ), true ) ); - BSONObj inclusiveInterval = BSON( "$gte" << 4 ); - ASSERT( isLtLowerBound( inclusiveInterval, BSON( "" << 3 ), false ) ); - ASSERT( !isLtLowerBound( inclusiveInterval, BSON( "" << 4 ), false ) ); - ASSERT( !isLtLowerBound( inclusiveInterval, BSON( "" << 5 ), false ) ); - ASSERT( !isLtLowerBound( inclusiveInterval, BSON( "" << 3 ), true ) ); - ASSERT( !isLtLowerBound( inclusiveInterval, BSON( "" << 4 ), true ) ); - ASSERT( isLtLowerBound( inclusiveInterval, BSON( "" << 5 ), true ) ); - } - private: - bool isLtLowerBound( const BSONObj &intervalSpec, const BSONObj &elementSpec, - bool reverse ) { - FieldRange range( intervalSpec.firstElement(), false, true ); - BSONElement element = elementSpec.firstElement(); - FieldRangeVectorIterator::FieldIntervalMatcher matcher - ( range.intervals()[ 0 ], element, reverse ); - return matcher.isLtLowerBound(); - } - }; - - class CheckLowerAfterUpper { - public: - void run() { - BSONObj intervalSpec = BSON( "$in" << BSON_ARRAY( 1 << 2 ) ); - BSONObj elementSpec = BSON( "" << 1 ); - FieldRange range( intervalSpec.firstElement(), false, true ); - BSONElement element = elementSpec.firstElement(); - FieldRangeVectorIterator::FieldIntervalMatcher matcher - ( range.intervals()[ 0 ], element, false ); - ASSERT( matcher.isEqInclusiveUpperBound() ); - ASSERT( matcher.isGteUpperBound() ); - ASSERT( !matcher.isEqExclusiveLowerBound() ); - ASSERT( !matcher.isLtLowerBound() ); - } - }; - - } // namespace FieldIntervalMatcher - - namespace SingleIntervalLimit { - - class NoLimit : public Base { - BSONObj query() { return BSON( "a" << 1 ); } - BSONObj index() { return BSON( "a" << 1 ); } - int singleIntervalLimit() { return 0; } - void check() { - for( int i = 0; i < 100; ++i ) { - assertAdvanceToNext( BSON( "a" << 1 ) ); - } - } - }; - - class OneIntervalLimit : public Base { - BSONObj query() { return BSON( "a" << 1 ); } - BSONObj index() { return BSON( "a" << 1 ); } - int singleIntervalLimit() { return 3; } - void check() { - for( int i = 0; i < 3; ++i ) { - assertAdvanceToNext( BSON( "a" << 1 ) ); - } - assertDoneAdvancing( BSON( "a" << 1 ) ); - } - }; - - class TwoIntervalLimit : public Base { - BSONObj query() { return fromjson( "{a:{$in:[0,1]}}" ); } - BSONObj index() { return BSON( "a" << 1 ); } - int singleIntervalLimit() { return 2; } - void check() { - for( int i = 0; i < 2; ++i ) { - assertAdvanceToNext( BSON( "a" << 0 ) ); - } - assertAdvanceTo( BSON( "a" << 0 ), BSON( "a" << 1 ) ); - for( int i = 0; i < 2; ++i ) { - assertAdvanceToNext( BSON( "a" << 1 ) ); - } - assertDoneAdvancing( BSON( "a" << 1 ) ); - } - }; - - class ThreeIntervalLimitUnreached : public Base { - BSONObj query() { return fromjson( "{a:{$in:[0,1,2]}}" ); } - BSONObj index() { return BSON( "a" << 1 ); } - int singleIntervalLimit() { return 2; } - void check() { - for( int i = 0; i < 2; ++i ) { - assertAdvanceToNext( BSON( "a" << 0 ) ); - } - assertAdvanceTo( BSON( "a" << 1.5 ), BSON( "a" << 2 ) ); - } - }; - - class FirstIntervalExhaustedBeforeLimit : public Base { - BSONObj query() { return fromjson( "{a:{$in:[0,1]}}" ); } - BSONObj index() { return BSON( "a" << 1 ); } - int singleIntervalLimit() { return 3; } - void check() { - assertAdvanceToNext( BSON( "a" << 0 ) ); - for( int i = 0; i < 3; ++i ) { - assertAdvanceToNext( BSON( "a" << 1 ) ); - } - assertDoneAdvancing( BSON( "a" << 1 ) ); - } - }; - - class FirstIntervalNotExhaustedAtLimit : public Base { - BSONObj query() { return fromjson( "{a:{$in:[0,1]}}" ); } - BSONObj index() { return BSON( "a" << 1 ); } - int singleIntervalLimit() { return 3; } - void check() { - for( int i = 0; i < 3; ++i ) { - assertAdvanceToNext( BSON( "a" << 0 ) ); - } - for( int i = 0; i < 3; ++i ) { - assertAdvanceToNext( BSON( "a" << 1 ) ); - } - assertDoneAdvancing( BSON( "a" << 1 ) ); - } - }; - - class EqualIn : public Base { - BSONObj query() { return fromjson( "{a:1,b:{$in:[0,1]}}" ); } - BSONObj index() { return BSON( "a" << 1 << "b" << 1 ); } - int singleIntervalLimit() { return 3; } - void check() { - for( int i = 0; i < 3; ++i ) { - assertAdvanceToNext( BSON( "a" << 1 << "b" << 0 ) ); - } - assertAdvanceTo( BSON( "a" << 1 << "b" << 0 ), BSON( "a" << 1 << "b" << 1 ) ); - for( int i = 0; i < 3; ++i ) { - assertAdvanceToNext( BSON( "a" << 1 << "b" << 1 ) ); - } - assertDoneAdvancing( BSON( "a" << 1 << "b" << 1 ) ); - } - }; - - class TwoIntervalIntermediateValue : public Base { - BSONObj query() { return fromjson( "{a:{$in:[0,1]}}" ); } - BSONObj index() { return BSON( "a" << 1 ); } - int singleIntervalLimit() { return 3; } - void check() { - for( int i = 0; i < 2; ++i ) { - assertAdvanceToNext( BSON( "a" << 0 ) ); - } - assertAdvanceTo( BSON( "a" << 0.5 ), BSON( "a" << 1 ) ); - for( int i = 0; i < 3; ++i ) { - assertAdvanceToNext( BSON( "a" << 1 ) ); - } - assertDoneAdvancing( BSON( "a" << 1 ) ); - } - }; - - class InGte : public Base { - BSONObj query() { return fromjson( "{a:{$in:[0,1]},b:{$gte:5}}" ); } - BSONObj index() { return BSON( "a" << 1 << "b" << 1 ); } - int singleIntervalLimit() { return 1; } - void check() { - assertAdvanceToNext( BSON( "a" << 0 << "b" << 5 ) ); - assertAdvanceToAfter( BSON( "a" << 0 << "b" << 5 ), BSON( "a" << 0 ) ); - assertAdvanceTo( BSON( "a" << 0.5 << "b" << 6 ), BSON( "a" << 1 << "b" << 5 ) ); - assertAdvanceToNext( BSON( "a" << 1 << "b" << 7 ) ); - assertDoneAdvancing( BSON( "a" << 1 << "b" << 7 ) ); - } - }; - - class InEmptyGte : public Base { - BSONObj query() { return fromjson( "{a:{$in:[0,1,2,3,4]},b:{$gte:5}}" ); } - BSONObj index() { return BSON( "a" << 1 << "b" << 1 ); } - int singleIntervalLimit() { return 1; } - void check() { - assertAdvanceToNext( BSON( "a" << 1 << "b" << 5 ) ); - assertAdvanceToAfter( BSON( "a" << 1 << "b" << 5 ), BSON( "a" << 1 ) ); - assertAdvanceTo( BSON( "a" << 2 << "b" << 2 ), - BSON( "a" << 2 << "b" << 5 ) ); - assertAdvanceToNext( BSON( "a" << 3 << "b" << 5 ) ); - assertDoneAdvancing( BSON( "a" << 4.1 << "b" << 2 ) ); - } - }; - - class InGt : public Base { - BSONObj query() { return fromjson( "{a:{$in:[0,1]},b:{$gt:5}}" ); } - BSONObj index() { return BSON( "a" << 1 << "b" << 1 ); } - int singleIntervalLimit() { return 1; } - void check() { - assertAdvanceToNext( BSON( "a" << 0 << "b" << 5.1 ) ); - assertAdvanceToAfter( BSON( "a" << 0 << "b" << 5.1 ), BSON( "a" << 0 ) ); - assertAdvanceToAfter( BSON( "a" << 1 << "b" << 5 ), - BSON( "a" << 1 << "b" << 5 ) ); - assertAdvanceToNext( BSON( "a" << 1 << "b" << 5.01 ) ); - assertDoneAdvancing( BSON( "a" << 1 << "b" << 5.01 ) ); - } - }; - - class InEmptyGt : public Base { - BSONObj query() { return fromjson( "{a:{$in:[0,1,2,3,4]},b:{$gt:5}}" ); } - BSONObj index() { return BSON( "a" << 1 << "b" << 1 ); } - int singleIntervalLimit() { return 1; } - void check() { - assertAdvanceToNext( BSON( "a" << 1 << "b" << 5.1 ) ); - assertAdvanceToAfter( BSON( "a" << 1 << "b" << 5.1 ), BSON( "a" << 1 ) ); - assertAdvanceTo( BSON( "a" << 2 << "b" << 2 ), - BSON( "a" << 2 << "b" << 5 ) ); - assertAdvanceToNext( BSON( "a" << 3 << "b" << 5.01 ) ); - assertDoneAdvancing( BSON( "a" << 4.1 << "b" << 2 ) ); - } - }; - - class InLt : public Base { - BSONObj query() { return fromjson( "{a:{$in:[0,1]},b:{$lt:5}}" ); } - BSONObj index() { return BSON( "a" << 1 << "b" << 1 ); } - int singleIntervalLimit() { return 1; } - void check() { - assertAdvanceToNext( BSON( "a" << 0 << "b" << 2 ) ); - assertAdvanceToAfter( BSON( "a" << 0 << "b" << 2 ), BSON( "a" << 0 ) ); - assertDoneAdvancing( BSON( "a" << 1 << "b" << 5 ) ); - } - }; - - class InGteLt : public Base { - BSONObj query() { return fromjson( "{a:{$in:[0,1,2]},b:{$gte:2,$lt:5}}" ); } - BSONObj index() { return BSON( "a" << 1 << "b" << 1 ); } - int singleIntervalLimit() { return 1; } - void check() { - assertAdvanceToNext( BSON( "a" << 0 << "b" << 3 ) ); - assertAdvanceToAfter( BSON( "a" << 0 << "b" << 4 ), BSON( "a" << 0 ) ); - assertAdvanceToAfter( BSON( "a" << 1 << "b" << 5 ), BSON( "a" << 1 ) ); - assertAdvanceToNext( BSON( "a" << 2 << "b" << 4 ) ); - assertDoneAdvancing( BSON( "a" << 2 << "b" << 4 ) ); - } - }; - - class InGtHigherLimit : public Base { - BSONObj query() { return fromjson( "{a:{$in:[0,1,2]},b:{$gt:5}}" ); } - BSONObj index() { return BSON( "a" << 1 << "b" << 1 ); } - int singleIntervalLimit() { return 2; } - void check() { - assertAdvanceToNext( BSON( "a" << 0 << "b" << 5.1 ) ); - assertAdvanceToNext( BSON( "a" << 0 << "b" << 6 ) ); - assertAdvanceToAfter( BSON( "a" << 0 << "b" << 7 ), BSON( "a" << 0 ) ); - assertAdvanceToAfter( BSON( "a" << 1 << "b" << 5 ), - BSON( "a" << 1 << "b" << 5 ) ); - assertAdvanceToAfter( BSON( "a" << 2 << "b" << 5 ), - BSON( "a" << 2 << "b" << 5 ) ); - assertAdvanceToNext( BSON( "a" << 2 << "b" << 5.01 ) ); - assertAdvanceToNext( BSON( "a" << 2 << "b" << 5.02 ) ); - assertDoneAdvancing( BSON( "a" << 2 << "b" << 5.02 ) ); - } - }; - - /** - * The singleIntervalLimit feature should not be used with multiple range bounds, in - * spite of the corner case checked in this test. - */ - class TwoRange : public Base { - BSONObj query() { return fromjson( "{a:{$in:[/^a/,/^c/]}}" ); } - BSONObj index() { return BSON( "a" << 1 ); } - int singleIntervalLimit() { return 2; } - void check() { - for( int i = 0; i < 2; ++i ) { - assertAdvanceToNext( BSON( "a" << "a" ) ); - } - assertAdvanceTo( BSON( "a" << "b" ), BSON( "a" << "c" ) ); - } - }; - - class IndexPrefix : public Base { - BSONObj query() { return fromjson( "{a:{$in:[1,2]}}" ); } - BSONObj index() { return BSON( "a" << 1 << "b" << 1 ); } - int singleIntervalLimit() { return 1; } - void check() { - assertAdvanceToNext( BSON( "a" << 1 << "b" << 1 ) ); - assertAdvanceTo( BSON( "a" << 1 << "b" << 3 ), - BSON( "a" << 2 << "b" << minElt() ) ); - assertAdvanceToNext( BSON( "a" << 2 << "b" << 0 ) ); - assertDoneAdvancing( BSON( "a" << 2 << "b" << 1 ) ); - } - }; - - class IndexPrefixReverseOrder : public Base { - BSONObj query() { return fromjson( "{a:{$in:[1,2]}}" ); } - BSONObj index() { return BSON( "a" << 1 << "b" << -1 ); } - int singleIntervalLimit() { return 1; } - void check() { - assertAdvanceToNext( BSON( "a" << 1 << "b" << 1 ) ); - assertAdvanceTo( BSON( "a" << 1 << "b" << 0 ), - BSON( "a" << 2 << "b" << maxElt() ) ); - assertAdvanceToNext( BSON( "a" << 2 << "b" << 1 ) ); - assertDoneAdvancing( BSON( "a" << 2 << "b" << 0 ) ); - } - }; - - class LongerIndexPrefix : public Base { - BSONObj query() { return fromjson( "{a:{$in:[1,2,3]}}" ); } - BSONObj index() { return BSON( "a" << 1 << "b" << 1 << "c" << 1 ); } - int singleIntervalLimit() { return 1; } - void check() { - assertAdvanceToNext( BSON( "a" << 1 << "b" << 1 << "c" << 1 ) ); - assertAdvanceTo( BSON( "a" << 1 << "b" << 1 << "c" << 1 ), - BSON( "a" << 2 << "b" << minElt() << "c" << minElt() ) ); - assertAdvanceTo( BSON( "a" << 2.5 << "b" << 1 << "c" << 2 ), - BSON( "a" << 3 << "b" << minElt() << "c" << minElt() ) ); - assertDoneAdvancing( BSON( "a" << 4 << "b" << 1 << "c" << 1 ) ); - } - }; - - } // namespace SameRangeLimit - - } // namespace FieldRangeVectorIteratorTests - - class All : public Suite { - public: - All() : Suite( "queryutil" ) {} - - void setupTests() { - add<FieldIntervalTests::ToString>(); - add<FieldRangeTests::ToString>(); - add<FieldRangeTests::EmptyQuery>(); - add<FieldRangeTests::Eq>(); - add<FieldRangeTests::DupEq>(); - add<FieldRangeTests::Lt>(); - add<FieldRangeTests::Lte>(); - add<FieldRangeTests::LtDate>(); - add<FieldRangeTests::Gt>(); - add<FieldRangeTests::Gte>(); - add<FieldRangeTests::GtString>(); - add<FieldRangeTests::TwoLt>(); - add<FieldRangeTests::TwoGt>(); - add<FieldRangeTests::EqGte>(); - add<FieldRangeTests::EqGteInvalid>(); - add<FieldRangeTests::Regex>(); - add<FieldRangeTests::RegexObj>(); - add<FieldRangeTests::UnhelpfulRegex>(); - add<FieldRangeTests::In>(); - add<FieldRangeTests::And>(); - add<FieldRangeTests::SingletonOr>(); - add<FieldRangeTests::NestedSingletonOr>(); - add<FieldRangeTests::NonSingletonOr>(); - add<FieldRangeTests::Empty>(); - add<FieldRangeTests::Equality>(); - add<FieldRangeTests::NoWhere>(); - add<FieldRangeTests::Numeric>(); - add<FieldRangeTests::InLowerBound>(); - add<FieldRangeTests::InUpperBound>(); - add<FieldRangeTests::BoundUnion>(); - add<FieldRangeTests::BoundUnionFullyContained>(); - add<FieldRangeTests::BoundUnionOverlapWithInclusivity>(); - add<FieldRangeTests::BoundUnionEmpty>(); - add<FieldRangeTests::MultiBound>(); - add<FieldRangeTests::Diff1>(); - add<FieldRangeTests::Diff2>(); - add<FieldRangeTests::Diff3>(); - add<FieldRangeTests::Diff4>(); - add<FieldRangeTests::Diff5>(); - add<FieldRangeTests::Diff6>(); - add<FieldRangeTests::Diff7>(); - add<FieldRangeTests::Diff8>(); - add<FieldRangeTests::Diff9>(); - add<FieldRangeTests::Diff10>(); - add<FieldRangeTests::Diff11>(); - add<FieldRangeTests::Diff12>(); - add<FieldRangeTests::Diff13>(); - add<FieldRangeTests::Diff14>(); - add<FieldRangeTests::Diff15>(); - add<FieldRangeTests::Diff16>(); - add<FieldRangeTests::Diff17>(); - add<FieldRangeTests::Diff18>(); - add<FieldRangeTests::Diff19>(); - add<FieldRangeTests::Diff20>(); - add<FieldRangeTests::Diff21>(); - add<FieldRangeTests::Diff22>(); - add<FieldRangeTests::Diff23>(); - add<FieldRangeTests::Diff24>(); - add<FieldRangeTests::Diff25>(); - add<FieldRangeTests::Diff26>(); - add<FieldRangeTests::Diff27>(); - add<FieldRangeTests::Diff28>(); - add<FieldRangeTests::Diff29>(); - add<FieldRangeTests::Diff30>(); - add<FieldRangeTests::Diff31>(); - add<FieldRangeTests::Diff32>(); - add<FieldRangeTests::Diff33>(); - add<FieldRangeTests::Diff34>(); - add<FieldRangeTests::Diff35>(); - add<FieldRangeTests::Diff36>(); - add<FieldRangeTests::Diff37>(); - add<FieldRangeTests::Diff38>(); - add<FieldRangeTests::Diff39>(); - add<FieldRangeTests::Diff40>(); - add<FieldRangeTests::Diff41>(); - add<FieldRangeTests::Diff42>(); - add<FieldRangeTests::Diff43>(); - add<FieldRangeTests::Diff44>(); - add<FieldRangeTests::Diff45>(); - add<FieldRangeTests::Diff46>(); - add<FieldRangeTests::Diff47>(); - add<FieldRangeTests::Diff48>(); - add<FieldRangeTests::Diff49>(); - add<FieldRangeTests::Diff50>(); - add<FieldRangeTests::Diff51>(); - add<FieldRangeTests::Diff52>(); - add<FieldRangeTests::Diff53>(); - add<FieldRangeTests::Diff54>(); - add<FieldRangeTests::Diff55>(); - add<FieldRangeTests::Diff56>(); - add<FieldRangeTests::Diff57>(); - add<FieldRangeTests::Diff58>(); - add<FieldRangeTests::Diff59>(); - add<FieldRangeTests::Diff60>(); - add<FieldRangeTests::Diff61>(); - add<FieldRangeTests::Diff62>(); - add<FieldRangeTests::Diff63>(); - add<FieldRangeTests::Diff64>(); - add<FieldRangeTests::DiffMulti1>(); - add<FieldRangeTests::DiffMulti2>(); - add<FieldRangeTests::Universal>(); - add<FieldRangeTests::ElemMatchRegex>(); - add<FieldRangeTests::PreserveNonUniversalElemMatchContext>(); - add<FieldRangeTests::IntersectSpecial>(); - add<FieldRangeTests::ExactMatchRepresentation::EqualArray>(); - add<FieldRangeTests::ExactMatchRepresentation::EqualEmptyArray>(); - add<FieldRangeTests::ExactMatchRepresentation::EqualNull>(); - add<FieldRangeTests::ExactMatchRepresentation::InArray>(); - add<FieldRangeTests::ExactMatchRepresentation::InRegex>(); - add<FieldRangeTests::ExactMatchRepresentation::InNull>(); - add<FieldRangeTests::ExactMatchRepresentation::Exists>(); - add<FieldRangeTests::ExactMatchRepresentation::UntypedRegex>(); - add<FieldRangeTests::ExactMatchRepresentation::UntypedRegexString>(); - add<FieldRangeTests::ExactMatchRepresentation::NotIn>(); - add<FieldRangeTests::ExactMatchRepresentation::NotGt>(); - add<FieldRangeTests::ExactMatchRepresentation::GtArray>(); - add<FieldRangeTests::ExactMatchRepresentation::GtNull>(); - add<FieldRangeTests::ExactMatchRepresentation::LtObject>(); - add<FieldRangeTests::ExactMatchRepresentation::NotNe>(); - add<FieldRangeTests::ExactMatchRepresentation::MultikeyIntersection>(); - add<FieldRangeTests::ExactMatchRepresentation::Intersection>(); - add<FieldRangeTests::ExactMatchRepresentation::Union>(); - add<FieldRangeTests::ExactMatchRepresentation::Difference>(); - add<FieldRangeSetTests::Basics>(); - add<FieldRangeSetTests::Intersect>(); - add<FieldRangeSetTests::MultiKeyIntersect>(); - add<FieldRangeSetTests::EmptyMultiKeyIntersect>(); - add<FieldRangeSetTests::MultiKeyDiff>(); - add<FieldRangeSetTests::MatchPossible>(); - add<FieldRangeSetTests::MatchPossibleForIndex>(); - add<FieldRangeSetTests::Subset>(); - add<FieldRangeSetTests::Prefixed>(); - add<FieldRangeSetTests::Atomic>(); - add<FieldRangeSetTests::ElemMatch::Ranges>(); - add<FieldRangeSetTests::ElemMatch::TopLevelElements>(); - add<FieldRangeSetTests::ElemMatch::TopLevelNotElement>(); - add<FieldRangeSetTests::ElemMatch::TopLevelNested>(); - add<FieldRangeSetTests::ElemMatch::Not>(); - add<FieldRangeSetTests::ElemMatch::Nested>(); - add<FieldRangeSetTests::ElemMatch::AllNested>(); - add<FieldRangeSetTests::ExactMatchRepresentation::EmptyQuery>(); - add<FieldRangeSetTests::ExactMatchRepresentation::Equal>(); - add<FieldRangeSetTests::ExactMatchRepresentation::In>(); - add<FieldRangeSetTests::ExactMatchRepresentation::Where>(); - add<FieldRangeSetTests::ExactMatchRepresentation::Not>(); - add<FieldRangeSetTests::ExactMatchRepresentation::Regex>(); - add<FieldRangeSetTests::ExactMatchRepresentation::UntypedRegex>(); - add<FieldRangeSetTests::ExactMatchRepresentation::UntypedRegexString>(); - add<FieldRangeSetTests::ExactMatchRepresentation::And>(); - add<FieldRangeSetTests::ExactMatchRepresentation::Or>(); - add<FieldRangeSetTests::ExactMatchRepresentation::All>(); - add<FieldRangeSetTests::ExactMatchRepresentation::ElemMatch>(); - add<FieldRangeSetTests::ExactMatchRepresentation::AllElemMatch>(); - add<FieldRangeSetTests::ExactMatchRepresentation::NotSecondField>(); - add<FieldRangeSetPairTests::ToString>(); - add<FieldRangeSetPairTests::NoNonUniversalRanges>(); - add<FieldRangeSetPairTests::MatchPossible>(); - add<FieldRangeVectorTests::ToString>(); - add<FieldRangeVectorTests::HasAllIndexedRanges>(); - add<FieldRangeVectorTests::SingleInterval>(); - add<FieldRangeVectorTests::StartEndKey>(); - add<FieldRangeVectorIteratorTests::AdvanceToNextIntervalEquality>(); - add<FieldRangeVectorIteratorTests::AdvanceToNextIntervalExclusiveInequality>(); - add<FieldRangeVectorIteratorTests::AdvanceToNextIntervalEqualityReverse>(); - add<FieldRangeVectorIteratorTests::AdvanceToNextIntervalEqualityCompound>(); - add<FieldRangeVectorIteratorTests::AdvanceToNextIntervalIntermediateEqualityCompound>(); - add<FieldRangeVectorIteratorTests::AdvanceToNextIntervalIntermediateInMixed>(); - add<FieldRangeVectorIteratorTests::BeforeLowerBound>(); - add<FieldRangeVectorIteratorTests::BeforeLowerBoundMixed>(); - add<FieldRangeVectorIteratorTests::AdvanceToNextExclusiveIntervalCompound>(); - add<FieldRangeVectorIteratorTests::AdvanceRange>(); - add<FieldRangeVectorIteratorTests::AdvanceInRange>(); - add<FieldRangeVectorIteratorTests::AdvanceRangeIn>(); - add<FieldRangeVectorIteratorTests::AdvanceRangeRange>(); - add<FieldRangeVectorIteratorTests::AdvanceRangeRangeMultiple>(); - add<FieldRangeVectorIteratorTests::AdvanceRangeRangeIn>(); - add<FieldRangeVectorIteratorTests::AdvanceRangeMixed>(); - add<FieldRangeVectorIteratorTests::AdvanceRangeMixed2>(); - add<FieldRangeVectorIteratorTests::AdvanceRangeMixedIn>(); - add<FieldRangeVectorIteratorTests::AdvanceRangeMixedMixed>(); - add<FieldRangeVectorIteratorTests::AdvanceMixedMixedIn>(); - add<FieldRangeVectorIteratorTests::CompoundRangeCounter::RangeTracking>(); - add<FieldRangeVectorIteratorTests::CompoundRangeCounter::SingleIntervalCount>(); - add<FieldRangeVectorIteratorTests::CompoundRangeCounter::Set>(); - add<FieldRangeVectorIteratorTests::CompoundRangeCounter::Inc>(); - add<FieldRangeVectorIteratorTests::CompoundRangeCounter::SetZeroes>(); - add<FieldRangeVectorIteratorTests::CompoundRangeCounter::SetUnknowns>(); - add<FieldRangeVectorIteratorTests::FieldIntervalMatcher::IsEqInclusiveUpperBound>(); - add<FieldRangeVectorIteratorTests::FieldIntervalMatcher::IsGteUpperBound>(); - add<FieldRangeVectorIteratorTests::FieldIntervalMatcher::IsEqExclusiveLowerBound>(); - add<FieldRangeVectorIteratorTests::FieldIntervalMatcher::IsLtLowerBound>(); - add<FieldRangeVectorIteratorTests::FieldIntervalMatcher::CheckLowerAfterUpper>(); - add<FieldRangeVectorIteratorTests::SingleIntervalLimit::NoLimit>(); - add<FieldRangeVectorIteratorTests::SingleIntervalLimit::OneIntervalLimit>(); - add<FieldRangeVectorIteratorTests::SingleIntervalLimit::TwoIntervalLimit>(); - add<FieldRangeVectorIteratorTests::SingleIntervalLimit::ThreeIntervalLimitUnreached>(); - add<FieldRangeVectorIteratorTests::SingleIntervalLimit:: - FirstIntervalExhaustedBeforeLimit>(); - add<FieldRangeVectorIteratorTests::SingleIntervalLimit:: - FirstIntervalNotExhaustedAtLimit>(); - add<FieldRangeVectorIteratorTests::SingleIntervalLimit::EqualIn>(); - add<FieldRangeVectorIteratorTests::SingleIntervalLimit::TwoIntervalIntermediateValue>(); - add<FieldRangeVectorIteratorTests::SingleIntervalLimit::InGte>(); - add<FieldRangeVectorIteratorTests::SingleIntervalLimit::InEmptyGte>(); - add<FieldRangeVectorIteratorTests::SingleIntervalLimit::InGt>(); - add<FieldRangeVectorIteratorTests::SingleIntervalLimit::InEmptyGt>(); - add<FieldRangeVectorIteratorTests::SingleIntervalLimit::InLt>(); - add<FieldRangeVectorIteratorTests::SingleIntervalLimit::InGteLt>(); - add<FieldRangeVectorIteratorTests::SingleIntervalLimit::InGtHigherLimit>(); - add<FieldRangeVectorIteratorTests::SingleIntervalLimit::TwoRange>(); - add<FieldRangeVectorIteratorTests::SingleIntervalLimit::IndexPrefix>(); - add<FieldRangeVectorIteratorTests::SingleIntervalLimit::IndexPrefixReverseOrder>(); - add<FieldRangeVectorIteratorTests::SingleIntervalLimit::LongerIndexPrefix>(); - } - } myall; - -} // namespace QueryUtilTests - diff --git a/src/mongo/dbtests/repltests.cpp b/src/mongo/dbtests/repltests.cpp index 64ef01e091a..ed67adaf6b0 100644 --- a/src/mongo/dbtests/repltests.cpp +++ b/src/mongo/dbtests/repltests.cpp @@ -36,7 +36,6 @@ #include "mongo/db/db.h" #include "mongo/db/instance.h" #include "mongo/db/json.h" -#include "mongo/db/queryutil.h" #include "mongo/db/repl/master_slave.h" #include "mongo/db/repl/oplog.h" #include "mongo/db/repl/repl_settings.h" diff --git a/src/mongo/s/chunk.cpp b/src/mongo/s/chunk.cpp index f030ef09ed6..33b85814617 100644 --- a/src/mongo/s/chunk.cpp +++ b/src/mongo/s/chunk.cpp @@ -35,7 +35,6 @@ #include "mongo/client/connpool.h" #include "mongo/client/dbclientcursor.h" #include "mongo/db/query/lite_parsed_query.h" -#include "mongo/db/queryutil.h" #include "mongo/db/write_concern.h" #include "mongo/platform/random.h" #include "mongo/s/chunk_diff.h" diff --git a/src/mongo/s/commands_public.cpp b/src/mongo/s/commands_public.cpp index c48000caacd..caed4bb8f82 100644 --- a/src/mongo/s/commands_public.cpp +++ b/src/mongo/s/commands_public.cpp @@ -50,7 +50,6 @@ #include "mongo/db/pipeline/document_source.h" #include "mongo/db/pipeline/expression_context.h" #include "mongo/db/query/lite_parsed_query.h" -#include "mongo/db/queryutil.h" #include "mongo/s/client_info.h" #include "mongo/s/chunk.h" #include "mongo/s/config.h" @@ -844,6 +843,30 @@ namespace mongo { actions.addAction(ActionType::find); out->push_back(Privilege(parseResourcePattern(dbname, cmdObj), actions)); } + long long applySkipLimit( long long num , const BSONObj& cmd ) { + BSONElement s = cmd["skip"]; + BSONElement l = cmd["limit"]; + + if ( s.isNumber() ) { + num = num - s.numberLong(); + if ( num < 0 ) { + num = 0; + } + } + + if ( l.isNumber() ) { + long long limit = l.numberLong(); + if( limit < 0 ){ + limit = -limit; + } + + if ( limit < num && limit != 0 ) { // 0 limit means no limit + num = limit; + } + } + + return num; + } bool run( const string& dbName, BSONObj& cmdObj, int options, diff --git a/src/mongo/s/shardkey.h b/src/mongo/s/shardkey.h index aa63fe9ec3c..8dec3e65b27 100644 --- a/src/mongo/s/shardkey.h +++ b/src/mongo/s/shardkey.h @@ -139,18 +139,6 @@ namespace mongo { */ BSONObj moveToFront(const BSONObj& obj) const; - /**@param queryConstraints a FieldRangeSet formed from parsing a query - * @return an ordered list of bounds generated using this KeyPattern - * and the constraints from the FieldRangeSet - * - * The value of frsp->matchPossibleForSingleKeyFRS(fromQuery) should be true, - * otherwise this function could throw. - * - */ - BoundList keyBounds( const FieldRangeSet& queryConstraints ) const{ - return pattern.keyBounds( queryConstraints ); - } - private: KeyPattern pattern; BSONObj gMin; |