summaryrefslogtreecommitdiff
path: root/jstests/aggregation/explain_limit.js
blob: e3451dc0c7c1dbe4100fce29d5b7ce9c08de3d07 (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
// Tests the behavior of explain() when used with the aggregation pipeline and limits.
// @tags: [do_not_wrap_aggregations_in_facets]
(function() {
    "use strict";

    load("jstests/libs/analyze_plan.js");  // For getAggPlanStages().

    let coll = db.explain_limit;

    const kMultipleSolutionLimit = 101;
    const kCollSize = kMultipleSolutionLimit + 5;
    const kLimit = 10;

    // Return whether or explain() was successful and contained the appropriate fields given the
    // requested verbosity. Checks that the number of documents examined is correct based on
    // 'multipleSolutions', which indicates there was more than one plan available.
    function checkResults({results, verbosity, multipleSolutions}) {
        let cursorSubdocs = getAggPlanStages(results, "$cursor");
        assert.gt(cursorSubdocs.length, 0);
        for (let stageResult of cursorSubdocs) {
            assert(stageResult.hasOwnProperty("$cursor"));
            let result = stageResult.$cursor;

            assert.eq(result.limit, NumberLong(kLimit), tojson(results));

            if (verbosity === "queryPlanner") {
                assert(!result.hasOwnProperty("executionStats"), tojson(results));
            } else {
                // If it's "executionStats" or "allPlansExecution".
                if (multipleSolutions) {
                    // If there's more than one plan available, we may run several of them against
                    // each other to see which is fastest. During this, our limit may be ignored
                    // and so explain may return that it examined more documents than we asked it
                    // to.
                    assert.lte(
                        result.executionStats.nReturned, kMultipleSolutionLimit, tojson(results));
                    assert.lte(result.executionStats.totalDocsExamined,
                               kMultipleSolutionLimit,
                               tojson(results));
                } else {
                    assert.eq(result.executionStats.nReturned, kLimit, tojson(results));
                    assert.eq(result.executionStats.totalDocsExamined, kLimit, tojson(results));
                }
            }
        }
    }

    // explain() should respect limit.
    coll.drop();
    assert.commandWorked(coll.createIndex({a: 1}));

    for (let i = 0; i < kCollSize; i++) {
        assert.writeOK(coll.insert({a: 1}));
    }

    const pipeline = [{$match: {a: 1}}, {$limit: kLimit}];

    let plannerLevel = coll.explain("queryPlanner").aggregate(pipeline);
    checkResults({results: plannerLevel, verbosity: "queryPlanner"});

    let execLevel = coll.explain("executionStats").aggregate(pipeline);
    checkResults({results: execLevel, verbosity: "executionStats", multipleSolutions: false});

    let allPlansExecLevel = coll.explain("allPlansExecution").aggregate(pipeline);
    checkResults(
        {results: allPlansExecLevel, verbosity: "allPlansExecution", multipleSolutions: false});

    // Create a second index so that more than one plan is available.
    assert.commandWorked(coll.createIndex({a: 1, b: 1}));

    plannerLevel = coll.explain("queryPlanner").aggregate(pipeline);
    checkResults({results: plannerLevel, verbosity: "queryPlanner"});

    execLevel = coll.explain("executionStats").aggregate(pipeline);
    checkResults({results: execLevel, verbosity: "executionStats", multipleSolutions: true});

    allPlansExecLevel = coll.explain("allPlansExecution").aggregate(pipeline);
    checkResults(
        {results: allPlansExecLevel, verbosity: "allPlansExecution", multipleSolutions: true});
})();