MongoDB的聚合操作主要是对数据的批量处理。一般都是将记录按条件分组之后进行一系列求最大值,最小值,平均值的简单操作,也可以对记录进行数据统计,数据挖掘的复杂操作。聚合操作的输入是集中的文档,输出可以是一个文档也可以是多个文档。
MongoDB 提供了三种强大的聚合操作:
Pipeline查询速度快于MapReduce,但是MapReduce的强大之处在于能够在多台Server上并行执行复杂的聚合逻辑。MongoDB不允许Pipeline的单个聚合操作占用过多的系统内存,如果一个聚合操作消耗20%以上的内存,那么MongoDB直接停止操作,并向客户端输出错误消息。
本篇主要讲解 MapReduce 编程模型。MapReduce是一种计算模型,简单的说就是将大批量的工作(数据)分解(MAP)执行,然后再将结果合并成最终结果(REDUCE)。
MapReduce 的基本语法如下:
>db.collection.mapReduce(
function() {emit(key,value);}, //map 函数
function(key,values) {return reduceFunction}, //reduce 函数
{
out: collection,
query: document,
sort: document,
limit: number,
finalize: ,
scope: ,
jsMode: ,
verbose:
}
)
使用 MapReduce 要实现两个函数 Map 函数和 Reduce 函数,Map 函数调用 emit(key, value), 遍历 collection 中所有的记录, 将 key 与 value 传递给 Reduce 函数进行处理。
参数说明:
关于MapReduce的工作流程如下:
在集合 orders 中查找 status:“A” 的数据,并根据 cust_id 来分组,并计算 amount 的总和。
对以下结构的文档进行统计。统计每个用户的文章数量。
>db.col.find()
{
"_id" : ObjectId("5c09dfcde354b306e46af7f3"),
"bookname" : "Java 8 实战",
"author" : "simon",
"status" : "active"
},
{
"_id" : ObjectId("5c09dfdee354b306e46af7f4"),
"bookname" : "MongoDB权威指南",
"author" : "simon",
"status" : "active"
},
{
"_id" : ObjectId("5c09dfffe354b306e46af7f5"),
"bookname" : "MyBatis 实战",
"author" : "simon",
"status" : "disabled"
},
{
"_id" : ObjectId("5c09e016e354b306e46af7f6"),
"bookname" : "MyBatis 从入门到具精通",
"author" : "Aaron",
"status" : "disabled"
},
{
"_id" : ObjectId("5c09e02ce354b306e46af7f7"),
"bookname" : "Spring Boot 2.0",
"author" : "Aaron",
"status" : "active"
},
{
"_id" : ObjectId("5c09e037e354b306e46af7f8"),
"bookname" : "Spring Cloud",
"author" : "Aaron",
"status" : "active"
},
{
"_id" : ObjectId("5c09e038e354b306e46af7f9"),
"bookname" : "Spring Cloud",
"author" : "Aaron",
"status" : "active"
}
将在 col 集合中使用 mapReduce 函数来选取已发布的文章(status:“active”),并通过author分组,计算每个用户的文章数
>db.col.mapReduce(
function() { emit(this.author,1); },
function(key, values) {return Array.sum(values)},
{
query:{status:"active"},
out:"total"
}
)
{
"result" : "total",
"timeMillis" : 422.0,
"counts" : {
"input" : 5,
"emit" : 5,
"reduce" : 2,
"output" : 2
},
"ok" : 1.0,
"_o" : {
"result" : "total",
"timeMillis" : 422,
"counts" : {
"input" : 5,
"emit" : 5,
"reduce" : 2,
"output" : 2
},
"ok" : 1.0
},
"_keys" : [
"result",
"timeMillis",
"counts",
"ok"
],
"_db" : {
"_mongo" : {
"slaveOk" : true,
"host" : "192.168.10.58:27017",
"defaultDB" : "test",
"_readMode" : "commands",
"_writeMode" : "commands"
},
"_name" : "ibase_dev"
},
"_coll" : {
"_mongo" : {
"slaveOk" : true,
"host" : "192.168.10.58:27017",
"defaultDB" : "test",
"_readMode" : "commands",
"_writeMode" : "commands"
},
"_db" : {
"_mongo" : {
"slaveOk" : true,
"host" : "192.168.10.58:27017",
"defaultDB" : "test",
"_readMode" : "commands",
"_writeMode" : "commands"
},
"_name" : "ibase_dev"
},
"_shortName" : "total",
"_fullName" : "ibase_dev.total"
}
}
从结果中可以看出,一共有5个文档服务{status:'active'}
的文档,在map函数中生成了5个键值对文档,最后使用reduce函数将相同的键值分为 2 组。
具体参数说明:
out: { inline: 1 }
不会创建集合,结果在内存中查看执行结果:
>db.total.find()
{
"_id" : "Aaron",
"value" : 3.0
},
{
"_id" : "simon",
"value" : 2.0
}