MongoDB Map Reduce
Map-Reduce — это вычислительная модель. Проще говоря, она разбивает большие объёмы работы (данных) на более мелкие задачи (MAP) для выполнения, а затем объединяет результаты в итоговый результат (REDUCE).
Map-Reduce в MongoDB очень гибок и вполне практичен для крупномасштабного анализа данных.
Команды MapReduce
Ниже приведён базовый синтаксис MapReduce:
>db.collection.mapReduce(
function() {emit(key,value);}, //map 函数
function(key,values) {return reduceFunction}, //reduce 函数
{
out: collection,
query: document,
sort: document,
limit: number
}
)
Чтобы использовать MapReduce, нужно реализовать две функции: функцию Map и функцию Reduce. Функция Map вызывает emit(key, value), перебирает все записи в коллекции и передаёт ключ и значение в функцию Reduce для обработки.
Функция Map должна вызывать emit(key, value), чтобы возвращать пары ключ-значение.
Описание параметра:
- map: функция сопоставления (генерирует последовательность пар ключ-значение, используемую в качестве параметра функции reduce).
- reduceФункция reduce. Задача функции reduce — превратить пары ключ-значение в пару ключ-значение, то есть преобразовать массив значений в одно значение.
- outКоллекция, в которой хранятся результаты статистики (если не указана, используется временная коллекция, которая автоматически удаляется после отключения клиента).
- queryУсловие фильтрации; только документы, удовлетворяющие условию, будут вызывать функцию map. (query, limit, sort можно свободно комбинировать)
- sortПараметр sort в сочетании с limit (также сортирует документы перед отправкой их в функцию map), что может оптимизировать механизм группировки.
- limitВерхний предел количества документов, отправляемых в функцию map (без limit использование только sort не очень полезно).
В следующем примере выполняется поиск данных со status:"A" в коллекции orders, группировка по cust_id и вычисление суммы amount.
Использование MapReduce
Рассмотрим следующую структуру документа для хранения статей пользователя. В документе хранится user_name пользователя и поле status статьи:
>db.posts.insert({
"post_text": "Example,最全的技术文档。",
"user_name": "mark",
"status":"active"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
"post_text": "Example,最全的技术文档。",
"user_name": "mark",
"status":"active"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
"post_text": "Example,最全的技术文档。",
"user_name": "mark",
"status":"active"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
"post_text": "Example,最全的技术文档。",
"user_name": "mark",
"status":"active"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
"post_text": "Example,最全的技术文档。",
"user_name": "mark",
"status":"disabled"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
"post_text": "Example,最全的技术文档。",
"user_name": "example",
"status":"disabled"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
"post_text": "Example,最全的技术文档。",
"user_name": "example",
"status":"disabled"
})
WriteResult({ "nInserted" : 1 })
>db.posts.insert({
"post_text": "Example,最全的技术文档。",
"user_name": "example",
"status":"active"
})
WriteResult({ "nInserted" : 1 })
Теперь мы используем функцию mapReduce для коллекции posts, чтобы выбрать опубликованные статьи (status:"active"), сгруппировать по user_name и вычислить количество статей для каждого пользователя:
>db.posts.mapReduce(
function() { emit(this.user_name,1); },
function(key, values) {return Array.sum(values)},
{
query:{status:"active"},
out:"post_total"
}
)
Вывод вышеуказанного mapReduce:
{
"result" : "post_total",
"timeMillis" : 23,
"counts" : {
"input" : 5,
"emit" : 5,
"reduce" : 1,
"output" : 2
},
"ok" : 1
}
Результат показывает, что есть 5 документов, соответствующих условию запроса (status:"active"), в функции map было сгенерировано 5 документов с парами ключ-значение, и в итоге функция reduce сгруппировала одинаковые значения ключей в 2 группы.
Описание параметров:
- result: имя коллекции, в которой хранятся результаты. Это временная коллекция, которая автоматически удаляется после закрытия соединения MapReduce.
- timeMillis: время выполнения в миллисекундах
- input: количество документов, которые удовлетворяют условию и передаются в функцию map
- emit: количество вызовов emit в функции map, т. е. общий объём данных во всех коллекциях
- output: количество документов в коллекции результатов(count очень помогает при отладке)
- ok: успешно ли выполнено, 1 означает успех
- err: если произошла ошибка, здесь можно найти причину сбоя. Однако, судя по опыту, причина довольно расплывчата и не очень полезна.
Используйте оператор find для просмотра результатов запроса mapReduce:
> var map=function() { emit(this.user_name,1); }
> var reduce=function(key, values) {return Array.sum(values)}
> var options={query:{status:"active"},out:"post_total"}
> db.posts.mapReduce(map,reduce,options)
{ "result" : "post_total", "ok" : 1 }
> db.post_total.find();
Приведённый выше запрос выводит следующие результаты:
{ "_id" : "mark", "value" : 4 }
{ "_id" : "example", "value" : 1 }
Аналогичным образом MapReduce можно использовать для построения больших сложных запросов агрегации.
Функции Map и Reduce можно реализовать с помощью JavaScript, что делает MapReduce очень гибким и мощным.
Другие расширения