2011-04-07 16:07:29 -07:00
// @file queryutil.cpp
2009-02-24 14:14:45 -05:00
2009-10-27 15:58:27 -04:00
/* 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 .
*/
2009-02-24 14:14:45 -05:00
2010-04-27 15:27:52 -04:00
# include "pch.h"
2009-02-24 14:14:45 -05:00
# include "btree.h"
2009-08-18 11:59:40 -04:00
# include "matcher.h"
2009-02-24 14:14:45 -05:00
# include "pdfile.h"
# include "queryoptimizer.h"
2010-01-14 20:57:50 -05:00
# include "../util/unittest.h"
2010-07-23 16:06:06 -04:00
# include "dbmessage.h"
2010-12-22 18:58:54 -08:00
# include "indexkey.h"
2009-02-24 14:14:45 -05:00
namespace mongo {
2010-07-16 03:47:26 -07:00
extern BSONObj staticNull ;
2011-01-04 00:40:41 -05:00
2010-02-25 21:42:46 -05:00
/** 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 ' ^ '
2010-01-14 20:57:50 -05:00
2010-02-25 21:42:46 -05:00
if purePrefix ! = NULL , sets it to whether the regex can be converted to a range query
*/
2011-01-04 00:40:41 -05:00
string simpleRegex ( const char * regex , const char * flags , bool * purePrefix ) {
2010-02-25 21:42:46 -05:00
string r = " " ;
if ( purePrefix ) * purePrefix = false ;
2010-03-29 20:17:10 -04:00
bool multilineOK ;
2011-01-04 00:40:41 -05:00
if ( regex [ 0 ] = = ' \\ ' & & regex [ 1 ] = = ' A ' ) {
2010-03-29 20:17:10 -04:00
multilineOK = true ;
regex + = 2 ;
2011-01-04 00:40:41 -05:00
}
else if ( regex [ 0 ] = = ' ^ ' ) {
2010-03-29 20:17:10 -04:00
multilineOK = false ;
regex + = 1 ;
2011-01-04 00:40:41 -05:00
}
else {
2010-03-29 20:17:10 -04:00
return r ;
}
2010-02-25 21:42:46 -05:00
bool extended = false ;
2011-01-04 00:40:41 -05:00
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
2010-01-14 22:47:34 -05:00
}
2010-02-25 21:42:46 -05:00
}
2010-01-14 20:57:50 -05:00
2010-02-25 21:42:46 -05:00
stringstream ss ;
2010-01-14 22:47:34 -05:00
2011-01-04 00:40:41 -05:00
while ( * regex ) {
2010-02-25 21:42:46 -05:00
char c = * ( regex + + ) ;
2011-01-04 00:40:41 -05:00
if ( c = = ' * ' | | c = = ' ? ' ) {
2010-02-25 21:42:46 -05:00
// 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?/
2011-01-04 00:40:41 -05:00
}
else if ( c = = ' \\ ' ) {
2010-02-25 21:42:46 -05:00
c = * ( regex + + ) ;
2011-03-29 15:38:03 -04:00
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 ' ) | |
2011-01-04 00:40:41 -05:00
( c > = ' a ' & & c < = ' z ' ) | |
( c > = ' 0 ' & & c < = ' 0 ' ) | |
( c = = ' \0 ' ) ) {
2011-03-29 15:38:03 -04:00
// don't know what to do with these
2010-01-14 22:47:34 -05:00
r = ss . str ( ) ;
break ;
2011-01-04 00:40:41 -05:00
}
else {
2011-03-29 15:38:03 -04:00
// slash followed by non-alphanumeric represents the following char
2010-01-14 22:47:34 -05:00
ss < < c ;
2010-01-14 20:57:50 -05:00
}
2011-01-04 00:40:41 -05:00
}
else if ( strchr ( " ^$.[|() + { " , c)) {
2010-02-25 21:42:46 -05:00
// list of "metacharacters" from man pcrepattern
r = ss . str ( ) ;
break ;
2011-01-04 00:40:41 -05:00
}
else if ( extended & & c = = ' # ' ) {
2010-02-25 21:42:46 -05:00
// comment
2010-01-14 20:57:50 -05:00
r = ss . str ( ) ;
2010-02-25 21:42:46 -05:00
break ;
2011-01-04 00:40:41 -05:00
}
else if ( extended & & isspace ( c ) ) {
2010-02-25 21:42:46 -05:00
continue ;
2011-01-04 00:40:41 -05:00
}
else {
2010-02-25 21:42:46 -05:00
// self-matching char
ss < < c ;
}
}
2010-01-14 20:57:50 -05:00
2011-01-04 00:40:41 -05:00
if ( r . empty ( ) & & * regex = = 0 ) {
2010-02-25 21:42:46 -05:00
r = ss . str ( ) ;
if ( purePrefix ) * purePrefix = ! r . empty ( ) ;
2010-01-14 20:57:50 -05:00
}
2010-02-25 21:42:46 -05:00
return r ;
}
2011-01-04 00:40:41 -05:00
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 : assert ( false ) ; return " " ; //return squashes compiler warning
2010-01-14 20:57:50 -05:00
}
}
2010-02-25 21:42:46 -05:00
string simpleRegexEnd ( string regex ) {
+ + regex [ regex . length ( ) - 1 ] ;
return regex ;
2011-01-04 00:40:41 -05:00
}
2011-04-25 11:20:58 -07:00
FieldRange : : FieldRange ( const BSONElement & e , bool singleKey , bool isNot , bool optimize )
: _singleKey ( singleKey ) {
2010-02-23 14:41:05 -08:00
// NOTE with $not, we could potentially form a complementary set of intervals.
if ( ! isNot & & ! e . eoo ( ) & & e . type ( ) ! = RegEx & & e . getGtLtOp ( ) = = BSONObj : : opIN ) {
2011-04-07 16:07:29 -07:00
set < BSONElement , element_lt > vals ;
vector < FieldRange > regexes ;
2010-01-28 17:30:58 -05:00
uassert ( 12580 , " invalid query " , e . isABSONObj ( ) ) ;
2009-08-18 11:59:40 -04:00
BSONObjIterator i ( e . embeddedObject ( ) ) ;
2010-03-08 16:53:45 -08:00
while ( i . more ( ) ) {
BSONElement ie = i . next ( ) ;
if ( ie . type ( ) = = RegEx ) {
2011-04-25 11:20:58 -07:00
regexes . push_back ( FieldRange ( ie , singleKey , false , optimize ) ) ;
2011-01-04 00:40:41 -05:00
}
else {
2011-03-24 22:50:43 -07:00
// A document array may be indexed by its first element, or
// as a full array if it is embedded within another array.
vals . insert ( ie ) ;
if ( ie . type ( ) = = Array ) {
if ( ! ie . embeddedObject ( ) . firstElement ( ) . eoo ( ) ) {
vals . insert ( ie . embeddedObject ( ) . firstElement ( ) ) ;
}
}
2010-03-08 16:53:45 -08:00
}
}
2009-10-09 10:12:19 -04:00
2011-04-07 16:07:29 -07:00
for ( set < BSONElement , element_lt > : : const_iterator i = vals . begin ( ) ; i ! = vals . end ( ) ; + + i )
2010-05-10 10:06:20 -07:00
_intervals . push_back ( FieldInterval ( * i ) ) ;
2009-10-09 10:12:19 -04:00
2011-04-07 16:07:29 -07:00
for ( vector < FieldRange > : : const_iterator i = regexes . begin ( ) ; i ! = regexes . end ( ) ; + + i )
2010-03-08 16:53:45 -08:00
* this | = * i ;
2011-01-04 00:40:41 -05:00
2009-10-09 10:12:19 -04:00
return ;
}
2011-01-04 00:40:41 -05:00
2011-03-24 22:50:43 -07:00
// A document array may be indexed by its first element, or
// as a full array if it is embedded within another array.
2011-01-04 00:40:41 -05:00
if ( e . type ( ) = = Array & & e . getGtLtOp ( ) = = BSONObj : : Equality ) {
2010-05-10 10:06:20 -07:00
_intervals . push_back ( FieldInterval ( e ) ) ;
2009-10-09 10:12:19 -04:00
const BSONElement & temp = e . embeddedObject ( ) . firstElement ( ) ;
2011-01-04 00:40:41 -05:00
if ( ! temp . eoo ( ) ) {
2009-10-09 10:12:19 -04:00
if ( temp < e )
2010-05-10 10:06:20 -07:00
_intervals . insert ( _intervals . begin ( ) , temp ) ;
2009-10-09 10:12:19 -04:00
else
2010-05-10 10:06:20 -07:00
_intervals . push_back ( FieldInterval ( temp ) ) ;
2009-08-18 11:59:40 -04:00
}
2011-01-04 00:40:41 -05:00
2009-08-18 11:59:40 -04:00
return ;
}
2010-05-10 10:06:20 -07:00
_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 ;
2009-08-18 11:59:40 -04:00
lower = minKey . firstElement ( ) ;
lowerInclusive = true ;
upper = maxKey . firstElement ( ) ;
upperInclusive = true ;
2009-02-24 14:14:45 -05:00
if ( e . eoo ( ) )
return ;
2011-04-04 17:51:38 -07:00
2010-08-30 20:11:00 -07:00
int op = e . getGtLtOp ( ) ;
2011-04-04 17:51:38 -07:00
bool existsSpec = false ;
if ( op = = BSONObj : : opEXISTS ) {
2011-04-04 18:14:58 -07:00
existsSpec = e . trueValue ( ) ;
2011-04-04 17:51:38 -07:00
}
2010-01-14 20:24:01 -05:00
if ( e . type ( ) = = RegEx
2011-01-04 00:40:41 -05:00
| | ( e . type ( ) = = Object & & ! e . embeddedObject ( ) [ " $regex " ] . eoo ( ) )
) {
2010-08-30 20:11:00 -07:00
uassert ( 13454 , " invalid regular expression operator " , op = = BSONObj : : Equality | | op = = BSONObj : : opREGEX ) ;
2010-02-23 14:20:28 -08:00
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 ;
2011-01-04 00:40:41 -05:00
}
else {
2010-02-25 16:16:31 -05:00
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
2010-02-23 14:20:28 -08:00
}
2010-03-01 17:59:20 -05:00
2010-03-08 16:53:45 -08:00
// regex matches self - regex type > string type
2011-01-04 00:40:41 -05:00
if ( e . type ( ) = = RegEx ) {
2010-03-01 17:59:20 -05:00
BSONElement re = addObj ( BSON ( " " < < e ) ) . firstElement ( ) ;
2010-05-10 10:06:20 -07:00
_intervals . push_back ( FieldInterval ( re ) ) ;
2011-01-04 00:40:41 -05:00
}
else {
2010-03-01 20:24:08 -05:00
BSONObj orig = e . embeddedObject ( ) ;
BSONObjBuilder b ;
b . appendRegex ( " " , orig [ " $regex " ] . valuestrsafe ( ) , orig [ " $options " ] . valuestrsafe ( ) ) ;
BSONElement re = addObj ( b . obj ( ) ) . firstElement ( ) ;
2010-05-10 10:06:20 -07:00
_intervals . push_back ( FieldInterval ( re ) ) ;
2010-03-01 17:59:20 -05:00
}
2010-02-23 14:20:28 -08:00
}
2009-02-24 14:14:45 -05:00
return ;
}
2010-02-23 14:20:28 -08:00
if ( isNot ) {
switch ( op ) {
2011-01-04 00:40:41 -05:00
case BSONObj : : Equality :
return ;
2010-08-30 14:49:52 -07:00
// op = BSONObj::NE;
// break;
2011-01-04 00:40:41 -05:00
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 ;
2011-04-04 17:51:38 -07:00
case BSONObj : : opEXISTS :
existsSpec = ! existsSpec ;
break ;
2011-01-04 00:40:41 -05:00
default : // otherwise doesn't matter
break ;
2010-02-23 14:20:28 -08:00
}
}
switch ( op ) {
2009-08-20 16:18:50 -04:00
case BSONObj : : Equality :
2009-08-18 11:59:40 -04:00
lower = upper = e ;
2009-08-20 16:18:50 -04:00
break ;
2010-08-30 14:09:01 -07:00
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 ;
}
2009-08-20 16:18:50 -04:00
case BSONObj : : LT :
2009-08-18 11:59:40 -04:00
upperInclusive = false ;
2009-08-20 16:18:50 -04:00
case BSONObj : : LTE :
2009-08-18 11:59:40 -04:00
upper = e ;
2009-08-20 16:18:50 -04:00
break ;
case BSONObj : : GT :
2009-08-18 11:59:40 -04:00
lowerInclusive = false ;
2009-08-20 16:18:50 -04:00
case BSONObj : : GTE :
2009-08-18 11:59:40 -04:00
lower = e ;
2009-08-20 16:18:50 -04:00
break ;
2009-08-18 11:59:40 -04:00
case BSONObj : : opALL : {
2009-12-28 16:43:43 -05:00
massert ( 10370 , " $all requires array " , e . type ( ) = = Array ) ;
2009-08-20 16:18:50 -04:00
BSONObjIterator i ( e . embeddedObject ( ) ) ;
2010-03-08 17:33:47 -08:00
bool bound = false ;
2011-01-04 00:40:41 -05:00
while ( i . more ( ) ) {
2010-02-19 16:11:55 -05:00
BSONElement x = i . next ( ) ;
2011-01-04 00:40:41 -05:00
if ( x . type ( ) = = Object & & x . embeddedObject ( ) . firstElement ( ) . getGtLtOp ( ) = = BSONObj : : opELEM_MATCH ) {
2010-03-09 11:53:40 -08:00
// taken care of elsewhere
2010-02-19 16:11:55 -05:00
}
2010-03-08 17:33:47 -08:00
else if ( x . type ( ) ! = RegEx ) {
2010-02-19 16:11:55 -05:00
lower = upper = x ;
2010-03-08 17:33:47 -08:00
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 ;
}
2010-02-19 16:11:55 -05:00
}
}
2009-08-20 16:18:50 -04:00
break ;
2009-08-18 11:59:40 -04:00
}
2009-08-20 16:50:58 -04:00
case BSONObj : : opMOD : {
2009-10-12 12:00:25 -04:00
{
BSONObjBuilder b ;
b . appendMinForType ( " " , NumberDouble ) ;
lower = addObj ( b . obj ( ) ) . firstElement ( ) ;
}
{
BSONObjBuilder b ;
b . appendMaxForType ( " " , NumberDouble ) ;
upper = addObj ( b . obj ( ) ) . firstElement ( ) ;
2011-01-04 00:40:41 -05:00
}
2009-08-20 16:50:58 -04:00
break ;
2009-08-20 16:18:50 -04:00
}
2009-10-12 11:58:14 -04:00
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 ( ) ;
}
2011-01-04 00:40:41 -05:00
2009-10-12 11:58:14 -04:00
break ;
}
2010-02-23 12:45:12 -08:00
case BSONObj : : opREGEX :
case BSONObj : : opOPTIONS :
// do nothing
break ;
2009-12-31 14:30:04 -05:00
case BSONObj : : opELEM_MATCH : {
log ( ) < < " warning: shouldn't get here? " < < endl ;
break ;
}
2010-02-25 16:24:34 -05:00
case BSONObj : : opNEAR :
2010-03-15 23:50:14 -04:00
case BSONObj : : opWITHIN :
2010-02-25 16:24:34 -05:00
_special = " 2d " ;
break ;
2011-04-04 17:51:38 -07:00
case BSONObj : : opEXISTS : {
if ( ! existsSpec ) {
lower = upper = staticNull . firstElement ( ) ;
}
optimize = false ;
break ;
}
2009-08-20 16:18:50 -04:00
default :
break ;
2009-02-24 14:14:45 -05:00
}
2011-01-04 00:40:41 -05:00
if ( optimize ) {
if ( lower . type ( ) ! = MinKey & & upper . type ( ) = = MaxKey & & lower . isSimpleType ( ) ) { // TODO: get rid of isSimpleType
2009-08-11 04:58:54 -04:00
BSONObjBuilder b ;
2009-08-18 11:59:40 -04:00
b . appendMaxForType ( lower . fieldName ( ) , lower . type ( ) ) ;
upper = addObj ( b . obj ( ) ) . firstElement ( ) ;
2009-08-11 04:58:54 -04:00
}
2011-01-04 00:40:41 -05:00
else if ( lower . type ( ) = = MinKey & & upper . type ( ) ! = MaxKey & & upper . isSimpleType ( ) ) { // TODO: get rid of isSimpleType
2009-08-11 04:58:54 -04:00
BSONObjBuilder b ;
2009-08-18 11:59:40 -04:00
b . appendMinForType ( upper . fieldName ( ) , upper . type ( ) ) ;
lower = addObj ( b . obj ( ) ) . firstElement ( ) ;
2009-08-11 04:58:54 -04:00
}
2009-08-10 17:08:43 -04:00
}
2009-07-06 10:42:22 -04:00
2009-02-24 14:14:45 -05:00
}
2009-08-18 10:19:27 -04:00
2011-04-07 16:07:29 -07:00
void FieldRange : : finishOperation ( const vector < FieldInterval > & newIntervals , const FieldRange & other ) {
2010-05-12 10:38:58 -07:00
_intervals = newIntervals ;
2011-04-07 16:07:29 -07:00
for ( vector < BSONObj > : : const_iterator i = other . _objData . begin ( ) ; i ! = other . _objData . end ( ) ; + + i )
2010-05-12 10:38:58 -07:00
_objData . push_back ( * i ) ;
if ( _special . size ( ) = = 0 & & other . _special . size ( ) )
_special = other . _special ;
}
2011-01-04 00:40:41 -05:00
2009-08-18 10:19:27 -04:00
// as called, these functions find the max/min of a bound in the
2009-09-07 10:39:32 -07:00
// opposite direction, so inclusive bounds are considered less
2009-08-18 10:19:27 -04:00
// superlative
FieldBound maxFieldBound ( const FieldBound & a , const FieldBound & b ) {
2010-05-10 10:06:20 -07:00
int cmp = a . _bound . woCompare ( b . _bound , false ) ;
if ( ( cmp = = 0 & & ! b . _inclusive ) | | cmp < 0 )
2009-08-18 10:19:27 -04:00
return b ;
return a ;
}
FieldBound minFieldBound ( const FieldBound & a , const FieldBound & b ) {
2010-05-10 10:06:20 -07:00
int cmp = a . _bound . woCompare ( b . _bound , false ) ;
if ( ( cmp = = 0 & & ! b . _inclusive ) | | cmp > 0 )
2009-08-18 10:19:27 -04:00
return b ;
return a ;
}
bool fieldIntervalOverlap ( const FieldInterval & one , const FieldInterval & two , FieldInterval & result ) {
2010-05-10 10:06:20 -07:00
result . _lower = maxFieldBound ( one . _lower , two . _lower ) ;
result . _upper = minFieldBound ( one . _upper , two . _upper ) ;
2010-07-16 01:49:15 -07:00
return result . strictValid ( ) ;
2009-08-18 10:19:27 -04:00
}
2011-01-04 00:40:41 -05:00
2009-08-11 16:43:30 -04:00
const FieldRange & FieldRange : : operator & = ( const FieldRange & other ) {
2011-04-25 11:20:58 -07:00
if ( ! _singleKey & & nontrivial ( ) ) {
return * this ;
}
2011-04-07 16:07:29 -07:00
vector < FieldInterval > newIntervals ;
vector < FieldInterval > : : const_iterator i = _intervals . begin ( ) ;
vector < FieldInterval > : : const_iterator j = other . _intervals . begin ( ) ;
2010-05-10 10:06:20 -07:00
while ( i ! = _intervals . end ( ) & & j ! = other . _intervals . end ( ) ) {
2009-08-18 10:19:27 -04:00
FieldInterval overlap ;
2010-08-30 14:09:01 -07:00
if ( fieldIntervalOverlap ( * i , * j , overlap ) ) {
2009-08-18 10:19:27 -04:00
newIntervals . push_back ( overlap ) ;
2010-08-30 14:09:01 -07:00
}
if ( i - > _upper = = minFieldBound ( i - > _upper , j - > _upper ) ) {
2009-08-18 10:19:27 -04:00
+ + i ;
2011-01-04 00:40:41 -05:00
}
else {
2010-08-30 14:09:01 -07:00
+ + j ;
}
2009-02-27 11:22:12 -05:00
}
2010-05-12 10:38:58 -07:00
finishOperation ( newIntervals , other ) ;
2009-02-24 14:14:45 -05:00
return * this ;
}
2011-01-04 00:40:41 -05:00
2011-04-07 16:07:29 -07:00
void handleInterval ( const FieldInterval & lower , FieldBound & low , FieldBound & high , vector < FieldInterval > & newIntervals ) {
2010-05-10 10:06:20 -07:00
if ( low . _bound . eoo ( ) ) {
low = lower . _lower ; high = lower . _upper ;
2011-01-04 00:40:41 -05:00
}
else {
2010-09-13 20:10:27 -07:00
int cmp = high . _bound . woCompare ( lower . _lower . _bound , false ) ;
if ( ( cmp < 0 ) | | ( cmp = = 0 & & ! high . _inclusive & & ! lower . _lower . _inclusive ) ) {
2010-03-08 16:53:45 -08:00
FieldInterval tmp ;
2010-05-10 10:06:20 -07:00
tmp . _lower = low ;
tmp . _upper = high ;
2010-03-08 16:53:45 -08:00
newIntervals . push_back ( tmp ) ;
2011-01-04 00:40:41 -05:00
low = lower . _lower ; high = lower . _upper ;
}
else {
2010-05-10 10:06:20 -07:00
high = lower . _upper ;
2010-03-08 16:53:45 -08:00
}
2011-01-04 00:40:41 -05:00
}
2010-03-08 16:53:45 -08:00
}
2011-01-04 00:40:41 -05:00
2010-03-08 16:53:45 -08:00
const FieldRange & FieldRange : : operator | = ( const FieldRange & other ) {
2011-04-07 16:07:29 -07:00
vector < FieldInterval > newIntervals ;
2010-03-08 16:53:45 -08:00
FieldBound low ;
FieldBound high ;
2011-04-07 16:07:29 -07:00
vector < FieldInterval > : : const_iterator i = _intervals . begin ( ) ;
vector < FieldInterval > : : const_iterator j = other . _intervals . begin ( ) ;
2010-05-10 10:06:20 -07:00
while ( i ! = _intervals . end ( ) & & j ! = other . _intervals . end ( ) ) {
int cmp = i - > _lower . _bound . woCompare ( j - > _lower . _bound , false ) ;
if ( ( cmp = = 0 & & i - > _lower . _inclusive ) | | cmp < 0 ) {
2010-03-08 16:53:45 -08:00
handleInterval ( * i , low , high , newIntervals ) ;
+ + i ;
2011-01-04 00:40:41 -05:00
}
else {
2010-03-08 16:53:45 -08:00
handleInterval ( * j , low , high , newIntervals ) ;
+ + j ;
2011-01-04 00:40:41 -05:00
}
2010-03-08 16:53:45 -08:00
}
2010-05-10 10:06:20 -07:00
while ( i ! = _intervals . end ( ) ) {
2010-03-08 16:53:45 -08:00
handleInterval ( * i , low , high , newIntervals ) ;
2011-01-04 00:40:41 -05:00
+ + i ;
2010-03-08 16:53:45 -08:00
}
2010-05-10 10:06:20 -07:00
while ( j ! = other . _intervals . end ( ) ) {
2010-03-08 16:53:45 -08:00
handleInterval ( * j , low , high , newIntervals ) ;
2011-01-04 00:40:41 -05:00
+ + j ;
2010-03-08 16:53:45 -08:00
}
FieldInterval tmp ;
2010-05-10 10:06:20 -07:00
tmp . _lower = low ;
tmp . _upper = high ;
2011-01-04 00:40:41 -05:00
newIntervals . push_back ( tmp ) ;
2010-05-12 10:38:58 -07:00
finishOperation ( newIntervals , other ) ;
2011-01-04 00:40:41 -05:00
return * this ;
2010-05-12 10:38:58 -07:00
}
2011-01-04 00:40:41 -05:00
2010-05-12 10:38:58 -07:00
const FieldRange & FieldRange : : operator - = ( const FieldRange & other ) {
2011-04-07 16:07:29 -07:00
vector < FieldInterval > newIntervals ;
vector < FieldInterval > : : iterator i = _intervals . begin ( ) ;
vector < FieldInterval > : : const_iterator j = other . _intervals . begin ( ) ;
2010-05-12 10:38:58 -07:00
while ( i ! = _intervals . end ( ) & & j ! = other . _intervals . end ( ) ) {
int cmp = i - > _lower . _bound . woCompare ( j - > _lower . _bound , false ) ;
2010-06-08 19:59:58 -07:00
if ( cmp < 0 | |
2011-01-04 00:40:41 -05:00
( cmp = = 0 & & i - > _lower . _inclusive & & ! j - > _lower . _inclusive ) ) {
2010-05-12 10:38:58 -07:00
int cmp2 = i - > _upper . _bound . woCompare ( j - > _lower . _bound , false ) ;
2010-06-08 19:59:58 -07:00
if ( cmp2 < 0 ) {
2010-09-14 14:29:58 -07:00
newIntervals . push_back ( * i ) ;
2010-06-08 19:59:58 -07:00
+ + i ;
2011-01-04 00:40:41 -05:00
}
else if ( cmp2 = = 0 ) {
2010-09-14 14:29:58 -07:00
newIntervals . push_back ( * i ) ;
if ( newIntervals . back ( ) . _upper . _inclusive & & j - > _lower . _inclusive ) {
newIntervals . back ( ) . _upper . _inclusive = false ;
2010-06-08 19:59:58 -07:00
}
2010-06-08 00:33:56 -07:00
+ + i ;
2011-01-04 00:40:41 -05:00
}
else {
2010-09-14 14:29:58 -07:00
newIntervals . push_back ( * i ) ;
newIntervals . back ( ) . _upper = j - > _lower ;
newIntervals . back ( ) . _upper . flipInclusive ( ) ;
2010-06-08 00:33:56 -07:00
int cmp3 = i - > _upper . _bound . woCompare ( j - > _upper . _bound , false ) ;
2010-06-08 19:59:58 -07:00
if ( cmp3 < 0 | |
2011-01-04 00:40:41 -05:00
( cmp3 = = 0 & & ( ! i - > _upper . _inclusive | | j - > _upper . _inclusive ) ) ) {
2010-06-08 00:33:56 -07:00
+ + i ;
2011-01-04 00:40:41 -05:00
}
else {
2010-09-14 14:29:58 -07:00
i - > _lower = j - > _upper ;
i - > _lower . flipInclusive ( ) ;
2010-06-08 00:33:56 -07:00
+ + j ;
}
2010-05-12 10:38:58 -07:00
}
2011-01-04 00:40:41 -05:00
}
else {
2010-06-08 00:33:56 -07:00
int cmp2 = i - > _lower . _bound . woCompare ( j - > _upper . _bound , false ) ;
2010-06-08 19:59:58 -07:00
if ( cmp2 > 0 | |
2011-01-04 00:40:41 -05:00
( cmp2 = = 0 & & ( ! i - > _lower . _inclusive | | ! j - > _upper . _inclusive ) ) ) {
2010-06-08 00:33:56 -07:00
+ + j ;
2011-01-04 00:40:41 -05:00
}
else {
2010-06-08 00:33:56 -07:00
int cmp3 = i - > _upper . _bound . woCompare ( j - > _upper . _bound , false ) ;
2010-06-08 19:59:58 -07:00
if ( cmp3 < 0 | |
2011-01-04 00:40:41 -05:00
( cmp3 = = 0 & & ( ! i - > _upper . _inclusive | | j - > _upper . _inclusive ) ) ) {
2010-09-14 14:29:58 -07:00
+ + i ;
2011-01-04 00:40:41 -05:00
}
else {
2010-06-08 00:33:56 -07:00
i - > _lower = j - > _upper ;
2011-01-04 00:40:41 -05:00
i - > _lower . flipInclusive ( ) ;
2010-06-08 00:33:56 -07:00
+ + j ;
}
2011-01-04 00:40:41 -05:00
}
2010-05-12 10:38:58 -07:00
}
}
2010-09-14 14:29:58 -07:00
while ( i ! = _intervals . end ( ) ) {
newIntervals . push_back ( * i ) ;
+ + i ;
}
finishOperation ( newIntervals , other ) ;
2011-01-04 00:40:41 -05:00
return * this ;
2010-03-08 16:53:45 -08:00
}
2011-01-04 00:40:41 -05:00
2010-07-19 00:39:16 -07:00
// TODO write a proper implementation that doesn't do a full copy
bool FieldRange : : operator < = ( const FieldRange & other ) {
FieldRange temp = * this ;
temp - = other ;
return temp . empty ( ) ;
}
2011-01-04 00:40:41 -05:00
2011-04-05 16:50:37 -07:00
void FieldRange : : setExclusiveBounds ( ) {
2011-04-07 16:07:29 -07:00
for ( vector < FieldInterval > : : iterator i = _intervals . begin ( ) ; i ! = _intervals . end ( ) ; + + i ) {
2011-04-05 16:50:37 -07:00
i - > _lower . _inclusive = false ;
i - > _upper . _inclusive = false ;
}
}
void FieldRange : : reverse ( FieldRange & ret ) const {
assert ( _special . empty ( ) ) ;
ret . _intervals . clear ( ) ;
ret . _objData = _objData ;
2011-04-07 16:07:29 -07:00
for ( vector < FieldInterval > : : const_reverse_iterator i = _intervals . rbegin ( ) ; i ! = _intervals . rend ( ) ; + + i ) {
2011-04-05 16:50:37 -07:00
FieldInterval fi ;
fi . _lower = i - > _upper ;
fi . _upper = i - > _lower ;
ret . _intervals . push_back ( fi ) ;
}
}
2009-08-11 16:43:30 -04:00
BSONObj FieldRange : : addObj ( const BSONObj & o ) {
2010-05-10 10:06:20 -07:00
_objData . push_back ( o ) ;
2009-02-24 14:14:45 -05:00
return o ;
}
2011-01-04 00:40:41 -05:00
2010-02-25 16:24:34 -05:00
string FieldRangeSet : : getSpecial ( ) const {
string s = " " ;
2011-04-25 11:20:58 -07:00
for ( map < string , FieldRange > : : const_iterator i = _ranges . begin ( ) ; i ! = _ranges . end ( ) ; i + + ) {
2010-02-25 16:24:34 -05:00
if ( i - > second . getSpecial ( ) . size ( ) = = 0 )
continue ;
uassert ( 13033 , " can't have 2 special fields " , s . size ( ) = = 0 ) ;
s = i - > second . getSpecial ( ) ;
}
return s ;
}
2011-04-05 16:50:37 -07:00
/**
* 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 ;
2011-04-07 16:07:29 -07:00
map < string , FieldRange > : : iterator i = _ranges . begin ( ) ;
map < string , FieldRange > : : const_iterator j = other . _ranges . begin ( ) ;
2011-04-05 16:50:37 -07:00
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
2011-04-25 11:20:58 -07:00
range ( unincludedKey . c_str ( ) ) - = other . range ( unincludedKey . c_str ( ) ) ;
2011-04-05 16:50:37 -07:00
appendQueries ( other ) ;
return * this ;
}
const FieldRangeSet & FieldRangeSet : : operator & = ( const FieldRangeSet & other ) {
2011-04-07 16:07:29 -07:00
map < string , FieldRange > : : iterator i = _ranges . begin ( ) ;
map < string , FieldRange > : : const_iterator j = other . _ranges . begin ( ) ;
2011-04-05 16:50:37 -07:00
while ( i ! = _ranges . end ( ) & & j ! = other . _ranges . end ( ) ) {
int cmp = i - > first . compare ( j - > first ) ;
if ( cmp = = 0 ) {
i - > second & = j - > second ;
+ + i ;
+ + j ;
}
else if ( cmp < 0 ) {
+ + i ;
}
else {
2011-04-25 11:20:58 -07:00
range ( j - > first . c_str ( ) ) = j - > second ;
2011-04-05 16:50:37 -07:00
+ + j ;
}
}
while ( j ! = other . _ranges . end ( ) ) {
2011-04-25 11:20:58 -07:00
range ( j - > first . c_str ( ) ) = j - > second ;
2011-04-05 16:50:37 -07:00
+ + j ;
}
appendQueries ( other ) ;
return * this ;
}
void FieldRangeSet : : appendQueries ( const FieldRangeSet & other ) {
2011-04-07 16:07:29 -07:00
for ( vector < BSONObj > : : const_iterator i = other . _queries . begin ( ) ; i ! = other . _queries . end ( ) ; + + i ) {
2011-04-05 16:50:37 -07:00
_queries . push_back ( * i ) ;
}
}
void FieldRangeSet : : makeEmpty ( ) {
2011-04-07 16:07:29 -07:00
for ( map < string , FieldRange > : : iterator i = _ranges . begin ( ) ; i ! = _ranges . end ( ) ; + + i ) {
2011-04-05 16:50:37 -07:00
i - > second . makeEmpty ( ) ;
}
}
2010-02-23 14:20:28 -08:00
void FieldRangeSet : : processOpElement ( const char * fieldName , const BSONElement & f , bool isNot , bool optimize ) {
2010-03-09 11:53:40 -08:00
BSONElement g = f ;
int op2 = g . getGtLtOp ( ) ;
if ( op2 = = BSONObj : : opALL ) {
BSONElement h = g ;
massert ( 13050 , " $all requires array " , h . type ( ) = = Array ) ;
BSONObjIterator i ( h . embeddedObject ( ) ) ;
if ( i . more ( ) ) {
BSONElement x = i . next ( ) ;
if ( x . type ( ) = = Object & & x . embeddedObject ( ) . firstElement ( ) . getGtLtOp ( ) = = BSONObj : : opELEM_MATCH ) {
g = x . embeddedObject ( ) . firstElement ( ) ;
op2 = g . getGtLtOp ( ) ;
}
}
}
2010-02-23 14:20:28 -08:00
if ( op2 = = BSONObj : : opELEM_MATCH ) {
2010-03-09 11:53:40 -08:00
BSONObjIterator k ( g . embeddedObjectUserCheck ( ) ) ;
2011-01-04 00:40:41 -05:00
while ( k . more ( ) ) {
2010-03-09 11:53:40 -08:00
BSONElement h = k . next ( ) ;
2010-02-23 14:20:28 -08:00
StringBuilder buf ( 32 ) ;
2010-03-09 11:53:40 -08:00
buf < < fieldName < < " . " < < h . fieldName ( ) ;
2010-02-23 14:20:28 -08:00
string fullname = buf . str ( ) ;
2011-01-04 00:40:41 -05:00
2010-03-09 11:53:40 -08:00
int op3 = getGtLtOp ( h ) ;
2011-01-04 00:40:41 -05:00
if ( op3 = = BSONObj : : Equality ) {
2011-04-25 11:20:58 -07:00
range ( fullname . c_str ( ) ) & = FieldRange ( h , _singleKey , isNot , optimize ) ;
2010-02-23 14:20:28 -08:00
}
else {
2010-03-09 11:53:40 -08:00
BSONObjIterator l ( h . embeddedObject ( ) ) ;
2011-01-04 00:40:41 -05:00
while ( l . more ( ) ) {
2011-04-25 11:20:58 -07:00
range ( fullname . c_str ( ) ) & = FieldRange ( l . next ( ) , _singleKey , isNot , optimize ) ;
2010-02-23 14:20:28 -08:00
}
}
2011-01-04 00:40:41 -05:00
}
}
else {
2011-04-25 11:20:58 -07:00
range ( fieldName ) & = FieldRange ( f , _singleKey , isNot , optimize ) ;
2011-01-04 00:40:41 -05:00
}
2010-02-23 14:20:28 -08:00
}
2011-01-04 00:40:41 -05:00
2010-05-12 10:38:58 -07:00
void FieldRangeSet : : processQueryField ( const BSONElement & e , bool optimize ) {
bool equality = ( getGtLtOp ( e ) = = BSONObj : : Equality ) ;
if ( equality & & e . type ( ) = = Object ) {
equality = ( strcmp ( e . embeddedObject ( ) . firstElement ( ) . fieldName ( ) , " $not " ) ! = 0 ) ;
}
2011-01-04 00:40:41 -05:00
2010-05-12 10:38:58 -07:00
if ( equality | | ( e . type ( ) = = Object & & ! e . embeddedObject ( ) [ " $regex " ] . eoo ( ) ) ) {
2011-04-25 11:20:58 -07:00
range ( e . fieldName ( ) ) & = FieldRange ( e , _singleKey , false , optimize ) ;
2010-05-12 10:38:58 -07:00
}
if ( ! equality ) {
BSONObjIterator j ( e . embeddedObject ( ) ) ;
while ( j . more ( ) ) {
BSONElement f = j . next ( ) ;
if ( strcmp ( f . fieldName ( ) , " $not " ) = = 0 ) {
switch ( f . type ( ) ) {
2011-01-04 00:40:41 -05:00
case Object : {
BSONObjIterator k ( f . embeddedObject ( ) ) ;
while ( k . more ( ) ) {
BSONElement g = k . next ( ) ;
uassert ( 13034 , " invalid use of $not " , g . getGtLtOp ( ) ! = BSONObj : : Equality ) ;
processOpElement ( e . fieldName ( ) , g , true , optimize ) ;
2010-05-12 10:38:58 -07:00
}
2011-01-04 00:40:41 -05:00
break ;
2010-05-12 10:38:58 -07:00
}
2011-01-04 00:40:41 -05:00
case RegEx :
processOpElement ( e . fieldName ( ) , f , true , optimize ) ;
break ;
default :
uassert ( 13041 , " invalid use of $not " , false ) ;
}
}
else {
2010-05-12 10:38:58 -07:00
processOpElement ( e . fieldName ( ) , f , false , optimize ) ;
2010-05-10 09:51:49 -07:00
}
2011-01-04 00:40:41 -05:00
}
}
2010-05-12 10:38:58 -07:00
}
2011-01-04 00:40:41 -05:00
2011-04-25 11:20:58 -07:00
FieldRangeSet : : FieldRangeSet ( const char * ns , const BSONObj & query , bool singleKey , bool optimize )
: _ns ( ns ) , _queries ( 1 , query . getOwned ( ) ) , _singleKey ( singleKey ) {
2011-01-04 00:40:41 -05:00
BSONObjIterator i ( _queries [ 0 ] ) ;
while ( i . more ( ) ) {
BSONElement e = i . next ( ) ;
// e could be x:1 or x:{$gt:1}
if ( strcmp ( e . fieldName ( ) , " $where " ) = = 0 ) {
continue ;
}
if ( strcmp ( e . fieldName ( ) , " $or " ) = = 0 ) {
continue ;
}
if ( strcmp ( e . fieldName ( ) , " $nor " ) = = 0 ) {
continue ;
}
processQueryField ( e , optimize ) ;
2009-02-24 14:14:45 -05:00
}
2011-01-04 00:40:41 -05:00
}
2011-04-05 16:50:37 -07:00
FieldRangeVector : : FieldRangeVector ( const FieldRangeSet & frs , const IndexSpec & indexSpec , int direction )
: _indexSpec ( indexSpec ) , _direction ( direction > = 0 ? 1 : - 1 ) {
_queries = frs . _queries ;
BSONObjIterator i ( _indexSpec . keyPattern ) ;
2010-06-01 16:21:21 -07:00
while ( i . more ( ) ) {
BSONElement e = i . next ( ) ;
2011-04-05 16:50:37 -07:00
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 ( frs . range ( e . fieldName ( ) ) ) ;
}
else {
2011-04-25 11:20:58 -07:00
_ranges . push_back ( FieldRange ( BSONObj ( ) . firstElement ( ) , frs . singleKey ( ) , false , true ) ) ;
2011-04-05 16:50:37 -07:00
frs . range ( e . fieldName ( ) ) . reverse ( _ranges . back ( ) ) ;
2010-06-01 16:21:21 -07:00
}
2011-04-05 16:50:37 -07:00
assert ( ! _ranges . back ( ) . empty ( ) ) ;
}
uassert ( 13385 , " combinatorial limit of $in partitioning of result set exceeded " , size ( ) < 1000000 ) ;
}
BSONObj FieldRangeVector : : startKey ( ) const {
BSONObjBuilder b ;
2011-04-07 16:07:29 -07:00
for ( vector < FieldRange > : : const_iterator i = _ranges . begin ( ) ; i ! = _ranges . end ( ) ; + + i ) {
2011-04-05 16:50:37 -07:00
const FieldInterval & fi = i - > intervals ( ) . front ( ) ;
b . appendAs ( fi . _lower . _bound , " " ) ;
2010-06-01 16:21:21 -07:00
}
2011-04-05 16:50:37 -07:00
return b . obj ( ) ;
2010-06-01 16:21:21 -07:00
}
2011-04-05 16:50:37 -07:00
BSONObj FieldRangeVector : : endKey ( ) const {
BSONObjBuilder b ;
2011-04-07 16:07:29 -07:00
for ( vector < FieldRange > : : const_iterator i = _ranges . begin ( ) ; i ! = _ranges . end ( ) ; + + i ) {
2011-04-05 16:50:37 -07:00
const FieldInterval & fi = i - > intervals ( ) . back ( ) ;
b . appendAs ( fi . _upper . _bound , " " ) ;
2010-12-07 23:01:19 -08:00
}
2011-04-05 16:50:37 -07:00
return b . obj ( ) ;
}
BSONObj FieldRangeVector : : obj ( ) const {
BSONObjBuilder b ;
BSONObjIterator k ( _indexSpec . keyPattern ) ;
for ( int i = 0 ; i < ( int ) _ranges . size ( ) ; + + i ) {
BSONArrayBuilder a ( b . subarrayStart ( k . next ( ) . fieldName ( ) ) ) ;
2011-04-07 16:07:29 -07:00
for ( vector < FieldInterval > : : const_iterator j = _ranges [ i ] . intervals ( ) . begin ( ) ;
2011-04-05 16:50:37 -07:00
j ! = _ranges [ i ] . intervals ( ) . end ( ) ; + + j ) {
a < < BSONArray ( BSON_ARRAY ( j - > _lower . _bound < < j - > _upper . _bound ) . clientReadable ( ) ) ;
2010-12-07 23:01:19 -08:00
}
2011-04-05 16:50:37 -07:00
a . done ( ) ;
2010-12-07 23:01:19 -08:00
}
2011-04-05 16:50:37 -07:00
return b . obj ( ) ;
2010-12-07 23:01:19 -08:00
}
2011-04-05 16:50:37 -07:00
2011-04-25 11:20:58 -07:00
FieldRange * FieldRangeSet : : __singleKeyTrivialRange = 0 ;
FieldRange * FieldRangeSet : : __multiKeyTrivialRange = 0 ;
2011-04-25 15:18:05 -07:00
const FieldRange & FieldRangeSet : : trivialRange ( ) const {
2011-04-25 11:20:58 -07:00
FieldRange * & ret = _singleKey ? __singleKeyTrivialRange : __multiKeyTrivialRange ;
if ( ret = = 0 ) {
ret = new FieldRange ( BSONObj ( ) . firstElement ( ) , _singleKey , false , true ) ;
}
return * ret ;
2009-02-24 14:14:45 -05:00
}
2011-01-04 00:40:41 -05:00
2010-07-16 03:47:26 -07:00
BSONObj FieldRangeSet : : simplifiedQuery ( const BSONObj & _fields ) const {
2009-05-13 11:10:29 -04:00
BSONObj fields = _fields ;
if ( fields . isEmpty ( ) ) {
BSONObjBuilder b ;
2011-04-07 16:07:29 -07:00
for ( map < string , FieldRange > : : const_iterator i = _ranges . begin ( ) ; i ! = _ranges . end ( ) ; + + i ) {
2010-07-20 12:07:14 -04:00
b . append ( i - > first , 1 ) ;
2009-05-13 11:10:29 -04:00
}
fields = b . obj ( ) ;
}
2009-02-24 14:14:45 -05:00
BSONObjBuilder b ;
2009-05-13 11:10:29 -04:00
BSONObjIterator i ( fields ) ;
2009-10-29 16:06:35 -04:00
while ( i . more ( ) ) {
2009-05-13 11:10:29 -04:00
BSONElement e = i . next ( ) ;
const char * name = e . fieldName ( ) ;
2011-04-25 11:20:58 -07:00
const FieldRange & eRange = range ( name ) ;
assert ( ! eRange . empty ( ) ) ;
if ( eRange . equality ( ) )
b . appendAs ( eRange . min ( ) , name ) ;
else if ( eRange . nontrivial ( ) ) {
2010-06-09 14:43:36 -07:00
BSONObj o ;
2010-07-16 03:47:26 -07:00
BSONObjBuilder c ;
2011-04-25 11:20:58 -07:00
if ( eRange . min ( ) . type ( ) ! = MinKey )
c . appendAs ( eRange . min ( ) , eRange . minInclusive ( ) ? " $gte " : " $gt " ) ;
if ( eRange . max ( ) . type ( ) ! = MaxKey )
c . appendAs ( eRange . max ( ) , eRange . maxInclusive ( ) ? " $lte " : " $lt " ) ;
2010-07-16 03:47:26 -07:00
o = c . obj ( ) ;
2010-06-10 00:44:24 -07:00
b . append ( name , o ) ;
2009-02-24 14:14:45 -05:00
}
}
return b . obj ( ) ;
}
2011-01-04 00:40:41 -05:00
2009-08-11 16:43:30 -04:00
QueryPattern FieldRangeSet : : pattern ( const BSONObj & sort ) const {
2009-02-24 15:20:29 -05:00
QueryPattern qp ;
2011-04-07 16:07:29 -07:00
for ( map < string , FieldRange > : : const_iterator i = _ranges . begin ( ) ; i ! = _ranges . end ( ) ; + + i ) {
2009-08-18 11:59:40 -04:00
assert ( ! i - > second . empty ( ) ) ;
2009-02-24 15:20:29 -05:00
if ( i - > second . equality ( ) ) {
2010-05-10 10:06:20 -07:00
qp . _fieldTypes [ i - > first ] = QueryPattern : : Equality ;
2011-01-04 00:40:41 -05:00
}
else if ( i - > second . nontrivial ( ) ) {
2009-08-11 16:43:30 -04:00
bool upper = i - > second . max ( ) . type ( ) ! = MaxKey ;
bool lower = i - > second . min ( ) . type ( ) ! = MinKey ;
2009-02-24 15:20:29 -05:00
if ( upper & & lower )
2010-05-10 10:06:20 -07:00
qp . _fieldTypes [ i - > first ] = QueryPattern : : UpperAndLowerBound ;
2009-02-24 15:20:29 -05:00
else if ( upper )
2010-05-10 10:06:20 -07:00
qp . _fieldTypes [ i - > first ] = QueryPattern : : UpperBound ;
2009-02-24 15:20:29 -05:00
else if ( lower )
2011-01-04 00:40:41 -05:00
qp . _fieldTypes [ i - > first ] = QueryPattern : : LowerBound ;
2009-02-24 15:20:29 -05:00
}
}
2009-02-26 11:13:36 -05:00
qp . setSort ( sort ) ;
2009-02-24 15:20:29 -05:00
return qp ;
}
2011-01-04 00:40:41 -05:00
2010-07-16 01:49:15 -07:00
// TODO get rid of this
2009-09-07 11:44:41 -07:00
BoundList FieldRangeSet : : indexBounds ( const BSONObj & keyPattern , int direction ) const {
2011-04-07 16:07:29 -07:00
typedef vector < pair < shared_ptr < BSONObjBuilder > , shared_ptr < BSONObjBuilder > > > BoundBuilders ;
2009-09-07 13:49:22 -07:00
BoundBuilders builders ;
2011-04-07 16:07:29 -07:00
builders . push_back ( make_pair ( shared_ptr < BSONObjBuilder > ( new BSONObjBuilder ( ) ) , shared_ptr < BSONObjBuilder > ( new BSONObjBuilder ( ) ) ) ) ;
2009-09-07 11:44:41 -07:00
BSONObjIterator i ( keyPattern ) ;
2010-06-22 15:07:08 -07:00
bool ineq = false ; // until ineq is true, we are just dealing with equality and $in bounds
2009-09-07 11:44:41 -07:00
while ( i . more ( ) ) {
BSONElement e = i . next ( ) ;
const FieldRange & fr = range ( e . fieldName ( ) ) ;
int number = ( int ) e . number ( ) ; // returns 0.0 if not numeric
bool forward = ( ( number > = 0 ? 1 : - 1 ) * ( direction > = 0 ? 1 : - 1 ) > 0 ) ;
2010-06-22 15:07:08 -07:00
if ( ! ineq ) {
2009-09-07 13:49:22 -07:00
if ( fr . equality ( ) ) {
2010-06-22 15:07:08 -07:00
for ( BoundBuilders : : const_iterator j = builders . begin ( ) ; j ! = builders . end ( ) ; + + j ) {
j - > first - > appendAs ( fr . min ( ) , " " ) ;
j - > second - > appendAs ( fr . min ( ) , " " ) ;
}
2011-01-04 00:40:41 -05:00
}
else {
2010-06-22 15:07:08 -07:00
if ( ! fr . inQuery ( ) ) {
ineq = true ;
}
BoundBuilders newBuilders ;
2011-04-07 16:07:29 -07:00
const vector < FieldInterval > & intervals = fr . intervals ( ) ;
2010-06-22 15:07:08 -07:00
for ( BoundBuilders : : const_iterator i = builders . begin ( ) ; i ! = builders . end ( ) ; + + i ) {
BSONObj first = i - > first - > obj ( ) ;
BSONObj second = i - > second - > obj ( ) ;
2010-12-10 16:02:27 -05:00
const unsigned maxCombinations = 4000000 ;
2010-06-22 15:07:08 -07:00
if ( forward ) {
2011-04-07 16:07:29 -07:00
for ( vector < FieldInterval > : : const_iterator j = intervals . begin ( ) ; j ! = intervals . end ( ) ; + + j ) {
2010-12-10 16:02:27 -05:00
uassert ( 13303 , " combinatorial limit of $in partitioning of result set exceeded " , newBuilders . size ( ) < maxCombinations ) ;
2011-04-07 16:07:29 -07:00
newBuilders . push_back ( make_pair ( shared_ptr < BSONObjBuilder > ( new BSONObjBuilder ( ) ) , shared_ptr < BSONObjBuilder > ( new BSONObjBuilder ( ) ) ) ) ;
2010-06-22 15:07:08 -07:00
newBuilders . back ( ) . first - > appendElements ( first ) ;
newBuilders . back ( ) . second - > appendElements ( second ) ;
newBuilders . back ( ) . first - > appendAs ( j - > _lower . _bound , " " ) ;
newBuilders . back ( ) . second - > appendAs ( j - > _upper . _bound , " " ) ;
}
2011-01-04 00:40:41 -05:00
}
else {
2011-04-07 16:07:29 -07:00
for ( vector < FieldInterval > : : const_reverse_iterator j = intervals . rbegin ( ) ; j ! = intervals . rend ( ) ; + + j ) {
2010-12-10 16:02:27 -05:00
uassert ( 13304 , " combinatorial limit of $in partitioning of result set exceeded " , newBuilders . size ( ) < maxCombinations ) ;
2011-04-07 16:07:29 -07:00
newBuilders . push_back ( make_pair ( shared_ptr < BSONObjBuilder > ( new BSONObjBuilder ( ) ) , shared_ptr < BSONObjBuilder > ( new BSONObjBuilder ( ) ) ) ) ;
2010-06-22 15:07:08 -07:00
newBuilders . back ( ) . first - > appendElements ( first ) ;
newBuilders . back ( ) . second - > appendElements ( second ) ;
newBuilders . back ( ) . first - > appendAs ( j - > _upper . _bound , " " ) ;
newBuilders . back ( ) . second - > appendAs ( j - > _lower . _bound , " " ) ;
}
2009-09-07 13:49:22 -07:00
}
}
2010-06-22 15:07:08 -07:00
builders = newBuilders ;
2009-09-07 13:49:22 -07:00
}
2011-01-04 00:40:41 -05:00
}
else {
2009-09-07 13:49:22 -07:00
for ( BoundBuilders : : const_iterator j = builders . begin ( ) ; j ! = builders . end ( ) ; + + j ) {
j - > first - > appendAs ( forward ? fr . min ( ) : fr . max ( ) , " " ) ;
j - > second - > appendAs ( forward ? fr . max ( ) : fr . min ( ) , " " ) ;
}
}
}
2009-09-07 11:44:41 -07:00
BoundList ret ;
2009-09-07 13:49:22 -07:00
for ( BoundBuilders : : const_iterator i = builders . begin ( ) ; i ! = builders . end ( ) ; + + i )
ret . push_back ( make_pair ( i - > first - > obj ( ) , i - > second - > obj ( ) ) ) ;
2009-09-07 11:44:41 -07:00
return ret ;
2011-01-04 00:40:41 -05:00
}
2010-12-07 23:01:19 -08:00
FieldRangeSet * FieldRangeSet : : subset ( const BSONObj & fields ) const {
2011-04-25 11:20:58 -07:00
FieldRangeSet * ret = new FieldRangeSet ( _ns , BSONObj ( ) , _singleKey , true ) ;
2010-12-07 23:01:19 -08:00
BSONObjIterator i ( fields ) ;
while ( i . more ( ) ) {
BSONElement e = i . next ( ) ;
2011-04-25 11:20:58 -07:00
if ( range ( e . fieldName ( ) ) . nontrivial ( ) ) {
ret - > range ( e . fieldName ( ) ) = range ( e . fieldName ( ) ) ;
2010-12-07 23:01:19 -08:00
}
}
ret - > _queries = _queries ;
return ret ;
}
2011-04-25 11:20:58 -07:00
const FieldRangeSet & FieldRangeSetPair : : frsForIndex ( const NamespaceDetails * nsd , int idxNo ) const {
assertValidIndexOrNoIndex ( nsd , idxNo ) ;
if ( idxNo < 0 ) {
// An unindexed cursor cannot have a "single key" constraint.
return _multiKey ;
}
return nsd - > isMultikey ( idxNo ) ? _multiKey : _singleKey ;
}
bool FieldRangeSetPair : : noNontrivialRanges ( ) const {
return _singleKey . matchPossible ( ) & & _singleKey . nNontrivialRanges ( ) = = 0 & &
_multiKey . matchPossible ( ) & & _multiKey . nNontrivialRanges ( ) = = 0 ;
}
2011-04-25 13:29:38 -07:00
bool FieldRangeSetPair : : matchPossibleForIndex ( NamespaceDetails * d , int idxNo , const BSONObj & keyPattern ) const {
2011-04-25 11:20:58 -07:00
assertValidIndexOrNoIndex ( d , idxNo ) ;
if ( ! matchPossible ( ) ) {
return false ;
}
if ( idxNo < 0 ) {
// multi key matchPossible() is true, so return true.
return true ;
}
2011-04-25 13:29:38 -07:00
return frsForIndex ( d , idxNo ) . matchPossibleForIndex ( keyPattern ) ;
2011-04-25 11:20:58 -07:00
}
FieldRangeSetPair & FieldRangeSetPair : : operator & = ( const FieldRangeSetPair & other ) {
_singleKey & = other . _singleKey ;
_multiKey & = other . _multiKey ;
return * this ;
}
2011-01-04 00:40:41 -05:00
2011-04-25 11:20:58 -07:00
FieldRangeSetPair & FieldRangeSetPair : : operator - = ( const FieldRangeSet & scanned ) {
_singleKey - = scanned ;
_multiKey - = scanned ;
return * this ;
}
void FieldRangeSetPair : : assertValidIndex ( const NamespaceDetails * d , int idxNo ) const {
2011-04-25 13:29:38 -07:00
massert ( 14048 , " FieldRangeSetPair invalid index specified " , idxNo > = 0 & & idxNo < d - > nIndexes ) ;
2011-04-25 11:20:58 -07:00
}
void FieldRangeSetPair : : assertValidIndexOrNoIndex ( const NamespaceDetails * d , int idxNo ) const {
2011-04-25 13:29:38 -07:00
massert ( 14049 , " FieldRangeSetPair invalid index specified " , idxNo > = - 1 ) ;
2011-04-25 11:20:58 -07:00
if ( idxNo > = 0 ) {
assertValidIndex ( d , idxNo ) ;
}
}
2011-04-25 13:29:38 -07:00
BSONObj FieldRangeSetPair : : simplifiedQueryForIndex ( NamespaceDetails * d , int idxNo , const BSONObj & keyPattern ) const {
return frsForIndex ( d , idxNo ) . simplifiedQuery ( keyPattern ) ;
2011-04-25 11:20:58 -07:00
}
2010-07-16 03:47:26 -07:00
bool FieldRangeVector : : matchesElement ( const BSONElement & e , int i , bool forward ) const {
2010-08-18 11:42:58 -07:00
bool eq ;
int l = matchingLowElement ( e , i , forward , eq ) ;
2011-01-04 00:40:41 -05:00
return ( l % 2 = = 0 ) ; // if we're inside an interval
2010-07-16 09:45:59 -07:00
}
2011-01-04 00:40:41 -05:00
2010-08-16 17:39:01 -07:00
// binary search for interval containing the specified element
// an even return value indicates that the element is contained within a valid interval
2010-08-18 11:42:58 -07:00
int FieldRangeVector : : matchingLowElement ( const BSONElement & e , int i , bool forward , bool & lowEquality ) const {
lowEquality = false ;
2010-07-16 03:47:26 -07:00
int l = - 1 ;
int h = _ranges [ i ] . intervals ( ) . size ( ) * 2 ;
while ( l + 1 < h ) {
int m = ( l + h ) / 2 ;
BSONElement toCmp ;
2010-08-18 11:42:58 -07:00
bool toCmpInclusive ;
const FieldInterval & interval = _ranges [ i ] . intervals ( ) [ m / 2 ] ;
2010-07-16 03:47:26 -07:00
if ( m % 2 = = 0 ) {
2010-08-18 11:42:58 -07:00
toCmp = interval . _lower . _bound ;
toCmpInclusive = interval . _lower . _inclusive ;
2011-01-04 00:40:41 -05:00
}
else {
2010-08-18 11:42:58 -07:00
toCmp = interval . _upper . _bound ;
toCmpInclusive = interval . _upper . _inclusive ;
2010-07-16 03:47:26 -07:00
}
int cmp = toCmp . woCompare ( e , false ) ;
if ( ! forward ) {
cmp = - cmp ;
}
if ( cmp < 0 ) {
l = m ;
2011-01-04 00:40:41 -05:00
}
else if ( cmp > 0 ) {
2010-07-16 03:47:26 -07:00
h = m ;
2011-01-04 00:40:41 -05:00
}
else {
2010-08-18 11:42:58 -07:00
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 ;
2010-07-16 03:47:26 -07:00
}
}
assert ( l + 1 = = h ) ;
2010-07-16 09:45:59 -07:00
return l ;
2010-07-16 03:47:26 -07:00
}
2011-01-04 00:40:41 -05:00
2010-07-16 03:47:26 -07:00
bool FieldRangeVector : : matches ( const BSONObj & obj ) const {
2010-12-22 18:58:54 -08:00
// 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.
BSONObjSetDefaultOrder keys ;
2011-03-21 21:51:52 -07:00
_indexSpec . getKeys ( obj , keys ) ;
2010-12-22 18:58:54 -08:00
for ( BSONObjSetDefaultOrder : : const_iterator i = keys . begin ( ) ; i ! = keys . end ( ) ; + + i ) {
BSONObjIterator j ( * i ) ;
2011-03-21 21:51:52 -07:00
BSONObjIterator k ( _indexSpec . keyPattern ) ;
2010-12-22 18:58:54 -08:00
bool match = true ;
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 ) ) {
match = false ;
2010-10-03 14:29:49 -07:00
break ;
2010-07-16 03:47:26 -07:00
}
2010-10-03 14:29:49 -07:00
}
2010-12-22 18:58:54 -08:00
if ( match ) {
// The *i key matched a valid range for every element.
return true ;
2010-07-16 03:47:26 -07:00
}
}
2010-12-22 18:58:54 -08:00
return false ;
2010-07-16 03:47:26 -07:00
}
2011-01-04 00:40:41 -05:00
2010-07-16 09:45:59 -07:00
// TODO optimize more
2011-04-25 11:20:58 -07:00
int FieldRangeVectorIterator : : advance ( const BSONObj & curr ) {
2010-07-16 01:49:15 -07:00
BSONObjIterator j ( curr ) ;
2011-03-21 21:51:52 -07:00
BSONObjIterator o ( _v . _indexSpec . keyPattern ) ;
2010-08-16 17:39:01 -07:00
// 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
2010-07-16 01:49:15 -07:00
int latestNonEndpoint = - 1 ;
2010-08-16 17:39:01 -07:00
// iterate over fields to determine appropriate advance method
2010-07-16 01:49:15 -07:00
for ( int i = 0 ; i < ( int ) _i . size ( ) ; + + i ) {
2010-07-16 09:45:59 -07:00
if ( i > 0 & & ! _v . _ranges [ i - 1 ] . intervals ( ) [ _i [ i - 1 ] ] . equality ( ) ) {
2010-08-16 17:39:01 -07:00
// if last bound was inequality, we don't know anything about where we are for this field
2010-08-18 11:42:58 -07:00
// 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
2010-07-16 09:45:59 -07:00
setMinus ( i ) ;
}
2010-07-16 01:49:15 -07:00
bool eq = false ;
BSONElement oo = o . next ( ) ;
2010-07-16 09:45:59 -07:00
bool reverse = ( ( oo . number ( ) < 0 ) ^ ( _v . _direction < 0 ) ) ;
BSONElement jj = j . next ( ) ;
2010-08-16 17:39:01 -07:00
if ( _i [ i ] = = - 1 ) { // unknown position for this field, do binary search
2010-08-18 11:42:58 -07:00
bool lowEquality ;
int l = _v . matchingLowElement ( jj , i , ! reverse , lowEquality ) ;
2010-08-16 17:39:01 -07:00
if ( l % 2 = = 0 ) { // we are in a valid range for this field
2010-07-16 09:45:59 -07:00
_i [ i ] = l / 2 ;
int diff = ( int ) _v . _ranges [ i ] . intervals ( ) . size ( ) - _i [ i ] ;
if ( diff > 1 ) {
latestNonEndpoint = i ;
2011-01-04 00:40:41 -05:00
}
else if ( diff = = 1 ) {
2010-07-16 09:45:59 -07:00
int x = _v . _ranges [ i ] . intervals ( ) [ _i [ i ] ] . _upper . _bound . woCompare ( jj , false ) ;
if ( x ! = 0 ) {
latestNonEndpoint = i ;
}
}
continue ;
2011-01-04 00:40:41 -05:00
}
else { // not in a valid range for this field - determine if and how to advance
2010-08-16 17:39:01 -07:00
// check if we're after the last interval for this field
2010-07-16 09:45:59 -07:00
if ( l = = ( int ) _v . _ranges [ i ] . intervals ( ) . size ( ) * 2 - 1 ) {
if ( latestNonEndpoint = = - 1 ) {
return - 2 ;
}
setZero ( latestNonEndpoint + 1 ) ;
// skip to curr / latestNonEndpoint + 1 / superlative
2010-08-18 11:42:58 -07:00
_after = true ;
2011-01-04 00:40:41 -05:00
return latestNonEndpoint + 1 ;
2010-07-16 09:45:59 -07:00
}
_i [ i ] = ( l + 1 ) / 2 ;
2010-08-18 11:42:58 -07:00
if ( lowEquality ) {
// skip to curr / i + 1 / superlative
_after = true ;
2011-01-04 00:40:41 -05:00
return i + 1 ;
2010-08-18 11:42:58 -07:00
}
2010-07-16 01:49:15 -07:00
// skip to curr / i / nextbounds
_cmp [ i ] = & _v . _ranges [ i ] . intervals ( ) [ _i [ i ] ] . _lower . _bound ;
2010-08-18 11:42:58 -07:00
_inc [ i ] = _v . _ranges [ i ] . intervals ( ) [ _i [ i ] ] . _lower . _inclusive ;
2010-07-16 01:49:15 -07:00
for ( int j = i + 1 ; j < ( int ) _i . size ( ) ; + + j ) {
_cmp [ j ] = & _v . _ranges [ j ] . intervals ( ) . front ( ) . _lower . _bound ;
2010-08-18 11:42:58 -07:00
_inc [ j ] = _v . _ranges [ j ] . intervals ( ) . front ( ) . _lower . _inclusive ;
2010-07-16 01:49:15 -07:00
}
2010-08-18 11:42:58 -07:00
_after = false ;
2011-01-04 00:40:41 -05:00
return i ;
2010-07-16 01:49:15 -07:00
}
2010-07-16 09:45:59 -07:00
}
2010-07-21 00:10:10 -07:00
bool first = true ;
2010-08-16 17:39:01 -07:00
// _i[ 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
2010-07-16 09:45:59 -07:00
while ( _i [ i ] < ( int ) _v . _ranges [ i ] . intervals ( ) . size ( ) ) {
2010-08-16 17:39:01 -07:00
// compare to current interval's upper bound
2010-07-16 09:45:59 -07:00
int x = _v . _ranges [ i ] . intervals ( ) [ _i [ i ] ] . _upper . _bound . woCompare ( jj , false ) ;
if ( reverse ) {
2010-07-16 01:49:15 -07:00
x = - x ;
}
2010-08-18 11:42:58 -07:00
if ( x = = 0 & & _v . _ranges [ i ] . intervals ( ) [ _i [ i ] ] . _upper . _inclusive ) {
2010-07-16 09:45:59 -07:00
eq = true ;
2010-07-16 01:49:15 -07:00
break ;
}
2010-08-16 17:39:01 -07:00
// see if we're less than the upper bound
2010-07-16 09:45:59 -07:00
if ( x > 0 ) {
2010-07-21 00:10:10 -07:00
if ( i = = 0 & & first ) {
2010-08-16 17:39:01 -07:00
// the value of 1st field won't go backward, so don't check lower bound
// TODO maybe we can check first only?
break ;
2010-07-21 00:10:10 -07:00
}
2010-08-16 17:39:01 -07:00
// if it's an equality interval, don't need to compare separately to lower bound
2010-07-16 09:45:59 -07:00
if ( ! _v . _ranges [ i ] . intervals ( ) [ _i [ i ] ] . equality ( ) ) {
2010-08-16 17:39:01 -07:00
// compare to current interval's lower bound
2010-07-16 09:45:59 -07:00
x = _v . _ranges [ i ] . intervals ( ) [ _i [ i ] ] . _lower . _bound . woCompare ( jj , false ) ;
if ( reverse ) {
x = - x ;
}
}
2010-08-18 11:42:58 -07:00
// if we're equal to and not inclusive the lower bound, advance
if ( ( x = = 0 & & ! _v . _ranges [ i ] . intervals ( ) [ _i [ i ] ] . _lower . _inclusive ) ) {
setZero ( i + 1 ) ;
// skip to curr / i + 1 / superlative
_after = true ;
2011-01-04 00:40:41 -05:00
return i + 1 ;
2010-08-18 11:42:58 -07:00
}
2010-08-16 17:39:01 -07:00
// if we're less than the lower bound, advance
2010-07-16 09:45:59 -07:00
if ( x > 0 ) {
setZero ( i + 1 ) ;
// skip to curr / i / nextbounds
_cmp [ i ] = & _v . _ranges [ i ] . intervals ( ) [ _i [ i ] ] . _lower . _bound ;
2010-08-18 11:42:58 -07:00
_inc [ i ] = _v . _ranges [ i ] . intervals ( ) [ _i [ i ] ] . _lower . _inclusive ;
2010-07-16 09:45:59 -07:00
for ( int j = i + 1 ; j < ( int ) _i . size ( ) ; + + j ) {
_cmp [ j ] = & _v . _ranges [ j ] . intervals ( ) . front ( ) . _lower . _bound ;
2010-08-18 11:42:58 -07:00
_inc [ j ] = _v . _ranges [ j ] . intervals ( ) . front ( ) . _lower . _inclusive ;
2010-07-16 09:45:59 -07:00
}
2010-08-18 11:42:58 -07:00
_after = false ;
2010-07-16 09:45:59 -07:00
return i ;
2011-01-04 00:40:41 -05:00
}
else {
2010-07-16 09:45:59 -07:00
break ;
}
}
2010-08-16 17:39:01 -07:00
// we're above the upper bound, so try next interval and reset remaining fields
2010-07-16 01:49:15 -07:00
+ + _i [ i ] ;
setZero ( i + 1 ) ;
2010-07-21 00:10:10 -07:00
first = false ;
2010-07-16 01:49:15 -07:00
}
int diff = ( int ) _v . _ranges [ i ] . intervals ( ) . size ( ) - _i [ i ] ;
if ( diff > 1 | | ( ! eq & & diff = = 1 ) ) {
2010-08-18 11:42:58 -07:00
// check if we're not at the end of valid values for this field
2010-07-16 01:49:15 -07:00
latestNonEndpoint = i ;
2011-01-04 00:40:41 -05:00
}
else if ( diff = = 0 ) { // check if we're past the last interval for this field
2010-07-16 01:49:15 -07:00
if ( latestNonEndpoint = = - 1 ) {
return - 2 ;
}
2010-08-16 17:39:01 -07:00
// more values possible, skip...
2010-07-16 01:49:15 -07:00
setZero ( latestNonEndpoint + 1 ) ;
// skip to curr / latestNonEndpoint + 1 / superlative
2010-08-18 11:42:58 -07:00
_after = true ;
2010-07-16 01:49:15 -07:00
return latestNonEndpoint + 1 ;
}
}
2011-01-04 00:40:41 -05:00
return - 1 ;
2010-07-16 01:49:15 -07:00
}
2011-01-04 00:40:41 -05:00
2011-04-25 11:20:58 -07:00
void FieldRangeVectorIterator : : prepDive ( ) {
2010-08-18 11:42:58 -07:00
for ( int j = 0 ; j < ( int ) _i . size ( ) ; + + j ) {
_cmp [ j ] = & _v . _ranges [ j ] . intervals ( ) . front ( ) . _lower . _bound ;
_inc [ j ] = _v . _ranges [ j ] . intervals ( ) . front ( ) . _lower . _inclusive ;
2011-01-04 00:40:41 -05:00
}
2010-08-18 11:42:58 -07:00
}
2011-01-04 00:40:41 -05:00
2011-04-25 11:20:58 -07:00
BSONObj FieldRangeVectorIterator : : startKey ( ) {
2011-04-05 16:50:37 -07:00
BSONObjBuilder b ;
for ( int unsigned i = 0 ; i < _i . size ( ) ; + + i ) {
const FieldInterval & fi = _v . _ranges [ i ] . intervals ( ) [ _i [ i ] ] ;
b . appendAs ( fi . _lower . _bound , " " ) ;
}
return b . obj ( ) ;
}
// temp
2011-04-25 11:20:58 -07:00
BSONObj FieldRangeVectorIterator : : endKey ( ) {
2011-04-05 16:50:37 -07:00
BSONObjBuilder b ;
for ( int unsigned i = 0 ; i < _i . size ( ) ; + + i ) {
const FieldInterval & fi = _v . _ranges [ i ] . intervals ( ) [ _i [ i ] ] ;
b . appendAs ( fi . _upper . _bound , " " ) ;
}
return b . obj ( ) ;
}
2011-04-25 15:47:01 -07:00
OrRangeGenerator : : OrRangeGenerator ( const char * ns , const BSONObj & query , bool optimize )
2011-04-05 16:50:37 -07:00
: _baseSet ( ns , query , optimize ) , _orFound ( ) {
2011-04-25 11:20:58 -07:00
BSONObjIterator i ( _baseSet . originalQuery ( ) ) ;
2011-04-05 16:50:37 -07:00
while ( i . more ( ) ) {
BSONElement e = i . next ( ) ;
if ( strcmp ( e . fieldName ( ) , " $or " ) = = 0 ) {
massert ( 13262 , " $or requires nonempty array " , e . type ( ) = = Array & & e . embeddedObject ( ) . nFields ( ) > 0 ) ;
BSONObjIterator j ( e . embeddedObject ( ) ) ;
while ( j . more ( ) ) {
BSONElement f = j . next ( ) ;
massert ( 13263 , " $or array must contain objects " , f . type ( ) = = Object ) ;
2011-04-25 11:20:58 -07:00
_orSets . push_back ( FieldRangeSetPair ( ns , f . embeddedObject ( ) , optimize ) ) ;
2011-04-05 16:50:37 -07:00
massert ( 13291 , " $or may not contain 'special' query " , _orSets . back ( ) . getSpecial ( ) . empty ( ) ) ;
_originalOrSets . push_back ( _orSets . back ( ) ) ;
}
_orFound = true ;
continue ;
}
}
}
2011-04-25 11:20:58 -07:00
2011-04-25 15:47:01 -07:00
void OrRangeGenerator : : assertMayPopOrClause ( ) {
2011-04-25 11:20:58 -07:00
massert ( 13274 , " no or clause to pop " , ! orFinished ( ) ) ;
}
2011-04-25 15:47:01 -07:00
void OrRangeGenerator : : popOrClause ( NamespaceDetails * nsd , int idxNo , const BSONObj & keyPattern ) {
2011-04-25 11:20:58 -07:00
assertMayPopOrClause ( ) ;
auto_ptr < FieldRangeSet > holder ;
const FieldRangeSet * toDiff = & _originalOrSets . front ( ) . frsForIndex ( nsd , idxNo ) ;
2011-04-25 13:29:38 -07:00
BSONObj indexSpec = keyPattern ;
2011-04-25 11:20:58 -07:00
if ( ! indexSpec . isEmpty ( ) & & toDiff - > matchPossibleForIndex ( indexSpec ) ) {
holder . reset ( toDiff - > subset ( indexSpec ) ) ;
toDiff = holder . get ( ) ;
}
2011-04-25 13:29:38 -07:00
popOrClause ( toDiff , nsd , idxNo , keyPattern ) ;
2011-04-25 11:20:58 -07:00
}
2011-04-25 15:47:01 -07:00
void OrRangeGenerator : : popOrClauseSingleKey ( ) {
2011-04-25 11:20:58 -07:00
assertMayPopOrClause ( ) ;
FieldRangeSet * toDiff = & _originalOrSets . front ( ) . _singleKey ;
popOrClause ( toDiff ) ;
}
2011-04-05 16:50:37 -07:00
/**
* Removes the top or clause , which would have been recently scanned , and
* removes the field ranges it covers from all subsequent or clauses . As a
* side effect , this function may invalidate the return values of topFrs ( )
* calls made before this function was called .
* @ param indexSpec - Keys of the index that was used to satisfy the last or
* clause . Used to determine the range of keys that were scanned . If
* empty we do not constrain the previous clause ' s ranges using index keys ,
* which may reduce opportunities for range elimination .
2011-04-25 11:20:58 -07:00
*/
2011-04-25 15:47:01 -07:00
void OrRangeGenerator : : popOrClause ( const FieldRangeSet * toDiff , NamespaceDetails * d , int idxNo , const BSONObj & keyPattern ) {
2011-04-25 11:20:58 -07:00
list < FieldRangeSetPair > : : iterator i = _orSets . begin ( ) ;
list < FieldRangeSetPair > : : iterator j = _originalOrSets . begin ( ) ;
2011-04-05 16:50:37 -07:00
+ + i ;
+ + j ;
while ( i ! = _orSets . end ( ) ) {
* i - = * toDiff ;
2011-04-25 11:20:58 -07:00
// Check if match is possible at all, and if it is possible for the recently scanned index.
2011-04-25 13:29:38 -07:00
if ( ! i - > matchPossible ( ) | | ( d & & ! i - > matchPossibleForIndex ( d , idxNo , keyPattern ) ) ) {
2011-04-05 16:50:37 -07:00
i = _orSets . erase ( i ) ;
j = _originalOrSets . erase ( j ) ;
}
else {
+ + i ;
+ + j ;
}
}
_oldOrSets . push_front ( _orSets . front ( ) ) ;
_orSets . pop_front ( ) ;
_originalOrSets . pop_front ( ) ;
}
2010-01-14 20:57:50 -05:00
struct SimpleRegexUnitTest : UnitTest {
2011-01-04 00:40:41 -05:00
void run ( ) {
2010-01-14 20:57:50 -05:00
{
BSONObjBuilder b ;
b . appendRegex ( " r " , " ^foo " ) ;
BSONObj o = b . done ( ) ;
assert ( simpleRegex ( o . firstElement ( ) ) = = " foo " ) ;
}
{
BSONObjBuilder b ;
b . appendRegex ( " r " , " ^f?oo " ) ;
BSONObj o = b . done ( ) ;
assert ( simpleRegex ( o . firstElement ( ) ) = = " " ) ;
}
{
BSONObjBuilder b ;
b . appendRegex ( " r " , " ^fz?oo " ) ;
BSONObj o = b . done ( ) ;
assert ( simpleRegex ( o . firstElement ( ) ) = = " f " ) ;
}
{
BSONObjBuilder b ;
b . appendRegex ( " r " , " ^f " , " " ) ;
BSONObj o = b . done ( ) ;
assert ( simpleRegex ( o . firstElement ( ) ) = = " f " ) ;
}
2010-03-29 20:17:10 -04:00
{
BSONObjBuilder b ;
b . appendRegex ( " r " , " \\ Af " , " " ) ;
BSONObj o = b . done ( ) ;
assert ( simpleRegex ( o . firstElement ( ) ) = = " f " ) ;
}
2010-01-14 20:57:50 -05:00
{
BSONObjBuilder b ;
b . appendRegex ( " r " , " ^f " , " m " ) ;
BSONObj o = b . done ( ) ;
2010-03-29 20:17:10 -04:00
assert ( simpleRegex ( o . firstElement ( ) ) = = " " ) ;
}
{
BSONObjBuilder b ;
b . appendRegex ( " r " , " \\ Af " , " m " ) ;
BSONObj o = b . done ( ) ;
2010-01-14 20:57:50 -05:00
assert ( simpleRegex ( o . firstElement ( ) ) = = " f " ) ;
}
{
BSONObjBuilder b ;
2010-03-29 20:17:10 -04:00
b . appendRegex ( " r " , " \\ Af " , " mi " ) ;
2010-01-14 20:57:50 -05:00
BSONObj o = b . done ( ) ;
assert ( simpleRegex ( o . firstElement ( ) ) = = " " ) ;
}
2010-01-14 22:47:34 -05:00
{
BSONObjBuilder b ;
2010-03-29 20:17:10 -04:00
b . appendRegex ( " r " , " \\ Af \t \v o \n \r o \\ \\ # #comment " , " mx " ) ;
2010-01-14 22:47:34 -05:00
BSONObj o = b . done ( ) ;
assert ( simpleRegex ( o . firstElement ( ) ) = = " foo # " ) ;
}
2011-03-29 15:38:03 -04:00
{
assert ( simpleRegex ( " ^ \\ Qasdf \\ E " , " " , NULL ) = = " asdf " ) ;
assert ( simpleRegex ( " ^ \\ Qasdf \\ E.* " , " " , NULL ) = = " asdf " ) ;
assert ( simpleRegex ( " ^ \\ Qasdf " , " " , NULL ) = = " asdf " ) ; // PCRE supports this
assert ( simpleRegex ( " ^ \\ Qasdf \\ \\ E " , " " , NULL ) = = " asdf \\ " ) ;
assert ( simpleRegex ( " ^ \\ Qas.*df \\ E " , " " , NULL ) = = " as.*df " ) ;
assert ( simpleRegex ( " ^ \\ Qas \\ Q[df \\ E " , " " , NULL ) = = " as \\ Q[df " ) ;
assert ( simpleRegex ( " ^ \\ Qas \\ E \\ \\ E \\ Q$df \\ E " , " " , NULL ) = = " as \\ E$df " ) ; // quoted string containing \E
}
2010-01-14 20:57:50 -05:00
}
} simple_regex_unittest ;
2010-07-22 09:51:22 -04:00
2011-01-04 00:40:41 -05:00
long long applySkipLimit ( long long num , const BSONObj & cmd ) {
2010-07-22 09:51:22 -04:00
BSONElement s = cmd [ " skip " ] ;
BSONElement l = cmd [ " limit " ] ;
2011-01-04 00:40:41 -05:00
if ( s . isNumber ( ) ) {
2010-07-22 09:51:22 -04:00
num = num - s . numberLong ( ) ;
if ( num < 0 ) {
num = 0 ;
}
}
2011-01-04 00:40:41 -05:00
if ( l . isNumber ( ) ) {
2010-07-22 09:51:22 -04:00
long long limit = l . numberLong ( ) ;
2011-01-04 00:40:41 -05:00
if ( limit < num ) {
2010-07-22 09:51:22 -04:00
num = limit ;
}
}
2011-01-04 00:40:41 -05:00
return num ;
2010-07-22 09:51:22 -04:00
}
2010-07-23 16:06:06 -04:00
2011-01-04 00:40:41 -05:00
string debugString ( Message & m ) {
2010-07-23 16:06:06 -04:00
stringstream ss ;
ss < < " op: " < < opToString ( m . operation ( ) ) < < " len: " < < m . size ( ) ;
2011-01-04 00:40:41 -05:00
if ( m . operation ( ) > = 2000 & & m . operation ( ) < 2100 ) {
2010-07-23 16:06:06 -04:00
DbMessage d ( m ) ;
ss < < " ns: " < < d . getns ( ) ;
2011-01-04 00:40:41 -05:00
switch ( m . operation ( ) ) {
2010-07-23 17:15:01 -04:00
case dbUpdate : {
int flags = d . pullInt ( ) ;
BSONObj q = d . nextJsObj ( ) ;
2010-09-22 17:46:15 -04:00
BSONObj o = d . nextJsObj ( ) ;
ss < < " flags: " < < flags < < " query: " < < q < < " update: " < < o ;
2010-07-23 16:06:06 -04:00
break ;
2010-07-23 17:15:01 -04:00
}
2010-07-23 16:06:06 -04:00
case dbInsert :
ss < < d . nextJsObj ( ) ;
break ;
2010-07-23 17:15:01 -04:00
case dbDelete : {
int flags = d . pullInt ( ) ;
BSONObj q = d . nextJsObj ( ) ;
ss < < " flags: " < < flags < < " query: " < < q ;
2010-07-23 16:06:06 -04:00
break ;
2010-07-23 17:15:01 -04:00
}
2010-07-23 16:06:06 -04:00
default :
ss < < " CANNOT HANDLE YET " ;
}
2011-01-04 00:40:41 -05:00
2010-07-23 16:06:06 -04:00
}
return ss . str ( ) ;
2011-01-04 00:40:41 -05:00
}
2010-07-22 09:51:22 -04:00
2009-02-24 14:14:45 -05:00
} // namespace mongo