summaryrefslogtreecommitdiff
path: root/jstests/noPassthroughWithMongod/group_pushdown.js
blob: 271d7c20ef51b0cf30680f4fda30f8e4f32d06fc (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
145
146
147
148
/**
 * Tests basic functionality of pushing $group into the find layer.
 */
(function() {
"use strict";

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

const featureEnabled =
    assert.commandWorked(db.adminCommand({getParameter: 1, featureFlagSBEGroupPushdown: 1}))
        .featureFlagSBEGroupPushdown.value;
if (!featureEnabled) {
    jsTestLog("Skipping test because the sbe group pushdown feature flag is disabled");
    return;
}

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

assert.commandWorked(coll.insert([
    {"_id": 1, "item": "a", "price": 10, "quantity": 2, "date": ISODate("2014-01-01T08:00:00Z")},
    {"_id": 2, "item": "b", "price": 20, "quantity": 1, "date": ISODate("2014-02-03T09:00:00Z")},
    {"_id": 3, "item": "a", "price": 5, "quantity": 5, "date": ISODate("2014-02-03T09:05:00Z")},
    {"_id": 4, "item": "b", "price": 10, "quantity": 10, "date": ISODate("2014-02-15T08:00:00Z")},
    {"_id": 5, "item": "c", "price": 5, "quantity": 10, "date": ISODate("2014-02-15T09:05:00Z")},
]));

let assertGroupPushdown = function(coll, pipeline, expectedResults, expectedGroupCountInExplain) {
    const explain = coll.explain().aggregate(pipeline);
    // When $group isnever pushed down it be present as a stage in the 'winningPlan' of $cursor.
    assert.eq(expectedGroupCountInExplain, getAggPlanStages(explain, "GROUP").length, explain);

    let results = coll.aggregate(pipeline).toArray();
    assert.sameMembers(results, expectedResults);
};

let assertNoGroupPushdown = function(coll, pipeline, expectedResults, options = {}) {
    const explain = coll.explain().aggregate(pipeline, options);
    assert.eq(null, getAggPlanStage(explain, "GROUP"), explain);

    let resultNoGroupPushdown = coll.aggregate(pipeline, options).toArray();
    assert.sameMembers(resultNoGroupPushdown, expectedResults);
};

let assertResultsMatchWithAndWithoutPushdown = function(
    coll, pipeline, expectedResults, expectedGroupCountInExplain) {
    // Make sure the provided pipeline is eligible for pushdown.
    assertGroupPushdown(coll, pipeline, expectedResults, expectedGroupCountInExplain);

    // Turn sbe off.
    db.adminCommand({setParameter: 1, internalQueryEnableSlotBasedExecutionEngine: false});

    // Sanity check the results when no pushdown happens.
    let resultNoGroupPushdown = coll.aggregate(pipeline).toArray();
    assert.sameMembers(resultNoGroupPushdown, expectedResults);

    // Turn sbe on which will allow $group stages that contain supported accumulators to be pushed
    // down under certain conditions.
    db.adminCommand({setParameter: 1, internalQueryEnableSlotBasedExecutionEngine: true});

    let resultWithGroupPushdown = coll.aggregate(pipeline).toArray();
    assert.sameMembers(resultNoGroupPushdown, resultWithGroupPushdown);
};

// Baseline with SBE off.
assertNoGroupPushdown(coll,
                      [{$group: {_id: "$item", s: {$sum: "$price"}}}],
                      [{_id: "a", s: 15}, {_id: "b", s: 30}, {_id: "c", s: 5}]);

// Turn sbe on which will allow $group stages that contain supported accumulators to be pushed
// down under certain conditions.
db.adminCommand({setParameter: 1, internalQueryEnableSlotBasedExecutionEngine: true});

// Try a pipeline with no group stage.
assert.eq(
    coll.aggregate([{$match: {item: "c"}}]).toArray(),
    [{"_id": 5, "item": "c", "price": 5, "quantity": 10, "date": ISODate("2014-02-15T09:05:00Z")}]);

// Run a simple $group with supported $sum accumulator, and check if it gets pushed down.
assertResultsMatchWithAndWithoutPushdown(coll,
                                         [{$group: {_id: "$item", s: {$sum: "$price"}}}],
                                         [{_id: "a", s: 15}, {_id: "b", s: 30}, {_id: "c", s: 5}],
                                         1);

// Two group stages both get pushed down.
assertResultsMatchWithAndWithoutPushdown(
    coll,
    [{$group: {_id: "$item", s: {$sum: "$price"}}}, {$group: {_id: "$quantity", c: {$count: {}}}}],
    [{_id: null, c: 3}],
    2);

// Run a group with an unsupported accumultor and check that it doesn't get pushed down.
assertNoGroupPushdown(coll, [{$group: {_id: "$item", s: {$stdDevSamp: "$quantity"}}}], [
    {"_id": "a", "s": 2.1213203435596424},
    {"_id": "b", "s": 6.363961030678928},
    {"_id": "c", "s": null}
]);

// Run a simple group with $sum and object _id, check if it doesn't get pushed down.
assertNoGroupPushdown(coll,
                      [{$group: {_id: {"i": "$item"}, s: {$sum: "$price"}}}],
                      [{_id: {i: "a"}, s: 15}, {_id: {i: "b"}, s: 30}, {_id: {i: "c"}, s: 5}]);

// Spilling isn't supported yet so $group with 'allowDiskUse' true won't get pushed down.
assertNoGroupPushdown(coll,
                      [{$group: {_id: "$item", s: {$sum: "$price"}}}],
                      [{"_id": "b", "s": 30}, {"_id": "a", "s": 15}, {"_id": "c", "s": 5}],
                      {allowDiskUse: true, cursor: {batchSize: 1}});

// Run a pipeline with match, sort, group to check if the whole pipeline gets pushed down.
assertGroupPushdown(coll,
                    [{$match: {item: "a"}}, {$sort: {price: 1}}, {$group: {_id: "$item"}}],
                    [{"_id": "a"}],
                    1);

// Make sure the DISTINCT_SCAN case where the sort is proided by an index still works and is not
// executed in SBE.
assert.commandWorked(coll.createIndex({item: 1}));
let explain = coll.explain().aggregate([{$sort: {item: 1}}, {$group: {_id: "$item"}}]);
assert.neq(null, getAggPlanStage(explain, "DISTINCT_SCAN"), explain);
assert.eq(null, getAggPlanStage(explain, "SORT"), explain);
assert.commandWorked(coll.dropIndex({item: 1}));

// Time to see if indexes prevent pushdown. Add an index on item, and make sure we don't execute in
// sbe because we won't support $group pushdown until SERVER-58429.
assert.commandWorked(coll.createIndex({item: 1}));
assertNoGroupPushdown(coll,
                      [{$group: {_id: "$item", s: {$sum: "$price"}}}],
                      [{"_id": "b", "s": 30}, {"_id": "a", "s": 15}, {"_id": "c", "s": 5}]);
assert.commandWorked(coll.dropIndex({item: 1}));

// Supported group and then a group with no supported accumulators.
explain = coll.explain().aggregate([
    {$group: {_id: "$item", s: {$sum: "$price"}}},
    {$group: {_id: "$quantity", c: {$stdDevPop: "$price"}}}
]);
assert.neq(null, getAggPlanStage(explain, "GROUP"), explain);
assert(explain.stages[1].hasOwnProperty("$group"));

// A group with one supported and one unsupported accumulators.
explain = coll.explain().aggregate(
    [{$group: {_id: "$item", s: {$sum: "$price"}, stdev: {$stdDevPop: "$price"}}}]);
assert.eq(null, getAggPlanStage(explain, "GROUP"), explain);
assert(explain.stages[1].hasOwnProperty("$group"));

// Leave sbe in the initial state.
db.adminCommand({setParameter: 1, internalQueryEnableSlotBasedExecutionEngine: false});
})();