summaryrefslogtreecommitdiff
path: root/jstests/core/wildcard_index_covered_queries.js
blob: df6142e859e3ff7e6cd4a821e5afb5709e817cd2 (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
/**
 * Test that $** indexes can provide a covered solution, given an appropriate query and projection.
 *
 * Cannot implicitly shard accessed collections, because queries on a sharded collection cannot be
 * covered unless they include the shard key. Does not support stepdowns because the test issues
 * getMores, which the stepdown/kill_primary passthroughs will reject.
 *
 * @tags: [assumes_unsharded_collection, does_not_support_stepdowns]
 */
(function() {
    "use strict";

    load("jstests/aggregation/extras/utils.js");  // For arrayEq.
    load("jstests/libs/analyze_plan.js");         // For getPlanStages and isIndexOnly.

    const assertArrayEq = (l, r) => assert(arrayEq(l, r));

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

    // Confirms that the $** index can answer the given query and projection, that it produces a
    // covered solution, and that the results are identical to those obtained by a COLLSCAN. If
    // 'shouldFailToCover' is true, inverts the assertion and confirms that the given query and
    // projection do *not* produce a covered plan.
    function assertWildcardProvidesCoveredSolution(query, proj, shouldFailToCover = false) {
        // Obtain the explain output for the given query and projection. We run the explain with
        // 'executionStats' so that we can subsequently validate the number of documents examined.
        const explainOut = assert.commandWorked(coll.find(query, proj).explain("executionStats"));
        const winningPlan = explainOut.queryPlanner.winningPlan;

        // Verify that the $** index provided the winning solution for this query.
        const ixScans = getPlanStages(winningPlan, "IXSCAN");
        assert.gt(ixScans.length, 0, tojson(explainOut));
        ixScans.forEach((ixScan) => assert(ixScan.keyPattern.hasOwnProperty("$_path")));

        // Verify that the solution is covered, and that no documents were examined. If the argument
        // 'shouldFailToCover' is true, invert the validation to confirm that it is NOT covered.
        assert.eq(!!explainOut.executionStats.totalDocsExamined, shouldFailToCover);
        assert.eq(isIndexOnly(coll.getDB(), winningPlan), !shouldFailToCover);

        // Verify that the query covered by the $** index produces the same results as a COLLSCAN.
        assertArrayEq(coll.find(query, proj).toArray(),
                      coll.find(query, proj).hint({$natural: 1}).toArray());
    }

    // Create a new collection and build a $** index on it.
    const bulk = coll.initializeUnorderedBulkOp();
    for (let i = 0; i < 200; i++) {
        bulk.insert({a: {b: i, c: `${(i+1)}`}, d: (i + 2)});
    }
    assert.commandWorked(bulk.execute());
    assert.commandWorked(coll.createIndex({"$**": 1}));

    // Verify that the $** index can cover an exact match on an integer value.
    assertWildcardProvidesCoveredSolution({"a.b": 10}, {_id: 0, "a.b": 1});

    // Verify that the $** index can cover an exact match on a string value.
    assertWildcardProvidesCoveredSolution({"a.c": "10"}, {_id: 0, "a.c": 1});

    // Verify that the $** index can cover a range query for integer values.
    assertWildcardProvidesCoveredSolution({"a.b": {$gt: 10, $lt: 99}}, {_id: 0, "a.b": 1});

    // Verify that the $** index can cover a range query for string values.
    assertWildcardProvidesCoveredSolution({"a.c": {$gt: "10", $lt: "99"}}, {_id: 0, "a.c": 1});

    // Verify that the $** index can cover an $in query for integer values.
    assertWildcardProvidesCoveredSolution({"a.b": {$in: [0, 50, 100, 150]}}, {_id: 0, "a.b": 1});

    // Verify that the $** index can cover an $in query for string values.
    assertWildcardProvidesCoveredSolution({"a.c": {$in: ["0", "50", "100", "150"]}},
                                          {_id: 0, "a.c": 1});

    // Verify that attempting to project the virtual $_path field from the $** keyPattern will fail
    // to do so and will instead produce a non-covered query. However, this query will nonetheless
    // output the correct results.
    const shouldFailToCover = true;
    assertWildcardProvidesCoveredSolution(
        {d: {$in: [0, 25, 50, 75, 100]}}, {_id: 0, d: 1, $_path: 1}, shouldFailToCover);

    // Verify that predicates which produce inexact-fetch bounds are not covered by a $** index.
    assertWildcardProvidesCoveredSolution(
        {d: {$elemMatch: {$eq: 50}}}, {_id: 0, d: 1}, shouldFailToCover);
})();