翻译或纠错本页面

Map-Reduce 例子

mongo 命令行, db.collection.mapReduce() 方法封装了 mapReduce 命令。下面是一些使用 db.collection.mapReduce() 方法的例子:

接下来的map-reduce操作都是在集合 orders 上执行,集合中的文档格式类似于:

{
     _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 } ]
}

计算每个顾客的总金额

首先在 orders 集合上按 cust_id 字段值执行分组map-reduce操作,并对每个分组内文档的 price 字段进行求和操作。

  1. 定义map方法来处理每一个输入文档:

    • 在方法中,this 指的是当前 map-reduce 操作正在处理的文档。

    • 该方法把每一个文档的 pricecust_id 字段映射为一对,并提交 cust_idprice 的配对。

    var mapFunction1 = function() {
                           emit(this.cust_id, this.price);
                       };
    
  2. 定义对应的reduce函数,入参是 keyCustIdvaluesPrices:

    • valuesPrices 字段是一个数组,保存了由map函数提交的按 keyCustId 分组的多个 price 值。

    • reduce函数最终对 valuesPrice 数组内的元素值执行求和运算。

    var reduceFunction1 = function(keyCustId, valuesPrices) {
                              return Array.sum(valuesPrices);
                          };
    
  3. 使用 mapFunction1 方法和 reduceFunction1 方法对 orders 集合中的文档执行 map-reduce。

    db.orders.mapReduce(
                         mapFunction1,
                         reduceFunction1,
                         { out: "map_reduce_example" }
                       )
    

    本次操作的结果输出到 map_reduce_example 集合中。如果 map_reduce_example 集合已经存在,本次操作会把旧的记录覆盖。

计算订单总量和每种 sku 订购量的平均值

在这个例子中,会对集合 orders 中所有的 ord_date 大于 01/01/2012 的文档执行map-reduce操作。该操作对所有文档按 item.sku 字段的值进行分组,并计算订单总数和每种 sku 订购量的总和,同时也会计算每种 sku 的平均值。

  1. 定义map方法来处理每一个输入文档:

    • 在方法中,this 指的是当前 map-reduce 操作正在处理的文档。

    • 该方法逐个处理文档中的每个名目,为每个名目创建一个 skuvalue 的联合,

    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函数,它使用两个参数 keySKUcountObjVals

    • countObjVals 是一个数组字段,保存了从map函数提交给reduce函数的分组后的多个 keySKU 值。

    • 该方法对 countObjVals 数组进行reduce,转换为一个单独的对象 reducedValue

    • reducedVal 中, 字段 count 的值是对数组中每个元素中的 count 值求和的结果,qty 字段的值是对对数组中每个元素中的 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. 定义一个使用两个参数 keyreducedVal 的结束函数。该函数在 reducedVal 中添加一个平均值 avg 字段,然后返回修改后的对象:

    var finalizeFunction2 = function (key, reducedVal) {
    
                           reducedVal.avg = reducedVal.qty/reducedVal.count;
    
                           return reducedVal;
    
                        };
    
  4. orders 集合上执行使用了 mapFunction2, reduceFunction2, 和 finalizeFunction2 方法的 map-reduce 操作。

    db.orders.mapReduce( mapFunction2,
                         reduceFunction2,
                         {
                           out: { merge: "map_reduce_example" },
                           query: { ord_date:
                                      { $gt: new Date('01/01/2012') }
                                  },
                           finalize: finalizeFunction2
                         }
                       )
    

    This operation uses the query field to select only those documents with ord_date greater than new Date(01/01/2012). Then it output the results to a collection map_reduce_example. If the map_reduce_example collection already exists, the operation will merge the existing contents with the results of this map-reduce operation.