MongoDB聚合操作符:$accumulator

$accumulator可以定义自定义累加器操作符。累加器是一种操作符,可在文档通过管道时保持其状态(如:总数、最大值、最小值和相关数据)。$accumulator操作符支持执行自定义的JavaScript函数,可以实现MongoDB查询语言不支持的行为。

$accumulator支持下列阶段:

  • $bucket
  • $bucketAuto
  • $group

**注意:**在聚合操作符内部执行JavaScript可能会降低性能,只有在MongoDB提供的管道操作符无法满足需求时才考虑使用$accumulator

语法

{$accumulator: {init: <code>,initArgs: <array expression>,        // 可选accumulate: <code>,accumulateArgs: <array expression>,merge: <code>,finalize: <code>,                    // 可选lang: <string>}
}

字段说明:

  • init
    字符串或代码,用于初始化状态的函数。init函数从initArgs数组接受参数,可以使用BSON类型代码或字符串来定义函数,init函数形式如下:

      function (<initArg1>, <initArg2>, ...) {...return <initialState>}
    

    **注意:**溢出到磁盘或在分片集群上运行查询会导致累加器以多个子累加器合并的形式计算,每个函数都以调用init()开始。要确保init()accumulative()merge()函数与此执行模式兼容。

  • initArgs
    数组,可选,传递给init函数的参数。initArgs为下面的形式:

    [ <initArg1>, <initArg2>, ... ]
    

    **注意:**应用于$bucketAuto阶段时,initArgs不能应用分组键(也就是说不能使用$<fieldName>语法),换而言之,在$bucketAuto阶段,只能给initArgs指定常量。

  • accumulate
    字符串或代码,用于文档累加的函数,accumulate函数从accumulateArgs数组表达式接受当前状态和参数,accumulate函数的返回值变为新状态,函数定义可以为BSON类型的代码或字符串。accumulate函数的形式如下:

    function(state, <accumArg1>, <accumArg2>, ...) {...return <newState>
    }
    
  • accumulateArgs
    传递给accumulate函数的参数,可以使用accumulateArgs来制定传递给accmulate函数的字段值。accumulateArgs的形式如下;

    [ <accumArg1>, <accumArg2>, ... ]
    
  • merge
    字符串或代码,用于合并两个内部状态的函数,merge必须是字符串或BSON代码类型,merge返回两个状态合并后的结果,mere函数的形式如下:

    function (<state1>, <state2>) {<logic to merge state1 and state2>return <newState>
    }
    
  • finalize
    字符串或代码,可选,用于更新累加结果的函数。finalize函数的形式如下:

    function (state) {...return <finalState>
    }
  • lang
    字符串类型,accumulator代码使用的语言。目前仅支持js

使用

下面是$accumulator操作符处理文档的过程和步骤:

  1. 操作符从初始状态开始,由init函数定义。
  2. 操作符基于accumulate指定的函数更新每个文档的状态。
  3. 当操作符需要合并多个中间状态时,会执行merge函数。
  4. 如果定义了finalize函数,一旦所有文档被处理完并且状态因此更新,最终确定将状态到最终输出。

使用$merge合并两个状态

作为其内部运作的一部分,累加器操作符可能需要合并两个独立的中间状态。合并函数指定操作符应如何合并两个状态。

例如,下面的情况下,$accumulator可能需要合并两个状态:

  • $accumulator运行在分片集群上,操作符需要合并每个分片的结果,以得到的最终结果。
  • 单个$accumulator操作超出了它指定的内存限制,如果指定了alloDiskUse选项,操作符将正在进行的操作放在磁盘上并在内存中完成操作。一旦操作完成,磁盘上、内存中的的结果将由merge函数进行合并。

**注意:**合并函数总是一次合并两个状态。当有两个以上状态需要合并时,则会先合并两个状态为一个状态,然后重复这一过程,直到所有的状态都被合并。也就是说每次合并只能合并两个状态。

内嵌javascript

要使用$accumulator,必须启用服务端脚本选项。如果不使用$accumulator(或$function$where、或mapRecuce),可以禁用服务端脚本:

  • 对于mongod实例,可以参考security.javascriptEnabled选项配置或--noscripting命令行选项。
  • 对于mongos实例,可以参考security.javascriptEnabled选项配置或--noscripting命令行选项(从MongoDB4.4开始)。
    对于更早的版本,MongoDB不允许在mongos实例上执行JavaScript。

不支持的数组和字符串函数

MongoDB6.0升级了用于服务端JavaScript的内部JavaScript引擎,包括$accumulator$function$where表达式,从MozJS-60升级到了MozJS-91MozJS-60中存在的一些不推荐使用的非标准数组和字符串函数在MozJS-91中被删除。

举例

$accumulator实现$avg操作符

**注意:**本例使用$accumulator实现的$avg在MongoDB中已经支持,本例的目的不是去实现一个新功能,而是演示accumulator操作符常见逻辑的行为和语法。

mongosh,使用下面的文档创建一个books集合:

db.books.insertMany([{ "_id" : 8751, "title" : "The Banquet", "author" : "Dante", "copies" : 2 },{ "_id" : 8752, "title" : "Divine Comedy", "author" : "Dante", "copies" : 1 },{ "_id" : 8645, "title" : "Eclogues", "author" : "Dante", "copies" : 2 },{ "_id" : 7000, "title" : "The Odyssey", "author" : "Homer", "copies" : 10 },{ "_id" : 7020, "title" : "Iliad", "author" : "Homer", "copies" : 10 }
])

下面的操作使用author对文档进行分组,并使用$accumulator计算每个作者书籍的平均拷贝数。

db.books.aggregate([
{$group :{_id : "$author",avgCopies:{$accumulator:{init: function() {                        // 设置初始状态return { count: 0, sum: 0 }},accumulate: function(state, numCopies) {  // 定义如何更新状态return {count: state.count + 1,sum: state.sum + numCopies}},accumulateArgs: ["$copies"],              // accumulate函数需要的参数merge: function(state1, state2) {         // 当操作符执行合并return {                                // 两个状态加到字段count: state1.count + state2.count,sum: state1.sum + state2.sum}},finalize: function(state) {               // 收集所有文档的结果后return (state.sum / state.count)        // 计算平均值},lang: "js"}}}
}
])
结果

操作返回下面的结果:

{ "_id" : "Dante", "avgCopies" : 1.6666666666666667 }
{ "_id" : "Homer", "avgCopies" : 10 }
过程分析

$accumulator定义了一个初始状态,其中countsum都设置为0$accumulator对每个文档使用以下方式更新状态:

  • count每次加1
  • 将文档copies字段的值加到sum,通过accumulateArgs指定的参数,累加器函数可以访问copies字段。

当所有的文档都被处理完后,accumulate函数返回更新后的状态。

一旦所有的文档被处理完,finilize函数使用拷贝的sum值除以文档数量count得到平均值。这样就不需要保持运行计算后的平均值,因为finilize函数会接收到sumcount的累计值。

对比$avg

下面的操作使用了$avg操作符,与上面的方法是等价的:

db.books.aggregate([
{$group : {_id : "$author",avgCopies: { $avg: "$copies" }}
}
])

使用initArgs按分组改变初始状态

可以使用initArgs选项去改变$accumulator的初始状态,在某些情况下是比较有用的,比如:

  • 使用状态中没有的字段值来影响状态
  • 根据正在处理的组,将初始状态设置为不同的值。

restaurants集合有下面的内容:

db.restaurants.insertMany([{ "_id" : 1, "name" : "Food Fury", "city" : "Bettles", "cuisine" : "American" },{ "_id" : 2, "name" : "Meal Macro", "city" : "Bettles", "cuisine" : "Chinese" },{ "_id" : 3, "name" : "Big Crisp", "city" : "Bettles", "cuisine" : "Latin" },{ "_id" : 4, "name" : "The Wrap", "city" : "Onida", "cuisine" : "American" },{ "_id" : 5, "name" : "Spice Attack", "city" : "Onida", "cuisine" : "Latin" },{ "_id" : 6, "name" : "Soup City", "city" : "Onida", "cuisine" : "Chinese" },{ "_id" : 7, "name" : "Crave", "city" : "Pyote", "cuisine" : "American" },{ "_id" : 8, "name" : "The Gala", "city" : "Pyote", "cuisine" : "Chinese" }
])

假设一个应用允许用户使用这些数据去查找饭店,或许显示更多与用户居住城市相关的饭店会更有用,在这个例子中,假定用户所在的城市是userProfileCity变量。

下面的聚合管道按照city进行分组,操作使用$accumulator来显示与用户资料匹配的城市餐厅的数量:

**注意:**如果在mongosh中执行,需要把initArgs<userProfileCity>替换为实际城市的字符串值,如:“Bettles”。

db.restaurants.aggregate([
{$group :{_id : { city: "$city" },restaurants:{$accumulator:{init: function(city, userProfileCity) {        // 设置初始状态return {max: city === userProfileCity ? 3 : 1,     // 如果分组匹配到用户的城市, 返回3个饭店restaurants: []                            // 否则, 返回1个饭店}},initArgs: ["$city", <userProfileCity>],        // 传递给init函数的参数accumulate: function(state, restaurantName) {  // 定义如何更新状态if (state.restaurants.length < state.max) {state.restaurants.push(restaurantName);}return state;},accumulateArgs: ["$name"],                     // accumulate函数需要的参数merge: function(state1, state2) {return {max: state1.max,restaurants: state1.restaurants.concat(state2.restaurants).slice(0, state1.max)}},finalize: function(state) {                   // 调整状态,只返回需要的字段return state.restaurants}lang: "js"}}}
}
])
返回结果

如果userProfileCityBettles,操作返回下面的结果:

{ "_id" : { "city" : "Bettles" }, "restaurants" : { "restaurants" : [ "Food Fury", "Meal Macro", "Big Crisp" ] } }
{ "_id" : { "city" : "Onida" }, "restaurants" : { "restaurants" : [ "The Wrap" ] } }
{ "_id" : { "city" : "Pyote" }, "restaurants" : { "restaurants" : [ "Crave" ] } }

如果userProfileCityOnida,操作返回下面的结果:

{ "_id" : { "city" : "Bettles" }, "restaurants" : { "restaurants" : [ "Food Fury" ] } }
{ "_id" : { "city" : "Onida" }, "restaurants" : { "restaurants" : [ "The Wrap", "Spice Attack", "Soup City" ] } }
{ "_id" : { "city" : "Pyote" }, "restaurants" : { "restaurants" : [ "Crave" ] } }

如果userProfileCity为是别的值,操作返回下面的结果:

{ "_id" : { "city" : "Bettles" }, "restaurants" : { "restaurants" : [ "Food Fury" ] } }
{ "_id" : { "city" : "Onida" }, "restaurants" : { "restaurants" : [ "The Wrap" ] } }
{ "_id" : { "city" : "Pyote" }, "restaurants" : { "restaurants" : [ "Crave" ] } }
执行过程分析

初始化函数init定义了初始状态,包含了maxrestaurants字段,max字段为指定分组设置饭店的最大数量。如果文档的city字段与userProfileCity匹配,分组最多包含3个饭店,否则,如果文档_iduserProfileCity不匹配,分组最多包含一个饭店。init函数从initArgs数组接受cityuserProfileCity参数。

对于$accumulator处理的每个文档,会把饭店的name放到restaurants数组,前提是名称不会让restaurants的长度超过max的值。在所有的文档都被处理完成后,accumulate函数返回更新后的状态。

merge函数定义了如何合并两个状态,它将每个状态的restaurant连接在一起,并且结果数组的长度使用slice方法进行了限制,确保它不会超过max的值。

一旦所有的文档被处理完,finalize函数修改结果状态,值返回饭店的名称,如果没有这个函数,max字段也会包含在输出中,这将不能满足应用的需求。

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

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

相关文章

【STM32 CubeMX】学STM必会的数据结构——环形缓冲区

文章目录 前言一、环形缓冲区是什么二、实现环形缓冲区实现分析2.1 环形缓冲区初始化2.2 写buf2.3 读buf2.4 测试 三、代码总况总结 前言 在嵌入式系统开发中&#xff0c;经常需要处理数据的缓存和传输&#xff0c;而环形缓冲区是一种常见且有效的数据结构&#xff0c;特别适用…

幻兽帕鲁官方更新了,服务器端怎么更新?

幻兽帕鲁官方客户端更新了&#xff0c;那么它的服务器端版本也是需要更新的&#xff0c;不然版本不一致的话&#xff0c;就不能进入游戏了。 具体的更新方法有两种&#xff0c;一是手动输入命令进行更新。第二种是在面板一键更新。 无论你是在阿里云或者腾讯云购买的一键部署…

Pycharm里如何设置多Python文件并行运行

点击上方“Python爬虫与数据挖掘”&#xff0c;进行关注 回复“书籍”即可获赠Python从入门到进阶共10本电子书 今 日 鸡 汤 夕阳何事近黄昏&#xff0c;不道人间犹有未招魂。 大家好&#xff0c;我是皮皮。 一、前言 相信使用Pycharm的粉丝们肯定有和我一样的想法&#xff0c;…

rollup 和 esbuild 的对比

Rollup 和 esbuild 都是 JavaScript 模块打包工具&#xff0c;用于将多个模块打包成一个或多个浏览器可执行的文件。Rollup 先被提出&#xff0c;esbuild 后被提出。 Rollup&#xff1a; 提出时间&#xff1a;Rollup 是在 2015 年首次发布的。它最初的目标是专注于 ES6 模块的静…

算法学习——LeetCode力扣贪心篇1

算法学习——LeetCode力扣贪心篇1 455. 分发饼干 455. 分发饼干 - 力扣&#xff08;LeetCode&#xff09; 描述 假设你是一位很棒的家长&#xff0c;想要给你的孩子们一些小饼干。但是&#xff0c;每个孩子最多只能给一块饼干。 对每个孩子 i&#xff0c;都有一个胃口值 g[…

Vulnhub靶机:DC3

一、介绍 运行环境&#xff1a;Virtualbox 攻击机&#xff1a;kali&#xff08;10.0.2.15&#xff09; 靶机&#xff1a;DC3&#xff08;10.0.2.56&#xff09; 目标&#xff1a;获取靶机root权限和flag 靶机下载地址&#xff1a;https://www.vulnhub.com/entry/dc-32,312…

[Python人工智能] 四十一.命名实体识别 (2)基于BiGRU-CRF的中文实体识别万字详解

从本专栏开始,作者正式研究Python深度学习、神经网络及人工智能相关知识。前文讲解如何实现威胁情报实体识别,利用BiLSTM-CRF算法实现对ATT&CK相关的技战术实体进行提取,是安全知识图谱构建的重要支撑。这篇文章将以中文语料为主,介绍中文命名实体识别研究,并构建BiGR…

总结FreeRTOS中的任务调度算法,空闲任务,任务状态等概念。

任务调度算法 抢占式调度&#xff1a;高优先级的任务优先执行&#xff0c;并且可以打断低优先级的任务执行。 时间片轮转&#xff1a;相同优先级的任务&#xff0c;拥有相同的时间片&#xff0c;当时间片被耗尽&#xff0c;就退出当前任务。 空闲任务 空闲指的就是当系统中…

嵌入式系统的基础知识:了解嵌入式系统的构成和工作原理

&#xff08;本文为简单介绍&#xff0c;个人观点仅供参考&#xff09; 嵌入式系统是建立在微处理器基础上的计算机系统,用于对专门的功能进行控制、运算和接口。它结合了硬件和软件,可以提供实时的响应,广泛应用于工业控制、通信、医疗、交通等领域。 嵌入式系统的核心是微处理…

猫头虎分享已解决Bug || 代码部署失败(Code Deployment Failure):DeploymentError, FailedRelease

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

代码随想录算法训练营29期Day51|LeetCode 139

文档讲解&#xff1a;单词拆分 139.单词拆分 题目链接&#xff1a;https://leetcode.cn/problems/word-break/description/ 思路&#xff1a; 单词就是物品&#xff0c;字符串s就是背包&#xff0c;单词能否组成字符串s&#xff0c;就是问物品能不能把背包装满。 拆分时可以重…

PMDG 737

在Simbrief中生成计划后下载两个文件 放到A:\Xbox\Community\pmdg-aircraft-738\Config\Flightplans中

机器视觉技术:提升安全与效率的关键

机器视觉技术&#xff1a;提升安全与效率的关键 随着技术的不断发展&#xff0c;机器视觉技术已经成为提高许多行业安全与效率的关键要素。无论是在工业制造、交通监控、安全防卫&#xff0c;还是在医疗诊断、零售管理等领域&#xff0c;机器视觉技术都发挥着越来越重要的作用…

SpringCloud第一天

1.认识微服务 随着互联网行业的发展&#xff0c;对服务的要求也越来越高&#xff0c;服务架构也从单体架构逐渐演变为现在流行的微服务架构。这些架构之间有怎样的差别呢&#xff1f; 1.1.单体架构 单体架构&#xff1a;将业务的所有功能集中在一个项目中开发&#xff0c;打…

波奇学Linux:文件系统

磁盘认识 磁盘被访问的基本单元是扇区-512字节。 磁盘可以看成多个同心圆&#xff0c;每个同心圆叫做磁道&#xff0c;多个扇区组成同心圆。 我们可以把磁盘看做由无数个扇区构成的存储介质。 要把数据存到磁盘&#xff0c;先定位扇区&#xff0c;用哪一个磁头&#xff0c;…

AI 对齐:深入剖析人工智能伦理和技术标准

AI 对齐&#xff1a;深入剖析人工智能伦理和技术标准 AI 对齐&#xff1a;深入剖析人工智能伦理和技术标准引言AI 对齐的重要性技术角度确立对齐的目标和价值观数据和模型的公正性可解释的AI算法&#xff08;XAI&#xff09;安全和可靠性动态学习和反馈机制跨学科队伍合作法规和…

数据结构红黑树

红黑树是一种自平衡的二叉搜索树&#xff0c;它通过确保任何从根到叶子的路径上不会有两个连续的红节点并且从根到叶子的所有路径上有相同数量的黑节点&#xff0c;从而近似平衡。这种平衡保证了在最坏情况下插入、删除、查找操作都能在O(log n)时间复杂度内完成。 下面&#…

【原创 附源码】Flutter集成Apple支付详细流程(附源码)

最近有时间&#xff0c;特意整理了一下之前使用过的Flutter平台的海外支付&#xff0c;附源码及demo可供参考 这篇文章只记录Apple支付的详细流程&#xff0c;其他相关Flutter文章链接如下&#xff1a; 【原创 附源码】Flutter集成谷歌支付详细流程(附源码) 【原创 附源码】F…

《Java 简易速速上手小册》第2章:面向对象的 Java(2024 最新版)

文章目录 2.1 类和对象 - 构建你的小宇宙2.1.1 基础知识2.1.2 重点案例&#xff1a;设计一个简单的图书类2.1.3 拓展案例 1&#xff1a;学生管理系统2.1.4 拓展案例 2&#xff1a;账户管理系统 2.2 继承与多态 - 让一切变得更有趣2.2.1 基础知识2.2.2 重点案例&#xff1a;动物…

【51单片机】蜂鸣器(江科大)

11.1蜂鸣器 1.蜂鸣器介绍 蜂鸣器是一种将电信号转换为声音信号的器件,常用来产生设备的按键音、报警音等提示信号 蜂鸣器按驱动方式可分为有源蜂鸣器和无源蜂鸣器 有源蜂鸣器:内部自带振荡源,将正负极接上直流电压即可持续发声,频率固定 无源蜂鸣器:内部不带振荡源,需…