Docs 菜单

Docs 主页开发应用程序MongoDB Manual

db.collection.mapReduce()

在此页面上

  • 语法
  • 输出
  • 限制
  • 更多信息

注意

作为 Map-Reduce 替代方案的聚合管道

从 MongoDB 5.0 开始, map-reduce已弃用:

有关 map-reduce 的聚合管道替代方案的示例,请参阅:

db.collection.mapReduce(map,reduce, { <options> })

重要

mongosh 方法

本页介绍了 mongosh方法。这不是数据库命令或特定语言驱动程序(例如 Node.js)的文档。

有关数据库命令,请参阅 mapReduce 命令。

对于 MongoDB API 驱动程序,请参阅特定语言的MongoDB 驱动程序文档。

对于传统 mongo Shell 文档,请参阅相应 MongoDB Server 版本的文档:

mongo shell v4.4

注意

视图不支持 map-reduce 操作。

注意

MongoDB 会忽略详细选项。

从版本 4.2 开始,MongoDB 弃用了:

  • 用于创建 新的分片集合的 map-reduce 选项,以及对 map-reduce 使用分片选项。要输出到分片集合,请首先创建分片集合。MongoDB 4.2 还弃用了替换现有分片集合的功能。

db.collection.mapReduce() 通过以下语法实现:

db.collection.mapReduce(
<map>,
<reduce>,
{
out: <collection>,
query: <document>,
sort: <document>,
limit: <number>,
finalize: <function>,
scope: <document>,
jsMode: <boolean>,
verbose: <boolean>,
bypassDocumentValidation: <boolean>
}
)

db.collection.mapReduce() 使用以下参数:

范围
类型
说明
map
JavaScript 或字符串

一个 JavaScript 函数,用于将valuekey关联或“映射”,并发出key和值pair 。 您可以将函数指定为 BSON 类型 JavaScript(即 BSON 类型 13 )或字符串(即 BSON 类型 2 )。

有关更多信息,请参阅map 函数的要求

reduce
JavaScript 或字符串

一个 JavaScript 函数,用于将与特定key关联的所有values “减少”为单个对象。 您可以将函数指定为 BSON 类型 JavaScript(即 BSON 类型 13 )或字符串(即 BSON 类型 2 )。

有关更多信息,请参阅reduce 函数的要求

options
文档

下表描述了db.collection.mapReduce()可以接受的其他参数。

字段
类型
说明
out
字符串或文档

指定 map-reduce 操作结果的位置。您可以输出到集合、通过动作操作输出到集合或内联输出。对集合的主要成员执行 map-reduce 操作时,可以输出到集合;在次节点成员上只能使用 inline 输出。

有关更多信息,请参阅“选项”。

query
文档
使用查询运算符指定选择标准,确定输入到 map 函数的文档。
sort
文档
输入文档排序。此选项对于优化非常有用。例如,将排序键指定为与发出键相同,这样可减少 reduce 操作。排序键必须位于此集合的现有索引中。
limit
数字
指定输入到 map 函数的最大文档数。
finalize
JavaScript 或字符串

可选。 一个 JavaScript 函数,用于修改reduce函数之后的输出。 您可以将函数指定为 BSON 类型 JavaScript(即 BSON 类型 13 )或字符串(即 BSON 类型 2 )。

有关更多信息,请参阅finalize 函数的要求

scope
文档
指定可在 mapreducefinalize 函数中访问的全局变量。reduce
jsMode
布尔

指定是否在计算 mapreduce 函数之间将中间数据转换为 BSON 格式。

默认值为 false

如果为 false

  • 在内部,MongoDB 将 map 函数发出的 JavaScript 对象转换为 BSON 对象。然后,在调用 reduce 函数时,这些 BSON 对象会转换回 JavaScript 对象。

  • map-reduce 操作将中间 BSON 对象放置在临时磁盘存储中。这允许对任意大的数据集执行 map-reduce 操作。

如果为 true

  • 在内部,map 函数期间发出的 JavaScript 对象仍保留为 JavaScript 对象。无需转换 reduce 函数的对象,这可以提高执行速度。

  • 对于映射器 emit() 函数的 key 参数少于 500,000 个的结果集,只能使用jsMode

verbose
布尔

指定是否在结果信息中包含 timing 信息。将 verbose 设置为 true,以包含 timing 信息。

默认值为 false

从 MongoDB4 4开始。 ,此选项将被忽略。结果信息始终不包括timing 信息。您可以通过在db.collection.explain() db.collection.mapReduce()"executionStats""allPlansExecution"verbosity 模式下运行带有 的 来查看计时信息。

collation
文档

可选。

指定用于操作的排序规则

排序规则允许用户为字符串比较指定特定于语言的规则,例如字母大小写和重音符号规则。

排序规则选项的语法如下:

collation: {
locale: <string>,
caseLevel: <boolean>,
caseFirst: <string>,
strength: <int>,
numericOrdering: <boolean>,
alternate: <string>,
maxVariable: <string>,
backwards: <boolean>
}

指定排序规则时,locale 字段为必填字段;所有其他排序规则字段均为可选字段。有关字段的说明,请参阅排序规则文档

如果未指定排序规则,但集合具有默认排序规则(请参阅 db.createCollection()),则操作将使用为集合指定的排序规则。

如果没有为收集或操作指定排序规则,MongoDB 将使用先前版本中使用的简单二进制比较来进行字符串比较。

您不能为一个操作指定多个排序规则。例如,您不能为每个字段指定不同的排序规则,或者如果执行带排序的查找,则不能使用一种排序规则进行查找而另一种排序规则进行排序。

版本 3.4 中的新增功能

bypassDocumentValidation
布尔

可选。启用 mapReduce 可在操作过程中绕过文档验证。这样就可以插入不符合验证要求的文件。

版本 3.2 中的新增功能

注意

map-reduce operations$where操作符表达式无法访问 mongosh 中提供的某些全局函数或属性,例如db

以下 JavaScript 函数和属性用于 map-reduce operations$where 操作符表达式:

可用属性
可用函数
args
MaxKey
MinKey
assert()
BinData()
DBPointer()
DBRef()
doassert()
emit()
gc()
HexData()
hex_md5()
isNumber()
isObject()
ISODate()
isString()
Map()
MD5()
NumberInt()
NumberLong()
ObjectId()
print()
printjson()
printjsononeline()
sleep()
Timestamp()
tojson()
tojsononeline()
tojsonObject()
UUID()
version()

map 函数负责将每个输入文档转换为零个或多个文档。它可以访问 scope 参数中定义的变量,并具有以下原型:

function() {
...
emit(key, value);
}

map 函数具有以下要求:

  • map 函数中,将当前文档引用为函数内的 this

  • map 函数不应 出于任何原因访问数据库。

  • map 函数应该是纯函数,或者对函数外部没有影响(即副作用)。

  • map 函数可以选择调用 emit(key,value) 任意次,以创建将 keyvalue 关联的输出文档。

以下 map 函数将调用 emit(key,value) 0 次或 1 次,具体取决于输入文档的 status 字段的值:

function() {
if (this.status == 'A')
emit(this.cust_id, 1);
}

以下 map 函数可能会多次调用 emit(key,value) ,具体取决于输入文档的 items 字段中的元素数量:

function() {
this.items.forEach(function(item){ emit(item.sku, 1); });
}

reduce 函数具有以下原型:

function(key, values) {
...
return result;
}

reduce 函数表现出以下行为:

  • reduce 函数不应访问数据库,即使是执行读操作也是如此。

  • reduce 函数不应 影响外部系统。

  • MongoDB 可以针对同一键多次调用 reduce 函数。在这种情况下,该键的 reduce 函数的先前输出将成为该键的下一个 reduce 函数调用的输入值之一。

  • reduce 函数可以访问 scope 参数中定义的变量。

  • reduce 的输入不得大于 MongoDB 最大 BSON 文档大小的一半。当返回大型文档并在后续的 reduce 步骤中将其连接在一起时,可能会违反此要求。

由于可以针对同一键多次调用 reduce 函数,因此以下属性必须为 true:

  • 返回对象的类型必须map 函数发出的 value 的类型相同

  • reduce 函数必须是关联函数。以下语句必须为 true:

    reduce(key, [ C, reduce(key, [ A, B ]) ] ) == reduce( key, [ C, A, B ] )
  • reduce 函数必须是幂等的。确保以下语句为 true:

    reduce( key, [ reduce(key, valuesArray) ] ) == reduce( key, valuesArray )
  • reduce 函数应该是可交换的:也就是说,valuesArray 中元素的顺序不应影响 reduce 函数的输出,因此以下语句成立:

    reduce( key, [ A, B ] ) == reduce( key, [ B, A ] )

您可以为 out 参数指定以下选项:

此选项输出到新集合,并且在副本集的从节点上不可用。

out: <collectionName>

注意

从版本 4.2 开始,MongoDB 弃用了:

  • 用于创建 新的分片集合的 map-reduce 选项,以及对 map-reduce 使用分片选项。要输出到分片集合,请首先创建分片集合。MongoDB 4.2 还弃用了替换现有分片集合的功能。

仅当将已存在的集合传递给 out 时,此选项才可用。它不适用于副本集的从节点。

out: { <action>: <collectionName>
[, db: <dbName>]
[, sharded: <boolean> ] }

当使用操作输出到集合时,out 具有以下参数:

  • <action>:指定以下操作之一:

    • replace

      如果存在具有 <collectionName> 的集合,则替换 <collectionName> 的内容。

    • merge

      如果输出集合已经存在,则将新结果与现有结果合并。如果现有文档与新结果具有相同的键,则覆盖该现有文档。

    • reduce

      如果输出集合已经存在,则将新结果与现有结果合并。如果现有文档与新结果具有相同的键,请将 reduce 函数应用于新文档和现有文档,并使用结果覆盖现有文档。

  • db

    可选。您希望 map-Reduce 操作写入其输出的数据库的名称。默认情况下,这将是与输入集合相同的数据库。

  • sharded

    注意

    从版本 4.2 开始,不推荐使用 sharded 选项。

    可选。如果为 true 并且您已在输出数据库上启用分片,则 Map-Reduce 操作将使用 _id 字段作为分片键对输出集合进行分片。

    如果为 truecollectionName 是现有的未分片集合,则 map-reduce 会失败。

在内存中执行 map-reduce 操作并返回结果。此选项是副本集的从节点上 out 的唯一可用选项。

out: { inline: 1 }

结果必须符合 BSON 文档的最大大小

finalize 函数具有以下原型:

function(key, reducedValue) {
...
return modifiedObject;
}

finalize 函数接收 key 值和来自 reduce 函数的 reducedValue 作为其参数。请注意:

  • finalize 函数不应 出于任何原因访问数据库。

  • finalize 函数应该是纯函数,或者对函数外部没有影响(即副作用)。

  • finalize 函数可以访问 scope 参数中定义的变量。

本节中的示例包括不带自定义聚合表达式的聚合管道替代方案。有关使用自定义表达式的替代方案,请参阅Map-Reduce 到聚合管道转换示例。

创建一个包含以下文档的样本集合 orders

db.orders.insertMany([
{ _id: 1, cust_id: "Ant O. Knee", ord_date: new Date("2020-03-01"), price: 25, items: [ { sku: "oranges", qty: 5, price: 2.5 }, { sku: "apples", qty: 5, price: 2.5 } ], status: "A" },
{ _id: 2, cust_id: "Ant O. Knee", ord_date: new Date("2020-03-08"), price: 70, items: [ { sku: "oranges", qty: 8, price: 2.5 }, { sku: "chocolates", qty: 5, price: 10 } ], status: "A" },
{ _id: 3, cust_id: "Busby Bee", ord_date: new Date("2020-03-08"), price: 50, items: [ { sku: "oranges", qty: 10, price: 2.5 }, { sku: "pears", qty: 10, price: 2.5 } ], status: "A" },
{ _id: 4, cust_id: "Busby Bee", ord_date: new Date("2020-03-18"), price: 25, items: [ { sku: "oranges", qty: 10, price: 2.5 } ], status: "A" },
{ _id: 5, cust_id: "Busby Bee", ord_date: new Date("2020-03-19"), price: 50, items: [ { sku: "chocolates", qty: 5, price: 10 } ], status: "A"},
{ _id: 6, cust_id: "Cam Elot", ord_date: new Date("2020-03-19"), price: 35, items: [ { sku: "carrots", qty: 10, price: 1.0 }, { sku: "apples", qty: 10, price: 2.5 } ], status: "A" },
{ _id: 7, cust_id: "Cam Elot", ord_date: new Date("2020-03-20"), price: 25, items: [ { sku: "oranges", qty: 10, price: 2.5 } ], status: "A" },
{ _id: 8, cust_id: "Don Quis", ord_date: new Date("2020-03-20"), price: 75, items: [ { sku: "chocolates", qty: 5, price: 10 }, { sku: "apples", qty: 10, price: 2.5 } ], status: "A" },
{ _id: 9, cust_id: "Don Quis", ord_date: new Date("2020-03-20"), price: 55, items: [ { sku: "carrots", qty: 5, price: 1.0 }, { sku: "apples", qty: 10, price: 2.5 }, { sku: "oranges", qty: 10, price: 2.5 } ], status: "A" },
{ _id: 10, cust_id: "Don Quis", ord_date: new Date("2020-03-23"), price: 25, items: [ { sku: "oranges", qty: 10, price: 2.5 } ], status: "A" }
])

针对 orders 集合执行 map-reduce 操作会按 cust_id 进行分组,并为每个 cust_id 计算 price 之和:

  1. 定义 Map 函数来处理每个输入文档:

    • 在函数中,this 指的是 Map-Reduce 操作正在处理的文档。

    • 该函数将每个文档的 price 映射到 cust_id,并输出 cust_idprice

    var mapFunction1 = function() {
    emit(this.cust_id, this.price);
    };
  2. 使用两个参数 keyCustIdvaluesPrices 定义相应的 Reduce 函数:

    • valuesPrices 是一个数组,其元素是由 Map 函数发出并按 keyCustId 分组的 price 值。

    • 该函数将 valuesPrice 数组缩减为其元素之和。

    var reduceFunction1 = function(keyCustId, valuesPrices) {
    return Array.sum(valuesPrices);
    };
  3. 使用 mapFunction1 Map 函数和 reduceFunction1 Reduce 函数对 orders 集合中的所有文档执行 Map-Reduce:

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

    此操作将结果输出到名为 map_reduce_example 的集合。如果 map_reduce_example 集合已存在,该操作将用此 Map-Reduce 操作的结果替换其内容。

  4. 查询 map_reduce_example 集合以验证结果:

    db.map_reduce_example.find().sort( { _id: 1 } )

    该操作会返回以下文档:

    { "_id" : "Ant O. Knee", "value" : 95 }
    { "_id" : "Busby Bee", "value" : 125 }
    { "_id" : "Cam Elot", "value" : 60 }
    { "_id" : "Don Quis", "value" : 155 }

利用可用的聚合管道操作符,您可以重写 Map-Reduce 操作,而无需定义自定义函数:

db.orders.aggregate([
{ $group: { _id: "$cust_id", value: { $sum: "$price" } } },
{ $out: "agg_alternative_1" }
])
  1. $group 阶段按 cust_id 分组并计算 value 字段(另见 $sum)。value 字段包含每个 cust_idprice 总额。

    此阶段将以下文档输出到下一阶段:

    { "_id" : "Don Quis", "value" : 155 }
    { "_id" : "Ant O. Knee", "value" : 95 }
    { "_id" : "Cam Elot", "value" : 60 }
    { "_id" : "Busby Bee", "value" : 125 }
  2. 然后,$out 将输出写入集合 agg_alternative_1。或者,你可以使用 $merge 代替 $out

  3. 查询 agg_alternative_1 集合以验证结果:

    db.agg_alternative_1.find().sort( { _id: 1 } )

    该操作将返回以下文档:

    { "_id" : "Ant O. Knee", "value" : 95 }
    { "_id" : "Busby Bee", "value" : 125 }
    { "_id" : "Cam Elot", "value" : 60 }
    { "_id" : "Don Quis", "value" : 155 }

提示

另请参阅:

有关使用自定义聚合表达式的替代方案,请参阅 Map-Reduce 到聚合管道转换示例

在以下示例中,您将看到针对 ord_date 值大于或等于 2020-03-01 的所有文档对 orders 集合执行的 Map-Reduce 操作。

示例中的操作:

  1. item.sku 字段分组,计算每个 sku 的订单数量和总订购量。

  2. 计算每个 sku 值的每个订单的平均数量,并将结果合并到输出集合中。

合并结果时,如果现有文档与新结果具有相同的键,则该操作将覆盖现有文档。如果没有具有相同键的现有文档,操作将插入该文档。

步骤示例:

  1. 定义 Map 函数来处理每个输入文档:

    • 在函数中,this 指的是 Map-Reduce 操作正在处理的文档。

    • 对于每款商品,该函数将 sku 与新对象 value 关联,该对象包含 1count 和订单的商品 qty,并发出 sku(存储在 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. 使用两个参数 keySKUcountObjVals 定义相应的 Reduce 函数:

    • countObjVals 是一个数组,其元素是映射到分组 keySKU 值的对象,这些值由 Map 函数传递到 Reducer 函数。

    • 该函数将 countObjVals 数组缩减为包含 countqty 字段的单个对象 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 定义 finalize 函数。该函数会修改 reducedVal 对象以添加名为 avg 的计算字段并返回修改后的对象:

    var finalizeFunction2 = function (key, reducedVal) {
    reducedVal.avg = reducedVal.qty/reducedVal.count;
    return reducedVal;
    };
  4. 使用 mapFunction2reduceFunction2finalizeFunction2 函数对 orders 集合执行 Map-Reduce 操作:

    db.orders.mapReduce(
    mapFunction2,
    reduceFunction2,
    {
    out: { merge: "map_reduce_example2" },
    query: { ord_date: { $gte: new Date("2020-03-01") } },
    finalize: finalizeFunction2
    }
    );

    此操作使用 query 字段来仅选择 ord_date 大于或等于 new Date("2020-03-01") 的文档。然后它将结果输出到集合 map_reduce_example2

    如果 map_reduce_example2 集合已存在,该操作会将现有内容与此 Map-Reduce 操作的结果合并。即,如果现有文档的键与新结果相同,则操作会覆盖现有文档。如果没有具有相同键的现有文档,操作将插入该文档。

  5. 查询 map_reduce_example2 集合以验证结果:

    db.map_reduce_example2.find().sort( { _id: 1 } )

    该操作会返回以下文档:

    { "_id" : "apples", "value" : { "count" : 4, "qty" : 35, "avg" : 8.75 } }
    { "_id" : "carrots", "value" : { "count" : 2, "qty" : 15, "avg" : 7.5 } }
    { "_id" : "chocolates", "value" : { "count" : 3, "qty" : 15, "avg" : 5 } }
    { "_id" : "oranges", "value" : { "count" : 7, "qty" : 63, "avg" : 9 } }
    { "_id" : "pears", "value" : { "count" : 1, "qty" : 10, "avg" : 10 } }

利用可用的聚合管道操作符,您可以重写 Map-Reduce 操作,而无需定义自定义函数:

db.orders.aggregate( [
{ $match: { ord_date: { $gte: new Date("2020-03-01") } } },
{ $unwind: "$items" },
{ $group: { _id: "$items.sku", qty: { $sum: "$items.qty" }, orders_ids: { $addToSet: "$_id" } } },
{ $project: { value: { count: { $size: "$orders_ids" }, qty: "$qty", avg: { $divide: [ "$qty", { $size: "$orders_ids" } ] } } } },
{ $merge: { into: "agg_alternative_3", on: "_id", whenMatched: "replace", whenNotMatched: "insert" } }
] )
  1. $match 阶段仅选择 ord_date 大于等于 new Date("2020-03-01") 的文档。

  2. $unwind 阶段按 items 数组字段对文档进行分解,为每个数组元素输出一个文档。例如:

    { "_id" : 1, "cust_id" : "Ant O. Knee", "ord_date" : ISODate("2020-03-01T00:00:00Z"), "price" : 25, "items" : { "sku" : "oranges", "qty" : 5, "price" : 2.5 }, "status" : "A" }
    { "_id" : 1, "cust_id" : "Ant O. Knee", "ord_date" : ISODate("2020-03-01T00:00:00Z"), "price" : 25, "items" : { "sku" : "apples", "qty" : 5, "price" : 2.5 }, "status" : "A" }
    { "_id" : 2, "cust_id" : "Ant O. Knee", "ord_date" : ISODate("2020-03-08T00:00:00Z"), "price" : 70, "items" : { "sku" : "oranges", "qty" : 8, "price" : 2.5 }, "status" : "A" }
    { "_id" : 2, "cust_id" : "Ant O. Knee", "ord_date" : ISODate("2020-03-08T00:00:00Z"), "price" : 70, "items" : { "sku" : "chocolates", "qty" : 5, "price" : 10 }, "status" : "A" }
    { "_id" : 3, "cust_id" : "Busby Bee", "ord_date" : ISODate("2020-03-08T00:00:00Z"), "price" : 50, "items" : { "sku" : "oranges", "qty" : 10, "price" : 2.5 }, "status" : "A" }
    { "_id" : 3, "cust_id" : "Busby Bee", "ord_date" : ISODate("2020-03-08T00:00:00Z"), "price" : 50, "items" : { "sku" : "pears", "qty" : 10, "price" : 2.5 }, "status" : "A" }
    { "_id" : 4, "cust_id" : "Busby Bee", "ord_date" : ISODate("2020-03-18T00:00:00Z"), "price" : 25, "items" : { "sku" : "oranges", "qty" : 10, "price" : 2.5 }, "status" : "A" }
    { "_id" : 5, "cust_id" : "Busby Bee", "ord_date" : ISODate("2020-03-19T00:00:00Z"), "price" : 50, "items" : { "sku" : "chocolates", "qty" : 5, "price" : 10 }, "status" : "A" }
    ...
  3. $group 阶段按 items.sku 分组,针对每个 sku 进行计算:

    • qty 字段。qty 字段包含
      每个 items.sku 的订购 qty 总计(参见 $sum)。
    • orders_ids 数组。orders_ids 字段包含一个
      items.sku 的不同顺序 _id 的数组(参见 $addToSet)。
    { "_id" : "chocolates", "qty" : 15, "orders_ids" : [ 2, 5, 8 ] }
    { "_id" : "oranges", "qty" : 63, "orders_ids" : [ 4, 7, 3, 2, 9, 1, 10 ] }
    { "_id" : "carrots", "qty" : 15, "orders_ids" : [ 6, 9 ] }
    { "_id" : "apples", "qty" : 35, "orders_ids" : [ 9, 8, 1, 6 ] }
    { "_id" : "pears", "qty" : 10, "orders_ids" : [ 3 ] }
  4. $project 阶段会重塑输出文档以镜像 Map-Reduce 的输出,使其具有两个字段 _idvalue$project 会:

  5. $unwind 阶段按 items 数组字段对文档进行分解,为每个数组元素输出一个文档。例如:

    { "_id" : 1, "cust_id" : "Ant O. Knee", "ord_date" : ISODate("2020-03-01T00:00:00Z"), "price" : 25, "items" : { "sku" : "oranges", "qty" : 5, "price" : 2.5 }, "status" : "A" }
    { "_id" : 1, "cust_id" : "Ant O. Knee", "ord_date" : ISODate("2020-03-01T00:00:00Z"), "price" : 25, "items" : { "sku" : "apples", "qty" : 5, "price" : 2.5 }, "status" : "A" }
    { "_id" : 2, "cust_id" : "Ant O. Knee", "ord_date" : ISODate("2020-03-08T00:00:00Z"), "price" : 70, "items" : { "sku" : "oranges", "qty" : 8, "price" : 2.5 }, "status" : "A" }
    { "_id" : 2, "cust_id" : "Ant O. Knee", "ord_date" : ISODate("2020-03-08T00:00:00Z"), "price" : 70, "items" : { "sku" : "chocolates", "qty" : 5, "price" : 10 }, "status" : "A" }
    { "_id" : 3, "cust_id" : "Busby Bee", "ord_date" : ISODate("2020-03-08T00:00:00Z"), "price" : 50, "items" : { "sku" : "oranges", "qty" : 10, "price" : 2.5 }, "status" : "A" }
    { "_id" : 3, "cust_id" : "Busby Bee", "ord_date" : ISODate("2020-03-08T00:00:00Z"), "price" : 50, "items" : { "sku" : "pears", "qty" : 10, "price" : 2.5 }, "status" : "A" }
    { "_id" : 4, "cust_id" : "Busby Bee", "ord_date" : ISODate("2020-03-18T00:00:00Z"), "price" : 25, "items" : { "sku" : "oranges", "qty" : 10, "price" : 2.5 }, "status" : "A" }
    { "_id" : 5, "cust_id" : "Busby Bee", "ord_date" : ISODate("2020-03-19T00:00:00Z"), "price" : 50, "items" : { "sku" : "chocolates", "qty" : 5, "price" : 10 }, "status" : "A" }
    ...
  6. $group 阶段按 items.sku 分组,针对每个 sku 进行计算:

    • qty 字段。qty 字段包含使用 $sum 为每个 items.sku 订购的 qty 总计。

    • orders_ids 数组。orders_ids 字段包含使用 $addToSetitems.sku 的不同顺序 _id 的数组。

    { "_id" : "chocolates", "qty" : 15, "orders_ids" : [ 2, 5, 8 ] }
    { "_id" : "oranges", "qty" : 63, "orders_ids" : [ 4, 7, 3, 2, 9, 1, 10 ] }
    { "_id" : "carrots", "qty" : 15, "orders_ids" : [ 6, 9 ] }
    { "_id" : "apples", "qty" : 35, "orders_ids" : [ 9, 8, 1, 6 ] }
    { "_id" : "pears", "qty" : 10, "orders_ids" : [ 3 ] }
  7. $project 阶段会重塑输出文档以镜像 Map-Reduce 的输出,使其具有两个字段 _idvalue$project 会:

    • 使用 $sizevalue.count 设置为 orders_ids 数组的大小

    • value.qty 设置为输入文档中的 qty 字段。

    • 使用 $divide$sizevalue.avg 设置为每个订单的平均数量

    { "_id" : "apples", "value" : { "count" : 4, "qty" : 35, "avg" : 8.75 } }
    { "_id" : "pears", "value" : { "count" : 1, "qty" : 10, "avg" : 10 } }
    { "_id" : "chocolates", "value" : { "count" : 3, "qty" : 15, "avg" : 5 } }
    { "_id" : "oranges", "value" : { "count" : 7, "qty" : 63, "avg" : 9 } }
    { "_id" : "carrots", "value" : { "count" : 2, "qty" : 15, "avg" : 7.5 } }
  8. 最后,$merge 将输出写入集合 agg_alternative_3。如果现有文档的键 _id 与新结果相同,则操作会覆盖现有文档。如果没有具有相同键的现有文档,操作将插入该文档。

  9. 查询 agg_alternative_3 集合以验证结果:

    db.agg_alternative_3.find().sort( { _id: 1 } )

    该操作将返回以下文档:

    { "_id" : "apples", "value" : { "count" : 4, "qty" : 35, "avg" : 8.75 } }
    { "_id" : "carrots", "value" : { "count" : 2, "qty" : 15, "avg" : 7.5 } }
    { "_id" : "chocolates", "value" : { "count" : 3, "qty" : 15, "avg" : 5 } }
    { "_id" : "oranges", "value" : { "count" : 7, "qty" : 63, "avg" : 9 } }
    { "_id" : "pears", "value" : { "count" : 1, "qty" : 10, "avg" : 10 } }

提示

另请参阅:

有关使用自定义聚合表达式的替代方案,请参阅 Map-Reduce 到聚合管道转换示例

db.collection.mapReduce()方法的输出与mapReduce命令的输出相同。有关db.collection.mapReduce()输出的信息,请参阅mapReduce命令的输出部分。

MongoDB 驱动程序会自动为与 因果一致会话 关联的操作设置 afterClusterTime 。从 MongoDB4 开始。2 中, 不再支持db.collection.mapReduce() db.collection.mapReduce()afterClusterTime 。因此, 不能与 因果一致的会话关联。

← db.collection.latencyStats()