Следует также отметить, что, согласно документации , "MongoDB может вызывать функцию уменьшения более одного раза для одного и того же ключа. В этом случае предыдущий вывод из функции уменьшения для этого ключа станет одним из входных значений для следующего вызова функции Reduce для этой клавиши. ".
Также, reduce
должен быть ассоциативным, коммутативным и идемпотентным:
reduce(key, [ C, reduce(key, [ A, B ]) ] ) == reduce( key, [ C, A, B ] )
reduce( key, [ reduce(key, valuesArray) ] ) == reduce( key, valuesArray )
reduce( key, [ A, B ] ) == reduce( key, [ B, A ] )
Таким образом, это означает, что функция reduce
должна быть готова к приему объекта, который является результатом предыдущего вызова самого себя. Что (по крайней мере лично для меня) означает, что лучший способ реализовать mapReduce
- заставить функцию map
(если возможно) выдавать значения в том же формате, что и функция reduce
. Тогда можно реализовать функцию reduce
для поддержки только одного формата ввода. И, как следствие, даже если есть только один объект, испускаемый map
(и, как результат, вызов reduce
пропускается), в конечном результате mapReduce
, значение для ключей, для которых reduce
, который никогда не вызывался, будет по-прежнему иметь тот же формат, что и значение для остальных клавиш.
Например, если у нас есть следующая структура документа:
{
"foo": <some_string>,
"status": ("foo"|"bar")
}
функция map
может быть следующей:
function() {
var value = {
"num_total": 1,
"num_foos": 0,
"num_bars": 0
};
if (this.status == "foo") {
value["num_foos"] += 1;
}
if (this.status == "bar") {
value["num_bars"] += 1;
}
emit(this.foo, value);
}
и функция reduce
будет:
function(key, values) {
var reduced = {
"num_total": 0,
"num_foos": 0,
"num_bars": 0
};
values.forEach(function(val) {
reduced["num_total"] += val["num_total"];
reduced["num_foos"] += val["num_foos"];
reduced["num_bars"] += val["num_bars"];
});
return reduced;
}