第二次修改这篇文章了,添加了我个人的一些理解,所有附加内容均会以红色、加粗表示
Map-Reduce 例子
在mongo数据库命令行中,db.collection,mapReduce()方法被封装成 mapReduce() 命令。接下来的例子会展示该方法。
接下来将在orders集合中使用下列数据来演示map-reduce操作
{
_id: ObjectId("50a8240b927d5d8b5891743c"),
cust_id: "abc123",
ord_date: new Date("Oct 04, 2012"),
status: 'A',
price: 25,
items: [ { sku: "mmm", qty: 5, price: 2.5 },
{ sku: "nnn", qty: 5, price: 2.5 } ]
}
返回每位顾客的消费总金额
其实就是根据 cust_id 分组,再来计算 price 的总和,如果下面的译文部分看不懂没关系,我在下面会有详细的解释的
使用map-reduce操作将orders集合按照 cust_id 分组,以及计算每个 cust_id 分组的 price 总和
1 定义 map 函数来操作该集合中的每个的文档:
。在此函数中,this代表map-reduce正在处理的文档
。此函数将筛选出每个文档的price、cust_id字段,并将此两个字段一起输出 使用 emit 表示输出的字段,这里可以看出就是将原字段中的 cust_id 和 price 字段输出
var mapFunction1 = function() {
emit(this.cust_id, this.price);
};
2 定义对应的reduce函数,此函数有两个输入参数 keyCustId 和 valuesPrices:
。valuesPrices 是一个数组,此数组的内容是map函数中的产生的数据经过 keyCustId 分组产生的
。此函数通将通过求各 valuePrices 数组的和的形式来减少 valuesPrices 中内容的数量
var reduceFunction1 = function(keyCustId, valuesPrices) {
return Array.sum(valuesPrices);
};
3 使用代表 map 的 mapFunctionl 函数,以及代表 reduce 的 reduceFunctionl 函数来构建 map-reduce,处理 orders 中的集合
db.orders.mapReduce(
mapFunction1,
reduceFunction1,
{ out: "map_reduce_example" }
)
该命令的操作结果会在当前数据库中产生一个名曰 map_reduce_example 的集合。如果该集合早已存在数据库中,此命令产生的数据会替换掉旧数据。
这里对上面的内容做一个我的解释,先看下面的图片
是不是看着很专业教科书配图般的存在啊,不耍嘴皮子了。
先看1,表示将当前文本字段中的cust_id、price字段筛选出来,这里注意!cust_id字段接下来是作为分组字段使用的,看下图:
接着传入的 price 就按照 cust_id 进行分组,以数组的形式存储,看上面的[500,250],而且cust_id和price的存储形式是
{cust_id:price},也就是2表示的形式
接着来看3,在reduce函数中,传入的两个参数就是键名,将2的形式拆成3的形式,也就是如下形式了
第一个文档传入 reduce 函数时
{keyCustId:"A123",valuesPrices:[500,250]}
第二个文档传入时
{keyCustId:'B212',valuesPrices:200}
接着在reduce函数中处理完成后,会产生一个返回值,那个返回值就是最终结果value字段的值
来看4,4的_id、value,键名都是固定的,不同的是后面的返回数据,_id 表示分组的依据,也就是 map 函数的第一个参数的值,value的值就是reduce函数的返回值,所以最开始那组数据的最终结果应该是
{_id:'abc123',value:25}
最终结果不会显示出来的,需要查看当前数据库下有一个新建的集合 map_reduce_example,使用find函数查看里面的命令就可以了,下面会展示最后的结果
顺便说一句,Array.sum( Array )是一个求数组中数据和的一个函数
统计订单以及计算每笔订单的总数
在此例子中,你将会在 orders 集合中使用 map-reduce 操作拥有 ord_date 字段,且 ord)date 字段的值大于 01/01/2012 的所有文档。此操作将文档按照 item.sku 字段分组,以及计算订单的数量,统计每个 sku 的数量。此操作通过统计每笔订单的平均值来计算 sku 的值。
1 定义map函数来操作该集合中的每个文档
。在此函数中,this 代表当前操作的文档本身
。对于 item 中的每个记录,此函数使用新定义的变量 value 对象使之与 sku 联系起来,value 对象中包含 count :1 以及 qty 等于文档中的qty 的字段,最终输出key和 value字段
var mapFunction2 = function() {
for (var idx = 0; idx < this.items.length; idx++) {
var key = this.items[idx].sku;
var value = {
count: 1,
qty: this.items[idx].qty
};
emit(key, value);
}
};
2 定义对应的 reduce 函数,此函数拥有两个参数 keySKU 和 countObjVals:
。countObjVals 是一个数组,数组中的内容是经过 map 函数筛选出来的数据经过 keySKU 分组后产生的
。reduce 函数将 countObjVals 数组中的内容缩减后存储在对象 reducedVal 中,此对象中含有 count 和 qty 字段
。在对象 reducedVal 中,count 字段是countObjVals 数组中 count 字段的总和,qty 字段是 coutObjVals 数组中 qty 字段的总和
var reduceFunction2 = function(keySKU, countObjVals) {
reducedVal = { count: 0, qty: 0 };
for (var idx = 0; idx < countObjVals.length; idx++) {
reducedVal.count += countObjVals[idx].count;
reducedVal.qty += countObjVals[idx].qty;
}
return reducedVal;
};
3 定义一个 finalize 函数,此函数包含两个参数,key 和 reduceVal。此函数通过添加一个 avg 字段到 reducedVal 对象中后返回 reducedVal 字段。
var finalizeFunction2 = function (key, reducedVal) {
reducedVal.avg = reducedVal.qty/reducedVal.count;
return reducedVal;
};
4 在 orders 集合中,使用 mapFunctions、reduceFunctions 以及 finalizeFunction2 函数来组成 map-reduce
db.orders.mapReduce( mapFunction2,
reduceFunction2,
{
out: { merge: "map_reduce_example" },
query: { ord_date:
{ $gt: new Date('01/01/2012') }
},
finalize: finalizeFunction2
}
)
此操作使用 query 字段来选择 ord_date 字段的值大于 new Date ('01/01/2012') 的文档。输出结果是一个名为 map_reduce_example的集合,如果数据库中存在该集合,
新的集合中的内容会被添加到旧集合中。
为了便于理解,我直接按照上面图片的格式,把每次函数执行前后的数据写出来,便于大家理解:
再来附一张最终结果的图
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
到上面为止就翻译完了,第一次翻译,还需要多多努力,接下来是一张上面操作的一张图片,也是mongo官网上来的