• 5.聚合管道


    单一聚合操作

    聚合操作处理数据记录并返回计算结果(诸如统计平均值,求和等)。聚合操作组值来自多个文档,可以对 分组数据执行各种操作以返回单个结果。

    • 单一作用聚合:提供了对常见聚合过程的简单访问,操作都从单个集合聚合文档。

    MongoDB提供 db.collection.estimatedDocumentCount(), db.collection.count(), db.collection.distinct() 这类单一作用的聚合函数。 所有这些操作都聚合来自单个集合的文档。虽然这 些操作提供了对公共聚合过程的简单访问,但它们缺乏聚合管道和map-Reduce的灵活性和功能。

    函数 描述
    db.collection.estimatedDocumentCount() 忽略查询条件,返回集合或视图中所有文档的计数
    db.collection.count() 返回与find()集合或视图的查询匹配的文档计数 。 等同于 db.collection.find(query).count()构造
    db.collection.distinct() 在单个集合或视图中查找指定字段的不同值,并在 数组中返回结果。
    #查询集合中所有文档的计数,忽略条件
    db.books.estimatedDocumentCount()
    #计算与查询匹配的所有文档数
    db.books.count({batch_index:{$gt:17})
    #返回不同batch_name的数组
    db.books.distinct("batch_name")
    #返回batch_index大于17的文档不同batch_name的数组
    db.books.distinct("batch_name",{batch_index:{$gt:17}})
    

    因为生产环境都是做的高可用,集群部署,所以单一集合操作用的不多

    更多命令见官网

    聚合管道

    聚合管道是一个数据聚合的框架,模型基于数据处理流水线的概念。文档进入多级管道,将文档转 换为聚合结果。

    MongoDB 聚合框架(Aggregation Framework)是一个计算框架,它能实现

    • 作用在一个或几个集合上;

    • 对集合中的数据进行的一系列运算;

    • 将这些数据转化为期望的形式;

    从效果而言,聚合框架相当于 SQL 查询中的GROUP BY、 LEFT OUTER JOIN 、 AS等。

    管道(Pipeline)和阶段(Stage)

    整个聚合运算过程称为管道(Pipeline),它是由多个阶段(Stage)组成的, 每个管道:

    • 接受一系列文档(原始数据);
    • 每个阶段对这些文档进行一系列运算;
    • 结果文档输出给下一个阶段;

    和JDK8中Stream管道思想差不多

    pipeline = [$stage1, $stage2, ...$stageN];
    db.collection.aggregate(pipeline, {options})
    
    • pipelines 一组数据聚合阶段。除$out、$Merge和$geonear阶段之外,每个阶段都可以在管道中 出现多次。
    • options 可选,聚合操作的其他参数。包含:查询计划、是否使用临时文件、 游标、最大操作时 间、读写策略、强制索引等等

    image-20220320152307572

    更多命令见官方

    $match&$group 匹配与分组统计

    为提高性能,match管道需写所有管道最前面

    db.my_table.aggregate([ { $match : { $or: [{author : "dave"},{"views":{$gte:1000}}] } },{ $group: { _id: null, count: { $sum: 1 } } } ])
    #匹配author=dave或views >=1000的数据,并且查询有多少条
    

    $project 别名与指定查询列

    db.my_table.aggregate([ { $match : { $or: [{author : "dave"},{"views":{$gte:1000}}] } },{$project:{zuozhe:"$author"}} ])
    #匹配author=dave或views >=100#0的数据,并且将author起别名为zuozhe后输出
    db.my_table.aggregate([ { $match : { $or: [{author : "dave"},{"views":{$gte:1000}}] } },{$project:{zuozhe:"$author",_id:0,score:1}} ])
    #匹配author=dave或views >=100#0的数据,并且将author起别名为zuozhe后,不显示_id列,显示score列
    db.my_table.aggregate([
    {$project:{"author.name":1}}
    ])
    或
    db.my_table.aggregate([
    {$project:{author:{name:1}}}
    ])
    #查询文档中子文档指定列,author对象里面又包含了name\sex等字段,
    
    

    $count 统计

    db.my_table.aggregate( [{$match:{name:"save"}},{$count:"countResult"}] )
    #统计name=save的记录,并且获得条数,输出结果key为countResult
    

    $group 分组求平均值、求和

    按指定的表达式对文档进行分组,并将每个不同分组的文档输出到下一个阶段。输出文档包含一个id字段,该字段按键包含不同的组。
    输出文档还可以包含计算字段,该字段保存由$group的id字段分组的一些accumulator表达式的值。$group不会输出具体的文档而只是统计信息。

    • id字段是必填的;但是,可以指定id值为null来为整个输入文档计算累计值。剩余的计算字段是可选的,并使用运算符进行计算。
    • _id和表达式可以接受任何有效的表达式。
    db.my_table.aggregate( [ {$match:{views:{$gte:100}}}, {$group:{_id:null,count:{$sum:1},sum:{$sum:"$score"},avg:{$avg:"$views"}}} ])
    #查询views>=100,统计一共有多少条记录、对score求和、对views求平均值
    db.my_table.aggregate( [ {$match:{views:{$gte:100}}}, {$group:{_id:"$author",count:{$sum:1},sum:{$sum:"$score"},avg:{$avg:"$views"}}} ])
    #查询views>=100,分别统计每个作者一共有多少条记录、对score求和、对views求平均值
    

    image-20220320181016138

    $unwind 将数组拆分为单独的文档

    {
        $unwind:
        {
            #要指定字段路径,在字段名称前加上$符并用引号括起来。
            path: <field path>,
            #可选,一个新字段的名称用于存放元素的数组索引。该名称不能以$开头。
            includeArrayIndex: <string>,
            #可选,default :false,若为true,如果路径为空,缺少或为空数组,则$unwind输出文档
            preserveNullAndEmptyArrays: <boolean>
        }
    }
    

    测试数据

    db.books.insert([
    {
    "title" : "book-51",
    "type" : "technology",
    "favCount" : 11,
    "tag":[],
    "author" : {
    "name" : "fox",
    "age" : 28
    }
    },{
    "title" : "book-52",
    "type" : "technology",
    "favCount" : 15,
    "author" : {
    "name" : "fox",
    "age" : 28
    }
    },{
    "title" : "book-53",
    "type" : "technology",
    "tag" : [
    "nosql",
    "document"
    ],
    "favCount" : 20,
    "author" : {
    "name" : "fox",
    "age" : 28
    }
    }])
    

    测试

    # 使用includeArrayIndex选项来输出数组元素的数组索引
    db.books.aggregate([
    {$match:{"author.name":"fox"}},
    {$unwind:{path:"$tag", includeArrayIndex: "arrayIndex"}}
    ])
    # 使用preserveNullAndEmptyArrays选项在输出中包含缺少size字段,null或空数组的文档
    db.books.aggregate([
    {$match:{"author.name":"fox"}},
    {$unwind:{path:"$tag", preserveNullAndEmptyArrays: true}}
    ])
    

    $limit

    限制传递到管道中下一阶段的文档数

    db.books.aggregate([
    	{$limit : 5 }
    ])
    

    此操作仅返回管道传递给它的前5个文档。 $limit对其传递的文档内容没有影响。

    注意:当$sort在管道中的$limit之前立即出现时,$sort操作只会在过程中维持前n个结果,其中n是指 定的限制,而MongoDB只需要将n个项存储在内存中。

    $skip

    跳过进入stage的指定数量的文档,并将其余文档传递到管道中的下一个阶段

    db.books.aggregate([
    	{$skip : 5 }
    ])
    
    

    此操作将跳过管道传递给它的前5个文档。 $skip对沿着管道传递的文档的内容没有影响。

    $sort

    对所有输入文档进行排序,并按排序顺序将它们返回到管道。 语法:

    { $sort: { <field1>: <sort order>, <field2>: <sort order> ... } }
    

    要对字段进行排序,请将排序顺序设置为1或-1,以分别指定升序或降序排序

    db.batch_table.aggregate([ {$sort:{batch_index:-1}},{$limit:5},{$skip:1}])
    #倒序查五条,显示除去第一条的数据
    

    $lookup 联表查询

    Mongodb 3.2版本新增,主要用来实现多表关联查询, 相当关系型数据库中多表关联查询。每个输入 待处理的文档,经过$lookup 阶段的处理,输出的新文档中会包含一个新生成的数组(可根据需要命名 新key )。数组列存放的数据是来自被Join集合的适配文档,如果没有,集合为空(即 为[ ])

    语法:

    db.collection.aggregate([{
        $lookup: {
            from: "<collection to join>", #同一个数据库下等待被Join的集合。
            localField: "<field from the input documents>",#源集合中的match值,如果输入的集合中,某文档没有 localField这个Key
    (Field),在处理的过程中,会默认为此文档含有 localField:null的键值对。
            foreignField: "<field from the documents of the from collection>",#待Join的集合的match值,如果待Join的集合中,文档没有foreignField值,在处理的过程中,会默认为此文档含有 foreignField:null的键值对。
            as: "<output array field>"#为输出文档的新增值命名。如果输入的集合中已存在该值,则会覆盖掉
        }
    })
    

    测试数据:

    db.customer.insert({customerCode:1,name:"customer1",phone:"13112345678",address:
    "test1"})
    db.customer.insert({customerCode:2,name:"customer2",phone:"13112345679",address:
    "test2"})
    db.order.insert({orderId:1,orderCode:"order001",customerCode:1,price:200})
    db.order.insert({orderId:2,orderCode:"order002",customerCode:2,price:400})
    db.orderItem.insert({itemId:1,productName:"apples",qutity:2,orderId:1})
    db.orderItem.insert({itemId:2,productName:"oranges",qutity:2,orderId:1})
    db.orderItem.insert({itemId:3,productName:"mangoes",qutity:2,orderId:1})
    db.orderItem.insert({itemId:4,productName:"apples",qutity:2,orderId:2})
    db.orderItem.insert({itemId:5,productName:"oranges",qutity:2,orderId:2})
    db.orderItem.insert({itemId:6,productName:"mangoes",qutity:2,orderId:2})
    #插入了两个用户、两个订单、六个订单项
    
    db.customer.aggregate([{$lookup:{
        from:"order",
        localField:"customerCode",
        foreignField:"customerCode",
        as:"customerOrder"
        }
    }]).pretty()
    #最终查询结果在名为customerOrder中
    #类似 select * from customer 
    #left join order on order.customerCode=customer.customerCode
    db.order.aggregate([{$lookup:{
        from:"customer",
        	localField:"customerCode",
        	foreignField:"customerCode",
        	as:"curstomer"
        }
        },{$lookup:{
        	from:"orderItem",
        	localField:"orderId",
        	foreignField:"orderId",
        	as:"orderItem"
        }
    }])
    #类似 select * from order 
    #left join customer on order.customerCode=customer.customerCode 
    #left join orderItem on orderItem.orderId=order.orderId
    #但如果添加过滤条件只能是添加order中的字段
    

    $bucket

    根据条件统计出值(求和、平均值等)后,根据定义的区间范围来得出每个区间有多少个值匹配

    db.books.aggregate([{
        $bucket:{
            groupBy:"$favCount", #要统计的列
            boundaries:[0,10,60,80,100], #定义的分组 0~9 10~59。。。
            default:"other", #不在范围的都算做其他
            output:{"count":{$sum:1}} #统计数据
        }
    }])
    
    

    MapReduce

    MapReduce能做的事通过聚合管道一样能做,所以多数还是使用聚合管道

    MapReduce操作将大量的数据处理工作拆分成多个线程并行处理,然后将结果合并在一起。MongoDB 提供的Map-Reduce非常灵活,对于大规模数据分析也相当实用。

    MapReduce具有两个阶段:

    • 将具有相同Key的文档数据整合在一起的map阶段
    • 组合map操作的结果进行统计输出的reduce阶段

    MapReduce的基本语法

    db.collection.mapReduce(
        function() {emit(key,value);}, #将数据拆分成键值对,交给reduce函数
        function(key,values) {return reduceFunction}, #根据键将值做统计运算
        {
            out: <collection>,#可选,将结果汇入指定表,数据写入到指定表中
            query: <document>,#可选筛选数据的条件,筛选的数据送入map
            sort: <document>,#排序完后,送入map
            limit: <number>,#限制送入map的文档数
            finalize: <function>,#可选,修改reduce的结果后进行输出
            scope: <document>,#可选,指定map、reduce、finalize的全局变量
            jsMode: <boolean>,#可选,默认false。在mapreduce过程中是否将数 据转换成bson格式
            verbose: <boolean>,#可选,是否在结果中显示时间,默认false
            bypassDocumentValidation: <boolean>#可选,是否略过数据校验
        }
    )
    
    

    image-20220320224135068

    参考<图灵课堂>

  • 相关阅读:
    Opencv保存摄像头视频&&各种编码器下视频文件占用空间对比
    生活的 tricks
    生活的 tricks
    词汇的积累 —— 反义词、同义词
    词汇的积累 —— 反义词、同义词
    目标跟踪系列十一:Exploiting the Circulant Structure of Tracking-by-detection with Kernels代码思路
    Java中Integer类的方法
    php中 重载(二)
    协方差的意义
    malloc函数具体解释
  • 原文地址:https://www.cnblogs.com/rb2010/p/16048718.html
Copyright © 2020-2023  润新知