#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] ])
Заметили дополнительный массив? Вот почему вы получаете вложенность.
Решение этой проблемы состоит из двух частей.
- Если
values
это будет массив, тоemit
следует вывести массив с одним элементом в нем. - Когда вы внесете это изменение,
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().