summaryrefslogtreecommitdiff
path: root/jstests/aggregation/expressions/let.js
blob: 5de6db8eebf8f93b8fcf63b266612e40635bbe3d (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
/**
 * Basic integration tests for the $let expression.
 */
(function() {
    "use strict";

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

    let coll = db.agg_expr_let;
    coll.drop();
    assert.commandWorked(coll.insert({zero: 0, one: 1, two: 2, three: 3, nested: {four: 4}}));

    function testExpr(expression, output) {
        const res = coll.aggregate([{$project: {output: expression}}]).toArray();
        assert.eq(res.length, 1, tojson(res));
        assert.eq(res[0].output, output, tojson(res));

        // Test in group:
        const result = coll.aggregate({$group: {_id: 0, res: {$sum: expression}}}).toArray();
        assert.eq(result, [{_id: 0, res: output}]);
    }

    // Basic tests.
    testExpr('$two', 2);
    testExpr('$$CURRENT.two', 2);
    testExpr('$$ROOT.two', 2);

    // Using sub expressions.
    testExpr({$add: ['$two', '$$CURRENT.three']}, 5);
    testExpr({$add: ['$$CURRENT.two', '$$ROOT.nested.four']}, 6);

    // Verify that the variables defined in $let work.
    testExpr({$let: {vars: {a: 10}, in : '$$a'}}, 10);
    testExpr({$let: {vars: {a: '$zero'}, in : '$$a'}}, 0);
    testExpr({$let: {vars: {a: {$add: ['$one', '$two']}, b: 10}, in : {$multiply: ['$$a', '$$b']}}},
             30);

    // Verify that the outer level variable works in inner level $let.
    testExpr({
        $let: {
            vars: {var1: 1},
            in : {$let: {vars: {var2: "$$var1"}, in : {$sum: ["$$var1", "$$var2"]}}}
        }
    },
             2);

    // Verify that the outer level variables get overwritten by inner level variables.
    testExpr({
        $let: {
            vars: {var1: "$one"},
            in : {$let: {vars: {var2: "$$var1", var1: 3}, in : {$sum: ["$$var2", "$$var1"]}}}
        }
    },
             4);

    // $let changing CURRENT
    testExpr({$let: {vars: {CURRENT: '$$ROOT.nested'}, in : {$multiply: ['$four', '$$ROOT.two']}}},
             8);
    testExpr({
        $let: {
            vars: {CURRENT: '$$CURRENT.nested'},  // using original value of CURRENT
            in : {$multiply: ['$four', '$$ROOT.two']}
        }
    },
             8);
    testExpr({
        $let: {
            vars: {CURRENT: '$nested'},  // same as last
            in : {$multiply: ['$four', '$$ROOT.two']}
        }
    },
             8);
    testExpr({
        $let: {
            vars: {CURRENT: {$const: {ten: 10}}},  // "artificial" object
            in : {$multiply: ['$ten', '$$ROOT.two']}
        }
    },
             20);
    testExpr({
        $let: {
            vars: {CURRENT: '$three'},  // sets current to the number 3 (not an object)
            in : {$multiply: ['$$CURRENT', '$$ROOT.two']}
        }
    },
             6);

    // Swapping with $let (ensures there is no ordering dependency in vars).
    testExpr({
        $let: {
            vars: {x: 6, y: 10},
            in : {
                $let: {
                    vars: {x: '$$y', y: '$$x'},  // now {x:10, y:6}
                    in : {$subtract: ['$$x', '$$y']}
                }
            }
        }
    },            // Not commutative!
             4);  // 10-6 not 6-10 or 6-6

    // Unicode is allowed.
    testExpr({$let: {vars: {'日本語': 10}, in : '$$日本語'}},
             10);  // Japanese for "Japanese language".

    // Can use ROOT and CURRENT directly with no subfield (SERVER-5916).
    coll.drop();
    coll.insert({_id: 'obj'});
    assert.eq(coll.aggregate({$project: {_id: 0, obj: '$$ROOT'}}).toArray(), [{obj: {_id: 'obj'}}]);
    assert.eq(coll.aggregate({$project: {_id: 0, obj: '$$CURRENT'}}).toArray(),
              [{obj: {_id: 'obj'}}]);
    assert.eq(coll.aggregate({$group: {_id: 0, objs: {$push: '$$ROOT'}}}).toArray(),
              [{_id: 0, objs: [{_id: 'obj'}]}]);
    assert.eq(coll.aggregate({$group: {_id: 0, objs: {$push: '$$CURRENT'}}}).toArray(),
              [{_id: 0, objs: [{_id: 'obj'}]}]);

    // Check name validity checks.
    assertErrorCode(coll, {$project: {a: {$let: {vars: {ROOT: 1}, in : '$$ROOT'}}}}, 16867);
    assertErrorCode(coll, {$project: {a: {$let: {vars: {FOO: 1}, in : '$$FOO'}}}}, 16867);
    assertErrorCode(coll, {$project: {a: {$let: {vars: {_underbar: 1}, in : '$$FOO'}}}}, 16867);
    assertErrorCode(coll, {$project: {a: {$let: {vars: {'a.b': 1}, in : '$$FOO'}}}}, 16868);
    assertErrorCode(coll, {$project: {a: {$let: {vars: {'a b': 1}, in : '$$FOO'}}}}, 16868);
    assertErrorCode(coll, {$project: {a: '$$_underbar'}}, 16870);
    assertErrorCode(coll, {$project: {a: '$$with spaces'}}, 16871);

    // Verify that variables defined in '$let' cannot be used to initialize other variables.
    assertErrorCode(
        coll,
        [{$project: {output: {$let: {vars: {var1: "$one", var2: "$$var1"}, in : "$$var1"}}}}],
        17276);

}());