MongoDB 8.0——聚合
聚合1、聚合方法2、聚合管道2.1、管道和阶段2.2、管道操作符2.3、管道表达式与表达式操作符2.3.1、算术表达式操作符2.3.2、数组表达式操作符2.3.3、位操作符2.3.4、布尔表达式操作符2.3.5、比较表达式操作符2.3.6、条件表达式操作符2.3.7、客户端聚合表达式操作符2.3.8、数据量操作符2.3.9、日期表达式操作符2.3.10、对象表达式操作符2.3.11、设置表达式操作符2.3.12、字符串表达式操作符2.3.13、文本表达式操作符2.3.14、时间戳表达式操作符2.3.15、三角函数表达式操作符2.3.16、类型表达式操作符2.3.17、聚合器操作符2.4、聚合操作1、聚合方法MongoDB的聚合操作是一种高级查询语言它允许用户通过转换和合并多个文档的数据来生成新的、在单个文档中不存在的文档信息。这种操作通常涉及将记录按条件分组后进行一系列操作如求最大值、最小值、平均值等简单操作也可以进行复杂的数据统计和数据挖掘。在MongoDB中聚合操作主要通过aggregate()方法实现。aggregate()方法提供了一种灵活且强大的方式来处理数据可以执行各种复杂的聚合任务。MongoDB提供了3种主要的聚合方法各自具有独特的特点和适用场景同时它们也存在一定的联系。聚合管道方法这种方法可以理解为一种流水线处理过程它将多个聚合阶段连接在一起每个阶段都对输入文档进行处理并将结果传递给下一个阶段。基于管道的概念文档进入一个多段管道通过一系列的管道阶段如过滤、分组、排序、计算等进行转换和处理最终输出聚合结果。聚合管道利用MongoDB的原生运算来提供高效的数据聚合是执行数据聚合的首选方法。它特别适合处理大规模的数据集并且可以通过索引来提高某些阶段的性能。Map-Reduce方法Map-Reduce是一种编程模型用于处理和生成大数据集。在MongoDB中Map-Reduce包括两个主要阶段Map阶段和Reduce阶段。通过将数据分成小块Map阶段​然后在这些小块上执行并行计算最后将所有结果合并Reduce阶段以生成最终结果。这种方法在处理大规模数据集时非常有效通常用于处理复杂的聚合计算特别是当需要在分布式环境中处理大量数据时。然而Map-Reduce相对于聚合管道来说可能更为复杂并且性能可能稍逊一筹。单一目标聚合方法这种方法提供了一些特定的聚合操作功能可以直接在集合上执行而无须使用聚合管道或Map-Reduce。这些操作通常更简单、更直接适用于一些常见的聚合需求。然而它们的功能相对有限可能无法满足所有复杂的聚合需求。在联系方面这3种聚合方法都是为了实现数据的聚合和转换以满足不同的查询和分析需求。它们都是MongoDB提供的数据处理工具可以根据具体的应用场景和需求选择合适的方法。同时这3种方法也可以相互补充共同构建出更强大的数据处理能力。在聚合操作中主要通过以下两种聚合方法进行处理。db.collection.aggregate()用于聚合管道。db.collection.mapReduce()用于大数据的Map-Reduce聚合。除以上两种方法外MongoDB中还有一些方法也涉及聚合操作这主要在单一目标聚合中使用如表所示。总的来说MongoDB的聚合操作提供了一种灵活且强大的方式来处理和分析数据可以满足各种复杂的查询和统计需求在实际应用中需要根据具体需求进行选择和使用。2、聚合管道聚合管道(Aggregation Pipeline)是MongoDB 2.2版本引入的一个新功能它基于数据处理管道模型的数据聚合框架其概念和工作方式类似于Linux中的管道操作符主要用于批量数据处理和统计分析类似于SQL的group by语句。聚合管道由多个阶段(Stage)组成每个阶段有自己特定的功能文档在一个阶段处理完毕后聚合管道会将处理的结果传递给下一阶段。每个阶段都有相应的阶段操作符来对文档进行特定的处理。待处理的文档会流经这些阶段最终完成计算计算结果可以直接输出也可以存储到集合中。整个过程像一个流水线每一阶段都是环环相扣的上一个工作阶段的输出即工作结果为下一个工作阶段的输入。这体现了聚合管道的两个功能特点对文档进行过滤筛选符合条件的文档。对文档进行转换以指定形式输出文档。聚合管道通过db.collection.aggregate()方法实现。聚合管道涉及几个常用的概念管道和阶段、管道操作符和管道表达式。2.1、管道和阶段传统意义上的管道是指用管子、管子连接件和阀门等连接成的用于输送气体、液体或带固体颗粒的流体的装置。管道在生活中很常见。例如水管管道、燃气管道、暖气管道这些管道都有共同点即管道内的流体是按照从前向后的顺序依次流通的。有些管道例如水管管道在一些重要的节点会进行处理例如过滤、净化、杀菌、消毒等最后流入住户的都是合格的饮用水。在MongoDB中聚合管道也是同样的道理只不过它的流体为数据。数据经过管道时会对数据进行筛选、排序、分组、计算等操作这些操作分属于不同的阶段。前一阶段完成后输出的数据成为下一个阶段的输入数据。最终输出的是符合需求的文档数据。使用聚合管道进行数据分析的基本步骤如下。步骤1构建聚合管道根据需求选择合适的阶段和操作符构建聚合管道。每个阶段都定义了数据的处理方式如筛选、分组、排序等。步骤2执行聚合管道将构建好的聚合管道作为参数传递给MongoDB的aggregate()方法执行聚合操作。在执行过程中数据会按照定义的顺序流经每个阶段每个阶段都会对数据进行相应的处理。步骤3处理聚合结果聚合操作完成后会得到一个包含聚合结果的游标(Cursor)。开发者可以遍历游标获取处理后的数据并进行进一步的分析或展示。例如一个聚合管道可能包含以下阶段首先从集合中根据条件查找出一批文档数据接着对搜索出来的文档数据进行分组统计最后对统计数据进行排序。整个过程就像一个流水线式的操作数据从管道的一端流入经过几个阶段的处理后输出一个最终的结果。在聚合管道中有些阶段可以使用索引来提高性能例如match阶段和group阶段。然而使用索引的条件和限制需要特别注意。如果要对集合创建聚合管道可以在MongoDB Shell中使用以下语法如果数据库要面对创建聚合管道可以使用如下语法db.aggregate([{stage},...])2.2、管道操作符管道由一个个阶段组成区分这些阶段的就是管道操作符。管道操作符的主要作用是将一个阶段的输出作为另一个阶段的输入从而实现数据的连续处理或转换。常用的管道操作符如表所示。在管道操作符中除$out、$merge、$geoNear、$changeStream、$changeStreamSplitLargeEvent外其他的操作符都可以多次出现。2.3、管道表达式与表达式操作符前面的管道操作符可以理解为阶段的key那么这里的管道表达式即为阶段的value。例如{KaTeX parse error: Expected EOF, got } at position 19: …ch:{title:活着}}̲其中match为管道操作符{title:‘活着’}为管道表达式。通常情况下管道表达式类似于一个携带参数的函数形式上为一个文档结构如下{operator:[argument1,argument2...]}如果只有一个参数的话可以简化为如下格式{operator:argument}在使用管道表达式时需要结合表达式的操作符。MongoDB提供了多种类型的表达式操作符涵盖多种数据类型和计算方法。2.3.1、算术表达式操作符在MongoDB的聚合管道中算术表达式操作符用于执行数学运算。这些操作符可以在$project、$group等阶段中使用以计算和转换字段的值。一些常用的算术表达式操作符如表所示。这些算术表达式操作符可以在聚合管道的各个阶段灵活使用以进行复杂的数学计算和数据处理。例如在$group阶段可以使用$sum来计算某个字段的总和或者使用$avg来计算平均值。在$project阶段可以使用$multiply或$divide来创建新的字段其值是基于现有字段的计算结果。注意当在聚合管道中使用这些操作符时通常需要将它们放在表达式的上下文中例如{ $sum:$field }其中$field是要对其执行数学运算的字段名。2.3.2、数组表达式操作符在MongoDB的聚合管道中数组表达式操作符用于处理数组字段执行诸如过滤、转换和计算数组元素等操作。一些常用的数组表达式操作符如表所示。这些数组表达式操作符在聚合管道的各个阶段都非常有用特别是在处理包含数组字段的文档时。读者可以根据需要在$project、$group或其他阶段中使用它们以执行复杂的数组操作和数据转换。2.3.3、位操作符在MongoDB的聚合管道中位操作符允许对数值字段进行位运算。这些操作符对于需要执行位级别操作的数据处理任务非常有用。一些常用的位操作符如表所示。2.3.4、布尔表达式操作符在布尔表达式操作符中null、0以及未定义的数据值均视为false非0数值和数组均视为true。常用的布尔表达式操作符如表所示。2.3.5、比较表达式操作符在所有的比较表达式操作符中除$cmp返回数值类型外其他均返回布尔型。常用的比较表达式操作符如表所示。2.3.6、条件表达式操作符MongoDB中的条件表达式操作符允许用户根据特定的条件来过滤查询结果。一些常用的MongoDB条件表达式操作符如表所示。2.3.7、客户端聚合表达式操作符客户端聚合表达式操作符是在版本4.4之后才引入的操作符用来定义聚合函数和客户端函数如表所示。2.3.8、数据量操作符数据量操作符用来返回数据的大小如表所示。2.3.9、日期表达式操作符日期表达式操作符是MongoDB操作中使用频率较高的操作符。MongoDB提供了多个日期表达式操作符用于在查询和聚合中对日期和时间字段进行各种操作。一些常用的MongoDB日期表达式操作符如表所示。2.3.10、对象表达式操作符MongoDB提供了3个对象表达式操作符用来操作文档对象如表所示。2.3.11、设置表达式操作符在MongoDB中设置表达式操作符通常用于更新文档时通过这些操作符可以修改字段的值或结构。这些操作符可以在update、updateOne、updateMany等方法的更新文档中使用如表所示。2.3.12、字符串表达式操作符在MongoDB中处理字符串字段时可以使用一系列的表达式操作符来执行各种字符串比较和操作。一些常用的字符串表达式操作符如表所示。2.3.13、文本表达式操作符文本表达式操作符主要用于访问文本搜索相关的元数据如表所示。2.3.14、时间戳表达式操作符时间戳表达式操作符主要用于基于时间戳返回对应的值如表所示。2.3.15、三角函数表达式操作符MongoDB提供了常用的三角函数表达式操作符如表所示。2.3.16、类型表达式操作符MongoDB中的类型表达式操作符主要用于在查询中根据字段的数据类型进行过滤。常用的类型表达式操作符如表所示。2.3.17、聚合器操作符在MongoDB中聚合器操作符用于处理和转换集合中的数据以便执行复杂的分析操作。在聚合操作中通过这些操作符可以对数据进行分组、过滤、排序、计算等。一些常用的MongoDB聚合器操作符如表所示。这些操作符可以根据需要在查询、更新和聚合操作中进行组合和使用以实现复杂的数据处理和分析任务。注意随着MongoDB版本的更新有些操作符可能会被废弃或替换因此建议查阅最新的官方文档以获取最准确的信息。2.4、聚合操作下面通过聚合来操作books集合对书籍表中的书籍进行聚合分析找出按照体裁分类的作品评分排行榜。首先给books集合添加如下数据db.books.insertMany([{title:平凡的世界,genres:[Novel,Fiction],words:1040000,year:1986,author:[路遥],characters:[孙少平,孙少安,田晓霞,田润叶],country:中国,douban:{rating:9.0,votes:320319,star:5}},{title:活着,genres:[Novel,Fiction],words:132000,year:1992,author:[余华],characters:[许富贵,家珍,凤霞,有庆],country:中国,douban:{rating:9.4,votes:813308,star:5}},{title:红楼梦,genres:[Novel,Fiction],words:960000,year:1760,author:[曹雪芹,高鹗],characters:[林黛玉,贾宝玉,薛宝钗],country:中国,douban:{rating:9.6,votes:813308,star:5}},{title:三体,genres:[Novel,Science Fiction],words:800000,year:[2006,2008,2010],author:[刘慈欣],characters:[叶文洁,汪淼,史强,逻辑],country:中国,douban:{rating:9.5,votes:180721,star:5}}])然后通过4个管道阶段对以上数据进行聚合统计以得到不同体裁作品的平均豆瓣评分。语句如下db.books.aggregate([//第一阶段{$project:{_id:0,genres:1,title:1,douban:1}},//第二阶段{$unwind:$genres},//第三阶段{$group:{_id:$genres,averageGenreRating:{$avg:$douban.rating}}},//第四阶段{$sort:{averageGenreRating:-1}}])第一阶段$project过滤出包含genres、title、words字段的文档传递到第二阶段。此阶段筛选出的数据如下[{title:平凡的世界,genres:[Novel,Fiction],douban:{rating:9,votes:320319,star:5}},{title:活着,genres:[Novel,Fiction],douban:{rating:9.4,votes:813308,star:5}},{title:红楼梦,genres:[Novel,Fiction],douban:{rating:9.6,votes:813308,star:5}},{title:三体,genres:[Novel,Science Fiction],douban:{rating:9.5,votes:180721,star:5}}]第二阶段$unwind将第一阶段产生的数据以genres数组中的每一个元素为准分别拆分然后将数据传递到第三阶段。第二阶段筛选出的数据如下[{title:平凡的世界,genres:Novel,douban:{rating:9,votes:320319,star:5}},{title:平凡的世界,genres:Fiction,douban:{rating:9,votes:320319,star:5}},{title:活着,genres:Novel,douban:{rating:9.4,votes:813308,star:5}},{title:活着,genres:Fiction,douban:{rating:9.4,votes:813308,star:5}},{title:红楼梦,genres:Novel,douban:{rating:9.6,votes:813308,star:5}},{title:红楼梦,genres:Fiction,douban:{rating:9.6,votes:813308,star:5}},{title:三体,genres:Novel,douban:{rating:9.5,votes:180721,star:5}},{title:三体,genres:Science Fiction,douban:{rating:9.5,votes:180721,star:5}}]第三阶段$group以去重后的genres的每个元素进行分组统计使用_id字段标记分组关键字添加一个新的字段averageWords统计每一个体裁的作品平均字数。筛选出的数据如下[{_id:Fiction,averageGenreRating:9.333333333333334},{_id:Novel,averageGenreRating:9.375},{_id:Science Fiction,averageGenreRating:9.5}]第四阶段$sort按评分从高到低排列最终得到的数据如下所示。[{_id:Science Fiction,averageGenreRating:9.5},{_id:Novel,averageGenreRating:9.375},{_id:Fiction,averageGenreRating:9.333333333333334}]Map-Reduce是一种计算模型简单来说就是将大批量的工作数据分解(Map)执行然后将结果合并成最终结果(Reduce)。MongoDB提供的Map-Reduce非常灵活对于大规模数据分析也相当实用。然而在MongoDB 5.0之后该方法已被标记为过时官方推荐使用聚合管道来替换。通过聚合管道的阶段例如$group、$merge等重写mapReduce函数结合$accumulator和$function操作符实现自定义客户端的处理逻辑。使用mapReduce函数时要先实现map函数和reduce函数。map函数通过调用emit(key, value)遍历集合中所有的记录将key与value传递给reduce函数进行处理。map函数必须调用emit(key,value)返回键值对。以下是mapReduce的基本语法参数说明map映射函数生成键值对序列作为reduce函数的参数。reduce分解函数reduce函数的任务是将key-values转换为key-value也就是把values数组中的多个值合并为一个单一的值value。out指定统计结果存放的集合。query筛选条件只有满足该条件的文档才会调用map函数。sort和limit结合使用的排序参数在文档传递给map函数之前进行排序​可以优化分组机制。limit传递给map函数的文档数量的上限。在集合orders中查找status:‘A’的数据并根据cust_id进行分组并计算amount字段的总和如图所示。考虑使用以下文档结构存储用户的文章文档存储了用户的user_name和文章的status字段db.posts.insertMany([{post_text:奔月教程最全的技术文档。,user_name:mark,status:active},{post_text:奔月教程最全的技术文档。,user_name:mark,status:active},{post_text:奔月教程最全的技术文档。,user_name:mark,status:active},{post_text:奔月教程最全的技术文档。,user_name:mark,status:active},{post_text:奔月教程最全的技术文档。,user_name:mark,status:disabled},{post_text:奔月教程最全的技术文档。,user_name:runoon,status:disabled},{post_text:奔月教程最全的技术文档。,user_name:runoon,status:disabled},{post_text:奔月教程最全的技术文档。,user_name:runoon,status:active}])现在我们在posts集合中使用mapReduce函数来选取已发布的文章(status:“active”)并通过user_name分组计算每个用户的文章数db.posts.mapReduce(function(){emit(this.user_name,1)},function(key,values){returnArray.sum(values)},{query:{status:active},out:post_total})以上mapReduce输出结果为结果表明共有5个符合查询条件(status:“active”)的文档在map函数中生成了5个键值对文档最后使用reduce函数将相同的键值分为两组。参数说明result存储结果的集合名称这是一个临时集合当mapReduce的连接关闭后该集合将自动被删除。timeMillis执行所花费的时间以毫秒为单位。input满足条件被发送到map函数的文档个数。emit在map函数中emit被调用的次数也就是所有集合中的数据总量。output结果集合中的文档个数count对调试非常有帮助​。ok是否成功成功时返回1。err如果失败这里会显示失败原因。不过从经验来看失败原因可能比较模糊参考价值不大。使用find操作符来查看mapReduce的查询结果db.posts.mapReduce(function(){emit(this.user_name,1)},function(key,values){returnArray.sum(values)},{query:{status:active},out:post_total}).find()查询结果如下{_id:mark,value:4}{_id:runoon,value:1}用类似的方式mapReduce可以被用来构建大型复杂的聚合查询。map函数和reduce函数可以使用JavaScript来实现使得mapReduce的使用非常灵活和强大。

相关新闻