MapReduce в коллекции MongoDB становится пустым - PullRequest
4 голосов
/ 25 марта 2012

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

Вот как выглядят мои данные (вот17 строк, на самом деле у меня 4+ миллиона):

{"user": 1, "day": 1, "type": "a", "sum": 10}
{"user": 1, "day": 2, "type": "a", "sum": 32}
{"user": 1, "day": 1, "type": "b", "sum": 11}
{"user": 2, "day": 4, "type": "b", "sum": 2}
{"user": 1, "day": 2, "type": "b", "sum": 1}
{"user": 1, "day": 3, "type": "b", "sum": 9}
{"user": 1, "day": 4, "type": "b", "sum": 12}
{"user": 2, "day": 2, "type": "a", "sum": 3}
{"user": 3, "day": 2, "type": "b", "sum": 81}
{"user": 1, "day": 4, "type": "a", "sum": 22}
{"user": 1, "day": 5, "type": "a", "sum": 39}
{"user": 2, "day": 5, "type": "a", "sum": 8}
{"user": 2, "day": 3, "type": "b", "sum": 1}
{"user": 3, "day": 3, "type": "b", "sum": 99}
{"user": 2, "day": 3, "type": "a", "sum": 5}
{"user": 1, "day": 3, "type": "a", "sum": 41}
{"user": 3, "day": 4, "type": "b", "sum": 106}
...  

В конце я пытаюсь, чтобы это выглядело так (массив для каждого типа, где содержимое - это просто суммы всоответствующий индекс, определенный днем, если этот день не существует для этого типа, это просто 0):

{"user": 1, "type_a_sums": [10, 32, 41, 22, 39], "type_b_sums": [11, 1, 9, 12, 0]}
{"user": 2, "type_a_sums": [0, 3, 5, 0, 8], "type_b_sums": [0, 0, 1, 2, 0]}
{"user": 3, "type_a_sums": [0, 0, 0, 0, 0], "type_b_sums": [0, 81, 99, 106, 0]}
...

Это MapReduce, который я пробовал:

var mapsum = function(){
    var output = {user: this.user, type_a_sums: [0, 0, 0, 0, 0], type_b_sums: [0, 0, 0, 0, 0], tempType: this.type, tempSum: this.sum, tempDay: this.day}

    if(this.type == "a") {
        output.type_a_sums[this.day-1] = this.sum;
    }

    if(this.type == "b") {
        output.type_b_sums[this.day-1] = this.sum;
    }

    emit(this.user, output);
};

var r = function(key, values) {
    var outs = {user: 0, type_a_sums: [0, 0, 0, 0, 0], type_b_sums: [0, 0, 0, 0, 0], tempType: -1, tempSum: -1, tempDay: -1}

    values.forEach(function(v){

        outs.user = v.user;

        if(v.tempType == "a") {
            outs.type_a_sums[v.tempDay-1] = v.tempSum;
        }

        if(v.tempType == "b") {
            outs.type_b_sums[v.tempDay-1] = v.tempSum;
        }

    });

    return outs;
};


res = db.sums.mapReduce(mapsum, r, {out: 'joined_sums'})

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

{"user": 1, "type_a_sums": [0, 0, 0, 0, 0], "type_b_sums": [0, 0, 0, 0, 0]}
{"user": 2, "type_a_sums": [0, 3, 5, 0, 8], "type_b_sums": [0, 0, 1, 2, 0]}
{"user": 3, "type_a_sums": [0, 0, 0, 0, 0], "type_b_sums": [0, 0, 0, 0, 0]}

Где большая часть users, которая должнау сумм в их массивах фактически просто заполняются нулями, которые были в фиктивном массиве в объекте reduce functions outs, прежде чем я заполняю его фактической функцией.

Что действительно странно, если язапустить ту же самую функцию на том же соllection, но проверьте только для одного пользователя res = db.sums.mapReduce(mapsum, r, {query: {user: 1}, out: 'joined_sums'}), который, как я знаю, должен иметь суммы в своих массивах, но ранее он показывал все 0, на самом деле я получу вывод, который хотел получить только для этого пользователя.Запустите его снова через все 4 миллиона, и я снова вернусь к нулю.Как будто он просто переписывает всю работу, которую он проделал с фиктивными массивами-заполнителями.

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

1 Ответ

2 голосов
/ 26 марта 2012

Спасибо, что включили много деталей.Здесь есть несколько проблем.

Давайте начнем с вершины.

В конце я пытаюсь сделать так, чтобы это выглядело

{"user ": 2," type_a_sums ": [0, 3, 5, 0, 8]," type_b_sums ": [0, 0, 1, 2, 0]}

Это будет выглядетькак это:

{ _id: { "user": 2 }, value: { "type_a_sums": [0, 3, 5, 0, 8], "type_b_sums": [0, 0, 1, 2, 0] }

Обратите внимание, что _id похож на вашу "группу по", а value похож на ваши столбцы "сумма".

Итак, проблема # 1это то, что вы излучаете user в качестве вашего ключа, но это также часть вашей ценности.Это не обязательно.При уменьшении будут уменьшены только два значения с одним и тем же ключом, эта строка также не нужна: outs.user = v.user;

У вас также есть проблема # 2: ваш reduce неверен .

Я думаю, что это связано с тем, что lower () вызывается более одного раза для каждого ключа.

Цель reduce() состоит в том, что он будет называться множественнымраз.Предполагается масштабировать между серверами.Таким образом, один сервер может вызывать уменьшение пару раз, и эти результаты могут быть объединены и отправлены на другой сервер.

Вот другой способ взглянуть на это. Reduce принимает массив из value объектов и сводит их к одному value объекту .

Здесь есть несколько следствий:

  • Если я это сделаюreduce([a, b]), оно должно быть таким же, как reduce([b, a]).
  • Если я сделаю reduce([a, reduce([b,c])), оно должно быть таким же, как reduce([reduce([a,b]), c])

Так что не должно иметь значения, чтоВ порядке, в котором я их запускаю, или во сколько раз значение уменьшается, это всегда один и тот же вывод.

Если вы посмотрите на свой код, это не то, что происходит.Просто взгляните на type_a_sums.Что произойдет, если я получу следующие два values для уменьшения?

reduce([ [0,0,1,0,0], [0,2,0,0,0] ]) => ???

Для меня это выглядит так, что результат должен быть [0,2,1,0,0].Если это так, то вам не нужны все эти temp_X поля.Вместо этого вам нужно сосредоточиться на emit правильных массивах и затем правильно объединить эти массивы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...