mongo-debian/jstests/explainc.js
2013-03-23 13:40:41 +01:00

195 lines
8.7 KiB
JavaScript

// 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 );