summaryrefslogtreecommitdiff
path: root/jstests/aggregation/bugs/server9625.js
blob: 4a525aba51851848c0b1a9b6c7cdefd6207d1214 (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
// SERVER-9625 Making accumulators $sum, $min, $max, $avg, $stdDevSamp, and $stdDevPop available as
// expressions.

// For assertErrorCode.
load('jstests/aggregation/extras/utils.js');

(function() {
    'use strict';
    var coll = db.server9625;
    coll.drop();
    assert.writeOK(coll.insert({}));

    // Helper for testing that op returns expResult.
    function testOp(op, expResult) {
        var pipeline = [{$project: {_id: 0, result: op}}];
        assert.eq(coll.aggregate(pipeline).toArray(), [{result: expResult}]);
    }

    // ExpressionFromAccumulators take either a list of arguments or a single array argument.
    testOp({$avg: [1, 2, 3, 4, 5]}, 3);
    testOp({$avg: [[1, 2, 3, 4, 5]]}, 3);
    testOp({$min: [1, 2, 3, 4, 5]}, 1);
    testOp({$min: [[1, 2, 3, 4, 5]]}, 1);
    testOp({$max: [1, 2, 3, 4, 5]}, 5);
    testOp({$max: [[1, 2, 3, 4, 5]]}, 5);
    testOp({$sum: [1, 2, 3, 4, 5]}, 15);
    testOp({$sum: [[1, 2, 3, 4, 5]]}, 15);
    testOp({$stdDevPop: [1, 3]}, 1);
    testOp({$stdDevPop: [[1, 3]]}, 1);
    testOp({$stdDevSamp: [1, 2, 3]}, 1);
    testOp({$stdDevSamp: [[1, 2, 3]]}, 1);

    // Null arguments are ignored.
    testOp({$avg: [1, 2, 3, 4, 5, null]}, 3);
    testOp({$min: [1, 2, 3, 4, 5, null]}, 1);
    testOp({$max: [1, 2, 3, 4, 5, null]}, 5);
    testOp({$sum: [1, 2, 3, 4, 5, null]}, 15);
    testOp({$stdDevPop: [1, 3, null]}, 1);
    testOp({$stdDevSamp: [1, 2, 3, null]}, 1);

    // NaN arguments are processed by all expressions.
    testOp({$avg: [1, 2, 3, 4, 5, NaN]}, NaN);
    testOp({$min: [1, 2, 3, 4, 5, NaN]}, NaN);
    testOp({$max: [1, 2, 3, 4, 5, NaN]}, 5);
    testOp({$sum: [1, 2, 3, 4, 5, NaN]}, NaN);
    testOp({$stdDevPop: [1, 3, NaN]}, NaN);
    testOp({$stdDevSamp: [1, 2, 3, NaN]}, NaN);

    // Use at least one non-constant value in the following tests, to ensure
    // isAssociative() and isCommutative() are called. If all arguments are constant, the
    // optimization will evaluate them all into one, without calling isAssociative() nor
    // isCommutative().
    coll.drop();
    assert.writeOK(coll.insert({"a": 1, "b": 6}));

    // These expressions are associative and commutative so inner expression can be combined with
    // outer.
    testOp({$sum: ["$a", 2, 3, {$sum: [4, 5]}]}, 15);
    testOp({$min: ["$a", 2, 3, {$min: [4, 5]}]}, 1);
    testOp({$max: ["$a", 2, 3, {$max: [4, 5]}]}, 5);

    // These expressions are not associative and commutative so inner expression cannot be combined
    // with outer.
    testOp({$avg: ["$a", 3, {$avg: [4, 6]}]}, 3);
    testOp({$stdDevPop: ["$a", {$stdDevPop: [1, 3]}]}, 0);
    testOp({$stdDevSamp: ["$a", {$stdDevSamp: [1, 2, 3]}]}, 0);

    // If isAssociative() and isCommutative() did not return false when provided a single argument,
    // the single array argument provided to the inner expression would be ignored instead of
    // treated as a list of arguments, and these tests would fail.
    testOp({$sum: ["$a", 2, 3, {$sum: [["$a", 4, 5]]}]}, 16);
    testOp({$min: ["$b", 2, 3, {$min: [["$a", 4, 5]]}]}, 1);
    testOp({$max: ["$a", 2, 3, {$max: [["$b", 4, 5]]}]}, 6);
}());