Mongo DB Map / Reduce - Уменьшение не вызывается - PullRequest
8 голосов
/ 10 апреля 2011

Я пытаюсь сделать простое уменьшение карты в оболочке Mongo, но функция сокращения никогда не вызывается.Это мой код:

db.sellers.mapReduce( 
    function(){ emit( this._id, 'Map') } , 
    function(k,vs){ return 'Reduce' }, 
    { out: { inline: 1}})

И результат

{
"results" : [
    {
        "_id" : ObjectId("4da0bdb56bd728c276911e1a"),
        "value" : "Map"
    },
    {
        "_id" : ObjectId("4da0df9a6bd728c276911e1b"),
        "value" : "Map"
    }
],
"timeMillis" : 0,
"counts" : {
    "input" : 2,
    "emit" : 2,
    "output" : 2
},
"ok" : 1,

}

Что не так?

Я использую MongoDB 1.8.1 32 бит на Ubuntu 10.10

Ответы [ 4 ]

18 голосов
/ 10 апреля 2011

Цель reduce состоит в том, чтобы ехем уменьшить набор значений, связанных с данным ключом, в одно значение (агрегированные результаты).Если вы выдаете только одно значение для каждого ключа MapReduce, нет необходимости уменьшать, вся работа выполнена.Но если вы выберете две пары для данного _id, будет вызвано уменьшение:

emit(this._id, 'Map1');
emit(this._id, 'Map2');

, это вызовет уменьшение со следующими параметрами:

reduce(_id, ['Map1', 'Map2'])

Скорее всего, вы захотитеиспользовать _id для ключа MapReduce при фильтрации набора данных: emit только тогда, когда данная запись удовлетворяет некоторому условию.Но опять же, reduce не будет вызываться в этом случае, что ожидается.

6 голосов
/ 18 июля 2014

Ну, MongoDB не вызывает функцию Reduce для клавиши, если для нее есть только одно значение.

На мой взгляд, это плохо. Это должно быть оставлено на усмотрение моего кода редуктора, чтобы решить, следует ли пропустить единственное значение или выполнить какую-либо операцию с ним.

Теперь, если мне нужно выполнить какую-то операцию с единичным значением, я заканчиваю тем, что пишу функцию финализации и в финализации пытаюсь определить, какое значение прошло через редуктор, а какое нет.

Я очень уверен, что в случае Hadoop такого не происходит.

1 голос
/ 23 мая 2016

Следует также отметить, что, согласно документации , "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;
}
1 голос
/ 22 июля 2015

При уменьшении карты значения с общим ключом собираются в одно значение.

В этом случае ничего не нужно делать, потому что каждое значение, излучаемое картой, имеет свой ключ. Никаких сокращений не требуется.

db.sellers.mapReduce( 
    function(){ emit( this._id, 'Map') } , 
    function(k,vs){ return 'Reduce' }, 
    { out: { inline: 1}})

Это не совсем понятно из прочтения документации.

Если вы хотите вызвать сокращение, вы можете жестко закодировать идентификатор, подобный этому:

db.sellers.mapReduce( 
    function(){ emit( 1, 'Map') } , 
    function(k,vs){ return 'Reduce' }, 
    { out: { inline: 1}})

Теперь все значения, излучаемые картой, будут уменьшаться, пока не останется только одно.

...