Mongodb 多文档聚合操作处理方法(Map-reduce 函数)

聚合

聚合操作处理多个文档并返回计算结果。您可以使用聚合操作来:

  • 将多个文档中的值分组在一起。

  • 对分组数据执行操作以返回单个结果。

  • 分析数据随时间的变化。

要执行聚合操作,您可以使用:

  • 聚合管道

  • 单一目的聚合方法

  • Map-reduce 函数

Map-reduce 函数

在mongoshell 中,该db.collection.mapReduce() 方法是命令的包装器mapReduce。下面的例子使用该db.collection.mapReduce()方法。

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

该map功能有以下要求:

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

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

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

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

# 原型如下:
function() {...emit(key, value);
}

该reduce函数表现出以下行为:

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

  • 该reduce功能不应影响外部系统。

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

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

# 该reduce函数具有以下原型:
function(key, values) {...return result;
}

插入测试数据。如下:

sit_rs1:PRIMARY> db.orders.insertMany([
...    { _id: 1, cust_id: "A", ord_date: new Date("2023-06-01"), price: 15, items: [ { sku: "apple", qty: 5, price: 2.5 }, { sku: "apples", qty: 5, price: 2.5 } ], status: "1" },
...    { _id: 2, cust_id: "A", ord_date: new Date("2023-06-08"), price: 60, items: [ { sku: "apple", qty: 8, price: 2.5 }, { sku: "banana", qty: 5, price: 10 } ], status: "1" },
...    { _id: 3, cust_id: "B", ord_date: new Date("2023-06-08"), price: 55, items: [ { sku: "apple", qty: 10, price: 2.5 }, { sku: "pears", qty: 10, price: 2.5 } ], status: "1" },
...    { _id: 4, cust_id: "B", ord_date: new Date("2023-06-18"), price: 26, items: [ { sku: "apple", qty: 10, price: 2.5 } ], status: "1" },
...    { _id: 5, cust_id: "B", ord_date: new Date("2023-06-19"), price: 40, items: [ { sku: "banana", qty: 5, price: 10 } ], status: "1"},
...    { _id: 6, cust_id: "C", ord_date: new Date("2023-06-19"), price: 38, items: [ { sku: "carrots", qty: 10, price: 1.0 }, { sku: "apples", qty: 10, price: 2.5 } ], status: "1" },
...    { _id: 7, cust_id: "C", ord_date: new Date("2023-06-20"), price: 21, items: [ { sku: "apple", qty: 10, price: 2.5 } ], status: "1" },
...    { _id: 8, cust_id: "D", ord_date: new Date("2023-06-20"), price: 76, items: [ { sku: "banana", qty: 5, price: 10 }, { sku: "apples", qty: 10, price: 2.5 } ], status: "1" },
...    { _id: 9, cust_id: "D", ord_date: new Date("2023-06-20"), price: 51, items: [ { sku: "carrots", qty: 5, price: 1.0 }, { sku: "apples", qty: 10, price: 2.5 }, { sku: "apple", qty: 10, price: 2.5 } ], status: "1" },
...    { _id: 10, cust_id: "D", ord_date: new Date("2023-06-23"), price: 23, items: [ { sku: "apple", qty: 10, price: 2.5 } ], status: "1" }
... ])
{"acknowledged" : true,"insertedIds" : [1,2,3,4,5,6,7,8,9,10]
}
sit_rs1:PRIMARY> db.orders.find()
{ "_id" : 4, "cust_id" : "B", "ord_date" : ISODate("2023-06-18T00:00:00Z"), "price" : 26, "items" : [ { "sku" : "apple", "qty" : 10, "price" : 2.5 } ], "status" : "1" }
{ "_id" : 6, "cust_id" : "C", "ord_date" : ISODate("2023-06-19T00:00:00Z"), "price" : 38, "items" : [ { "sku" : "carrots", "qty" : 10, "price" : 1 }, { "sku" : "apples", "qty" : 10, "price" : 2.5 } ], "status" : "1" }
{ "_id" : 1, "cust_id" : "A", "ord_date" : ISODate("2023-06-01T00:00:00Z"), "price" : 15, "items" : [ { "sku" : "apple", "qty" : 5, "price" : 2.5 }, { "sku" : "apples", "qty" : 5, "price" : 2.5 } ], "status" : "1" }
{ "_id" : 2, "cust_id" : "A", "ord_date" : ISODate("2023-06-08T00:00:00Z"), "price" : 60, "items" : [ { "sku" : "apple", "qty" : 8, "price" : 2.5 }, { "sku" : "banana", "qty" : 5, "price" : 10 } ], "status" : "1" }
{ "_id" : 9, "cust_id" : "D", "ord_date" : ISODate("2023-06-20T00:00:00Z"), "price" : 51, "items" : [ { "sku" : "carrots", "qty" : 5, "price" : 1 }, { "sku" : "apples", "qty" : 10, "price" : 2.5 }, { "sku" : "apple", "qty" : 10, "price" : 2.5 } ], "status" : "1" }
{ "_id" : 3, "cust_id" : "B", "ord_date" : ISODate("2023-06-08T00:00:00Z"), "price" : 55, "items" : [ { "sku" : "apple", "qty" : 10, "price" : 2.5 }, { "sku" : "pears", "qty" : 10, "price" : 2.5 } ], "status" : "1" }
{ "_id" : 5, "cust_id" : "B", "ord_date" : ISODate("2023-06-19T00:00:00Z"), "price" : 40, "items" : [ { "sku" : "banana", "qty" : 5, "price" : 10 } ], "status" : "1" }
{ "_id" : 7, "cust_id" : "C", "ord_date" : ISODate("2023-06-20T00:00:00Z"), "price" : 21, "items" : [ { "sku" : "apple", "qty" : 10, "price" : 2.5 } ], "status" : "1" }
{ "_id" : 8, "cust_id" : "D", "ord_date" : ISODate("2023-06-20T00:00:00Z"), "price" : 76, "items" : [ { "sku" : "banana", "qty" : 5, "price" : 10 }, { "sku" : "apples", "qty" : 10, "price" : 2.5 } ], "status" : "1" }
{ "_id" : 10, "cust_id" : "D", "ord_date" : ISODate("2023-06-23T00:00:00Z"), "price" : 23, "items" : [ { "sku" : "apple", "qty" : 10, "price" : 2.5 } ], "status" : "1" }

示例:按客户统计

对集合 orders 执行map-reduce操作, 按 cust_id 进行分组, 然后统计每个客户的 price 计算总和,如下:

首先, 我们需要 定义map函数来处理每个输入文档:

  • 在函数中,this指的是map-reduce操作正在处理的文档。
  • 该函数将每个文档的 price 映射为 cust_id,并发出 cust_id 和 price 。
sit_rs1:PRIMARY> var myMapFun = function() {
...    emit(this.cust_id, this.price);
... };sit_rs1:PRIMARY> print(myMapFun)
function() {emit(this.cust_id, this.price);
}

然后,用两个参数 keyCustId 和 valuesPrices 定义相应的reduce函数。 这里需要调用数组的 sum 方法计算客户订单总价。

  • valuesPrices 是一个数组,其元素是map函数发出的price 字段的值,并按 keyCustId 分组。
  • 该函数将 valuesPrice 数组缩减为其元素的总和
# 计算数组元素总和
sit_rs1:PRIMARY> Array.sum([2,2,6,8])
18# 计算数组平均值
sit_rs1:PRIMARY> Array.avg([1,2,3])
2sit_rs1:PRIMARY> var myReduceFun = function(keyCustId, valuesPrices) {
...    return Array.sum(valuesPrices);
... };sit_rs1:PRIMARY> print(myReduceFun)
function(keyCustId, valuesPrices) {return Array.sum(valuesPrices);
}

最后,使用 myMapFun 函数和 myReduceFun 函数对集合 orders 中的所有文档执行map-reduce统计:

  • out: 指定map-reduce操作结果的位置。您可以输出到集合、通过操作输出到集合或内联输出。
  • 此操作将结果输出到名为 的集合 map_reduce_out。如果该 map_reduce_out 集合已存在,则该操作将使用此 Map-Reduce 操作的结果替换内容。
sit_rs1:PRIMARY> db.orders.mapReduce(
...    myMapFun,
...    myReduceFun,
...    { out: "map_reduce_out" }
... )
{"result" : "map_reduce_out","ok" : 1,"$clusterTime" : {"clusterTime" : Timestamp(1690259241, 6),"signature" : {"hash" : BinData(0,"Kur+ueslJYcT5oExd8ujPIC/J3Q="),"keyId" : NumberLong("7205479298910650370")}},"operationTime" : Timestamp(1690259241, 6)
}

查询 map_reduce_out 集合以验证结果是否正确:

sit_rs1:PRIMARY> db.map_reduce_out.find().sort( { _id: 1 } )
{ "_id" : "A", "value" : 75 }
{ "_id" : "B", "value" : 121 }
{ "_id" : "C", "value" : 59 }
{ "_id" : "D", "value" : 150 }# 检查 cust_id 为 A 的客户, 总和是 75 正确
sit_rs1:PRIMARY> db.orders.find({ "cust_id" : "A"}, {"price": 1})
{ "_id" : 1, "price" : 15 }
{ "_id" : 2, "price" : 60 }# 检查 cust_id 为 B 的客户,总和是 121 正确
sit_rs1:PRIMARY> db.orders.find({ "cust_id" : "B"}, {"price": 1})
{ "_id" : 4, "price" : 26 }
{ "_id" : 3, "price" : 55 }
{ "_id" : 5, "price" : 40 }

示例:按日期统计

按日期统计,和上面示例一样,只需要把 map 函数重新定义如下,将每个文档的 price 映射为 ord_date,并发出 ord_date 和 price 。

sit_rs1:PRIMARY> var myMapFun2 = function() {
...     emit(this.ord_date, this.price);
... };sit_rs1:PRIMARY> print(myMapFun2)
function() {emit(this.ord_date, this.price);
}

然后,用两个参数 keyOrdDate 和 valuesPrices 定义相应的reduce函数。 这里需要调用数组的 avg 方法计算平均客单价。

  • valuesPrices 是一个数组,其元素是map函数发出的 price 字段的值,并按 keyOrdDate 分组。
  • 该函数将 valuesPrice 数组缩减为其元素的总和的平均值
sit_rs1:PRIMARY> var myReduceFun2 = function(keyOrdDate, valuesPrices) {
...    return Array.avg(valuesPrices);
... };sit_rs1:PRIMARY> print(myReduceFun2)
function(keyOrdDate, valuesPrices) {return Array.avg(valuesPrices);
}

最后,使用 myMapFun2 函数和 myReduceFun2 函数对集合 orders 中的所有文档执行map-reduce统计:

sit_rs1:PRIMARY> db.orders.mapReduce(
...    myMapFun2,
...    myReduceFun2,
...    { out: "map_reduce_out2" }
... )
{"result" : "map_reduce_out2","ok" : 1,"$clusterTime" : {"clusterTime" : Timestamp(1690265083, 8),"signature" : {"hash" : BinData(0,"pCWskY3HjLGEjSk00ARYdZKECDE="),"keyId" : NumberLong("7205479298910650370")}},"operationTime" : Timestamp(1690265083, 8)
}

查询 map_reduce_out2 集合以验证结果是否正确:

sit_rs1:PRIMARY> db.map_reduce_out2.find()
{ "_id" : ISODate("2023-06-08T00:00:00Z"), "value" : 57.5 }
{ "_id" : ISODate("2023-06-01T00:00:00Z"), "value" : 15 }
{ "_id" : ISODate("2023-06-18T00:00:00Z"), "value" : 26 }
{ "_id" : ISODate("2023-06-20T00:00:00Z"), "value" : 49.333333333333336 }
{ "_id" : ISODate("2023-06-23T00:00:00Z"), "value" : 23 }
{ "_id" : ISODate("2023-06-19T00:00:00Z"), "value" : 39 }# 检查日期2023-06-08的订单平均值
sit_rs1:PRIMARY> db.orders.find({ "ord_date" : ISODate("2023-06-08T00:00:00Z")}, {"price": 1})
{ "_id" : 2, "price" : 60 }
{ "_id" : 3, "price" : 55 }
sit_rs1:PRIMARY> print((60+55)/2)
57.5# 检查日期2023-06-20的订单平均值
sit_rs1:PRIMARY> db.orders.find({ "ord_date" : ISODate("2023-06-20T00:00:00Z")}, {"price": 1})
{ "_id" : 9, "price" : 51 }
{ "_id" : 7, "price" : 21 }
{ "_id" : 8, "price" : 76 }sit_rs1:PRIMARY> print((51+21+76)/3)
49.333333333333336

对于需要自定义功能的 Map-Reduce 操作,MongoDB 从 4.4 版本开始提供 $accumulator 和 $function 聚合运算符。使用这些运算符在 JavaScript 中自定义聚合表达式。

  • 聚合管道作为 Map-Reduce 的替代方案, 聚合管道提供比 Map-Reduce 操作更好的性能和可用性。

  • 可以使用聚合管道运算符(例如 $group、$merge等)重写 Map-reduce 操作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/8233.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于Java+SpringBoot+vue前后端分离在线商城系统设计实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

spring 存储对象 + 获取对象

前言 本篇在spring中如何使用五大类注释与方法注释将对象加入IOC容器中&#xff0c;了解如何使用注释来获取容器中的Bean对象&#xff0c;如有错误&#xff0c;请在评论区指正&#xff0c;让我们一起交流&#xff0c;共同进步&#xff01; 文章目录 前言1.通过注释将类加入IoC…

【云计算小知识】云环境是什么意思?有什么优点?

随着云计算的快速发展&#xff0c;了解云计算相关知识也是运维人员必备的。那你知道云环境是什么意思&#xff1f;有什么优点&#xff1f;云环境安全威胁有哪些&#xff1f;如何保证云环境的运维安全&#xff1f;这里我们就来简单聊聊。 云环境是什么意思&#xff1f; 云环境是…

SpringBoot中配置文件的加载

springboot 启动会扫描一下位置的application.properties或者application.yml文件作为springboot的默认配置文件 file:./config/(项目根目录config文件夹下的配置文件) file:./(项目根目录下的配置文件) classpath:/config/(resources目录config文件下的配置文件) classpat…

如何在3ds max中创建可用于真人场景的巨型机器人:第 2 部分

推荐&#xff1a; NSDT场景编辑器助你快速搭建可二次开发的3D应用场景 1. 创建主体 步骤 1 打开 3ds Max。选择机器人头部后&#xff0c;二次单击鼠标并选择隐藏未选中。机器人的其他部分 除了头部之外&#xff0c;将被隐藏。 打开 3ds Max 步骤 2 在人脸选择模式下&#x…

视图和视图解析器

SpringMVC–视图和视图解析器 基本介绍 在springMVC 中的目标方法最终返回都是一个视图(有各种视图)。 返回的视图都会由一个视图解析器来处理(视图解析器有很多种)。 自定义视图 为什么需要自定义视图 ​ 在默认情况下&#xff0c;我们都是返回默认的视图, 然后这个返回…

Java通过InputStream判断word格式

Java通过InputStream判断word格式 1. 传入inputStream文件流2. 读取文件流前八个字节到字节流数组中3. 转为字符串4. 对比字符串判断文件类型5. 完整代码 Java通过InputStream判断word格式) 1. 传入inputStream文件流 2. 读取文件流前八个字节到字节流数组中 byte[] byteArra…

flink1.16使用消费/生产kafka之DataStream

flink高级版本后&#xff0c;消费kafka数据一种是Datastream 一种之tableApi。 上官网 Kafka | Apache Flink Kafka Source 引入依赖 flink和kafka的连接器&#xff0c;里面内置了kafka-client <dependency><groupId>org.apache.flink</groupId><arti…

C++类与对象 - 2(构造函数和析构函数)(超详细)

构造函数和析构函数 - 超详细讲解 1. 构造函数1.1 概念1.2特性 2. 析构函数2.1 概念2.2特性 1. 构造函数 1.1 概念 对于以下Date类&#xff1a; class Date { public:void Init(int year, int month, int day){_year year;_month month;_day day;}void Print(){cout <&l…

C语言float类型学习

C语言的小数类型有两种&#xff0c;float和double&#xff1b; float 称为单精度浮点型&#xff0c;double 称为双精度浮点型&#xff1b; float 占用4个字节&#xff0c;double 占用8个字节&#xff1b; 下面看一下float&#xff1b; #include <stdio.h>int main() {…

电脑记事本在哪里?电脑桌面显示记事本要怎么设置?

绝大多数上班族在使用电脑办公时&#xff0c;都需要随手记录一些琐碎或重要的事情&#xff0c;例如工作注意事项、常用的文案、某项工作的具体要求、多个平台的账号和密码等。于是就有不少小伙伴想要使用电脑记事本软件来记录&#xff0c;那么电脑记事本在哪里呢&#xff1f;想…

JAVA基础-集合的工具类Collections

目录 引言 一&#xff0c;Collections工具类的操作方法方法 1&#xff0c;排序操作 2&#xff0c;替换 和 查找操作 二&#xff0c;Collections工具类的使用 2.1&#xff0c;排序操作 2.1.1&#xff0c;集合的逆序 2.1.2&#xff0c;集合的随机排序 2.1.3&#xff0c;集…

SkyWalking链路追踪中Trace概念以及Trace与span的关系

基本概念 在SkyWalking链路追踪中&#xff0c;Trace&#xff08;追踪&#xff09;是指一个请求或者一个操作从开始到结束的完整路径。它涵盖了分布式系统中所有相关组件的调用关系和性能信息。 具体来说&#xff0c;Trace包含了一系列的span&#xff08;跨度&#xff09;&…

【C#】.Net Framework框架使用JWT

2023年&#xff0c;第31周&#xff0c;第2篇文章。给自己一个目标&#xff0c;然后坚持总会有收货&#xff0c;不信你试试&#xff01; 本篇文章主要简单讲讲&#xff0c;.Net Framework框架下使用JWT的代码例子&#xff0c;以及他们的基本概念。 2002年微软发布了.net framewo…

GFLv2 论文学习

1. 解决了什么问题&#xff1f; 预测定位质量对于目标检测很重要&#xff0c;在 NMS 时它能提供准确的得分排序&#xff0c;提高模型的表现。现有方法都是通过分类或回归的卷积特征来预测定位质量得分。 2. 提出了什么方法&#xff1f; 受到 GFLv1 的 general distribution …

前端开发实习总结参考范文(合集)

▼前端开发实习总结篇一 今天就简单聊聊上面的StrutsSpringHibernate吧。 Struts 代表&#xff1a;表示层;Spring代表&#xff1a;业务逻辑层;Hibernate则代表持久层。他们是目前在Java Web编程开发中用得最多的框架&#xff0c;其实这样区分是为了适应软件开发过程中各个分工…

Shell输出帮助手册

代码&#xff1a; 帮助手册雏形 function help(){echo -e "Help manual":echo -e " -h. -- help View the help manual"echo -e " install Installation"echo -e " uninstall Uninstall" }case "$1&qu…

无涯教程-不是(选择器)

not(selector)方法从匹配的元素集中过滤掉所有与指定选择器匹配的元素。 not( selector ) - 语法 selector.not( selector ) 这是此方法使用的所有参数的描述- selector - 可能是一个逗号分隔的选择器列表&#xff0c;可一次应用多个过滤器(如not(".class1&#…

【stable diffusion】保姆级入门课程05-Stable diffusion(SD)图生图-涂鸦重绘的用法

1.什么是涂鸦重绘 涂鸦重绘又称手涂蒙版。 简单来说&#xff0c;局部重绘手涂蒙版 就是涂鸦局部重绘的结合体&#xff0c;这个功能的出现是为了解决用户不想改变整张图片的情况下&#xff0c;对多个元素进行修改。 功能支持&#xff1a; 1.支持蒙版功能 2.笔刷决定绘制的元素…

基于Java+SpringBoot+Vue前后端分离摄影分享网站平台系统

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…