summaryrefslogtreecommitdiff
path: root/jstests/core/explain_count.js
blob: ee7cb31de940f9a4bd871917f372a98f39b70038 (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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// Test running explains on count commands.

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

var collName = "jstests_explain_count";
var t = db[collName];
t.drop();

/**
 * Given explain output 'explain' at executionStats level verbosity,
 * confirms that the root stage is COUNT and that the result of the
 * count is equal to 'nCounted'.
 */
function checkCountExplain(explain, nCounted) {
    printjson(explain);
    var execStages = explain.executionStats.executionStages;

    // If passed through mongos, then the root stage should be the mongos SINGLE_SHARD stage or
    // SHARD_MERGE stages, with COUNT as the root stage on each shard. If explaining directly on the
    // shard, then COUNT is the root stage.
    if ("SINGLE_SHARD" == execStages.stage || "SHARD_MERGE" == execStages.stage) {
        let totalCounted = 0;
        for (let shardExplain of execStages.shards) {
            const countStage = shardExplain.executionStages;
            assert.eq(countStage.stage, "COUNT", "root stage on shard is not COUNT");
            totalCounted += countStage.nCounted;
        }
        assert.eq(totalCounted, nCounted, "wrong count result");
    } else {
        assert.eq(execStages.stage, "COUNT", "root stage is not COUNT");
        assert.eq(execStages.nCounted, nCounted, "wrong count result");
    }
}

/**
 * Given an explain output from a COUNT_SCAN stage, check that a indexBounds field is present.
 */
function checkCountScanIndexExplain(explain, startKey, endKey, startInclusive, endInclusive) {
    var countStage = getPlanStage(explain.executionStats.executionStages, "COUNT_SCAN");

    assert.eq(countStage.stage, "COUNT_SCAN");
    assert("indexBounds" in countStage);
    assert.eq(bsonWoCompare(countStage.indexBounds.startKey, startKey), 0);
    assert.eq(bsonWoCompare(countStage.indexBounds.endKey, endKey), 0);
    assert.eq(countStage.indexBounds.startKeyInclusive, startInclusive);
    assert.eq(countStage.indexBounds.endKeyInclusive, endInclusive);
}

// Collection does not exist.
assert.eq(0, t.count());
var explain = db.runCommand({explain: {count: collName}, verbosity: "executionStats"});
checkCountExplain(explain, 0);

// Collection does not exist with skip, limit, and/or query.
assert.eq(0, db.runCommand({count: collName, skip: 3}).n);
explain = db.runCommand({explain: {count: collName, skip: 3}, verbosity: "executionStats"});
checkCountExplain(explain, 0);

assert.eq(0, db.runCommand({count: collName, limit: 3}).n);
explain = db.runCommand({explain: {count: collName, limit: 3}, verbosity: "executionStats"});
checkCountExplain(explain, 0);

assert.eq(0, db.runCommand({count: collName, limit: -3}).n);
explain = db.runCommand({explain: {count: collName, limit: -3}, verbosity: "executionStats"});
checkCountExplain(explain, 0);

assert.eq(0, db.runCommand({count: collName, limit: -3, skip: 4}).n);
explain =
    db.runCommand({explain: {count: collName, limit: -3, skip: 4}, verbosity: "executionStats"});
checkCountExplain(explain, 0);

assert.eq(0, db.runCommand({count: collName, query: {a: 1}, limit: -3, skip: 4}).n);
explain = db.runCommand(
    {explain: {count: collName, query: {a: 1}, limit: -3, skip: 4}, verbosity: "executionStats"});
checkCountExplain(explain, 0);

// Now add a bit of data to the collection.
t.ensureIndex({a: 1});
for (var i = 0; i < 10; i++) {
    t.insert({_id: i, a: 1});
}

// Trivial count with no skip, limit, or query.
assert.eq(10, t.count());
explain = db.runCommand({explain: {count: collName}, verbosity: "executionStats"});
checkCountExplain(explain, 10);

// Trivial count with skip.
assert.eq(7, db.runCommand({count: collName, skip: 3}).n);
explain = db.runCommand({explain: {count: collName, skip: 3}, verbosity: "executionStats"});
checkCountExplain(explain, 7);

// Trivial count with limit.
assert.eq(3, db.runCommand({count: collName, limit: 3}).n);
explain = db.runCommand({explain: {count: collName, limit: 3}, verbosity: "executionStats"});
checkCountExplain(explain, 3);

// Trivial count with negative limit.
assert.eq(3, db.runCommand({count: collName, limit: -3}).n);
explain = db.runCommand({explain: {count: collName, limit: -3}, verbosity: "executionStats"});
checkCountExplain(explain, 3);

// Trivial count with both limit and skip.
assert.eq(3, db.runCommand({count: collName, limit: -3, skip: 4}).n);
explain =
    db.runCommand({explain: {count: collName, limit: -3, skip: 4}, verbosity: "executionStats"});
checkCountExplain(explain, 3);

// With a query.
assert.eq(10, db.runCommand({count: collName, query: {a: 1}}).n);
explain = db.runCommand({explain: {count: collName, query: {a: 1}}, verbosity: "executionStats"});
checkCountExplain(explain, 10);
checkCountScanIndexExplain(explain, {a: 1}, {a: 1}, true, true);

// With a query and skip.
assert.eq(7, db.runCommand({count: collName, query: {a: 1}, skip: 3}).n);
explain = db.runCommand(
    {explain: {count: collName, query: {a: 1}, skip: 3}, verbosity: "executionStats"});
checkCountExplain(explain, 7);
checkCountScanIndexExplain(explain, {a: 1}, {a: 1}, true, true);

// With a query and limit.
assert.eq(3, db.runCommand({count: collName, query: {a: 1}, limit: 3}).n);
explain = db.runCommand(
    {explain: {count: collName, query: {a: 1}, limit: 3}, verbosity: "executionStats"});
checkCountExplain(explain, 3);
checkCountScanIndexExplain(explain, {a: 1}, {a: 1}, true, true);

// Insert one more doc for the last few tests.
t.insert({a: 2});

// Case where all results are skipped.
assert.eq(0, db.runCommand({count: collName, query: {a: 2}, skip: 2}).n);
explain = db.runCommand(
    {explain: {count: collName, query: {a: 2}, skip: 2}, verbosity: "executionStats"});
checkCountExplain(explain, 0);
checkCountScanIndexExplain(explain, {a: 2}, {a: 2}, true, true);

// Case where we have a limit, but we don't hit it.
assert.eq(1, db.runCommand({count: collName, query: {a: 2}, limit: 2}).n);
explain = db.runCommand(
    {explain: {count: collName, query: {a: 2}, limit: 2}, verbosity: "executionStats"});
checkCountExplain(explain, 1);
checkCountScanIndexExplain(explain, {a: 2}, {a: 2}, true, true);