summaryrefslogtreecommitdiff
path: root/jstests/aggregation
diff options
context:
space:
mode:
authorDavis Haupt <davis.haupt@mongodb.com>2019-08-09 15:12:33 -0400
committerNick Zolnierz <nicholas.zolnierz@mongodb.com>2019-08-13 14:09:03 -0400
commit10305ccf2e8e3717cd834ce4f7090514d170c2a8 (patch)
tree1b04d9b0aa37e8196344657e25499abc9c420648 /jstests/aggregation
parentf3238c1a1bb18d3cd819c026ec90758892ae3133 (diff)
downloadmongo-10305ccf2e8e3717cd834ce4f7090514d170c2a8.tar.gz
SERVER-42694 Add internal JS reduce accumulator
Diffstat (limited to 'jstests/aggregation')
-rw-r--r--jstests/aggregation/accumulators/internal_js_reduce.js116
1 files changed, 116 insertions, 0 deletions
diff --git a/jstests/aggregation/accumulators/internal_js_reduce.js b/jstests/aggregation/accumulators/internal_js_reduce.js
new file mode 100644
index 00000000000..16583bf2315
--- /dev/null
+++ b/jstests/aggregation/accumulators/internal_js_reduce.js
@@ -0,0 +1,116 @@
+// Tests basic functionality of the $_internalJsReduce accumulator, which provides capability for
+// the reduce stage of MapReduce.
+(function() {
+"use strict";
+
+load('jstests/aggregation/extras/utils.js');
+
+db.js_reduce.drop();
+
+for (const i of ["hello", "world", "world", "hello", "hi"]) {
+ db.js_reduce.insert({word: i, val: 1});
+}
+
+// Simple reduce function which calculates the word count.
+function reduce(key, values) {
+ return Array.sum(values);
+}
+
+let groupPipe = [{
+ $group: {
+ _id: "$word",
+ wordCount: {
+ $_internalJsReduce: {
+ data: {k: "$word", v: "$val"},
+ eval: reduce,
+ }
+ }
+ }
+}];
+
+let command = {
+ aggregate: 'js_reduce',
+ cursor: {},
+ pipeline: groupPipe,
+ allowDiskUse: true // Set allowDiskUse to true to force the expression to run on a shard in the
+ // passthrough suites, where javascript execution is supported.
+};
+
+const expectedResults = [
+ {_id: "hello", wordCount: 2},
+ {_id: "world", wordCount: 2},
+ {_id: "hi", wordCount: 1},
+];
+
+let res = assert.commandWorked(db.runCommand(command));
+assert(resultsEq(res.cursor.firstBatch, expectedResults, res.cursor));
+
+//
+// Test that the reduce function also accepts a string argument.
+//
+groupPipe[0].$group.wordCount.$_internalJsReduce.eval = reduce.toString();
+res = assert.commandWorked(db.runCommand(command));
+assert(resultsEq(res.cursor.firstBatch, expectedResults), res.cursor);
+
+//
+// Test that a null word is considered a valid value.
+//
+assert.commandWorked(db.js_reduce.insert({word: null, val: 1}));
+expectedResults.push({_id: null, wordCount: 1});
+res = assert.commandWorked(db.runCommand(command));
+assert(resultsEq(res.cursor.firstBatch, expectedResults), res.cursor);
+
+//
+// Test that the command fails for a missing key and/or value.
+//
+assert.commandWorked(db.js_reduce.insert({sentinel: 1}));
+assert.commandFailedWithCode(db.runCommand(command), 31251);
+assert.commandWorked(db.js_reduce.remove({sentinel: 1}));
+
+//
+// Test that the accumulator fails if the size of the accumulated values exceeds the internal BSON
+// limit.
+//
+db.js_reduce.drop();
+const longStringLength = 1 * 1024 * 1024;
+const nDocs = 20;
+let bulk = db.js_reduce.initializeUnorderedBulkOp();
+for (let i = 0; i < nDocs; i++) {
+ bulk.insert({word: "hello", val: "a".repeat(longStringLength)});
+}
+assert.commandWorked(bulk.execute());
+assert.commandFailedWithCode(db.runCommand(command), ErrorCodes.BSONObjectTooLarge);
+
+//
+// Test that the accumulator correctly fails for invalid arguments.
+//
+db.js_reduce.drop();
+assert.commandWorked(db.js_reduce.insert({word: "oi", val: 1}));
+assert.commandWorked(db.js_reduce.insert({word: "oi", val: 2}));
+groupPipe[0].$group.wordCount.$_internalJsReduce.eval = "UDFs are great!";
+assert.commandFailedWithCode(db.runCommand(command), ErrorCodes.JSInterpreterFailure);
+
+groupPipe[0].$group.wordCount.$_internalJsReduce.eval = 5;
+assert.commandFailedWithCode(db.runCommand(command), 31244);
+
+groupPipe[0].$group.wordCount.$_internalJsReduce.eval = reduce;
+groupPipe[0].$group.wordCount.$_internalJsReduce.data = 5;
+assert.commandFailedWithCode(db.runCommand(command), 31245);
+
+groupPipe[0].$group.wordCount.$_internalJsReduce = {
+ notEval: 1,
+ notData: 1
+};
+assert.commandFailedWithCode(db.runCommand(command), 31243);
+
+groupPipe[0].$group.wordCount.$_internalJsReduce = {
+ eval: reduce,
+ data: {v: 1}
+};
+assert.commandFailedWithCode(db.runCommand(command), 31251);
+groupPipe[0].$group.wordCount.$_internalJsReduce.data = {
+ key: 1,
+ value: 1
+};
+assert.commandFailedWithCode(db.runCommand(command), 31251);
+})();