diff options
author | Aaron <aaron@10gen.com> | 2012-09-24 10:57:29 -0700 |
---|---|---|
committer | Aaron <aaron@10gen.com> | 2012-09-27 19:44:44 -0700 |
commit | 6e555988bff53d807635ef59796c45c824b770ba (patch) | |
tree | c924595a2914407d6e1b8ffb250297d70cae176e /jstests/explainc.js | |
parent | 3f02a780112135da725070a8c43b2d8195a9f434 (diff) | |
download | mongo-6e555988bff53d807635ef59796c45c824b770ba.tar.gz |
SERVER-4161 Report nscannedObjects properly when covered indexes are used.
Diffstat (limited to 'jstests/explainc.js')
-rw-r--r-- | jstests/explainc.js | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/jstests/explainc.js b/jstests/explainc.js new file mode 100644 index 00000000000..203e9ed72eb --- /dev/null +++ b/jstests/explainc.js @@ -0,0 +1,195 @@ +// Test cases for explain()'s nscannedObjects. SERVER-4161 + +t = db.jstests_explainc; +t.drop(); + +t.save( { a:1 } ); +t.ensureIndex( { a:1 } ); + +function assertExplain( expected, explain, checkAllPlans ) { + for( field in expected ) { + assert.eq( expected[ field ], explain[ field ], field ); + } + if ( checkAllPlans && explain.allPlans && explain.allPlans.length == 1 ) { + for( field in { n:1, nscanned:1, nscannedObjects:1 } ) { + assert.eq( expected[ field ], explain.allPlans[ 0 ][ field ], field ); + } + } + return explain; +} + +function assertHintedExplain( expected, cursor ) { + return assertExplain( expected, cursor.hint( { a:1 } ).explain( true ), true ); +} + +function assertUnhintedExplain( expected, cursor, checkAllPlans ) { + return assertExplain( expected, cursor.explain( true ), checkAllPlans ); +} + +// Standard query. +assertHintedExplain( { n:1, nscanned:1, nscannedObjects:1 }, + t.find( { a:1 } ) ); + +// Covered index query. +assertHintedExplain( { n:1, nscanned:1, nscannedObjects:0 /* no object loaded */ }, + t.find( { a:1 }, { _id:0, a:1 } ) ); + +// Covered index query, but matching requires loading document. +assertHintedExplain( { n:1, nscanned:1, nscannedObjects:1 }, + t.find( { a:1, b:null }, { _id:0, a:1 } ) ); + +// $returnKey query. +assertHintedExplain( { n:1, nscanned:1, nscannedObjects:0 }, + t.find( { a:1 } )._addSpecial( "$returnKey", true ) ); + +// $returnKey query but matching requires loading document. +assertHintedExplain( { n:1, nscanned:1, nscannedObjects:1 }, + t.find( { a:1, b:null } )._addSpecial( "$returnKey", true ) ); + +// Skip a result. +assertHintedExplain( { n:0, nscanned:1, nscannedObjects:0 }, + t.find( { a:1 } ).skip( 1 ) ); + +// Cursor sorted covered index query. +assertHintedExplain( { n:1, nscanned:1, nscannedObjects:0, scanAndOrder:false }, + t.find( { a:1 }, { _id:0, a:1 } ).sort( { a:1 } ) ); + +t.dropIndex( { a:1 } ); +t.ensureIndex( { a:1, b:1 } ); + +// In memory sort covered index query. +assertUnhintedExplain( { n:1, nscanned:1, nscannedObjects:1, scanAndOrder:true }, + t.find( { a:{ $gt:0 } }, { _id:0, a:1 } ).sort( { b:1 } ) + .hint( { a:1, b:1 } ) ); + +// In memory sort $returnKey query. +assertUnhintedExplain( { n:1, nscanned:1, nscannedObjects:0, scanAndOrder:true }, + t.find( { a:{ $gt:0 } } )._addSpecial( "$returnKey", true ).sort( { b:1 } ) + .hint( { a:1, b:1 } ) ); + +// In memory sort with skip. +assertUnhintedExplain( { n:0, nscanned:1, nscannedObjects:1 /* The record is still loaded. */ }, + t.find( { a:{ $gt:0 } } ).sort( { b:1 } ).skip( 1 ).hint( { a:1, b:1 } ), + false ); + +// With a multikey index. +t.drop(); +t.ensureIndex( { a:1 } ); +t.save( { a:[ 1, 2 ] } ); + +// A multikey index with duplicate keys matched. +assertHintedExplain( { n:1, nscanned:2, nscannedObjects:2, scanAndOrder:false }, + t.find( { a:{ $gt:0 } }, { _id:0, a:1 } ) ); + +// A multikey index with duplicate keys matched, and an in memory sort. +assertHintedExplain( { n:1, nscanned:2, nscannedObjects:2, scanAndOrder:true }, + t.find( { a:{ $gt:0 } }, { _id:0, a:1 } ).sort( { b:1 } ) ); + +// Dedup matches from multiple query plans. +t.drop(); +t.ensureIndex( { a:1, b:1 } ); +t.ensureIndex( { b:1, a:1 } ); +t.save( { a:1, b:1 } ); + +// Document matched by three query plans. +assertUnhintedExplain( { n:1, nscanned:1, nscannedObjects:1, + nscannedObjectsAllPlans:2 /* Result is not loaded if a dup. */ }, + t.find( { a:{ $gt:0 }, b:{ $gt:0 } } ) ); + +// Document matched by three query plans, with sorting. +assertUnhintedExplain( { n:1, nscanned:1, nscannedObjects:1, nscannedObjectsAllPlans:2 }, + t.find( { a:{ $gt:0 }, b:{ $gt:0 } } ).sort( { c:1 } ) ); + +// Document matched by three query plans, with a skip. +assertUnhintedExplain( { n:0, nscanned:1, nscannedObjects:1, nscannedObjectsAllPlans:1 }, + t.find( { a:{ $gt:0 }, b:{ $gt:0 } } ).skip( 1 ) ); + +// Hybrid ordered and unordered plans. + +t.drop(); +t.ensureIndex( { a:1, b:1 } ); +t.ensureIndex( { b:1 } ); +for( i = 0; i < 30; ++i ) { + t.save( { a:i, b:i } ); +} + +// Ordered plan chosen. +assertUnhintedExplain( { cursor:'BtreeCursor a_1_b_1', n:30, nscanned:30, nscannedObjects:30, + scanAndOrder:false }, + t.find( { b:{ $gte:0 } } ).sort( { a:1 } ) ); + +// Ordered plan chosen with a covered index. +assertUnhintedExplain( { cursor:'BtreeCursor a_1_b_1', n:30, nscanned:30, nscannedObjects:0, + scanAndOrder:false }, + t.find( { b:{ $gte:0 } }, { _id:0, b:1 } ).sort( { a:1 } ) ); + +// Ordered plan chosen, with a skip. Skip is not included in counting nscannedObjects for a single +// plan. +assertUnhintedExplain( { cursor:'BtreeCursor a_1_b_1', n:29, nscanned:30, nscannedObjects:30, + nscannedObjectsAllPlans:89, scanAndOrder:false }, + t.find( { b:{ $gte:0 } } ).sort( { a:1 } ).skip( 1 ) ); + +// Unordered plan chosen. +assertUnhintedExplain( { cursor:'BtreeCursor b_1', n:1, nscanned:1, nscannedObjects:1, + nscannedObjectsAllPlans:2, scanAndOrder:true }, + t.find( { b:1 } ).sort( { a:1 } ) ); + +// Unordered plan chosen and projected. +assertUnhintedExplain( { cursor:'BtreeCursor b_1', n:1, nscanned:1, nscannedObjects:1, + nscannedObjectsAllPlans:2, scanAndOrder:true }, + t.find( { b:1 }, { _id:0, b:1 } ).sort( { a:1 } ) ); + +// Unordered plan chosen, with a skip. +assertUnhintedExplain( { cursor:'BtreeCursor b_1', n:0, nscanned:1, nscannedObjects:1, + nscannedObjectsAllPlans:2, scanAndOrder:true }, + t.find( { b:1 }, { _id:0, b:1 } ).sort( { a:1 } ).skip( 1 ) ); + +// Ordered plan chosen, $returnKey specified. +assertUnhintedExplain( { cursor:'BtreeCursor a_1_b_1', n:30, nscanned:30, nscannedObjects:0, + scanAndOrder:false }, + t.find( { b:{ $gte:0 } }, { _id:0, b:1 } ).sort( { a:1 } ) + ._addSpecial( "$returnKey", true ) ); + +// Ordered plan chosen, $returnKey specified, matching requires loading document. +assertUnhintedExplain( { cursor:'BtreeCursor a_1_b_1', n:30, nscanned:30, nscannedObjects:30, + scanAndOrder:false }, + t.find( { b:{ $gte:0 }, c:null }, { _id:0, b:1 } ).sort( { a:1 } ) + ._addSpecial( "$returnKey", true ) ); + +// Unordered plan chosen, $returnKey specified. +assertUnhintedExplain( { cursor:'BtreeCursor b_1', n:1, nscanned:1, nscannedObjects:0, + nscannedObjectsAllPlans:1, scanAndOrder:true }, + t.find( { b:1 }, { _id:0, b:1 } ).sort( { a:1 } ) + ._addSpecial( "$returnKey", true ) ); + +// Unordered plan chosen, $returnKey specified, matching requires loading document. +assertUnhintedExplain( { cursor:'BtreeCursor b_1', n:1, nscanned:1, nscannedObjects:1, + nscannedObjectsAllPlans:2, scanAndOrder:true }, + t.find( { b:1, c:null }, { _id:0, b:1 } ).sort( { a:1 } ) + ._addSpecial( "$returnKey", true ) ); + +t.ensureIndex( { a:1, b:1, c:1 } ); + +// Documents matched by four query plans. +assertUnhintedExplain( { n:30, nscanned:30, nscannedObjects:30, + nscannedObjectsAllPlans:90 // Not 120 because deduping occurs before + // loading results. + }, + t.find( { a:{ $gte:0 }, b:{ $gte:0 } } ).sort( { b:1 } ) ); + +for( i = 30; i < 150; ++i ) { + t.save( { a:i, b:i } ); +} + +// The matches in the second $or clause are loaded to dedup against the first clause. +explain = assertUnhintedExplain( { n:150, nscannedObjects:150, nscannedObjectsAllPlans:150 }, + t.find( { $or:[ { a:{ $gte:-1, $lte:200 }, + b:{ $gte:0, $lte:201 } }, + { a:{ $gte:0, $lte:201 }, + b:{ $gte:-1, $lte:200 } } ] }, + { _id:0, a:1, b:1 } ).hint( { a:1, b:1 } ) ); +// Check nscannedObjects for each clause. +assert.eq( 0, explain.clauses[ 0 ].nscannedObjects ); +assert.eq( 0, explain.clauses[ 0 ].nscannedObjectsAllPlans ); +assert.eq( 150, explain.clauses[ 1 ].nscannedObjects ); +assert.eq( 150, explain.clauses[ 1 ].nscannedObjectsAllPlans ); |