Воссоединение с разделенными массивами MapReduce в Mongo

#javascript #arrays #mongodb #mapreduce

#javascript #массивы #mongodb #mapreduce

Вопрос:

Я только изучаю MapReduce. У меня есть следующая функция map reduce, вызываемая для набора пользователей.

 function () {

    m = function () {
            emit(this.city, {num:1, arr:this});
    }

    r = function (key, arr_values) {
            var resultArray = [];
            var count = 0;
            arr_values.forEach(function (value) {
                                    resultArray.push(value);
                                    count  ;
                                });
            return {num:count, arr:resultArray};
    }

    res = db.AdsOnPage.mapReduce(m, r, {out:"ReducedCollection"});


}
  

В итоге это дает мне то, что мне нужно — «город» в качестве ключа, а затем массив пользователей в этом городе в качестве значения. Но на самом деле это дает мне это в абсурдном количестве вложенных массивов. Я предполагаю, что это происходит в результате сегментирования? Но как мне воссоединиться со всем? Прямо сейчас результаты выглядят примерно так:

 {
  "city":"Chicago",
  "value" : {
    "num" : 2.0,
    "arr" : [{
        "num" : 2.0,
        "arr" : [{
            "num" : 1.0,
            "arr" : [{
                <user doc is here>
              }]
          }, {
            "num" : 1.0,
            "arr" : [{
                <user doc is here>
              }]
          }]
      }
.......
for many many arrays
  

Почему это происходит? Есть ли какой-либо способ объединить мои результаты в единый массив?

Ответ №1:

Ничего общего с сегментированием, это связано с логикой отображения / уменьшения.

Функция value from map должна иметь ту же форму, что и возвращаемая из reduce .

Помните, что reduce можно запускать несколько раз. Фактически, в случае сегментирования он будет выполняться один раз для каждого сегмента, а затем снова при mongos выполнении запроса.

Вы думаете о том, что происходит, когда вы запускаете

reduce(key, [a,b,c])

Для работы Map / Reduce выходные данные должны совпадать со следующими:

reduce(key, [a, reduce(key, [b,c]) ) или

reduce(key, [reduce(key, [a,b]), c] )

В вашем случае reduce(key, [b,c]) возвращает массив, поэтому вы получаете следующее:

reduce(key, [a, reduce(key, [b,c]) ) => reduce(key, [a, [b,c] ])

Заметили дополнительный массив? Вот почему вы получаете вложенность.

Решение этой проблемы состоит из двух частей.

  1. Если values это будет массив, то emit следует вывести массив с одним элементом в нем.
  2. Когда вы внесете это изменение, arr_values будет «массив массивов». Вам нужно будет правильно их объединить.

Надеюсь, это указывает вам правильное направление. Для более подробных методов отладки вы можете захотеть взглянуть на страницу, посвященную устранению неполадок M / R.

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

1. Спасибо, @Gates. Но я предполагаю, что реальная проблема заключается в том факте, что даже после решения проблемы вложенности мои результаты возвращаются разделенными на несколько массивов. Вы говорите «объединить их правильно». Как бы я это сделал?

2. В reduce методе arr_values это массив значений. Каждое значение само по себе является массивом. Таким образом, вы не можете сделать resultArray.push(value); , то value , что вы нажимаете, является массивом. Итак, вам понадобится еще один цикл внутри reduce , который перебирает каждое значение.

Ответ №2:

Я использую функцию массива Array.isArray (param) и indexOf (param) для решения такого рода проблем, но я помещаю уникальный элемент в свой массив стека.

 if(Array.isArray(param)) {
    for(var i in param) {
       if(stack.indexOf(param[i]) == -1)
           arr.push(param[i]) ;
    }
}
else {
    if(stack.indexOf(param) == -1)
        arr.push(param) ;
}
  

вы можете создать try Array.isArray().