mirror of https://github.com/mongodb/mongo
99 lines
3.2 KiB
JavaScript
99 lines
3.2 KiB
JavaScript
// Tests where the QueryOptimizerCursor enters takeover mode during a query rather than a get more.
|
|
|
|
t = db.jstests_finda;
|
|
t.drop();
|
|
|
|
numDocs = 200;
|
|
|
|
function clearQueryPlanCache() {
|
|
t.ensureIndex( { c:1 } );
|
|
t.dropIndex( { c:1 } );
|
|
}
|
|
|
|
function assertAllFound( matches ) {
|
|
// printjson( matches );
|
|
found = new Array( numDocs );
|
|
for( i = 0; i < numDocs; ++i ) {
|
|
found[ i ] = false;
|
|
}
|
|
for( i in matches ) {
|
|
m = matches[ i ];
|
|
found[ m._id ] = true;
|
|
}
|
|
for( i = 0; i < numDocs; ++i ) {
|
|
assert( found[ i ], i );
|
|
}
|
|
}
|
|
|
|
function makeCursor( query, projection, sort, batchSize, returnKey ) {
|
|
cursor = t.find( query, projection );
|
|
if ( sort ) {
|
|
cursor.sort( sort );
|
|
}
|
|
if ( batchSize ) {
|
|
cursor.batchSize( batchSize );
|
|
}
|
|
if ( returnKey ) {
|
|
cursor._addSpecial( "$returnKey", true );
|
|
}
|
|
return cursor;
|
|
}
|
|
|
|
function checkCursorWithBatchSizeProjection( query, projection, sort, batchSize,
|
|
expectedLeftInBatch ) {
|
|
clearQueryPlanCache();
|
|
cursor = makeCursor( query, projection, sort, batchSize );
|
|
assert.eq( expectedLeftInBatch, cursor.objsLeftInBatch() );
|
|
assertAllFound( cursor.toArray() );
|
|
}
|
|
|
|
function checkCursorWithBatchSize( query, sort, batchSize, expectedLeftInBatch ) {
|
|
checkCursorWithBatchSizeProjection( query, {}, sort, batchSize, expectedLeftInBatch );
|
|
checkCursorWithBatchSizeProjection( query, { a:1, _id:1 }, sort, batchSize,
|
|
expectedLeftInBatch );
|
|
// In the cases tested, when expectedLeftInBatch is high enough takeover will occur during
|
|
// the query operation rather than getMore and the last few matches should properly return keys
|
|
// from the a,_id index.
|
|
clearQueryPlanCache();
|
|
if ( expectedLeftInBatch > 110 ) {
|
|
cursor = makeCursor( query, {}, sort, batchSize, true );
|
|
lastNonAIndexResult = -1;
|
|
for( i = 0; i < expectedLeftInBatch; ++i ) {
|
|
next = cursor.next();
|
|
// Identify the query plan used by checking the fields of a returnKey query.
|
|
if ( !friendlyEqual( [ 'a', '_id' ], Object.keySet( next ) ) ) {
|
|
lastNonAIndexResult = i;
|
|
}
|
|
}
|
|
// The last results should come from the a,_id index.
|
|
assert.lt( lastNonAIndexResult, expectedLeftInBatch - 5 );
|
|
}
|
|
}
|
|
|
|
function queryWithPlanTypes( withDups ) {
|
|
t.drop();
|
|
for( i = 1; i < numDocs; ++i ) {
|
|
t.save( { _id:i, a:i, b:0 } );
|
|
}
|
|
if ( withDups ) {
|
|
t.save( { _id:0, a:[ 0, numDocs ], b:0 } ); // Add a dup on a:1 index.
|
|
}
|
|
else {
|
|
t.save( { _id:0, a:0, b:0 } );
|
|
}
|
|
t.ensureIndex( { a:1, _id:1 } ); // Include _id for a covered index projection.
|
|
|
|
// All plans in order.
|
|
checkCursorWithBatchSize( { a:{ $gte:0 } }, null, 150, 150 );
|
|
|
|
// All plans out of order.
|
|
checkCursorWithBatchSize( { a:{ $gte:0 } }, { c:1 }, null, 200 );
|
|
|
|
// Some plans in order, some out of order.
|
|
checkCursorWithBatchSize( { a:{ $gte:0 }, b:0 }, { a:1 }, 150, 150 );
|
|
checkCursorWithBatchSize( { a:{ $gte:0 }, b:0 }, { a:1 }, null, 101 );
|
|
}
|
|
|
|
queryWithPlanTypes( false );
|
|
queryWithPlanTypes( true );
|