Permalink
Browse files

SERVER-4150 add covered index support

  • Loading branch information...
1 parent 7e8cf0c commit 2817787a28cf4fbe1c62a075e61f8b35d1654386 @astaple astaple committed Feb 20, 2012
View
@@ -0,0 +1,66 @@
+// Test use of covered indexes when there are multiple candidate indexes.
+
+t = db.jstests_coveredIndex5;
+t.drop();
+
+t.ensureIndex( { a:1, b:1 } );
+t.ensureIndex( { a:1, c:1 } );
+
+function checkFields( query, projection ) {
+ t.ensureIndex( { z:1 } ); // clear query patterns
+ t.dropIndex( { z:1 } );
+
+ results = t.find( query, projection ).toArray();
+
+ expectedFields = [];
+ for ( k in projection ) {
+ if ( k != '_id' ) {
+ expectedFields.push( k );
+ }
+ }
+
+ vals = [];
+ for ( i in results ) {
+ r = results[ i ];
+ assert.eq( 0, r.a );
+ assert.eq( expectedFields, Object.keySet( r ) );
+ for ( k in projection ) {
+ if ( k != '_id' && k != 'a' ) {
+ vals.push( r[ k ] );
+ }
+ }
+ }
+
+ if ( vals.length != 0 ) {
+ vals.sort();
+ assert.eq( [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], vals );
+ }
+}
+
+function checkCursorCovered( cursor, covered, count, query, projection ) {
+ checkFields( query, projection );
+ explain = t.find( query, projection ).explain( true );
+ assert.eq( cursor, explain.cursor );
+ assert.eq( covered, explain.indexOnly );
+ assert.eq( count, explain.n );
+}
+
+for( i = 0; i < 10; ++i ) {
+ t.save( { a:0, b:i, c:9-i } );
+}
+
+checkCursorCovered( 'BtreeCursor a_1_b_1', true, 10, { a:0 }, { _id:0, a:1 } );
+checkCursorCovered( 'BtreeCursor a_1_b_1', true, 10, { a:0, d:null }, { _id:0, a:1 } );
+checkCursorCovered( 'BtreeCursor a_1_b_1', true, 10, { a:0, d:null }, { _id:0, a:1, b:1 } );
+
+// Covered index on a,c not preferentially selected.
+checkCursorCovered( 'BtreeCursor a_1_b_1', false, 10, { a:0, d:null }, { _id:0, a:1, c:1 } );
+
+t.save( { a:0, c:[ 1, 2 ] } );
+t.save( { a:1 } );
+checkCursorCovered( 'BtreeCursor a_1_b_1', true, 11, { a:0, d:null }, { _id:0, a:1 } );
+
+t.save( { a:0, b:[ 1, 2 ] } );
+t.save( { a:1 } );
+checkCursorCovered( 'BtreeCursor a_1_b_1', false, 12, { a:0, d:null }, { _id:0, a:1 } );
+
View
@@ -31,15 +31,17 @@ namespace mongo {
_nscannedObjects(),
_nscanned(),
_scanAndOrder(),
+ _indexOnly(),
_nYields(),
_picked(),
_done() {
}
- void ExplainPlanInfo::notePlan( const Cursor &cursor, bool scanAndOrder ) {
+ void ExplainPlanInfo::notePlan( const Cursor &cursor, bool scanAndOrder, bool indexOnly ) {
_cursorName = const_cast<Cursor&>(cursor).toString();
_indexBounds = cursor.prettyIndexBounds();
_scanAndOrder = scanAndOrder;
+ _indexOnly = indexOnly;
noteCursorUpdate( cursor );
}
@@ -82,7 +84,7 @@ namespace mongo {
"nscannedObjects" << clauseInfo.nscannedObjects() <<
"nscanned" << clauseInfo.nscanned() <<
"scanAndOrder" << _scanAndOrder <<
- "indexOnly" << false << // TODO
+ "indexOnly" << _indexOnly <<
"nYields" << _nYields <<
"nChunkSkips" << clauseInfo.nChunkSkips() <<
"millis" << clauseInfo.millis() <<
View
@@ -39,7 +39,7 @@ namespace mongo {
public:
ExplainPlanInfo();
- void notePlan( const Cursor &cursor, bool scanAndOrder );
+ void notePlan( const Cursor &cursor, bool scanAndOrder, bool indexOnly );
void noteIterate( bool match, bool loadedObject, const Cursor &cursor );
void noteYield();
void noteDone( const Cursor &cursor );
@@ -62,6 +62,7 @@ namespace mongo {
long long _nscannedObjects;
long long _nscanned;
bool _scanAndOrder;
+ bool _indexOnly;
int _nYields;
BSONObj _indexBounds;
bool _picked;
@@ -117,8 +118,8 @@ namespace mongo {
public:
ExplainSinglePlanQueryInfo();
- void notePlan( const Cursor &cursor, bool scanAndOrder ) {
- _planInfo->notePlan( cursor, scanAndOrder );
+ void notePlan( const Cursor &cursor, bool scanAndOrder, bool indexOnly ) {
+ _planInfo->notePlan( cursor, scanAndOrder, indexOnly );
}
void noteIterate( bool match, bool loadedObject, bool chunkSkip, const Cursor &cursor ) {
_planInfo->noteIterate( match, loadedObject, cursor );
View
@@ -542,7 +542,7 @@ namespace mongo {
return shared_ptr<Cursor>( new BasicCursor( DiskLoc() ) );
}
FieldRangeSetPair frsp( ns, query );
- QueryPlan oplogPlan( d, -1, frsp, 0, query, order, false );
+ QueryPlan oplogPlan( d, -1, frsp, 0, query, shared_ptr<Projection>(), order, false );
FindingStartCursor finder( oplogPlan );
ElapsedTracker yieldCondition( 256, 20 );
while( !finder.done() ) {
View
@@ -682,8 +682,8 @@ namespace mongo {
_explainInfo( new ExplainSinglePlanQueryInfo() ) {
}
- void SimpleCursorExplainStrategy::notePlan( bool scanAndOrder ) {
- _explainInfo->notePlan( *_cursor, scanAndOrder );
+ void SimpleCursorExplainStrategy::notePlan( bool scanAndOrder, bool indexOnly ) {
+ _explainInfo->notePlan( *_cursor, scanAndOrder, indexOnly );
}
void SimpleCursorExplainStrategy::noteIterate( bool match, bool loadedObject, bool chunkSkip ) {
@@ -716,25 +716,45 @@ namespace mongo {
}
ResponseBuildStrategy::ResponseBuildStrategy( const ParsedQuery &parsedQuery,
- const shared_ptr<Cursor> &cursor, BufBuilder &buf ) :
+ const shared_ptr<Cursor> &cursor, BufBuilder &buf,
+ const QueryPlan::Summary &queryPlan ) :
_parsedQuery( parsedQuery ),
_cursor( cursor ),
- _buf( buf ) {
+ _queryOptimizerCursor( dynamic_pointer_cast<QueryOptimizerCursor>( _cursor ) ),
+ _buf( buf ),
+ _planKeyFieldsOnly( queryPlan._keyFieldsOnly ) {
}
- BSONObj ResponseBuildStrategy::current() const {
- if ( !_parsedQuery.returnKey() ) {
- return _cursor->current();
+ BSONObj ResponseBuildStrategy::current( bool allowCovered ) const {
+ if ( _parsedQuery.returnKey() ) {
+ BSONObjBuilder bob;
+ bob.appendKeys( _cursor->indexKeyPattern(), _cursor->currKey() );
+ return bob.obj();
+ }
+ if ( allowCovered ) {
+ const Projection::KeyOnly *fields = keyFieldsOnly();
+ if ( fields ) {
+ return fields->hydrate( _cursor->currKey() );
+ }
}
- BSONObjBuilder bob;
- bob.appendKeys( _cursor->indexKeyPattern(), _cursor->currKey() );
- return bob.obj();
+ return _cursor->current();
+ }
+
+ const Projection::KeyOnly *ResponseBuildStrategy::keyFieldsOnly() const {
+ if ( !_parsedQuery.getFields() ) {
+ return 0;
+ }
+ if ( _queryOptimizerCursor ) {
+ return _queryOptimizerCursor->keyFieldsOnly();
+ }
+ return _planKeyFieldsOnly.get();
}
OrderedBuildStrategy::OrderedBuildStrategy( const ParsedQuery &parsedQuery,
const shared_ptr<Cursor> &cursor,
- BufBuilder &buf ) :
- ResponseBuildStrategy( parsedQuery, cursor, buf ),
+ BufBuilder &buf,
+ const QueryPlan::Summary &queryPlan ) :
+ ResponseBuildStrategy( parsedQuery, cursor, buf, queryPlan ),
_skip( _parsedQuery.getSkip() ) {
}
@@ -750,7 +770,7 @@ namespace mongo {
if ( !_parsedQuery.isExplain() ) {
BSONObj js = _cursor->current();
assert( js.isValid() );
- fillQueryResultFromObj( _buf, _parsedQuery.getFields(), current(), ( _parsedQuery.showDiskLoc() ? &loc : 0 ) );
+ fillQueryResultFromObj( _buf, _parsedQuery.getFields(), current( true ), ( _parsedQuery.showDiskLoc() ? &loc : 0 ) );
}
return true;
}
@@ -759,7 +779,7 @@ namespace mongo {
const shared_ptr<Cursor> &cursor,
BufBuilder &buf,
const QueryPlan::Summary &queryPlan ) :
- ResponseBuildStrategy( parsedQuery, cursor, buf ),
+ ResponseBuildStrategy( parsedQuery, cursor, buf, queryPlan ),
_scanAndOrder( newScanAndOrder( queryPlan ) ) {
}
@@ -772,7 +792,7 @@ namespace mongo {
bool ReorderBuildStrategy::_handleMatchNoDedup() {
DiskLoc loc = _cursor->currLoc();
- _scanAndOrder->add( current(), _parsedQuery.showDiskLoc() ? &loc : 0 );
+ _scanAndOrder->add( current( false ), _parsedQuery.showDiskLoc() ? &loc : 0 );
return false;
}
@@ -803,9 +823,8 @@ namespace mongo {
HybridBuildStrategy::HybridBuildStrategy( const ParsedQuery &parsedQuery,
const shared_ptr<QueryOptimizerCursor> &cursor,
BufBuilder &buf ) :
- ResponseBuildStrategy( parsedQuery, cursor, buf ),
- _queryOptimizerCursor( cursor ),
- _orderedBuild( parsedQuery, cursor, buf ),
+ ResponseBuildStrategy( parsedQuery, cursor, buf, QueryPlan::Summary() ),
+ _orderedBuild( parsedQuery, cursor, buf, QueryPlan::Summary() ),
_reorderBuild( newReorderBuildStrategy() ) {
}
@@ -977,7 +996,8 @@ namespace mongo {
}
shared_ptr<ExplainRecordingStrategy> ret
( new SimpleCursorExplainStrategy( ancillaryInfo, _cursor ) );
- ret->notePlan( queryPlan.valid() ? queryPlan._scanAndOrderRequired : false );
+ ret->notePlan( queryPlan.valid() ? queryPlan._scanAndOrderRequired : false,
+ queryPlan._keyFieldsOnly );
return ret;
}
@@ -988,7 +1008,7 @@ namespace mongo {
if ( !_cursor->ok() || singleOrderedPlan ||
( _queryOptimizerCursor && !_queryOptimizerCursor->mayRunOutOfOrderPlans() ) ) {
return shared_ptr<ResponseBuildStrategy>
- ( new OrderedBuildStrategy( _parsedQuery, _cursor, _buf ) );
+ ( new OrderedBuildStrategy( _parsedQuery, _cursor, _buf, queryPlan ) );
}
if ( singlePlan || !_queryOptimizerCursor->mayRunInOrderPlans() ) {
return shared_ptr<ResponseBuildStrategy>
@@ -1132,6 +1152,7 @@ namespace mongo {
exhaust = ns;
curop.debug().exhaust = true;
}
+ ccPointer->fields = pq.getFieldPtr();
ccPointer.release();
}
@@ -1289,7 +1310,7 @@ namespace mongo {
// TODO clean this up a bit.
BSONObj oldPlan;
if ( explain && ! pq.hasIndexSpecifier() ) {
- MultiPlanScanner mps( ns, query, order );
+ MultiPlanScanner mps( ns, query, shared_ptr<Projection>(), order );
if ( mps.usingCachedPlan() ) {
oldPlan =
mps.oldExplain().firstElement().embeddedObject()
@@ -1310,11 +1331,11 @@ namespace mongo {
{
BSONObj oldPlan;
if ( explain && ! pq.hasIndexSpecifier() ) {
- MultiPlanScanner mps( ns, query, order );
+ MultiPlanScanner mps( ns, query, shared_ptr<Projection>(), order );
if ( mps.usingCachedPlan() )
oldPlan = mps.oldExplain();
}
- auto_ptr< MultiPlanScanner > mps( new MultiPlanScanner( ns, query, order, hint, explain ? QueryPlanSet::Ignore : QueryPlanSet::Use, pq.getMin(), pq.getMax(), true ) );
+ auto_ptr< MultiPlanScanner > mps( new MultiPlanScanner( ns, query, shared_ptr<Projection>(), order, hint, explain ? QueryPlanSet::Ignore : QueryPlanSet::Use, pq.getMin(), pq.getMax(), true ) );
BSONObj explainSuffix;
if ( explain ) {
BSONObjBuilder bb;
View
@@ -45,7 +45,7 @@ namespace mongo {
public:
ExplainRecordingStrategy( const ExplainQueryInfo::AncillaryInfo &ancillaryInfo );
virtual ~ExplainRecordingStrategy() {}
- virtual void notePlan( bool scanAndOrder ) {}
+ virtual void notePlan( bool scanAndOrder, bool indexOnly ) {}
virtual void noteIterate( bool match, bool loadedObject, bool chunkSkip ) {}
virtual void noteYield() {}
shared_ptr<ExplainQueryInfo> doneQueryInfo();
@@ -67,7 +67,7 @@ namespace mongo {
SimpleCursorExplainStrategy( const ExplainQueryInfo::AncillaryInfo &ancillaryInfo,
const shared_ptr<Cursor> &cursor );
private:
- virtual void notePlan( bool scanAndOrder );
+ virtual void notePlan( bool scanAndOrder, bool indexOnly );
virtual void noteIterate( bool match, bool loadedObject, bool chunkSkip );
virtual void noteYield();
virtual shared_ptr<ExplainQueryInfo> _doneQueryInfo();
@@ -88,21 +88,25 @@ namespace mongo {
class ResponseBuildStrategy {
public:
ResponseBuildStrategy( const ParsedQuery &parsedQuery, const shared_ptr<Cursor> &cursor,
- BufBuilder &buf );
+ BufBuilder &buf, const QueryPlan::Summary &queryPlan );
virtual ~ResponseBuildStrategy() {}
virtual bool handleMatch() = 0;
virtual int rewriteMatches() { return -1; }
protected:
- BSONObj current() const;
+ BSONObj current( bool allowCovered ) const;
const ParsedQuery &_parsedQuery;
shared_ptr<Cursor> _cursor;
+ shared_ptr<QueryOptimizerCursor> _queryOptimizerCursor;
BufBuilder &_buf;
+ private:
+ const Projection::KeyOnly *keyFieldsOnly() const;
+ shared_ptr<Projection::KeyOnly> _planKeyFieldsOnly;
};
class OrderedBuildStrategy : public ResponseBuildStrategy {
public:
OrderedBuildStrategy( const ParsedQuery &parsedQuery, const shared_ptr<Cursor> &cursor,
- BufBuilder &buf );
+ BufBuilder &buf, const QueryPlan::Summary &queryPlan );
virtual bool handleMatch();
private:
long long _skip;
@@ -137,7 +141,6 @@ namespace mongo {
void handleReorderMatch();
bool handleOrderedMatch();
ReorderBuildStrategy *newReorderBuildStrategy() const;
- shared_ptr<QueryOptimizerCursor> _queryOptimizerCursor;
SmallDupSet _scanAndOrderDups;
OrderedBuildStrategy _orderedBuild;
shared_ptr<ReorderBuildStrategy> _reorderBuild;
Oops, something went wrong.

0 comments on commit 2817787

Please sign in to comment.