summaryrefslogtreecommitdiff
path: root/jstests/finda.js
blob: c17bcc3c6962dd6ffc50ade658778f409bca5cba (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
// 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 );