Не вызывается отображение базы данных Mongo / Уменьшить — Reduce

#javascript #mongodb #map #mapreduce #reduce

#javascript #mongodb #словарь #mapreduce #уменьшить

Вопрос:

Я пытаюсь выполнить простое уменьшение карты в оболочке 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,
  

}

Что не так?

Я использую 32-разрядную версию MongoDB 1.8.1 в Ubuntu 10.10

Ответ №1:

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

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

это вызовет reduce со следующими параметрами:

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

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

Комментарии:

1. Ты прав, Томаш. Я выбрал этот пример из документов MongoDB. Это очень помогло бы отображать / уменьшать новичков, подобных мне, если бы это было четко упомянуто. Скромный «зеленый чек» для вас!

2. Все еще можно сделать вывод, что reduce получит, используя ваш пример, reduce(_id, [‘Map1’]) в случае отправки только 1 элемента. Отсутствие прохождения через reduce нарушает мой результирующий набор.

3. Я думаю, что не вызывать reduce для отдельных значений — странный выбор реализации. Таким образом, каждый раз, когда вы меняете структуру результата, map вам также приходится корректировать reduce , чтобы получать единообразные результаты в случае результатов с одним значением map .

4. Да, это настоящая боль. Я собираю большой набор данных, и это в основном означает, что я не могу агрегировать наборы, которые содержат только один объект. Странный выбор реализации…

Ответ №2:

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

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

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

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

Комментарии:

1. Спасибо! это очень нелогично, часть заданий reducers заключается в создании элемента определенным образом, который вполне может отличаться.

Ответ №3:

Map reduce соберет значения с общим ключом в одно значение.

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

 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}})
  

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

Ответ №4:

Следует также упомянуть, что, согласно документации, «MongoDB может вызывать функцию уменьшения более одного раза для одного и того же ключа. В этом случае предыдущий вывод функции уменьшения для этого ключа станет одним из входных значений для следующего вызова функции уменьшения для этого ключа.».

Кроме того, 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;
}