117 lines
3.8 KiB
JavaScript
117 lines
3.8 KiB
JavaScript
// Tests that mapReduce fails gracefully when given a map or reduce function which fails in some
|
|
// way.
|
|
// @tags: [
|
|
// # mapReduce does not support afterClusterTime.
|
|
// does_not_support_causal_consistency,
|
|
// does_not_support_stepdowns,
|
|
// uses_map_reduce_with_temp_collections,
|
|
// ]
|
|
(function() {
|
|
"use strict";
|
|
|
|
const coll = db.mr_fail_invalid_js;
|
|
const outputColl = db.mr_fail_invalid_js_out;
|
|
|
|
// Test that a map or reduce function which references a path which doesn't exist fails gracefully.
|
|
(function testReferencingInvalidPaths() {
|
|
coll.drop();
|
|
outputColl.drop();
|
|
|
|
assert.commandWorked(coll.insert([
|
|
{x: 1, tags: ["a", "b"]},
|
|
{x: 2, tags: ["b", "c"]},
|
|
{x: 3, tags: ["c", "a"]},
|
|
{x: 4, tags: ["b", "c"]}
|
|
]));
|
|
|
|
let reduceFn = function(key, values) {
|
|
return Array.sum(values);
|
|
};
|
|
|
|
const goodMapFn = function() {
|
|
for (let tag of this.tags) {
|
|
emit(tag, 1);
|
|
}
|
|
};
|
|
assert.commandWorked(coll.mapReduce(goodMapFn, reduceFn, {out: {merge: outputColl.getName()}}));
|
|
outputColl.drop();
|
|
|
|
// mapReduce fails when attempting to merge a missing key.
|
|
const singleInvalidPathMapFn = function() {
|
|
emit(this.missing_field, this.x);
|
|
};
|
|
|
|
assert.throws(() => coll.mapReduce(
|
|
singleInvalidPathMapFn, reduceFn, {out: {merge: outputColl.getName()}}),
|
|
[]);
|
|
|
|
// Now test that a traversal through a missing path will cause an error.
|
|
const badMapFn = function() {
|
|
emit(this._id, this.missing_field.nested_missing);
|
|
};
|
|
|
|
assert.throws(
|
|
() => coll.mapReduce(newMapFn, reduceFn, {out: {merge: outputColl.getName()}}),
|
|
[],
|
|
"expected mapReduce to throw because map function references path that does not exist");
|
|
|
|
// Test the same thing but in the reduce function.
|
|
reduceFn = function(k, v) {
|
|
return v.missing_field.increasingly_missing.all_hope_is_lost;
|
|
};
|
|
assert.throws(
|
|
() => coll.mapReduce(goodMapFn, reduceFn, outputColl.getName()),
|
|
[],
|
|
"expected mapReduce to throw because reduce function references path that does not exist");
|
|
}());
|
|
|
|
// Test that a map function which supplies the wrong number of arguments to 'emit' will fail
|
|
// gracefully.
|
|
(function testBadCallToEmit() {
|
|
coll.drop();
|
|
outputColl.drop();
|
|
assert.commandWorked(coll.insert([{a: [1, 2, 3]}, {a: [2, 3, 4]}]));
|
|
const goodMapFn = function() {
|
|
for (let i = 0; i < this.a.length; i++) {
|
|
emit(this.a[i], 1);
|
|
}
|
|
};
|
|
|
|
const goodReduceFn = function(k, v) {
|
|
let total = 0;
|
|
for (let i = 0; i < v.length; i++)
|
|
total += v[i];
|
|
return total;
|
|
};
|
|
|
|
// First test that a valid command succeeds.
|
|
let res = coll.mapReduce(goodMapFn, goodReduceFn, {out: {merge: outputColl.getName()}});
|
|
|
|
assert.eq([{_id: 1, value: 1}, {_id: 2, value: 2}, {_id: 3, value: 2}, {_id: 4, value: 1}],
|
|
outputColl.find().sort({_id: 1}).toArray());
|
|
assert(outputColl.drop());
|
|
|
|
const badMapFn = function() {
|
|
for (let i = 0; i < this.a.length; i++) {
|
|
emit(this.a[i]);
|
|
}
|
|
};
|
|
|
|
const error = assert.commandFailed(db.runCommand({
|
|
mapReduce: coll.getName(),
|
|
map: badMapFn,
|
|
reduce: goodReduceFn,
|
|
out: outputColl.getName()
|
|
}));
|
|
assert(error.errmsg.indexOf("emit") >= 0, () => tojson(error));
|
|
|
|
// Test that things are still in an ok state and the next mapReduce can succeed.
|
|
outputColl.drop();
|
|
assert.commandWorked(
|
|
coll.mapReduce(goodMapFn, goodReduceFn, {out: {merge: outputColl.getName()}}));
|
|
assert.eq([{_id: 1, value: 1}, {_id: 2, value: 2}, {_id: 3, value: 2}, {_id: 4, value: 1}],
|
|
outputColl.find().sort({_id: 1}).toArray());
|
|
assert(outputColl.drop());
|
|
}());
|
|
}());
|