Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

SERVER-1528 account for expensive skipping operations in nscanned

  • Loading branch information...
commit 701dc65282b7e154aca20ae1882ae7962c11e0b2 1 parent 5240b2c
@astaple astaple authored
View
3  db/btree.h
@@ -364,6 +364,8 @@ namespace mongo {
_matcher = matcher;
}
+ virtual long long nscanned() { return _nscanned; }
+
// for debugging only
DiskLoc getBucket() const { return bucket; }
@@ -408,6 +410,7 @@ namespace mongo {
const IndexSpec& _spec;
shared_ptr< CoveredIndexMatcher > _matcher;
bool _independentFieldRanges;
+ long long _nscanned;
};
View
23 db/btreecursor.cpp
@@ -38,7 +38,8 @@ namespace mongo {
_ordering( Ordering::make( order ) ),
direction( _direction ),
_spec( _id.getSpec() ),
- _independentFieldRanges( false )
+ _independentFieldRanges( false ),
+ _nscanned( 0 )
{
audit();
init();
@@ -57,7 +58,8 @@ namespace mongo {
bounds_( ( assert( _bounds.get() ), _bounds ) ),
_boundsIterator( new FieldRangeVector::Iterator( *bounds_ ) ),
_spec( _id.getSpec() ),
- _independentFieldRanges( true )
+ _independentFieldRanges( true ),
+ _nscanned( 0 )
{
massert( 13384, "BtreeCursor FieldRangeVector constructor doesn't accept special indexes", !_spec.getType() );
audit();
@@ -93,6 +95,9 @@ namespace mongo {
bool found;
bucket = indexDetails.head.btree()->
locate(indexDetails, indexDetails.head, startKey, _ordering, keyOfs, found, direction > 0 ? minDiskLoc : maxDiskLoc, direction);
+ if ( ok() ) {
+ _nscanned = 1;
+ }
skipUnusedKeys( false );
checkEnd();
}
@@ -119,8 +124,10 @@ namespace mongo {
bucket = DiskLoc();
return false;
} else if ( ret == -1 ) {
+ ++_nscanned;
return false;
}
+ ++_nscanned;
advanceTo( currKeyNode().key, ret, _boundsIterator->cmp() );
return true;
}
@@ -137,6 +144,8 @@ namespace mongo {
break;
bucket = b->advance(bucket, keyOfs, direction, "skipUnusedKeys");
u++;
+ //don't include unused keys in nscanned
+ //++_nscanned;
if ( mayJump && ( u % 10 == 0 ) ) {
skipOutOfRangeKeysAndCheckEnd();
}
@@ -175,14 +184,16 @@ namespace mongo {
return false;
bucket = bucket.btree()->advance(bucket, keyOfs, direction, "BtreeCursor::advance");
-
+
if ( !_independentFieldRanges ) {
skipUnusedKeys( false );
checkEnd();
- return ok();
+ if ( ok() ) {
+ ++_nscanned;
+ }
+ } else {
+ skipAndCheck();
}
-
- skipAndCheck();
return ok();
}
View
5 db/cursor.cpp
@@ -24,7 +24,7 @@ namespace mongo {
killCurrentOp.checkForInterrupt();
if ( eof() ) {
if ( tailable_ && !last.isNull() ) {
- curr = s->next( last );
+ curr = s->next( last );
} else {
return false;
}
@@ -32,6 +32,7 @@ namespace mongo {
last = curr;
curr = s->next( curr );
}
+ incNscanned();
return ok();
}
@@ -89,6 +90,7 @@ namespace mongo {
}
curr = start;
s = this;
+ incNscanned();
}
DiskLoc ForwardCappedCursor::next( const DiskLoc &prev ) const {
@@ -125,6 +127,7 @@ namespace mongo {
}
curr = start;
s = this;
+ incNscanned();
}
DiskLoc ReverseCappedCursor::next( const DiskLoc &prev ) const {
View
11 db/cursor.h
@@ -96,6 +96,8 @@ namespace mongo {
virtual bool capped() const { return false; }
+ virtual long long nscanned() = 0;
+
// The implementation may return different matchers depending on the
// position of the cursor. If matcher() is nonzero at the start,
// matcher() should be checked each time advance() is called.
@@ -124,10 +126,12 @@ namespace mongo {
protected:
DiskLoc curr, last;
const AdvanceStrategy *s;
+ void incNscanned() { if ( !curr.isNull() ) { ++_nscanned; } }
private:
bool tailable_;
shared_ptr< CoveredIndexMatcher > _matcher;
+ long long _nscanned;
void init() {
tailable_ = false;
}
@@ -153,10 +157,11 @@ namespace mongo {
bool advance();
- BasicCursor(DiskLoc dl, const AdvanceStrategy *_s = forward()) : curr(dl), s( _s ) {
+ BasicCursor(DiskLoc dl, const AdvanceStrategy *_s = forward()) : curr(dl), s( _s ), _nscanned() {
+ incNscanned();
init();
}
- BasicCursor(const AdvanceStrategy *_s = forward()) : s( _s ) {
+ BasicCursor(const AdvanceStrategy *_s = forward()) : s( _s ), _nscanned() {
init();
}
virtual string toString() {
@@ -180,6 +185,8 @@ namespace mongo {
_matcher = matcher;
}
+ virtual long long nscanned() { return _nscanned; }
+
};
/* used for order { $natural: -1 } */
View
4 db/dbhelpers.cpp
@@ -114,6 +114,10 @@ namespace mongo {
c_->advance();
}
}
+ virtual long long nscanned() {
+ assert( c_.get() );
+ return c_->nscanned();
+ }
virtual bool mayRecordPlan() const { return false; }
virtual QueryOp *_createChild() const { return new FindOne( requireIndex_ ); }
BSONObj one() const { return one_; }
View
40 db/geo/2d.cpp
@@ -976,7 +976,10 @@ namespace mongo {
public:
GeoSearchCursor( shared_ptr<GeoSearch> s )
: GeoCursorBase( s->_spec ) ,
- _s( s ) , _cur( s->_hopper->_points.begin() ) , _end( s->_hopper->_points.end() ) {
+ _s( s ) , _cur( s->_hopper->_points.begin() ) , _end( s->_hopper->_points.end() ), _nscanned() {
+ if ( _cur != _end ) {
+ ++_nscanned;
+ }
}
virtual ~GeoSearchCursor() {}
@@ -988,7 +991,7 @@ namespace mongo {
virtual Record* _current(){ assert(ok()); return _cur->_loc.rec(); }
virtual BSONObj current(){ assert(ok()); return _cur->_o; }
virtual DiskLoc currLoc(){ assert(ok()); return _cur->_loc; }
- virtual bool advance(){ _cur++; return ok(); }
+ virtual bool advance(){ _cur++; incNscanned(); return ok(); }
virtual BSONObj currKey() const { return _cur->_key; }
virtual string toString() {
@@ -1004,18 +1007,22 @@ namespace mongo {
temp.move( 1 , 1 );
return BSON( _s->_spec->_geo << temp.toString() );
}
-
+
+ virtual long long nscanned() { return _nscanned; }
shared_ptr<GeoSearch> _s;
GeoHopper::Holder::iterator _cur;
GeoHopper::Holder::iterator _end;
+
+ void incNscanned() { if ( ok() ) { ++_nscanned; } }
+ long long _nscanned;
};
class GeoBrowse : public GeoCursorBase , public GeoAccumulator {
public:
GeoBrowse( const Geo2dType * g , string type , BSONObj filter = BSONObj() )
: GeoCursorBase( g ) ,GeoAccumulator( g , filter ) ,
- _type( type ) , _filter( filter ) , _firstCall(true) {
+ _type( type ) , _filter( filter ) , _firstCall(true), _nscanned() {
}
virtual string toString() {
@@ -1023,17 +1030,26 @@ namespace mongo {
}
virtual bool ok(){
+ bool first = _firstCall;
if ( _firstCall ){
fillStack();
_firstCall = false;
}
- if ( ! _cur.isEmpty() || _stack.size() )
+ if ( ! _cur.isEmpty() || _stack.size() ) {
+ if ( first ) {
+ ++_nscanned;
+ }
return true;
+ }
while ( moreToDo() ){
fillStack();
- if ( ! _cur.isEmpty() )
+ if ( ! _cur.isEmpty() ) {
+ if ( first ) {
+ ++_nscanned;
+ }
return true;
+ }
}
return false;
@@ -1045,6 +1061,7 @@ namespace mongo {
if ( _stack.size() ){
_cur = _stack.front();
_stack.pop_front();
+ ++_nscanned;
return true;
}
@@ -1053,7 +1070,7 @@ namespace mongo {
while ( _cur.isEmpty() && moreToDo() )
fillStack();
- return ! _cur.isEmpty();
+ return ! _cur.isEmpty() && ++_nscanned;
}
virtual Record* _current(){ assert(ok()); return _cur._loc.rec(); }
@@ -1072,12 +1089,21 @@ namespace mongo {
_stack.push_back( GeoPoint( node , d ) );
}
+ virtual long long nscanned() {
+ if ( _firstCall ) {
+ ok();
+ }
+ return _nscanned;
+ }
+
string _type;
BSONObj _filter;
list<GeoPoint> _stack;
GeoPoint _cur;
bool _firstCall;
+
+ long long _nscanned;
};
View
29 db/query.cpp
@@ -75,6 +75,10 @@ namespace mongo {
massert( 13340, "cursor dropped during delete", false );
}
}
+ virtual long long nscanned() {
+ assert( c_.get() );
+ return c_->nscanned();
+ }
virtual void next() {
if ( !c_->ok() ) {
setComplete();
@@ -89,7 +93,7 @@ namespace mongo {
}
c_->advance();
- ++_nscanned;
+ _nscanned = c_->nscanned();
if ( count_ > bestCount_ )
bestCount_ = count_;
@@ -409,6 +413,11 @@ namespace mongo {
}
}
+ virtual long long nscanned() {
+ assert( c_.get() );
+ return c_->nscanned();
+ }
+
virtual bool prepareToYield() {
if ( ! _cc ) {
_cc.reset( new ClientCursor( QueryOption_NoCursorTimeout , c_ , _ns.c_str() ) );
@@ -657,6 +666,14 @@ namespace mongo {
}
}
+ virtual long long nscanned() {
+ if ( _findingStartCursor.get() ) {
+ return 0; // should only be one query plan, so value doesn't really matter.
+ }
+ assert( _c.get() );
+ return _c->nscanned();
+ }
+
virtual void next() {
if ( _findingStartCursor.get() ) {
if ( _findingStartCursor->done() ) {
@@ -684,7 +701,7 @@ namespace mongo {
return;
}
- _nscanned++;
+ _nscanned = _c->nscanned();
if ( !matcher()->matches(_c->currKey(), _c->currLoc() , &_details ) ) {
// not a match, continue onward
if ( _details.loadedObject )
@@ -794,7 +811,7 @@ namespace mongo {
}
void finishExplain( const BSONObj &suffix ) {
- BSONObj obj = _eb.finishWithSuffix( nscanned(), nscannedObjects(), n(), _curop.elapsedMillis(), suffix);
+ BSONObj obj = _eb.finishWithSuffix( totalNscanned(), nscannedObjects(), n(), _curop.elapsedMillis(), suffix);
fillQueryResultFromObj(_buf, 0, obj);
_n = 1;
_oldN = 0;
@@ -810,7 +827,7 @@ namespace mongo {
}
UserQueryOp *ret = new UserQueryOp( _pq, _response, _eb, _curop );
ret->_oldN = n();
- ret->_oldNscanned = nscanned();
+ ret->_oldNscanned = totalNscanned();
ret->_oldNscannedObjects = nscannedObjects();
ret->_ntoskip = _ntoskip;
return ret;
@@ -819,7 +836,7 @@ namespace mongo {
bool scanAndOrderRequired() const { return _inMemSort; }
shared_ptr<Cursor> cursor() { return _c; }
int n() const { return _oldN + _n; }
- long long nscanned() const { return _nscanned + _oldNscanned; }
+ long long totalNscanned() const { return _nscanned + _oldNscanned; }
long long nscannedObjects() const { return _nscannedObjects + _oldNscannedObjects; }
bool saveClientCursor() const { return _saveClientCursor; }
bool wouldSaveClientCursor() const { return _wouldSaveClientCursor; }
@@ -1025,7 +1042,7 @@ namespace mongo {
dqo.finishExplain( explainSuffix );
}
n = dqo.n();
- long long nscanned = dqo.nscanned();
+ long long nscanned = dqo.totalNscanned();
if ( dqo.scanAndOrderRequired() )
ss << " scanAndOrder ";
shared_ptr<Cursor> cursor = dqo.cursor();
View
60 db/queryoptimizer.cpp
@@ -24,6 +24,7 @@
#include "queryoptimizer.h"
#include "cmdline.h"
#include "clientcursor.h"
+#include <queue>
//#define DEBUGQO(x) cout << x << endl;
#define DEBUGQO(x)
@@ -524,6 +525,15 @@ namespace mongo {
}
}
+ struct OpHolder {
+ OpHolder( const shared_ptr< QueryOp > &op ) : _op( op ), _offset() {}
+ shared_ptr< QueryOp > _op;
+ long long _offset;
+ bool operator<( const OpHolder &other ) const {
+ return _op->nscanned() + _offset > other._op->nscanned() + other._offset;
+ }
+ };
+
shared_ptr< QueryOp > QueryPlanSet::Runner::run() {
massert( 10369 , "no plans", plans_.plans_.size() > 0 );
@@ -548,32 +558,29 @@ namespace mongo {
return *i;
}
- long long nScanned = 0;
- long long nScannedBackup = 0;
- while( 1 ) {
- ++nScanned;
- unsigned errCount = 0;
- bool first = true;
- for( vector< shared_ptr< QueryOp > >::iterator i = ops.begin(); i != ops.end(); ++i ) {
- mayYield( ops );
- QueryOp &op = **i;
- nextOp( op );
- if ( op.complete() ) {
- if ( first ) {
- nScanned += nScannedBackup;
- }
- if ( plans_.mayRecordPlan_ && op.mayRecordPlan() ) {
- op.qp().registerSelf( nScanned );
- }
- return *i;
+ std::priority_queue< OpHolder > queue;
+ for( vector< shared_ptr< QueryOp > >::iterator i = ops.begin(); i != ops.end(); ++i ) {
+ queue.push( *i );
+ }
+
+ while( !queue.empty() ) {
+ mayYield( ops );
+ OpHolder holder = queue.top();
+ queue.pop();
+ QueryOp &op = *holder._op;
+ nextOp( op );
+ if ( op.complete() ) {
+ if ( plans_.mayRecordPlan_ && op.mayRecordPlan() ) {
+ op.qp().registerSelf( op.nscanned() );
}
- if ( op.error() )
- ++errCount;
- first = false;
+ return holder._op;
}
- if ( errCount == ops.size() )
- break;
- if ( !plans_._bestGuessOnly && plans_.usingPrerecordedPlan_ && nScanned > plans_.oldNScanned_ * 10 && plans_._special.empty() ) {
+ if ( op.error() ) {
+ continue;
+ }
+ queue.push( holder );
+ if ( !plans_._bestGuessOnly && plans_.usingPrerecordedPlan_ && op.nscanned() > plans_.oldNScanned_ * 10 && plans_._special.empty() ) {
+ holder._offset = -op.nscanned();
plans_.addOtherPlans( true );
PlanSet::iterator i = plans_.plans_.begin();
++i;
@@ -584,12 +591,11 @@ namespace mongo {
initOp( *op );
if ( op->complete() )
return op;
+ queue.push( op );
}
plans_.mayRecordPlan_ = true;
plans_.usingPrerecordedPlan_ = false;
- nScannedBackup = nScanned;
- nScanned = 0;
- }
+ }
}
return ops[ 0 ];
}
View
13 db/queryoptimizer.h
@@ -114,6 +114,8 @@ namespace mongo {
virtual bool prepareToYield() { massert( 13335, "yield not supported", false ); return false; }
virtual void recoverFromYield() { massert( 13336, "yield not supported", false ); }
+ virtual long long nscanned() = 0;
+
/** @return a copy of the inheriting class, which will be run with its own
query plan. If multiple plan sets are required for an $or query,
the QueryOp of the winning plan from a given set will be cloned
@@ -312,7 +314,7 @@ namespace mongo {
};
// takes ownership of 'op'
MultiCursor( const char *ns, const BSONObj &pattern, const BSONObj &order, shared_ptr< CursorOp > op = shared_ptr< CursorOp >(), bool mayYield = false )
- : _mps( new MultiPlanScanner( ns, pattern, order, 0, true, BSONObj(), BSONObj(), !op.get(), mayYield ) ) {
+ : _mps( new MultiPlanScanner( ns, pattern, order, 0, true, BSONObj(), BSONObj(), !op.get(), mayYield ) ), _nscanned() {
if ( op.get() ) {
_op = op;
} else {
@@ -329,7 +331,7 @@ namespace mongo {
}
// used to handoff a query to a getMore()
MultiCursor( auto_ptr< MultiPlanScanner > mps, const shared_ptr< Cursor > &c, const shared_ptr< CoveredIndexMatcher > &matcher, const QueryOp &op )
- : _op( new NoOp( op ) ), _c( c ), _mps( mps ), _matcher( matcher ) {
+ : _op( new NoOp( op ) ), _c( c ), _mps( mps ), _matcher( matcher ), _nscanned( -1 ) {
_mps->setBestGuessOnly();
_mps->mayYield( false ); // with a NoOp, there's no need to yield in QueryPlanSet
if ( !ok() ) {
@@ -365,6 +367,8 @@ namespace mongo {
return _c->getsetdup( loc );
}
virtual CoveredIndexMatcher *matcher() const { return _matcher.get(); }
+ // return -1 if we're a getmore handoff
+ virtual long long nscanned() { return _nscanned >= 0 ? _nscanned + _c->nscanned() : _nscanned; }
// just for testing
shared_ptr< Cursor > sub_c() const { return _c; }
private:
@@ -377,8 +381,12 @@ namespace mongo {
virtual bool mayRecordPlan() const { return false; }
virtual QueryOp *_createChild() const { return new NoOp(); }
virtual shared_ptr< Cursor > newCursor() const { return qp().newCursor(); }
+ virtual long long nscanned() { assert( false ); return 0; }
};
void nextClause() {
+ if ( _nscanned >= 0 && _c.get() ) {
+ _nscanned += _c->nscanned();
+ }
shared_ptr< CursorOp > best = _mps->runOpOnce( *_op );
if ( ! best->complete() )
throw MsgAssertionException( best->exception() );
@@ -390,6 +398,7 @@ namespace mongo {
shared_ptr< Cursor > _c;
auto_ptr< MultiPlanScanner > _mps;
shared_ptr< CoveredIndexMatcher > _matcher;
+ long long _nscanned;
};
// NOTE min, max, and keyPattern will be updated to be consistent with the selected index.
View
6 db/update.cpp
@@ -747,7 +747,11 @@ namespace mongo {
_cc.reset();
massert( 13339, "cursor dropped during update", false );
}
- }
+ }
+ virtual long long nscanned() {
+ assert( _c.get() );
+ return _c->nscanned();
+ }
virtual void next() {
if ( ! _c->ok() ) {
setComplete();
View
9 dbtests/queryoptimizertests.cpp
@@ -1200,6 +1200,7 @@ namespace QueryOptimizerTests {
return op;
}
virtual bool mayRecordPlan() const { return true; }
+ virtual long long nscanned() { return 0; }
private:
bool iThrow_;
bool &threw_;
@@ -1233,6 +1234,7 @@ namespace QueryOptimizerTests {
return new TestOp();
}
virtual bool mayRecordPlan() const { return true; }
+ virtual long long nscanned() { return 0; }
};
};
@@ -1305,6 +1307,7 @@ namespace QueryOptimizerTests {
return new TestOp();
}
virtual bool mayRecordPlan() const { return true; }
+ virtual long long nscanned() { return 0; }
};
class NoRecordTestOp : public TestOp {
virtual bool mayRecordPlan() const { return false; }
@@ -1332,6 +1335,7 @@ namespace QueryOptimizerTests {
private:
class TestOp : public QueryOp {
public:
+ TestOp() {}
virtual void _init() {}
virtual void next() {
if ( qp().indexKey().firstElement().fieldName() == string( "$natural" ) )
@@ -1342,6 +1346,7 @@ namespace QueryOptimizerTests {
return new TestOp();
}
virtual bool mayRecordPlan() const { return true; }
+ virtual long long nscanned() { return 1; }
};
class ScanOnlyTestOp : public TestOp {
virtual void next() {
@@ -1380,7 +1385,7 @@ namespace QueryOptimizerTests {
theDataFileMgr.insertWithObjMod( ns(), one );
deleteObjects( ns(), BSON( "a" << 1 ), false );
ASSERT( BSON( "a" << 1 ).woCompare( NamespaceDetailsTransient::_get( ns() ).indexForPattern( FieldRangeSet( ns(), BSON( "a" << 1 ) ).pattern() ) ) == 0 );
- ASSERT_EQUALS( 2, NamespaceDetailsTransient::_get( ns() ).nScannedForPattern( FieldRangeSet( ns(), BSON( "a" << 1 ) ).pattern() ) );
+ ASSERT_EQUALS( 1, NamespaceDetailsTransient::_get( ns() ).nScannedForPattern( FieldRangeSet( ns(), BSON( "a" << 1 ) ).pattern() ) );
}
};
@@ -1445,7 +1450,7 @@ namespace QueryOptimizerTests {
runQuery( m2, q);
}
ASSERT( BSON( "a" << 1 ).woCompare( NamespaceDetailsTransient::_get( ns() ).indexForPattern( FieldRangeSet( ns(), BSON( "b" << 0 << "a" << GTE << 0 ) ).pattern() ) ) == 0 );
- ASSERT_EQUALS( 2, NamespaceDetailsTransient::_get( ns() ).nScannedForPattern( FieldRangeSet( ns(), BSON( "b" << 0 << "a" << GTE << 0 ) ).pattern() ) );
+ ASSERT_EQUALS( 3, NamespaceDetailsTransient::_get( ns() ).nScannedForPattern( FieldRangeSet( ns(), BSON( "b" << 0 << "a" << GTE << 0 ) ).pattern() ) );
}
};
View
2  jstests/explain2.js
@@ -19,7 +19,7 @@ q = { a : { $gt : 3 } }
go( q , 6 , 7 , 6 );
q.b = 5
-go( q , 1 , 1 , 1 );
+go( q , 1 , 2 , 1 );
delete q.b
q.c = 5
View
2  jstests/in3.js
@@ -8,4 +8,4 @@ assert.eq( {i:[[3,3],[6,6]]}, t.find( {i:{$in:[3,6]}} ).explain().indexBounds ,
for ( var i=0; i<20; i++ )
t.insert( { i : i } );
-assert.eq( 2 , t.find( {i:{$in:[3,6]}} ).explain().nscanned , "B1" )
+assert.eq( 3 , t.find( {i:{$in:[3,6]}} ).explain().nscanned , "B1" )
View
2  jstests/in4.js
@@ -41,7 +41,7 @@ assert.eq.automsg( "1", "t.find( {a:2,b:{$in:[3,4]},c:5} ).explain().nscanned" )
t.remove();
t.save( {a:2,b:4,c:5} );
t.save( {a:2,b:4,c:4} );
-assert.eq.automsg( "1", "t.find( {a:2,b:{$in:[3,4]},c:5} ).explain().nscanned" );
+assert.eq.automsg( "2", "t.find( {a:2,b:{$in:[3,4]},c:5} ).explain().nscanned" );
t.drop();
t.ensureIndex( {a:1,b:-1} );
View
45 jstests/index_check6.js
@@ -12,10 +12,12 @@ for ( var age=10; age<50; age++ ){
assert.eq( 10 , t.find( { age : 30 } ).explain().nscanned , "A" );
assert.eq( 20 , t.find( { age : { $gte : 29 , $lte : 30 } } ).explain().nscanned , "B" );
-assert.eq( 12 , t.find( { age : { $gte : 25 , $lte : 30 }, rating: {$in: [0,9] } } ).explain().nscanned , "C1" );
+assert.eq( 18 , t.find( { age : { $gte : 25 , $lte : 30 }, rating: {$in: [0,9] } } ).explain().nscanned , "C1" );
+assert.eq( 23 , t.find( { age : { $gte : 25 , $lte : 30 }, rating: {$in: [0,8] } } ).explain().nscanned , "C2" );
+assert.eq( 28 , t.find( { age : { $gte : 25 , $lte : 30 }, rating: {$in: [1,8] } } ).explain().nscanned , "C3" );
-assert.eq( 2 , t.find( { age : { $gte : 29 , $lte : 30 } , rating : 5 } ).explain().nscanned , "C" ); // SERVER-371
-assert.eq( 4 , t.find( { age : { $gte : 29 , $lte : 30 } , rating : { $gte : 4 , $lte : 5 } } ).explain().nscanned , "D" ); // SERVER-371
+assert.eq( 4 , t.find( { age : { $gte : 29 , $lte : 30 } , rating : 5 } ).explain().nscanned , "C" ); // SERVER-371
+assert.eq( 6 , t.find( { age : { $gte : 29 , $lte : 30 } , rating : { $gte : 4 , $lte : 5 } } ).explain().nscanned , "D" ); // SERVER-371
assert.eq.automsg( "2", "t.find( { age:30, rating:{ $gte:4, $lte:5} } ).explain().nscanned" );
@@ -36,19 +38,30 @@ assert.eq.automsg( "2", "t.find( { a:5, b:5, c:{$gte:5,$lte:6} } ).sort( sort ).
assert.eq.automsg( "1", "t.find( { a:5, b:5, c:{$gte:5.5,$lte:6} } ).sort( sort ).explain().nscanned" );
assert.eq.automsg( "1", "t.find( { a:5, b:5, c:{$gte:5,$lte:5.5} } ).sort( sort ).explain().nscanned" );
assert.eq.automsg( "3", "t.find( { a:5, b:5, c:{$gte:5,$lte:7} } ).sort( sort ).explain().nscanned" );
-assert.eq.automsg( "2", "t.find( { a:5, b:{$gte:5,$lte:6}, c:5 } ).sort( sort ).explain().nscanned" );
-assert.eq.automsg( "1", "t.find( { a:5, b:{$gte:5.5,$lte:6}, c:5 } ).sort( sort ).explain().nscanned" );
-assert.eq.automsg( "1", "t.find( { a:5, b:{$gte:5,$lte:5.5}, c:5 } ).sort( sort ).explain().nscanned" );
-assert.eq.automsg( "3", "t.find( { a:5, b:{$gte:5,$lte:7}, c:5 } ).sort( sort ).explain().nscanned" );
-assert.eq.automsg( "2", "t.find( { a:{$gte:5,$lte:6}, b:5, c:5 } ).sort( sort ).explain().nscanned" );
-assert.eq.automsg( "1", "t.find( { a:{$gte:5.5,$lte:6}, b:5, c:5 } ).sort( sort ).explain().nscanned" );
-assert.eq.automsg( "1", "t.find( { a:{$gte:5,$lte:5.5}, b:5, c:5 } ).sort( sort ).explain().nscanned" );
-assert.eq.automsg( "3", "t.find( { a:{$gte:5,$lte:7}, b:5, c:5 } ).sort( sort ).explain().nscanned" );
-assert.eq.automsg( "4", "t.find( { a:{$gte:5,$lte:6}, b:5, c:{$gte:5,$lte:6} } ).sort( sort ).explain().nscanned" );
-assert.eq.automsg( "2", "t.find( { a:{$gte:5.5,$lte:6}, b:5, c:{$gte:5,$lte:6} } ).sort( sort ).explain().nscanned" );
-assert.eq.automsg( "4", "t.find( { a:5, b:{$gte:5,$lte:6}, c:{$gte:5,$lte:6} } ).sort( sort ).explain().nscanned" );
-assert.eq.automsg( "4", "t.find( { a:{$gte:5,$lte:6}, b:{$gte:5,$lte:6}, c:5 } ).sort( sort ).explain().nscanned" );
-assert.eq.automsg( "8", "t.find( { a:{$gte:5,$lte:6}, b:{$gte:5,$lte:6}, c:{$gte:5,$lte:6} } ).sort( sort ).explain().nscanned" );
+assert.eq.automsg( "4", "t.find( { a:5, b:{$gte:5,$lte:6}, c:5 } ).sort( sort ).explain().nscanned" );
+ if ( s.b > 0 ) {
+ assert.eq.automsg( "2", "t.find( { a:5, b:{$gte:5.5,$lte:6}, c:5 } ).sort( sort ).explain().nscanned" );
+ assert.eq.automsg( "1", "t.find( { a:5, b:{$gte:5,$lte:5.5}, c:5 } ).sort( sort ).explain().nscanned" );
+ } else {
+ assert.eq.automsg( "1", "t.find( { a:5, b:{$gte:5.5,$lte:6}, c:5 } ).sort( sort ).explain().nscanned" );
+ assert.eq.automsg( "2", "t.find( { a:5, b:{$gte:5,$lte:5.5}, c:5 } ).sort( sort ).explain().nscanned" );
+ }
+assert.eq.automsg( "7", "t.find( { a:5, b:{$gte:5,$lte:7}, c:5 } ).sort( sort ).explain().nscanned" );
+assert.eq.automsg( "4", "t.find( { a:{$gte:5,$lte:6}, b:5, c:5 } ).sort( sort ).explain().nscanned" );
+ if ( s.a > 0 ) {
+ assert.eq.automsg( "2", "t.find( { a:{$gte:5.5,$lte:6}, b:5, c:5 } ).sort( sort ).explain().nscanned" );
+ assert.eq.automsg( "1", "t.find( { a:{$gte:5,$lte:5.5}, b:5, c:5 } ).sort( sort ).explain().nscanned" );
+ assert.eq.automsg( "3", "t.find( { a:{$gte:5.5,$lte:6}, b:5, c:{$gte:5,$lte:6} } ).sort( sort ).explain().nscanned" );
+ } else {
+ assert.eq.automsg( "1", "t.find( { a:{$gte:5.5,$lte:6}, b:5, c:5 } ).sort( sort ).explain().nscanned" );
+ assert.eq.automsg( "2", "t.find( { a:{$gte:5,$lte:5.5}, b:5, c:5 } ).sort( sort ).explain().nscanned" );
+ assert.eq.automsg( "2", "t.find( { a:{$gte:5.5,$lte:6}, b:5, c:{$gte:5,$lte:6} } ).sort( sort ).explain().nscanned" );
+ }
+assert.eq.automsg( "7", "t.find( { a:{$gte:5,$lte:7}, b:5, c:5 } ).sort( sort ).explain().nscanned" );
+assert.eq.automsg( "6", "t.find( { a:{$gte:5,$lte:6}, b:5, c:{$gte:5,$lte:6} } ).sort( sort ).explain().nscanned" );
+assert.eq.automsg( "6", "t.find( { a:5, b:{$gte:5,$lte:6}, c:{$gte:5,$lte:6} } ).sort( sort ).explain().nscanned" );
+assert.eq.automsg( "10", "t.find( { a:{$gte:5,$lte:6}, b:{$gte:5,$lte:6}, c:5 } ).sort( sort ).explain().nscanned" );
+assert.eq.automsg( "14", "t.find( { a:{$gte:5,$lte:6}, b:{$gte:5,$lte:6}, c:{$gte:5,$lte:6} } ).sort( sort ).explain().nscanned" );
}
for ( var a = -1; a <= 1; a += 2 ) {
View
16 jstests/indexi.js
@@ -0,0 +1,16 @@
+t = db.jstests_indexi;
+
+t.drop();
+
+for( var a = 0; a < 10; ++a ) {
+ for( var b = 0; b < 10; ++b ) {
+ for( var c = 0; c < 10; ++c ) {
+ t.save( {a:a,b:b,c:c} );
+ }
+ }
+}
+
+t.ensureIndex( {a:1,b:1,c:1} );
+t.ensureIndex( {a:1,c:1} );
+
+assert.automsg( "!t.find( {a:{$gt:1,$lt:10},c:{$gt:1,$lt:10}} ).explain().indexBounds.b" );
Please sign in to comment.
Something went wrong with that request. Please try again.