summaryrefslogtreecommitdiff
path: root/jstests/noPassthroughWithMongod/plan_cache_not_in_regex.js
blob: 52829d7471788d99b00208b38e988bbc29c0a799 (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
/**
 * Tests that a $not-$in-$regex query, which cannot be supported by an index, cannot incorrectly
 * hijack the cached plan for an earlier $not-$in query.
 */
(function() {
    "use strict";

    load('jstests/libs/analyze_plan.js');  // For isCollScan.

    const coll = db.plan_cache_not_in_regex;
    coll.drop();

    // Helper function which obtains the cached plan, if any, for a given query shape.
    function getPlanForCacheEntry(query, proj, sort) {
        const key = {query: query, sort: sort, projection: proj};
        const cursor = coll.aggregate([
            {$planCacheStats: {}},
            {
              $match: {
                  "createdFromQuery.query": query,
                  "createdFromQuery.projection": proj,
                  "createdFromQuery.sort": sort
              }
            }
        ]);
        const entryStats = cursor.toArray();
        assert.eq(entryStats.length, 1, `Expected one cached plan, found: ${tojson(entryStats)}`);
        return entryStats.shift();
    }

    // Insert a document containing a field 'a', and create two indexes that can support queries on
    // this field. This is to ensure that the plan we choose will be cached, since if only a single
    // index is available, the solution will not be cached.
    assert.commandWorked(coll.insert({a: "foo"}));
    assert.commandWorked(coll.createIndex({a: 1}));
    assert.commandWorked(coll.createIndex({a: 1, b: 1}));

    // Repeat the test for query, query with projection, and query with projection and sort.
    for (let [proj, sort] of[[{}, {}], [{_id: 0, a: 1}, {}], [{_id: 0, a: 1}, {a: 1}]]) {
        // Perform a plain $not-$in query on 'a' and confirm that the plan is cached.
        const queryShape = {a: {$not: {$in: [32, 33]}}};
        assert.eq(1, coll.find(queryShape, proj).sort(sort).itcount());
        let cacheEntry = getPlanForCacheEntry(queryShape, proj, sort);
        assert(cacheEntry);

        // If the cached plan is inactive, perform the same query to activate it.
        if (cacheEntry.isActive === false) {
            assert.eq(1, coll.find(queryShape, proj).sort(sort).itcount());
            cacheEntry = getPlanForCacheEntry(queryShape, proj, sort);
            assert(cacheEntry);
            assert(cacheEntry.isActive);
        }

        // Now perform a $not-$in-$regex query, confirm that it obtains the correct results, and
        // that it used a COLLSCAN rather than planning from the cache.
        const explainOutput = assert.commandWorked(
            coll.find({a: {$not: {$in: [34, /bar/]}}}).explain("executionStats"));
        assert(isCollscan(coll.getDB(), explainOutput.queryPlanner.winningPlan));
        assert.eq(1, explainOutput.executionStats.nReturned);

        // Flush the plan cache before the next iteration.
        coll.getPlanCache().clear();
    }
})();