英文原文地址:https://docs.mongodb.com/v3.2/tutorial/perform-incremental-map-reduce/。
本文章属个人翻译,作个人学习之用,如有雷同,纯属巧合。如有错误之处,欢迎指正。
执行增量Map-Reduce
Map-reduce操作可以处理复杂的聚集任务。MongoDB对聚集操作提供了mapReduce
命令,在mongo
shell中提供了db.collection.mapReduce()
方法。
如果map-reduce要处理的数据经常增加,你有可能需要执行增量map-reduce,而不是每次在整个数据集上执行map-reduce操作。
要执行增量map-reduce:
- 对当前集合运行map-reduce作业,然后把结果输出到另一个集合。
- 当有更多的数据需要处理时,用以下方法运行后面的map-reduce:
- 用
query
参数来指定条件,只匹配新文档。 - 用
out
参数来指定reduce
操作,把新结果合并至已存在的输出集合中。
- 用
考虑以下例子,在每天结束时定时在sessions
集合上执行map-reduce操作。
数据集
sessions
集合包含了记录每日用户会话的文档,如下:
db.sessions.save( { userid: "a", ts: ISODate('2011-11-03 14:17:00'), length: 95 } );
db.sessions.save( { userid: "b", ts: ISODate('2011-11-03 14:23:00'), length: 110 } );
db.sessions.save( { userid: "c", ts: ISODate('2011-11-03 15:02:00'), length: 120 } );
db.sessions.save( { userid: "d", ts: ISODate('2011-11-03 16:45:00'), length: 45 } );
db.sessions.save( { userid: "a", ts: ISODate('2011-11-04 11:05:00'), length: 105 } );
db.sessions.save( { userid: "b", ts: ISODate('2011-11-04 13:14:00'), length: 120 } );
db.sessions.save( { userid: "c", ts: ISODate('2011-11-04 17:00:00'), length: 130 } );
db.sessions.save( { userid: "d", ts: ISODate('2011-11-04 15:37:00'), length: 65 } );
当前集合的首次Map-Reduce
按如下运行第一次map-reduce操作:
定义一个map函数,把userid映射到包含userid,total_time,count和avg_time字段的对象:
var mapFunction = function() { var key = this.userid; var value = { userid: this.userid, total_time: this.length, count: 1, avg_time: 0 }; emit( key, value ); };
定义相应的reduce函数,它有两个参数:key和values,以计算总时间和总数。key对应的是userid,values是一个数组,其元素对应的是
mapFunction
中映射到userid的各个对象。var reduceFunction = function(key, values) { var reducedObject = { userid: key, total_time: 0, count:0, avg_time:0 }; values.forEach( function(value) { reducedObject.total_time += value.total_time; reducedObject.count += value.count; }); return reducedObject; };
定义一个有两个参数:key和reducedValue 的终止函数。该函数修改reducedValue文档,添加了一个average字段,并返回修改后的文档。
var finalizeFunction = function (key, reducedValue) { if (reducedValue.count > 0) reducedValue.avg_time = reducedValue.total_time / reducedValue.count; return reducedValue; };
用
mapFunction
、reduceFunction
和finalizeFunction
对sessions
集合执行map-reduce操作。把结果输出至session_stat
集合。如果该集合已经存在,该操作将会把它的内容替换掉:db.sessions.mapReduce( mapFunction, reduceFunction, { out: "session_stat", finalize: finalizeFunction } )
后续的增量Map-Reduce
之后,随着
sessions
集合的增长,你可以运行附加的map-reduce操作。例如,添加一些新文档到sessions
集合中:db.sessions.save( { userid: "a", ts: ISODate('2011-11-05 14:17:00'), length: 100 } ); db.sessions.save( { userid: "b", ts: ISODate('2011-11-05 14:23:00'), length: 115 } ); db.sessions.save( { userid: "c", ts: ISODate('2011-11-05 15:02:00'), length: 125 } ); db.sessions.save( { userid: "d", ts: ISODate('2011-11-05 16:45:00'), length: 55 } );
在每天结束时,在
sessions
集合上执行增量map-reduce,要用query
字段来只选出新文档。把结果输出至session_stat
集合,把该集合的内容与增量map-reduce的结果再reduce:db.sessions.mapReduce( mapFunction, reduceFunction, { query: { ts: { $gt: ISODate('2011-11-05 00:00:00') } }, out: { reduce: "session_stat" }, finalize: finalizeFunction } );