summaryrefslogtreecommitdiff
path: root/jstests/aggregation/expressions/internal_js_emit_with_scope.js
blob: 261addf7f8979542efd41c45067ea3e3ead68cfb (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
// Tests the ability of the $_internalJsEmit expression to access javascript scope explicitly
// specified in runtimeConstants.
//
// Do not run in sharded passthroughs since 'runtimeConstants' is disallowed on mongos.
// @tags: [
//   assumes_unsharded_collection,
// ]
(function() {
"use strict";

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

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

const weights = {
    wood: 5,
    chuck: 2,
    could: 0
};

let constants = {
    localNow: new Date(),
    clusterTime: new Timestamp(0, 0),
    jsScope: {weights: weights}
};

function fmap() {
    for (let word of this.text.split(' ')) {
        emit(word, weights[word]);
    }
}

let pipeline = [
    {
        $project: {
            emits: {
                $_internalJsEmit: {
                    this: '$$ROOT',
                    eval: fmap,
                },
            },
            _id: 0,
        }
    },
    {$unwind: '$emits'},
    {$replaceRoot: {newRoot: '$emits'}}
];

assert.commandWorked(coll.insert({text: 'wood chuck could chuck wood'}));

let results = coll.aggregate(pipeline, {cursor: {}, runtimeConstants: constants}).toArray();
assert(resultsEq(results,
                 [
                     {k: "wood", v: weights["wood"]},
                     {k: "chuck", v: weights["chuck"]},
                     {k: "could", v: weights["could"]},
                     {k: "chuck", v: weights["chuck"]},
                     {k: "wood", v: weights["wood"]}
                 ],
                 results));

//
// Test that the scope variables are mutable from within a user-defined javascript function.
//
pipeline[0].$project.emits.$_internalJsEmit.eval = function() {
    for (let word of this.text.split(' ')) {
        emit(word, weights[word]);
        weights[word] += 1;
    }
};

results = coll.aggregate(pipeline, {cursor: {}, runtimeConstants: constants}).toArray();
assert(resultsEq(results,
                 [
                     {k: "wood", v: weights["wood"]},
                     {k: "chuck", v: weights["chuck"]},
                     {k: "could", v: weights["could"]},
                     {k: "chuck", v: weights["chuck"] + 1},
                     {k: "wood", v: weights["wood"] + 1}
                 ],
                 results));

//
// Test that the jsScope is allowed to have any number of fields.
//
constants.jsScope.multiplier = 5;
pipeline[0].$project.emits.$_internalJsEmit.eval = function() {
    for (let word of this.text.split(' ')) {
        emit(word, weights[word] * multiplier);
    }
};
results = coll.aggregate(pipeline, {cursor: {}, runtimeConstants: constants}).toArray();
assert(resultsEq(results,
                 [
                     {k: "wood", v: weights["wood"] * 5},
                     {k: "chuck", v: weights["chuck"] * 5},
                     {k: "could", v: weights["could"] * 5},
                     {k: "chuck", v: weights["chuck"] * 5},
                     {k: "wood", v: weights["wood"] * 5}
                 ],
                 results));
constants.jsScope = {};
pipeline[0].$project.emits.$_internalJsEmit.eval = function() {
    for (let word of this.text.split(' ')) {
        emit(word, 1);
    }
};
results = coll.aggregate(pipeline, {cursor: {}, runtimeConstants: constants}).toArray();
assert(resultsEq(results,
                 [
                     {k: "wood", v: 1},
                     {k: "chuck", v: 1},
                     {k: "could", v: 1},
                     {k: "chuck", v: 1},
                     {k: "wood", v: 1},
                 ],
                 results));

//
// Test that the command fails if the jsScope is not an object.
//
constants.jsScope = "you cant do this";
assert.commandFailedWithCode(
    db.runCommand(
        {aggregate: coll.getName(), pipeline: pipeline, cursor: {}, runtimeConstants: constants}),
    ErrorCodes.TypeMismatch);
})();