summaryrefslogtreecommitdiff
path: root/jstests/aggregation/expressions/reduce.js
blob: 54a66fc8b560eeb3815bd7ca938e2a0fdeb13b8b (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
// In SERVER-17258, the $reduce expression was introduced. In this test file, we check the
// functionality and error cases of the expression.
load("jstests/aggregation/extras/utils.js");  // For assertErrorCode and testExpression.

(function() {
    "use strict";

    var coll = db.reduce;

    testExpression(
        coll,
        {
          $reduce:
              {input: [1, 2, 3], initialValue: {$literal: 0}, in : {$sum: ["$$this", "$$value"]}}
        },
        6);
    testExpression(coll, {$reduce: {input: [], initialValue: {$literal: 0}, in : 10}}, 0);
    testExpression(
        coll,
        {
          $reduce:
              {input: [1, 2, 3], initialValue: [], in : {$concatArrays: ["$$value", ["$$this"]]}}
        },
        [1, 2, 3]);
    testExpression(coll,
                   {
                     $reduce: {
                         input: [1, 2],
                         initialValue: [],
                         in : {$concatArrays: ["$$value", ["$$value.notAField"]]}
                     }
                   },
                   [[], []]);

    // A nested $reduce which sums each subarray, then multiplies the results.
    testExpression(coll,
                   {
                     $reduce: {
                         input: [[1, 2, 3], [4, 5]],
                         initialValue: 1,
                         in : {
                             $multiply: [
                                 "$$value",
                                 {
                                   $reduce: {
                                       input: "$$this",
                                       initialValue: 0,
                                       in : {$sum: ["$$value", "$$this"]}
                                   }
                                 }
                             ]
                         }
                     }
                   },
                   54);

    // A nested $reduce using a $let to allow the inner $reduce to access the variables of the
    // outer.
    testExpression(coll,
                   {
                     $reduce: {
                         input: [[0, 1], [2, 3]],
                         initialValue: {allElements: [], sumOfInner: {$literal: 0}},
                         in : {
                             $let: {
                                 vars: {outerValue: "$$value", innerArray: "$$this"},
                                 in : {
                                     $reduce: {
                                         input: "$$innerArray",
                                         initialValue: "$$outerValue",
                                         in : {
                                             allElements: {
                                                 $concatArrays:
                                                     ["$$value.allElements", ["$$this"]]
                                             },
                                             sumOfInner:
                                                 {$sum: ["$$value.sumOfInner", "$$this"]}
                                         }
                                     }
                                 }
                             }
                         }
                     }
                   },
                   {allElements: [0, 1, 2, 3], sumOfInner: 6});

    // Nullish input produces null as an output.
    testExpression(coll, {$reduce: {input: null, initialValue: {$literal: 0}, in : 5}}, null);
    testExpression(
        coll, {$reduce: {input: "$nonexistent", initialValue: {$literal: 0}, in : 5}}, null);

    // Error cases for $reduce.

    // $reduce requires an object.
    var pipeline = {$project: {reduced: {$reduce: 0}}};
    assertErrorCode(coll, pipeline, 40075);

    // Unknown field specified.
    pipeline = {
        $project: {
            reduced: {
                $reduce: {
                    input: {$literal: 0},
                    initialValue: {$literal: 0},
                    in : {$literal: 0},
                    notAField: {$literal: 0}
                }
            }
        }
    };
    assertErrorCode(coll, pipeline, 40076);

    // $reduce requires input to be specified.
    pipeline = {$project: {reduced: {$reduce: {initialValue: {$literal: 0}, in : {$literal: 0}}}}};
    assertErrorCode(coll, pipeline, 40077);

    // $reduce requires initialValue to be specified.
    pipeline = {$project: {reduced: {$reduce: {input: {$literal: 0}, in : {$literal: 0}}}}};
    assertErrorCode(coll, pipeline, 40078);

    // $reduce requires in to be specified.
    pipeline = {
        $project: {reduced: {$reduce: {input: {$literal: 0}, initialValue: {$literal: 0}}}}
    };
    assertErrorCode(coll, pipeline, 40079);

    // $$value is undefined in the non-'in' arguments of $reduce.
    pipeline = {$project: {reduced: {$reduce: {input: "$$value", initialValue: [], in : []}}}};
    assertErrorCode(coll, pipeline, 17276);

    // $$this is undefined in the non-'in' arguments of $reduce.
    pipeline = {$project: {reduced: {$reduce: {input: "$$this", initialValue: [], in : []}}}};
    assertErrorCode(coll, pipeline, 17276);
}());