Функция MapReduce для возврата двух выходных данных.MongoDB - PullRequest
0 голосов
/ 20 мая 2019

В настоящее время я использую базовый mapReduce с использованием MongoDB.

У меня сейчас есть данные, которые выглядят так:

db.football_team.insert({name: "Tane Shane", weight: 93, gender: "m"});
db.football_team.insert({name: "Lily Jones", weight: 45, gender: "f"});
...

Я хочу создать функцию mapReduce для группировки данных по полу и отображения

  1. Общее количество каждого пола, мужчины и женщины
  2. Средний вес каждого пола

Я могу создать функцию отображения / уменьшения, чтобы выполнять каждую функцию отдельно, просто не могу понять, как отобразить вывод для обоих. Я предполагаю, что поскольку группировка основана на Gender, функция Map должна оставаться прежней и просто изменять что-то в секции сокращения ...

Работа до сих пор

var map1 = function()
           {var key = this.gender;
            emit(key, {count:1});}

var reduce1 = function(key, values)
              {var sum=0;
               values.forEach(function(value){sum+=value["count"];});
               return{count: sum};};

db.football_team.mapReduce(map1, reduce1, {out: "gender_stats"});

выход

db.football_team.find()
{"_id" : "f", "value" : {"count": 12} }
{"_id" : "m", "value" : {"count": 18} }

Спасибо

1 Ответ

1 голос
/ 20 мая 2019

Ключевое правило «отобразить / уменьшить» в любой реализации в основном заключается в том, что одна и та же форма данных должна быть отправлена ​​ маппером , что также возвращается редуктор . Ключевая причина этого заключается в том, что концептуально работает схема "map / Reduce", вполне возможно, вызывая reducer несколько раз. Это означает, что вы можете вызывать функцию reducer на выходе, который уже был получен из предыдущего прохода через reducer вместе с другими данными из mapper .

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

Тем не менее, ваш лучший подход к «среднему» - это, следовательно, итого данных вместе с count , а затем просто разделить их. Это фактически добавляет еще один шаг к операции «сопоставление / уменьшение» как finalize function.

db.football_team.mapReduce(
  // mapper
  function() {
    emit(this.gender, { count: 1, weight: this.weight });
  },
  // reducer
  function(key,values) {
    var output = { count: 0, weight: 0 };

    values.forEach(value => {
      output.count += value.count;
      output.weight += value.weight;
    });

    return output;
  },
  // options and finalize
  {
    "out": "gender_stats",   // or { "inline": 1 } if you don't need another collection
    "finalize": function(key,value) {
      value.avg_weight = value.weight / value.count;  // take an average
      delete value.weight;                            // optionally remove the unwanted key

      return value;
    }
  }
)

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

Как уже отмечалось, метод aggregate() на самом деле делает это гораздо более эффективно и в методах с собственным кодированием, которые не несут накладных расходов (и потенциальных угроз безопасности) при интерпретации и исполнении JavaScript на стороне сервера:

db.football_team.aggregate([
  { "$group": {
    "_id": "$gender",
    "count": { "$sum": 1 },
    "avg_weight": { "$avg": "$weight" }
  }}
])

И это в основном все. Более того, вы можете продолжать и делать другие вещи после a $group этапа конвейера (или любого этапа в этом отношении) способами, которые вы не можете сделать с реализацией MongoDB mapReduce. Что-то вроде применения $sort к результатам:

db.football_team.aggregate([
  { "$group": {
    "_id": "$gender",
    "count": { "$sum": 1 },
    "avg_weight": { "$avg": "$weight" }
  }},
  { "$sort": { "avg_weight": -1 } }
])

Единственная сортировка , разрешенная mapReduce, заключается только в том, что ключ , используемый с emit, это всегда , отсортированный в по возрастанию порядок. Но вы не можете сортировать агрегированный результат в выводе любым другим способом, конечно, не выполняя запросы при выводе в другую коллекцию или работая "в памяти" с возвращенными результатами с сервера .


В качестве "дополнительного примечания" (хотя и важного), вы, вероятно, также должны учитывать при "изучении" , что реальность - это "серверный JavaScript « функциональность MongoDB действительно обходной путь больше, чем функция . Когда MongoDB был впервые представлен, он применил движок JavaScript для выполнения сервера, главным образом, чтобы компенсировать функции , которые еще не были реализованы.

Таким образом, к компенсируется отсутствие полной реализации многих операторов запросов и функций агрегирования, которые появятся позже, добавление механизма JavaScript было "быстрым исправлением" , чтобы позволить некоторые вещи должны быть выполнены с минимальной реализацией.

Результатом стало то, что функции движка JavaScript постепенно удаляются. Функция group() API удалена. Функция eval() API устарела и планируется удалить в следующей основной версии. В основном это "на стене" для ограниченного будущего этих функций JavaScript на сервере, поскольку ясная схема - это когда функции native что-то поддерживают, тогда возникает необходимость продолжение поддержки движка JavaScript в основном исчезает.

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

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