Permalink
Browse files

SERVER-4150 Separate match reporting from ordered match reporting for…

… proper plan specific output when a single out of order plan runs; fix incorrect explain4 test.
  • Loading branch information...
1 parent f427d5c commit 88ea9f1fac06685d59a61a7f1d445b396f02441e @astaple astaple committed Mar 14, 2012
Showing with 81 additions and 69 deletions.
  1. +20 −20 jstests/explain4.js
  2. +1 −1 jstests/explain6.js
  3. +38 −31 src/mongo/db/ops/query.cpp
  4. +22 −17 src/mongo/db/ops/query.h
View
@@ -1,43 +1,43 @@
-// Basic validation of explain output fields
+// Basic validation of explain output fields.
t = db.jstests_explain4;
t.drop();
-function checkField( name, value ) {
+function checkField( explain, name, value ) {
assert( explain.hasOwnProperty( name ) );
if ( value != null ) {
assert.eq( value, explain[ name ], name );
}
}
function checkPlanFields( explain, matches, n ) {
- checkField( "cursor", "BasicCursor" );
- checkField( "n", n );
- checkField( "nscannedObjects", matches );
- checkField( "nscanned", matches );
- checkField( "indexBounds", {} );
+ checkField( explain, "cursor", "BasicCursor" );
+ checkField( explain, "n", n );
+ checkField( explain, "nscannedObjects", matches );
+ checkField( explain, "nscanned", matches );
+ checkField( explain, "indexBounds", {} );
}
function checkFields( matches, sort, limit ) {
- it = t.find();
+ cursor = t.find();
if ( sort ) {
- it.sort({a:1});
+ cursor.sort({a:1});
}
if ( limit ) {
- it.limit( limit );
+ cursor.limit( limit );
}
- explain = it.explain( true );
+ explain = cursor.explain( true );
// printjson( explain );
checkPlanFields( explain, matches, matches > 0 ? 1 : 0 );
- checkField( "scanAndOrder", sort );
- checkField( "millis" );
- checkField( "nYields" );
- checkField( "nChunkSkips", 0 );
- checkField( "isMultiKey", false );
- checkField( "indexOnly", false );
- checkField( "server" );
- checkField( "allPlans" );
- explain.allPlans.forEach( function( x ) { checkPlanFields( x, matches ); } );
+ checkField( explain, "scanAndOrder", sort );
+ checkField( explain, "millis" );
+ checkField( explain, "nYields" );
+ checkField( explain, "nChunkSkips", 0 );
+ checkField( explain, "isMultiKey", false );
+ checkField( explain, "indexOnly", false );
+ checkField( explain, "server" );
+ checkField( explain, "allPlans" );
+ explain.allPlans.forEach( function( x ) { checkPlanFields( x, matches, matches ); } );
}
checkFields( 0, false );
View
@@ -20,4 +20,4 @@ t.dropIndexes();
explain = t.find().skip( 1 ).sort({a:1}).explain( true );
// Skip is applied for an in memory sort.
assert.eq( 0, explain.n );
-assert.eq( 0, explain.allPlans[ 0 ].n );
+assert.eq( 1, explain.allPlans[ 0 ].n );
View
@@ -234,13 +234,14 @@ namespace mongo {
MatchCountingExplainStrategy::MatchCountingExplainStrategy
( const ExplainQueryInfo::AncillaryInfo &ancillaryInfo ) :
ExplainRecordingStrategy( ancillaryInfo ),
- _matches() {
+ _orderedMatches() {
}
- void MatchCountingExplainStrategy::noteIterate( bool match, bool loadedObject, bool chunkSkip ) {
- _noteIterate( match, loadedObject, chunkSkip );
- if ( match ) {
- ++_matches;
+ void MatchCountingExplainStrategy::noteIterate( bool match, bool orderedMatch,
+ bool loadedObject, bool chunkSkip ) {
+ _noteIterate( match, orderedMatch, loadedObject, chunkSkip );
+ if ( orderedMatch ) {
+ ++_orderedMatches;
}
}
@@ -256,7 +257,8 @@ namespace mongo {
_explainInfo->notePlan( *_cursor, scanAndOrder, indexOnly );
}
- void SimpleCursorExplainStrategy::_noteIterate( bool match, bool loadedObject, bool chunkSkip ) {
+ void SimpleCursorExplainStrategy::_noteIterate( bool match, bool orderedMatch,
+ bool loadedObject, bool chunkSkip ) {
_explainInfo->noteIterate( match, loadedObject, chunkSkip, *_cursor );
}
@@ -276,9 +278,11 @@ namespace mongo {
_cursor( cursor ) {
}
- void QueryOptimizerCursorExplainStrategy::_noteIterate( bool match, bool loadedObject,
- bool chunkSkip ) {
- _cursor->noteIterate( match, loadedObject, chunkSkip );
+ void QueryOptimizerCursorExplainStrategy::_noteIterate( bool match, bool orderedMatch,
+ bool loadedObject, bool chunkSkip ) {
+ // Note ordered matches only; if an unordered plan is selected, the explain result will
+ // be updated with reviseN().
+ _cursor->noteIterate( orderedMatch, loadedObject, chunkSkip );
}
shared_ptr<ExplainQueryInfo> QueryOptimizerCursorExplainStrategy::_doneQueryInfo() {
@@ -336,22 +340,22 @@ namespace mongo {
_bufferedMatches() {
}
- bool OrderedBuildStrategy::handleMatch() {
+ bool OrderedBuildStrategy::handleMatch( bool &orderedMatch ) {
DiskLoc loc = _cursor->currLoc();
if ( _cursor->getsetdup( loc ) ) {
- return false;
+ return orderedMatch = false;
}
if ( _skip > 0 ) {
--_skip;
- return false;
+ return orderedMatch = false;
}
// Explain does not obey soft limits, so matches should not be buffered.
if ( !_parsedQuery.isExplain() ) {
fillQueryResultFromObj( _buf, _parsedQuery.getFields(), current( true ),
( _parsedQuery.showDiskLoc() ? &loc : 0 ) );
++_bufferedMatches;
}
- return true;
+ return orderedMatch = true;
}
ReorderBuildStrategy::ReorderBuildStrategy( const ParsedQuery &parsedQuery,
@@ -363,17 +367,18 @@ namespace mongo {
_bufferedMatches() {
}
- bool ReorderBuildStrategy::handleMatch() {
+ bool ReorderBuildStrategy::handleMatch( bool &orderedMatch ) {
+ orderedMatch = false;
if ( _cursor->getsetdup( _cursor->currLoc() ) ) {
return false;
}
- return _handleMatchNoDedup();
+ _handleMatchNoDedup();
+ return true;
}
- bool ReorderBuildStrategy::_handleMatchNoDedup() {
+ void ReorderBuildStrategy::_handleMatchNoDedup() {
DiskLoc loc = _cursor->currLoc();
_scanAndOrder->add( current( false ), _parsedQuery.showDiskLoc() ? &loc : 0 );
- return false;
}
int ReorderBuildStrategy::rewriteMatches() {
@@ -412,18 +417,18 @@ namespace mongo {
_reorderedMatches() {
}
- bool HybridBuildStrategy::handleMatch() {
+ bool HybridBuildStrategy::handleMatch( bool &orderedMatch ) {
if ( !_queryOptimizerCursor->currentPlanScanAndOrderRequired() ) {
- return _orderedBuild.handleMatch();
+ return _orderedBuild.handleMatch( orderedMatch );
}
- handleReorderMatch();
- return false;
+ orderedMatch = false;
+ return handleReorderMatch();
}
- void HybridBuildStrategy::handleReorderMatch() {
+ bool HybridBuildStrategy::handleReorderMatch() {
DiskLoc loc = _cursor->currLoc();
if ( _scanAndOrderDups.getsetdup( loc ) ) {
- return;
+ return false;
}
try {
_reorderBuild._handleMatchNoDedup();
@@ -435,11 +440,12 @@ namespace mongo {
}
else if ( _queryOptimizerCursor->runningInitialInOrderPlan() ) {
_queryOptimizerCursor->abortOutOfOrderPlans();
- return;
+ return true;
}
}
throw;
- }
+ }
+ return true;
}
int HybridBuildStrategy::rewriteMatches() {
@@ -482,9 +488,10 @@ namespace mongo {
if ( !chunkMatches() ) {
return false;
}
- bool orderedMatch = _builder->handleMatch();
- _explain->noteIterate( orderedMatch, true, false );
- return true;
+ bool orderedMatch = false;
+ bool match = _builder->handleMatch( orderedMatch );
+ _explain->noteIterate( match, orderedMatch, true, false );
+ return match;
}
void QueryResponseBuilder::noteYield() {
@@ -497,7 +504,7 @@ namespace mongo {
bool QueryResponseBuilder::enoughTotalResults() const {
if ( _parsedQuery.isExplain() ) {
- return _parsedQuery.enoughForExplain( _explain->matches() );
+ return _parsedQuery.enoughForExplain( _explain->orderedMatches() );
}
return ( _parsedQuery.enough( _builder->bufferedMatches() ) ||
_buf.len() >= MaxBytesToReturnToClientAtOnce );
@@ -584,7 +591,7 @@ namespace mongo {
if ( _cursor->currentMatches( &details ) ) {
return true;
}
- _explain->noteIterate( false, details._loadedObject, false );
+ _explain->noteIterate( false, false, details._loadedObject, false );
return false;
}
@@ -596,7 +603,7 @@ namespace mongo {
if ( _chunkManager->belongsToMe( _cursor->current() ) ) {
return true;
}
- _explain->noteIterate( false, true, true );
+ _explain->noteIterate( false, false, true, true );
return false;
}
View
@@ -54,11 +54,12 @@ namespace mongo {
/** Note information about a single query plan. */
virtual void notePlan( bool scanAndOrder, bool indexOnly ) {}
/** Note an iteration of the query. */
- virtual void noteIterate( bool match, bool loadedObject, bool chunkSkip ) {}
+ virtual void noteIterate( bool match, bool orderedMatch, bool loadedObject,
+ bool chunkSkip ) {}
/** Note that the query yielded. */
virtual void noteYield() {}
- /** @return number of matches noted. */
- virtual long long matches() const { return 0; }
+ /** @return number of ordered matches noted. */
+ virtual long long orderedMatches() const { return 0; }
/** @return ExplainQueryInfo for a complete query. */
shared_ptr<ExplainQueryInfo> doneQueryInfo();
protected:
@@ -81,11 +82,13 @@ namespace mongo {
public:
MatchCountingExplainStrategy( const ExplainQueryInfo::AncillaryInfo &ancillaryInfo );
protected:
- virtual void _noteIterate( bool match, bool loadedObject, bool chunkSkip ) = 0;
+ virtual void _noteIterate( bool match, bool orderedMatch, bool loadedObject,
+ bool chunkSkip ) = 0;
private:
- virtual void noteIterate( bool match, bool loadedObject, bool chunkSkip );
- virtual long long matches() const { return _matches; }
- long long _matches;
+ virtual void noteIterate( bool match, bool orderedMatch, bool loadedObject,
+ bool chunkSkip );
+ virtual long long orderedMatches() const { return _orderedMatches; }
+ long long _orderedMatches;
};
/** Record explain events for a simple cursor representing a single clause and plan. */
@@ -95,7 +98,8 @@ namespace mongo {
const shared_ptr<Cursor> &cursor );
private:
virtual void notePlan( bool scanAndOrder, bool indexOnly );
- virtual void _noteIterate( bool match, bool loadedObject, bool chunkSkip );
+ virtual void _noteIterate( bool match, bool orderedMatch, bool loadedObject,
+ bool chunkSkip );
virtual void noteYield();
virtual shared_ptr<ExplainQueryInfo> _doneQueryInfo();
shared_ptr<Cursor> _cursor;
@@ -111,7 +115,8 @@ namespace mongo {
QueryOptimizerCursorExplainStrategy( const ExplainQueryInfo::AncillaryInfo &ancillaryInfo,
const shared_ptr<QueryOptimizerCursor> &cursor );
private:
- virtual void _noteIterate( bool match, bool loadedObject, bool chunkSkip );
+ virtual void _noteIterate( bool match, bool orderedMatch, bool loadedObject,
+ bool chunkSkip );
virtual shared_ptr<ExplainQueryInfo> _doneQueryInfo();
shared_ptr<QueryOptimizerCursor> _cursor;
};
@@ -128,9 +133,10 @@ namespace mongo {
virtual ~ResponseBuildStrategy() {}
/**
* Handle the current iterate of the supplied cursor as a (possibly duplicate) match.
- * @return true if an ordered match is found.
+ * @return true if a match is found.
+ * @param orderedMatch set if it is an ordered match.
*/
- virtual bool handleMatch() = 0;
+ virtual bool handleMatch( bool &orderedMatch ) = 0;
/**
* Write all matches into the buffer, overwriting existing data.
* @return number of matches written, or -1 if no op.
@@ -165,7 +171,7 @@ namespace mongo {
public:
OrderedBuildStrategy( const ParsedQuery &parsedQuery, const shared_ptr<Cursor> &cursor,
BufBuilder &buf, const QueryPlan::Summary &queryPlan );
- virtual bool handleMatch();
+ virtual bool handleMatch( bool &orderedMatch );
virtual int bufferedMatches() const { return _bufferedMatches; }
private:
int _skip;
@@ -181,9 +187,9 @@ namespace mongo {
const shared_ptr<Cursor> &cursor,
BufBuilder &buf,
const QueryPlan::Summary &queryPlan );
- virtual bool handleMatch();
+ virtual bool handleMatch( bool &orderedMatch );
/** Handle a match without performing deduping. */
- bool _handleMatchNoDedup();
+ void _handleMatchNoDedup();
virtual int rewriteMatches();
virtual int bufferedMatches() const { return _bufferedMatches; }
private:
@@ -202,12 +208,11 @@ namespace mongo {
const shared_ptr<QueryOptimizerCursor> &cursor,
BufBuilder &buf );
private:
- virtual bool handleMatch();
+ virtual bool handleMatch( bool &orderedMatch );
virtual int rewriteMatches();
virtual int bufferedMatches() const;
virtual void finishedFirstBatch();
- void handleReorderMatch();
- bool handleOrderedMatch();
+ bool handleReorderMatch();
DiskLocDupSet _scanAndOrderDups;
OrderedBuildStrategy _orderedBuild;
ReorderBuildStrategy _reorderBuild;

0 comments on commit 88ea9f1

Please sign in to comment.