mongo/jstests/aggregation/expressions/internal_js_emit_with_scope.js

153 lines
3.9 KiB
JavaScript

// 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.
// Must also set 'fromRouter: true' as otherwise 'runtimeConstants' is disallowed on mongod.
// @tags: [
// assumes_against_mongod_not_mongos,
// requires_scripting,
// requires_fcv_81,
// ]
import {resultsEq} from "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, fromRouter: true}).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, fromRouter: true}).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.
//
/* eslint-disable */
constants.jsScope.multiplier = 5;
pipeline[0].$project.emits.$_internalJsEmit.eval = function () {
for (let word of this.text.split(" ")) {
emit(word, weights[word] * multiplier);
}
};
/* eslint-enable */
results = coll.aggregate(pipeline, {cursor: {}, runtimeConstants: constants, fromRouter: true}).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, fromRouter: true}).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,
fromRouter: true,
}),
ErrorCodes.TypeMismatch,
);