summaryrefslogtreecommitdiff
path: root/jstests/core
diff options
context:
space:
mode:
authorJacob Evans <jacob.evans@mongodb.com>2019-11-05 19:45:39 +0000
committerevergreen <evergreen@mongodb.com>2019-11-05 19:45:39 +0000
commit9561ea73bc0004fc1835430f9789546484c1e7e7 (patch)
tree926cce8e3ec8fd37e82d24a450f9166001023aa5 /jstests/core
parent853bdc4b34d9c3505e2af1f443ad7a99a619adea (diff)
downloadmongo-9561ea73bc0004fc1835430f9789546484c1e7e7.tar.gz
SERVER-43749 Modernize mapReduce tests in core
Diffstat (limited to 'jstests/core')
-rw-r--r--jstests/core/geo_mapreduce.js64
-rw-r--r--jstests/core/geo_mapreduce2.js43
-rw-r--r--jstests/core/mr_killop.js220
-rw-r--r--jstests/core/mr_merge.js128
-rw-r--r--jstests/core/mr_merge2.js55
-rw-r--r--jstests/core/mr_mutable_properties.js13
-rw-r--r--jstests/core/mr_outreduce.js65
-rw-r--r--jstests/core/mr_outreduce2.js39
-rw-r--r--jstests/core/mr_reduce.js104
-rw-r--r--jstests/core/mr_undef.js33
10 files changed, 294 insertions, 470 deletions
diff --git a/jstests/core/geo_mapreduce.js b/jstests/core/geo_mapreduce.js
deleted file mode 100644
index 8bf93d7fd81..00000000000
--- a/jstests/core/geo_mapreduce.js
+++ /dev/null
@@ -1,64 +0,0 @@
-// @tags: [
-// # mapReduce does not support afterClusterTime.
-// does_not_support_causal_consistency,
-// does_not_support_stepdowns,
-// ]
-
-// Test script from SERVER-1742
-
-// MongoDB test script for mapreduce with geo query
-
-// setup test collection
-db.apples.drop();
-db.apples.insert({"geo": {"lat": 32.68331909, "long": 69.41610718}, "apples": 5});
-db.apples.insert({"geo": {"lat": 35.01860809, "long": 70.92027283}, "apples": 2});
-db.apples.insert({"geo": {"lat": 31.11639023, "long": 64.19970703}, "apples": 11});
-db.apples.insert({"geo": {"lat": 32.64500046, "long": 69.36251068}, "apples": 4});
-db.apples.insert({"geo": {"lat": 33.23638916, "long": 69.81360626}, "apples": 9});
-db.apples.ensureIndex({"geo": "2d"});
-
-center = [32.68, 69.41];
-radius = 10 / 111; // 10km; 1 arcdegree ~= 111km
-geo_query = {
- geo: {'$within': {'$center': [center, radius]}}
-};
-
-// geo query on collection works fine
-res = db.apples.find(geo_query);
-assert.eq(2, res.count());
-
-// map function
-m = function() {
- emit(null, {"apples": this.apples});
-};
-
-// reduce function
-r = function(key, values) {
- var total = 0;
- for (var i = 0; i < values.length; i++) {
- total += values[i].apples;
- }
- return {"apples": total};
-};
-
-// mapreduce without geo query works fine
-res = db.apples.mapReduce(m, r, {out: {inline: 1}});
-
-printjson(res);
-total = res.results[0];
-assert.eq(31, total.value.apples);
-
-// mapreduce with regular query works fine too
-res = db.apples.mapReduce(m, r, {out: {inline: 1}, query: {apples: {'$lt': 9}}});
-total = res.results[0];
-assert.eq(11, total.value.apples);
-
-// mapreduce with geo query gives error on mongodb version 1.6.2
-// uncaught exception: map reduce failed: {
-// "assertion" : "manual matcher config not allowed",
-// "assertionCode" : 13285,
-// "errmsg" : "db assertion failure",
-// "ok" : 0 }
-res = db.apples.mapReduce(m, r, {out: {inline: 1}, query: geo_query});
-total = res.results[0];
-assert.eq(9, total.value.apples);
diff --git a/jstests/core/geo_mapreduce2.js b/jstests/core/geo_mapreduce2.js
deleted file mode 100644
index a740e723101..00000000000
--- a/jstests/core/geo_mapreduce2.js
+++ /dev/null
@@ -1,43 +0,0 @@
-// @tags: [
-// # mapReduce does not support afterClusterTime.
-// does_not_support_causal_consistency,
-// does_not_support_stepdowns,
-// uses_map_reduce_with_temp_collections,
-// ]
-
-// Geo mapreduce 2 from SERVER-3478
-
-var coll = db.geoMR2;
-coll.drop();
-
-for (var i = 0; i < 300; i++)
- coll.insert({i: i, location: [10, 20]});
-
-coll.ensureIndex({location: "2d"});
-
-// map function
-m = function() {
- emit(null, {count: this.i});
-};
-
-// reduce function
-r = function(key, values) {
- var total = 0;
- for (var i = 0; i < values.length; i++) {
- total += values[i].count;
- }
-
- return {count: total};
-};
-
-try {
- coll.mapReduce(m, r, {
- out: coll.getName() + "_mr",
- sort: {_id: 1},
- query: {'location': {$within: {$centerSphere: [[10, 20], 0.01]}}}
- });
-
-} catch (e) {
- // This should occur, since we can't in-mem sort for mreduce
- printjson(e);
-}
diff --git a/jstests/core/mr_killop.js b/jstests/core/mr_killop.js
index 6c837c52943..07a92e289eb 100644
--- a/jstests/core/mr_killop.js
+++ b/jstests/core/mr_killop.js
@@ -1,3 +1,4 @@
+// Test killop applied to m/r operations and child ops of m/r operations.
// Cannot implicitly shard accessed collections because the "command" field in the currentOp()
// output is reported as {"mapreduce.shardedfinish": { mapreduce: "jstests_mr_killop", ... }, ... }
// when the "finalize" option to the "mapReduce" command is used on a sharded collection.
@@ -9,77 +10,54 @@
// uses_multiple_connections,
// uses_map_reduce_with_temp_collections,
// ]
-
-// Test killop applied to m/r operations and child ops of m/r operations.
-
-t = db.jstests_mr_killop;
-t.drop();
-t2 = db.jstests_mr_killop_out;
-t2.drop();
-db.adminCommand({"configureFailPoint": 'mr_killop_test_fp', "mode": 'alwaysOn'});
-function debug(x) {
- // printjson( x );
-}
-
-/** @return op code for map reduce op created by spawned shell, or that op's child */
-function op(childLoop) {
- p = db.currentOp().inprog;
- debug(p);
-
- let isMapReduce = function(op) {
+(function() {
+"use strict";
+const source = db.jstests_mr_killop;
+source.drop();
+const out = db.jstests_mr_killop_out;
+out.drop();
+assert.commandWorked(db.adminCommand({configureFailPoint: "mr_killop_test_fp", mode: "alwaysOn"}));
+
+/** @return op code for map reduce op created by spawned shell. */
+function getOpCode() {
+ const inProg = db.currentOp().inprog;
+
+ function isMapReduce(op) {
if (!op.command) {
return false;
}
- let cmdBody = op.command;
+ const cmdBody = op.command;
if (cmdBody.$truncated) {
- let stringifiedCmd = cmdBody.$truncated;
- print('str: ' + tojson(stringifiedCmd));
+ const stringifiedCmd = cmdBody.$truncated;
return stringifiedCmd.search('mapreduce') >= 0 &&
- stringifiedCmd.search('jstests_mr_killop') >= 0;
+ stringifiedCmd.search(source.getName()) >= 0;
}
- return cmdBody.mapreduce && cmdBody.mapreduce == "jstests_mr_killop";
- };
-
- for (var i in p) {
- var o = p[i];
- // Identify a map/reduce or where distinct operation by its collection, whether or not
- // it is currently active.
- if (childLoop) {
- if ((o.active || o.waitingForLock) && o.command && o.command.query &&
- o.command.query.$where && o.command.distinct == "jstests_mr_killop") {
- return o.opid;
- }
- } else {
- if ((o.active || o.waitingForLock) && isMapReduce(o)) {
- return o.opid;
- }
- }
+ return cmdBody.mapreduce && cmdBody.mapreduce == source.getName();
+ }
+
+ for (let i in inProg) {
+ const o = inProg[i];
+ // Identify a map/reduce operation by its collection, whether or not it is currently active.
+ if ((o.active || o.waitingForLock) && isMapReduce(o))
+ return o.opid;
}
return -1;
}
/**
- * Run one map reduce with the specified parameters in a parallel shell, kill the
- * map reduce op or its child op with killOp, and wait for the map reduce op to
- * terminate.
- * @param childLoop - if true, a distinct $where op is killed rather than the map reduce op.
- * This is necessay for a child distinct $where of a map reduce op because child
- * ops currently mask parent ops in currentOp.
+ * Run one mapReduce with the specified parameters in a parallel shell. Kill the map reduce op and
+ * wait for the map reduce op to terminate.
*/
-function testOne(map, reduce, finalize, scope, childLoop, wait) {
- debug("testOne - map = " + tojson(map) + "; reduce = " + tojson(reduce) +
- "; finalize = " + tojson(finalize) + "; scope = " + tojson(scope) +
- "; childLoop = " + childLoop + "; wait = " + wait);
-
- t.drop();
- t2.drop();
- // Ensure we have 2 documents for the reduce to run
- t.save({a: 1});
- t.save({a: 1});
-
- spec = {mapreduce: "jstests_mr_killop", out: "jstests_mr_killop_out", map: map, reduce: reduce};
+function runTest(map, reduce, finalize, scope, wait) {
+ source.drop();
+ out.drop();
+ // Ensure we have 2 documents for the reduce to run.
+ assert.commandWorked(source.insert({a: 1}));
+ assert.commandWorked(source.insert({a: 1}));
+
+ const spec = {mapreduce: source.getName(), out: out.getName(), map: map, reduce: reduce};
if (finalize) {
spec["finalize"] = finalize;
}
@@ -87,85 +65,94 @@ function testOne(map, reduce, finalize, scope, childLoop, wait) {
spec["scope"] = scope;
}
- // Windows shell strips all double quotes from command line, so use
- // single quotes.
- stringifiedSpec = tojson(spec).toString().replace(/\n/g, ' ').replace(/\"/g, "\'");
+ // Windows shell strips all double quotes from command line, so use single quotes.
+ const stringifiedSpec = tojson(spec).toString().replace(/\n/g, ' ').replace(/\"/g, "\'");
- // The assert below won't be caught by this test script, but it will cause error messages
- // to be printed.
- var awaitShell =
+ // The assert below won't be caught by this test script, but it will cause error messages to be
+ // printed.
+ const awaitShell =
startParallelShell("assert.commandWorked( db.runCommand( " + stringifiedSpec + " ) );");
if (wait) {
sleep(2000);
}
- o = null;
+ let opCode = null;
assert.soon(function() {
- o = op(childLoop);
- return o != -1;
+ opCode = getOpCode();
+ return opCode != -1;
});
- res = db.killOp(o);
- debug("did kill : " + tojson(res));
+ db.killOp(opCode);
// When the map reduce op is killed, the spawned shell will exit
- var exitCode = awaitShell({checkExitSuccess: false});
+ const exitCode = awaitShell({checkExitSuccess: false});
assert.neq(0,
exitCode,
"expected shell to exit abnormally due to map-reduce execution being terminated");
- debug("parallel shell completed");
+ assert.eq(-1, getOpCode());
+}
- assert.eq(-1, op(childLoop));
+/** Test using wait and non wait modes. */
+function runTests(map, reduce, finalize, scope) {
+ runTest(map, reduce, finalize, scope, false);
+ runTest(map, reduce, finalize, scope, true);
}
-/** Test using wait and non wait modes */
-function test(map, reduce, finalize, scope, childLoop) {
- debug(" Non wait mode");
- testOne(map, reduce, finalize, scope, childLoop, false);
+/** Test looping in map function. */
+function runMapTests(loop) {
+ // Without scope.
+ runTests(
+ loop, // map
+ function(k, v) {
+ return v[0];
+ }, // reduce
+ null, // finalize
+ null // scope
+ );
- debug(" Wait mode");
- testOne(map, reduce, finalize, scope, childLoop, true);
+ // With scope.
+ runTests(
+ function() {
+ loop();
+ }, // map
+ function(k, v) {
+ return v[0];
+ }, // reduce
+ null, // finalize
+ {loop: loop} // scope
+ );
}
-/** Test looping in map and reduce functions */
-function runMRTests(loop, childLoop) {
- debug(" Running MR test - loop map function. no scope ");
- test(loop, // map
- function(k, v) {
- return v[0];
- }, // reduce
- null, // finalize
- null, // scope
- childLoop);
-
- debug(" Running MR test - loop reduce function ");
- test(
+/** Test looping in reduce function. */
+function runReduceTests(loop) {
+ // Without scope.
+ runTests(
function() {
emit(this.a, 1);
}, // map
loop, // reduce
null, // finalize
- null, // scope
- childLoop);
+ null // scope
+ );
- debug(" Running finalization test - loop map function. with scope ");
- test(
+ // With scope.
+ runTests(
function() {
- loop();
+ emit(this.a, 1);
}, // map
- function(k, v) {
- return v[0];
- }, // reduce
- null, // finalize
- {loop: loop}, // scope
- childLoop);
+ function() {
+ loop();
+ }, // reduce
+ null, // finalize
+ {loop: loop} // scope
+ );
}
-/** Test looping in finalize function */
-function runFinalizeTests(loop, childLoop) {
- debug(" Running finalization test - no scope ");
- test(
+/** Test looping in finalize function. */
+function runFinalizeTests(loop) {
+ // Without scope.
+ runTests(
function() {
emit(this.a, 1);
}, // map
@@ -173,11 +160,11 @@ function runFinalizeTests(loop, childLoop) {
return v[0];
}, // reduce
loop, // finalize
- null, // scope
- childLoop);
+ null // scope
+ );
- debug(" Running finalization test - with scope ");
- test(
+ // With scope.
+ runTests(
function() {
emit(this.a, 1);
}, // map
@@ -186,17 +173,18 @@ function runFinalizeTests(loop, childLoop) {
}, // reduce
function(a, b) {
loop();
- }, // finalize
- {loop: loop}, // scope
- childLoop);
+ }, // finalize
+ {loop: loop} // scope
+ );
}
-// Run inside server. No access to debug().
-var loop = function() {
+const loop = function() {
while (1) {
sleep(1000);
}
};
-runMRTests(loop, false);
+runMapTests(loop, false);
+runReduceTests(loop, false);
runFinalizeTests(loop, false);
-db.adminCommand({"configureFailPoint": 'mr_killop_test_fp', "mode": 'off'});
+db.adminCommand({configureFailPoint: "mr_killop_test_fp", mode: "off"});
+}());
diff --git a/jstests/core/mr_merge.js b/jstests/core/mr_merge.js
index 835cee55c27..fa9d2c3d873 100644
--- a/jstests/core/mr_merge.js
+++ b/jstests/core/mr_merge.js
@@ -1,3 +1,4 @@
+// Tests the 'merge' output mode for mapReduce.
// Cannot implicitly shard accessed collections because of following errmsg: Cannot output to a
// non-sharded collection because sharded collection exists already.
// @tags: [
@@ -7,70 +8,97 @@
// does_not_support_stepdowns,
// uses_map_reduce_with_temp_collections,
// ]
+(function() {
+"use strict";
+(function() {
+const source = db.mr_merge;
+source.drop();
-t = db.mr_merge;
-t.drop();
+assert.commandWorked(source.insert({_id: 1, a: [1, 2]}));
+assert.commandWorked(source.insert({_id: 2, a: [2, 3]}));
+assert.commandWorked(source.insert({_id: 3, a: [3, 4]}));
-t.insert({a: [1, 2]});
-t.insert({a: [2, 3]});
-t.insert({a: [3, 4]});
-
-outName = "mr_merge_out";
-out = db[outName];
+const out = db.mr_merge_out;
+const outName = out.getName();
out.drop();
-m = function() {
- for (i = 0; i < this.a.length; i++)
+const map = function() {
+ for (let i = 0; i < this.a.length; i++)
emit(this.a[i], 1);
};
-r = function(k, vs) {
+const reduce = function(k, vs) {
return Array.sum(vs);
};
-function tos(o) {
- var s = "";
- for (var i = 0; i < 100; i++) {
- if (o[i])
- s += i + "_" + o[i];
- }
- return s;
-}
-
-assert.commandWorked(t.mapReduce(m, r, {out: outName}));
-
-expected = {
- "1": 1,
- "2": 2,
- "3": 2,
- "4": 1
-};
-assert.eq(tos(expected), tos(out.convertToSingleObject("value")), "A");
+assert.commandWorked(source.mapReduce(map, reduce, {out: outName}));
+
+let expected = [{_id: 1, value: 1}, {_id: 2, value: 2}, {_id: 3, value: 2}, {_id: 4, value: 1}];
+assert.docEq(expected, out.find().sort({_id: 1}).toArray());
+
+assert.commandWorked(source.insert({_id: 4, a: [4, 5]}));
+// Insert something that should be unaltered by the mapReduce into the output collection.
+assert.commandWorked(out.insert({_id: 10, value: 5}));
+assert.commandWorked(
+ source.mapReduce(map, reduce, {out: {merge: outName}, query: {_id: {$gt: 3}}}));
+
+expected = [
+ {_id: 1, value: 1},
+ {_id: 2, value: 2},
+ {_id: 3, value: 2},
+ {_id: 4, value: 1},
+ {_id: 5, value: 1},
+ {_id: 10, value: 5}
+];
+assert.docEq(expected, out.find().sort({_id: 1}).toArray());
-t.insert({a: [4, 5]});
-out.insert({_id: 10, value: "5"});
-assert.commandWorked(t.mapReduce(m, r, {out: outName}));
+assert.commandWorked(source.insert({_id: 5, a: [5, 6]}));
+// Insert something that should be unaltered by the mapReduce into the output collection.
+assert.commandWorked(out.insert({_id: 20, value: 10}));
+assert.commandWorked(
+ source.mapReduce(map, reduce, {out: {merge: outName}, query: {_id: {$gt: 4}}}));
-expected["4"]++;
-expected["5"] = 1;
-assert.eq(tos(expected), tos(out.convertToSingleObject("value")), "B");
+expected = [
+ {_id: 1, value: 1},
+ {_id: 2, value: 2},
+ {_id: 3, value: 2},
+ {_id: 4, value: 1},
+ {_id: 5, value: 1},
+ {_id: 6, value: 1},
+ {_id: 10, value: 5},
+ {_id: 20, value: 10}
+];
+assert.docEq(expected, out.find().sort({_id: 1}).toArray());
+}());
+(function() {
+const source = db.mr_merge;
+source.drop();
-t.insert({a: [5, 6]});
-out.insert({_id: 10, value: "5"});
-assert.commandWorked(t.mapReduce(m, r, {out: {merge: outName}}));
+assert.commandWorked(source.insert({_id: 1, x: 1}));
+assert.commandWorked(source.insert({_id: 2, x: 1}));
+assert.commandWorked(source.insert({_id: 3, x: 2}));
-expected["5"]++;
-expected["10"] = 5;
-expected["6"] = 1;
+const out = db.mr_merge_out;
+const outName = out.getName();
+out.drop();
+
+const map = function() {
+ emit(this.x, 1);
+};
+const reduce = function(k, v) {
+ return Array.sum(v);
+};
-assert.eq(tos(expected), tos(out.convertToSingleObject("value")), "C");
+assert.commandWorked(
+ source.mapReduce(map, reduce, {out: {merge: outName}, query: {_id: {$gt: 0}}}));
-// test that the nonAtomic output gives valid result
-t.insert({a: [6, 7]});
-out.insert({_id: 20, value: "10"});
-assert.commandWorked(t.mapReduce(m, r, {out: {merge: outName, nonAtomic: true}}));
+assert.eq(2, out.findOne({_id: 1}).value);
+assert.eq(1, out.findOne({_id: 2}).value);
-expected["6"]++;
-expected["20"] = 10;
-expected["7"] = 1;
+assert.commandWorked(source.insert({_id: 4, x: 2}));
+assert.commandWorked(
+ source.mapReduce(map, reduce, {out: {merge: outName}, query: {_id: {$gt: 3}}}));
-assert.eq(tos(expected), tos(out.convertToSingleObject("value")), "D");
+assert.eq(2, out.findOne({_id: 1}).value);
+assert.eq(1, out.findOne({_id: 2}).value);
+}());
+}());
diff --git a/jstests/core/mr_merge2.js b/jstests/core/mr_merge2.js
deleted file mode 100644
index df2ada36749..00000000000
--- a/jstests/core/mr_merge2.js
+++ /dev/null
@@ -1,55 +0,0 @@
-// Cannot implicitly shard accessed collections because of following errmsg: Cannot output to a
-// non-sharded collection because sharded collection exists already.
-// @tags: [
-// assumes_unsharded_collection,
-// # mapReduce does not support afterClusterTime.
-// does_not_support_causal_consistency,
-// does_not_support_stepdowns,
-// uses_map_reduce_with_temp_collections,
-// ]
-t = db.mr_merge2;
-t.drop();
-
-t.insert({a: [1, 2]});
-t.insert({a: [2, 3]});
-t.insert({a: [3, 4]});
-
-outName = "mr_merge2_out";
-out = db[outName];
-out.drop();
-
-m = function() {
- for (i = 0; i < this.a.length; i++)
- emit(this.a[i], 1);
-};
-r = function(k, vs) {
- return Array.sum(vs);
-};
-
-function tos(o) {
- var s = "";
- for (var i = 0; i < 100; i++) {
- if (o[i])
- s += i + "_" + o[i] + "|";
- }
- return s;
-}
-
-outOptions = {
- out: {merge: outName}
-};
-
-res = t.mapReduce(m, r, outOptions);
-expected = {
- "1": 1,
- "2": 2,
- "3": 2,
- "4": 1
-};
-assert.eq(tos(expected), tos(out.convertToSingleObject("value")), "A");
-
-t.insert({a: [4, 5]});
-res = t.mapReduce(m, r, outOptions);
-expected["4"]++;
-expected["5"] = 1;
-assert.eq(tos(expected), tos(out.convertToSingleObject("value")), "B");
diff --git a/jstests/core/mr_mutable_properties.js b/jstests/core/mr_mutable_properties.js
index d93ab8ce343..0110a7a858c 100644
--- a/jstests/core/mr_mutable_properties.js
+++ b/jstests/core/mr_mutable_properties.js
@@ -7,11 +7,13 @@
// See SERVER-9448
// Test argument and receiver (aka 'this') objects and their children can be mutated
// in Map, Reduce and Finalize functions
-var collection = db.mrMutableReceiver;
+(function() {
+"use strict";
+const collection = db.mrMutableReceiver;
collection.drop();
collection.insert({a: 1});
-var map = function() {
+const map = function() {
// set property on receiver
this.feed = {beef: 1};
@@ -21,7 +23,7 @@ var map = function() {
emit(this._id, this.a);
};
-var reduce = function(key, values) {
+const reduce = function(key, values) {
// set property on receiver
this.feed = {beat: 1};
@@ -39,7 +41,7 @@ var reduce = function(key, values) {
return {food: values};
};
-var finalize = function(key, values) {
+const finalize = function(key, values) {
// set property on receiver
this.feed = {ice: 1};
@@ -58,7 +60,7 @@ var finalize = function(key, values) {
return values;
};
-var mr = collection.mapReduce(map, reduce, {finalize: finalize, out: {inline: 1}});
+const mr = collection.mapReduce(map, reduce, {finalize: finalize, out: {inline: 1}});
printjson(mr);
// verify mutated properties exist (order dictated by emit sequence and properties added)
@@ -71,3 +73,4 @@ assert.eq(mr.results[0].value.food[5].cream, 1);
mr.results[0].value.food.forEach(function(val) {
assert.eq(val.mod, 1);
});
+}());
diff --git a/jstests/core/mr_outreduce.js b/jstests/core/mr_outreduce.js
deleted file mode 100644
index ff9e297a5d2..00000000000
--- a/jstests/core/mr_outreduce.js
+++ /dev/null
@@ -1,65 +0,0 @@
-// Cannot implicitly shard accessed collections because of following errmsg: Cannot output to a
-// non-sharded collection because sharded collection exists already.
-// @tags: [
-// assumes_unsharded_collection,
-// # mapReduce does not support afterClusterTime.
-// does_not_support_causal_consistency,
-// does_not_support_stepdowns,
-// uses_map_reduce_with_temp_collections,
-// ]
-
-t = db.mr_outreduce;
-t.drop();
-
-t.insert({_id: 1, a: [1, 2]});
-t.insert({_id: 2, a: [2, 3]});
-t.insert({_id: 3, a: [3, 4]});
-
-outName = "mr_outreduce_out";
-out = db[outName];
-out.drop();
-
-m = function() {
- for (i = 0; i < this.a.length; i++)
- emit(this.a[i], 1);
-};
-r = function(k, vs) {
- return Array.sum(vs);
-};
-
-function tos(o) {
- var s = "";
- for (var i = 0; i < 100; i++) {
- if (o[i])
- s += i + "_" + o[i] + "|";
- }
- return s;
-}
-
-assert.commandWorked(t.mapReduce(m, r, {out: outName}));
-
-expected = {
- "1": 1,
- "2": 2,
- "3": 2,
- "4": 1
-};
-assert.eq(tos(expected), tos(out.convertToSingleObject("value")), "A");
-
-t.insert({_id: 4, a: [4, 5]});
-out.insert({_id: 10, value: "5"}); // this is a sentinal to make sure it wasn't killed
-assert.commandWorked(t.mapReduce(m, r, {out: {reduce: outName}, query: {_id: {$gt: 3}}}));
-
-expected["4"]++;
-expected["5"] = 1;
-expected["10"] = 5;
-assert.eq(tos(expected), tos(out.convertToSingleObject("value")), "B");
-
-t.insert({_id: 5, a: [5, 6]});
-out.insert({_id: 20, value: "10"}); // this is a sentinal to make sure it wasn't killed
-assert.commandWorked(t.mapReduce(m, r, {out: {reduce: outName}, query: {_id: {$gt: 4}}}));
-
-expected["5"]++;
-expected["6"] = 1;
-expected["20"] = 10;
-assert.eq(tos(expected), tos(out.convertToSingleObject("value")), "C");
diff --git a/jstests/core/mr_outreduce2.js b/jstests/core/mr_outreduce2.js
deleted file mode 100644
index a309ad91af8..00000000000
--- a/jstests/core/mr_outreduce2.js
+++ /dev/null
@@ -1,39 +0,0 @@
-// Cannot implicitly shard accessed collections because of following errmsg: Cannot output to a
-// non-sharded collection because sharded collection exists already.
-// @tags: [
-// assumes_unsharded_collection,
-// # mapReduce does not support afterClusterTime.
-// does_not_support_causal_consistency,
-// does_not_support_stepdowns,
-// uses_map_reduce_with_temp_collections,
-// ]
-
-normal = "mr_outreduce2";
-out = normal + "_out";
-
-t = db[normal];
-t.drop();
-
-db[out].drop();
-
-t.insert({_id: 1, x: 1});
-t.insert({_id: 2, x: 1});
-t.insert({_id: 3, x: 2});
-
-m = function() {
- emit(this.x, 1);
-};
-r = function(k, v) {
- return Array.sum(v);
-};
-
-res = t.mapReduce(m, r, {out: {reduce: out}, query: {_id: {$gt: 0}}});
-
-assert.eq(2, db[out].findOne({_id: 1}).value, "A1");
-assert.eq(1, db[out].findOne({_id: 2}).value, "A2");
-
-t.insert({_id: 4, x: 2});
-res = t.mapReduce(m, r, {out: {reduce: out}, query: {_id: {$gt: 3}}});
-
-assert.eq(2, db[out].findOne({_id: 1}).value, "B1");
-assert.eq(2, db[out].findOne({_id: 2}).value, "B2");
diff --git a/jstests/core/mr_reduce.js b/jstests/core/mr_reduce.js
new file mode 100644
index 00000000000..aded1cf471d
--- /dev/null
+++ b/jstests/core/mr_reduce.js
@@ -0,0 +1,104 @@
+// Tests the 'reduce' output mode for mapReduce.
+// Cannot implicitly shard accessed collections because of following errmsg: Cannot output to a
+// non-sharded collection because sharded collection exists already.
+// @tags: [
+// assumes_unsharded_collection,
+// # mapReduce does not support afterClusterTime.
+// does_not_support_causal_consistency,
+// does_not_support_stepdowns,
+// uses_map_reduce_with_temp_collections,
+// ]
+(function() {
+"use strict";
+(function() {
+const source = db.mr_reduce;
+source.drop();
+
+assert.commandWorked(source.insert({_id: 1, a: [1, 2]}));
+assert.commandWorked(source.insert({_id: 2, a: [2, 3]}));
+assert.commandWorked(source.insert({_id: 3, a: [3, 4]}));
+
+const out = db.mr_reduce_out;
+const outName = out.getName();
+out.drop();
+
+const map = function() {
+ for (let i = 0; i < this.a.length; i++)
+ emit(this.a[i], 1);
+};
+const reduce = function(k, vs) {
+ return Array.sum(vs);
+};
+
+assert.commandWorked(source.mapReduce(map, reduce, {out: outName}));
+
+let expected = [{_id: 1, value: 1}, {_id: 2, value: 2}, {_id: 3, value: 2}, {_id: 4, value: 1}];
+assert.docEq(expected, out.find().sort({_id: 1}).toArray());
+
+assert.commandWorked(source.insert({_id: 4, a: [4, 5]}));
+// Insert something that should be unaltered by the mapReduce into the output collection.
+assert.commandWorked(out.insert({_id: 10, value: 5}));
+assert.commandWorked(
+ source.mapReduce(map, reduce, {out: {reduce: outName}, query: {_id: {$gt: 3}}}));
+
+expected = [
+ {_id: 1, value: 1},
+ {_id: 2, value: 2},
+ {_id: 3, value: 2},
+ {_id: 4, value: 2},
+ {_id: 5, value: 1},
+ {_id: 10, value: 5}
+];
+assert.docEq(expected, out.find().sort({_id: 1}).toArray());
+
+assert.commandWorked(source.insert({_id: 5, a: [5, 6]}));
+// Insert something that should be unaltered by the mapReduce into the output collection.
+assert.commandWorked(out.insert({_id: 20, value: 10}));
+assert.commandWorked(
+ source.mapReduce(map, reduce, {out: {reduce: outName}, query: {_id: {$gt: 4}}}));
+
+expected = [
+ {_id: 1, value: 1},
+ {_id: 2, value: 2},
+ {_id: 3, value: 2},
+ {_id: 4, value: 2},
+ {_id: 5, value: 2},
+ {_id: 6, value: 1},
+ {_id: 10, value: 5},
+ {_id: 20, value: 10}
+];
+assert.docEq(expected, out.find().sort({_id: 1}).toArray());
+}());
+(function() {
+const source = db.mr_reduce;
+source.drop();
+
+assert.commandWorked(source.insert({_id: 1, x: 1}));
+assert.commandWorked(source.insert({_id: 2, x: 1}));
+assert.commandWorked(source.insert({_id: 3, x: 2}));
+
+const out = db.mr_reduce_out;
+const outName = out.getName();
+out.drop();
+
+const map = function() {
+ emit(this.x, 1);
+};
+const reduce = function(k, v) {
+ return Array.sum(v);
+};
+
+assert.commandWorked(
+ source.mapReduce(map, reduce, {out: {reduce: outName}, query: {_id: {$gt: 0}}}));
+
+assert.eq(2, out.findOne({_id: 1}).value);
+assert.eq(1, out.findOne({_id: 2}).value);
+
+assert.commandWorked(source.insert({_id: 4, x: 2}));
+assert.commandWorked(
+ source.mapReduce(map, reduce, {out: {reduce: outName}, query: {_id: {$gt: 3}}}));
+
+assert.eq(2, out.findOne({_id: 1}).value);
+assert.eq(2, out.findOne({_id: 2}).value);
+}());
+}());
diff --git a/jstests/core/mr_undef.js b/jstests/core/mr_undef.js
deleted file mode 100644
index 5754a1c3084..00000000000
--- a/jstests/core/mr_undef.js
+++ /dev/null
@@ -1,33 +0,0 @@
-// @tags: [
-// # mapReduce does not support afterClusterTime.
-// does_not_support_causal_consistency,
-// does_not_support_stepdowns,
-// uses_map_reduce_with_temp_collections,
-// ]
-t = db.mr_undef;
-t.drop();
-
-outname = "mr_undef_out";
-out = db[outname];
-out.drop();
-
-t.insert({x: 0});
-
-var m = function() {
- emit(this.mod, this.x);
-};
-var r = function(k, v) {
- total = 0;
- for (i in v) {
- total += v[i];
- }
- return total;
-};
-
-res = t.mapReduce(m, r, {out: outname});
-
-assert.eq(0, out.find({_id: {$type: 6}}).itcount(), "A1");
-assert.eq(1, out.find({_id: {$type: 10}}).itcount(), "A2");
-
-x = out.findOne();
-assert.eq(x, out.findOne({_id: x["_id"]}), "A3");