zoukankan      html  css  js  c++  java
  • 5.mongodb的group与sum操作

    一、group与sum的概念

    1.知识储备:聚合与管道

    1.1 MongoDB 聚合:

    MongoDB 中聚合(aggregate)主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果。

    有点类似 SQL 语句中的 count(*)。

    介绍一下聚合的中的一些表达式方法:

    表达式描述实例
    $sum 计算总和。 db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$sum : "$likes"}}}])
    $avg 计算平均值 db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$avg : "$likes"}}}])
    $min 获取集合中所有文档对应值得最小值。 db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$min : "$likes"}}}])
    $max 获取集合中所有文档对应值得最大值。 db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$max : "$likes"}}}])
    $push 在结果文档中插入值到一个数组中。 db.mycol.aggregate([{$group : {_id : "$by_user", url : {$push: "$url"}}}])
    $addToSet 在结果文档中插入值到一个数组中,但不创建副本。 db.mycol.aggregate([{$group : {_id : "$by_user", url : {$addToSet : "$url"}}}])
    $first 根据资源文档的排序获取第一个文档数据。 db.mycol.aggregate([{$group : {_id : "$by_user", first_url : {$first : "$url"}}}])
    $last 根据资源文档的排序获取最后一个文档数据 db.mycol.aggregate([{$group : {_id : "$by_user", last_url : {$last : "$url"}}}])

    1.2 MongoDB 管道:

    管道在Unix和Linux中一般用于将当前命令的输出结果作为下一个命令的参数。

    MongoDB的聚合管道将MongoDB文档在一个管道处理完毕后将结果传递给下一个管道处理。管道操作是可以重复的。

    表达式:处理输入文档并输出。表达式是无状态的,只能用于计算当前聚合管道的文档,不能处理其它的文档。

    这里我们介绍一下聚合框架中常用的几个操作:

    • $project:修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档。
    • $match:用于过滤数据,只输出符合条件的文档。$match使用MongoDB的标准查询操作。
    • $limit:用来限制MongoDB聚合管道返回的文档数。
    • $skip:在聚合管道中跳过指定数量的文档,并返回余下的文档。
    • $unwind:将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。
    • $group:将集合中的文档分组,可用于统计结果。
    • $sort:将输入文档排序后输出。
    • $geoNear:输出接近某一地理位置的有序文档。

    1.3 聚合和管道的合作:

    例子:这段将会把文章的分数在70到90之间的所有文章,然后将符合条件的记录送到下一阶段$group管道操作符进行处理。(所以理论上你可以定义一个聚合,在聚合下使用无数个管道)

    group将会使用id进行分组,然后sum来计算每一个相同_id的总数并赋值给count。最后打印count的值就是所有符合条件的文章的数目了。

    db.articles.aggregate( [
                            { $match : { score : { $gt : 70, $lte : 90 } } },
                            { $group: { _id: null, count: { $sum: 1 } } }
                           ] );

    此时看到此处,有的人可能会有疑问:

    1.group使用id进行分组,那么id怎么为null;

    2.sum进行计算,为什么给sum后面赋值个1;

    别急,往下看:

    2.正文:

    group上面已解释是一种管道,将符合条件的进行分组,sum是聚合的计算方法。

    sum可以在下面的管道或者方法中使用:

     sum后面通常跟得是一个field(翻译为领域,我把它理解为变量领域,可能不太恰当,但是我找不到其它词去形容它):

    1.如果这个变量领域是数值类型的,那么它会根据变量的数值进行计算;

    2.如果这个变量领域有数值也有非数值类型的,它会根据那个数值类型的进行计算;

    3.如果这个变量领域(不管是不是数值类型),只要他在表中没出现,那么它会返回0;

    4.如果这个变量领域是非数值类型的,那么它会返回0;

     看了sum后面的field的介绍,有的人可能马上反应过来,但是,有的人还可能一脸闷逼,那么我继续解释:

    1.由于在group中使用_id进行分组,那么mogodb的group管道就会根据_id的具体值进行分组,但是当我们传入_id为:null时,mongodb的group此时就不会根据_id的具体值进行分组,它会把所有符合条件的记录看成一个整体,此时group取出的将是整个符合条件的所有记录。

    2.由于sum后面跟的是1的值,那么sum在遍历每一个group组合的成员时,或者说sum在遍历变量领域的每一个成员的时候,由于这个变量领域里的值都是1,它每遍历到一个变量(表里的每条符合条件的记录)都会进行加1操作,此时由于group本身只有一个,就是一整个表中符合条件的所有记录,那么sum遍历整个表的每一条符合条件的记录时就会进行加1操作,随后count保存的就是所有符合条件的值。

    所以通常情况下,我可以使用group+sum的组合写法,将数据库每条记录进行分类:

    我有这样一个数据库:其中除了flag是布尔类型,agree是number类型,其它的都是字符串类型

     现在我想做的是,如何计算每一条记录中的agree使他们相加起来,再返回给我:

    例子:下面这段代码的执行过程:

    1.使用group将每一个_id进行分组。

    2.计算每一个agree的总和,由于我的_id是不重复的,所以sum的作用其实没有体现出来,不过当你的表里有多个记录是同一个id值,那么sum将会把每一个相同_id下的agree值进行计算,然后打印。

    3.把计算后的每一个_id的agree总和作为commentAgreesCount的值。

    4.打印输出。

    db.article_comments.aggregate(      [{ $group :        {  _id:"$_id",  commentAgreesCount: { $sum: "$agree" }} }]   )
    运行结果: {
    "_id" : ObjectId("606a70ac298cbe2978047810"), "commentAgreesCount" : 1 } { "_id" : ObjectId("606a60ba3878e73b78a9f5f0"), "commentAgreesCount" : 1 } { "_id" : ObjectId("606a7694298cbe2978047814"), "commentAgreesCount" : 0 } { "_id" : ObjectId("606a5ce13878e73b78a9f5ec"), "commentAgreesCount" : 1 }

    但是我的本意并不是想获得每一个分组后的结果,我是要获取所有的记录的agree总和。

    所以解决方法:

    1.把id定义为null,mongodb内部就会不区分_id,即把所有的_id进行计算,然后输出结果

    db.article_comments.aggregate(      [{ $group :        {  _id:null, commentAgreesCount: { $sum: "$agree" }} }]   )
    运行结果: {
    "_id" : null, "commentAgreesCount" : 3 }

    但是sum方法只能计算数值类型的,如果是字符串类型的,它就会默认把值设置为0。这个我上面也有贴官网的图,也有进行相应的解释。

    例子:

    db.article_comments.aggregate(
         [{$group : 
          {
           _id:null,
           commentAgreesCount: { $sum: "$topic_id" }
        
            }
         }]
      )

    结果:

    { "_id" : null, "commentAgreesCount" : 0 }

    3.后话:

    如果你要查询的是非数值类型的总和,打比方:我的agree设置成了String类型,但是我想获得每一条记录中agree加起来的总和。

    又或者是我有一个数据库,里面保存着这个月卖出去的所有商品,所有商品的id是字符类型的,我想统计所有商品的卖出情况,比如id:1的所有商品,因为id:1的商品一个月内可能卖出很多次。id:2的所有商品,因为id:2的商品一个月内也可能卖出很多次 ……以此类推,然后我想计算所有id相同的商品卖出去的情况,并使用sum进行计算所有相同id商品的价格*件数=每件商品的营业额

    此时再假设你的商品价格也是字符串类型的,那么此时就无法用上面那个sum方法计算了,此时解决方法也有,就是根据数据库查询到的所有记录,再进行foreach循环遍历一下所有商品的价格字符,再把他们转换为数值类型的进行计算。

    所以数据库的方法并不是万能的,有时候你无法根据数据库进行查询,获取想要的数据类型时,那么只能将数据库获得的冗余数据,使用其他语言再进行数据处理。

    穷则独善其身,达则兼济天下……
  • 相关阅读:
    嵌入式开发之web服务器---boa移植
    图像处理之基础---去污算法
    图像处理之基础---傅里叶
    图像处理之基础---卷积
    图像处理之基础---各种滤波
    二维矩阵卷积运算实现
    图像处理之基础---卷积傅立叶变换中的复数
    图像处理-线性滤波-2 图像微分(1、2阶导数和拉普拉斯算子)
    Live555 中的客户端动态库.so的调用方式之一 程序中调用
    20个Flutter实例视频教程-01节底部导航栏和切换效果的制作-1
  • 原文地址:https://www.cnblogs.com/hmy-666/p/14618306.html
Copyright © 2011-2022 走看看