• 企业400电话
  • 微网小程序
  • AI电话机器人
  • 电商代运营
  • 全 部 栏 目

    企业400电话 网络优化推广 AI电话机器人 呼叫中心 网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    MongoDB中强大的统计框架Aggregation使用实例解析

    听说项目里面Aggregation用的多,那就专门针对这个多多练习一下。

    基本的操作包括:

    •$project - 可以从子文档中提取字段,可以重命名字段

    •$match - 可以实现查找的功能

    •$limit - 接受一个数字n,返回结果集中的前n个文档。

    •$skip - 接受一个数字n,丢弃结果集中的前n个文档。效率比较低,依然会遍历前n个文档。

    •$unwind - 可以将一个包含数组的文档切分成多个, 比如你的文档有 中有个数组字段 A, A中有10个元素, 那么经过 $unwind处理后会产生10个文档,这些文档只有 字段 A不同

    •$group - 统计操作, 还提供了一系列子命令

    –$avg, $sum …

    •$sort - 排序

    Python篇
    实验一、学生数据统计
    1、生成学生数据:

    #!/usr/bin/env python
    # coding=utf-8
    from pymongo import MongoClient
    from random import randint
    name1 = ["yang ", "li ", "zhou "]
    name2 = [
      "chao",
      "hao",
      "gao",
      "qi gao",
      "hao hao",
      "gao gao",
      "chao hao",
      "ji gao",
      "ji hao",
      "li gao",
      "li hao",
    ]
    provinces = [
      "guang dong",
      "guang xi",
      "shan dong",
      "shan xi",
      "he nan"
    ]
    client = MongoClient('localhost', 27017)
    db = client.student
    sm = db.smessage
    sm.remove()
    for i in range(1, 100):
      name = name1[randint(0, 2)] + name2[randint(0, 10)]
      province = provinces[randint(0, 4)]
      new_student = {
        "name": name,
        "age": randint(1, 30),
        "province": province,
        "subject": [
          {"name": "chinese", "score": randint(0, 100)},
          {"name": "math", "score": randint(0, 100)},
          {"name": "english", "score": randint(0, 100)},
          {"name": "chemic", "score": randint(0, 100)},
        ]}
      print new_student
      sm.insert_one(new_student)
    
    print sm.count()
    
    

    好了,现在数据库里面有100条学生数据了。

    现在我要得到广东学生的平均年龄,在mongo控制台输入:

     如果想到得到所有省份的平均年龄,那就更加简单了:

    db.smessage.aggregate(
    {$match: {province: "guang dong"}}
    )
    
    { "_id" : "guang xi", "age" : 15.19047619047619 }
    { "_id" : "guang dong", "age" : 16.05263157894737 }
    { "_id" : "shan dong", "age" : 17.44 }
    { "_id" : "he nan", "age" : 20 }
    { "_id" : "shan xi", "age" : 16.41176470588235 }
    
    

    如果想得到广东省所有科目的平均成绩:

    db.smessage.aggregate(
    {$match: {province: "guang dong"}},
    {$unwind: "$subject"},
    {$group: { _id: {province:"$province",sujname:"$subject.name"}, per:{$avg:"$subject.score"}}}
    )
    

    加上排序:

    db.smessage.aggregate(
    {$match: {province: "guang dong"}},
    {$unwind: "$subject"},
    {$group: { _id: {province:"$province",sujname:"$subject.name"}, per:{$avg:"$subject.score"}}},
    {$sort:{per:1}}
    )
    

    实验二、寻找发帖水王
    有一个保存着杂志文章的集合,你可能希望找出发表文章最多的那个作者。假设每篇文章被保存为MongoDB中的一个文档。

    1、插入数据

    #!/usr/bin/env python
    # coding=utf-8
    from pymongo import MongoClient
    from random import randint
    
    
    name = [
      'yangx',
      'yxxx',
      'laok',
      'kkk',
      'ji',
      'gaoxiao',
      'laoj',
      'meimei',
      'jj',
      'manwang',
    ]
    
    title = [
      '123',
      '321',
      '12',
      '21',
      'aaa',
      'bbb',
      'ccc',
      'sss',
      'aaaa',
      'cccc',
    ]
    
    client = MongoClient('localhost', 30999)
    db = client.test
    bbs = db.bbs
    bbs.remove()
    for i in range(1, 10000):
      na = name[randint(0, 9)]
      ti = title[randint(0, 9)]
      newcard = {
        'author': na,
        'title': ti,
      }
      bbs.insert_one(newcard)
    
    print bbs.count()
    
    

    现在我们拥有了10000条文章数据了。

    2、用$project将author字段投射出来

    {"$project": {"author":1}}
    
    

    这个语法与查询中的字段选择器比较像:可以通过指定"fieldname" : 1选择需要投射的字段,或者通过指定"fieldname":0排除不需要的字段。

    执行完这个"$project"操作之后,结果集中的每个文档都会以{"_id" : id, "author" : "authorName"}这样的形式表示。这些结果只会在内存中存在,不会被写入磁盘。

    3、用group将作者名称分组

    {"group":{"_id":"$author","count":{"$sum":1}}}
    
    

    这样就会将作者按照名字排序,某个作者的名字每出现一次,就会对这个作者的"count"加1。

    这里首先指定了需要进行分组的字段"author"。这是由"_id" : "$author"指定的。可以将这个操作想象为:这个操作执行完后,每个作者只对应一个结果文档,所以"author"就成了文档的唯一标识符("_id")。

    第二个字段的意思是为分组内每个文档的"count"字段加1。注意,新加入的文档中并不会有"count"字段;这"$group"创建的一个新字段。

    执行完这一步之后,结果集中的每个文档会是这样的结构:{"_id" : "authorName", "count" : articleCount}。

    4、用sort排序

    {"$sort" : {"count" : -1}}
    
    

    这个操作会对结果集中的文档根据"count"字段进行降序排列。

    5、限制结果为前5个文档

    {"$limit" : 5}
    
    

    这个操作将最终的返回结果限制为当前结果中的前5个文档。
    在MongoDB中实际运行时,要将这些操作分别传给aggregate()函数:

    > db.articles.aggregate({"$project" : {"author" : 1}},
    ... {"$group" : {"_id" : "$author", "count" : {"$sum" : 1}}},
    ... {"$sort" : {"count" : -1}},
    ... {"$limit" : 5}
    ... )
    

    aggregate()会返回一个文档数组,其中的内容是发表文章最多的5个作者。

    { "_id" : "yangx", "count" : 1028 }
    { "_id" : "laok", "count" : 1027 }
    { "_id" : "kkk", "count" : 1012 }
    { "_id" : "yxxx", "count" : 1010 }
    { "_id" : "ji", "count" : 1007 }
    
    
    
    Java篇

    我在db中造了些数据(数据时随机生成的, 能用即可),没有建索引,文档结构如下:

    Document结构:

     {
      "_id" : ObjectId("509944545"),
      "province" : "海南",
      "age" : 21,
      "subjects" : [
      {
      "name":"语文",
      "score" : 53
      },
      {
      "name":"数学",
      "score" : 27
      },
      {
      "name":"英语",
      "score" : 35
      }
       ],
      "name" : "刘雨"
     }
    

         接下来要实现两个功能:

    1.     统计上海学生平均年龄
    2.     统计每个省各科平均成绩

    接下来一一道来

    统计上海学生平均年龄

    从这个需求来讲,要实现功能要有几个步骤: 1. 找出上海的学生. 2. 统计平均年龄 (当然也可以先算出所有省份的平均值再找出上海的)。如此思路也就清晰了

    首先上 $match, 取出上海学生

    {$match:{'province':'上海'}}
    

    接下来 用 $group 统计平均年龄

    {$group:{_id:'$province',$avg:'$age'}}
    

    $avg 是 $group的子命令,用于求平均值,类似的还有 $sum, $max ....
    上面两个命令等价于

    select province, avg(age) 
     from student 
     where province = '上海'
     group by province
    

    下面是Java代码

    Mongo m = new Mongo("localhost", 27017);
     DB db = m.getDB("test");
     DBCollection coll = db.getCollection("student");
     
     /*创建 $match, 作用相当于query*/
     DBObject match = new BasicDBObject("$match", new BasicDBObject("province", "上海"));
     
     /* Group操作*/
     DBObject groupFields = new BasicDBObject("_id", "$province");
     groupFields.put("AvgAge", new BasicDBObject("$avg", "$age"));
     DBObject group = new BasicDBObject("$group", groupFields);
     
     /* 查看Group结果 */
     AggregationOutput output = coll.aggregate(match, group); // 执行 aggregation命令
     System.out.println(output.getCommandResult());
    

    输出结果:

    { "serverUsed" : "localhost/127.0.0.1:27017" ,    
     "result" : [ 
      { "_id" : "上海" , "AvgAge" : 32.09375}
      ] ,     
      "ok" : 1.0
     }
    

    如此工程就结束了,再看另外一个需求

    统计每个省各科平均成绩

    首先更具数据库文档结构,subjects是数组形式,需要先‘劈'开,然后再进行统计

    主要处理步骤如下:

    1. 先用$unwind 拆数组 2. 按照 province, subject 分租并求各科目平均分

    $unwind 拆数组

    {$unwind:'$subjects'}
    

    按照 province, subject 分组,并求平均分

    {$group:{
       _id:{
         subjname:”$subjects.name”,  // 指定group字段之一 subjects.name, 并重命名为 subjname
         province:'$province'     // 指定group字段之一 province, 并重命名为 province(没变)
       },
       AvgScore:{
        $avg:”$subjects.score”    // 对 subjects.score 求平均
       }
     }
    

    java代码如下:

    Mongo m = new Mongo("localhost", 27017);
     DB db = m.getDB("test");
     DBCollection coll = db.getCollection("student");
     
     /* 创建 $unwind 操作, 用于切分数组*/
     DBObject unwind = new BasicDBObject("$unwind", "$subjects");
     
     /* Group操作*/
     DBObject groupFields = new BasicDBObject("_id", new BasicDBObject("subjname", "$subjects.name").append("province", "$province"));
     groupFields.put("AvgScore", new BasicDBObject("$avg", "$subjects.scores"));
     DBObject group = new BasicDBObject("$group", groupFields);
     
     /* 查看Group结果 */
     AggregationOutput output = coll.aggregate(unwind, group); // 执行 aggregation命令
     System.out.println(output.getCommandResult());
    

    输出结果

    { "serverUsed" : "localhost/127.0.0.1:27017" , 
      "result" : [ 
       { "_id" : { "subjname" : "英语" , "province" : "海南"} , "AvgScore" : 58.1} , 
       { "_id" : { "subjname" : "数学" , "province" : "海南"} , "AvgScore" : 60.485} ,
       { "_id" : { "subjname" : "语文" , "province" : "江西"} , "AvgScore" : 55.538} , 
       { "_id" : { "subjname" : "英语" , "province" : "上海"} , "AvgScore" : 57.65625} , 
       { "_id" : { "subjname" : "数学" , "province" : "广东"} , "AvgScore" : 56.690} , 
       { "_id" : { "subjname" : "数学" , "province" : "上海"} , "AvgScore" : 55.671875} ,
       { "_id" : { "subjname" : "语文" , "province" : "上海"} , "AvgScore" : 56.734375} , 
       { "_id" : { "subjname" : "英语" , "province" : "云南"} , "AvgScore" : 55.7301 } ,
       .
       .
       .
       .
       "ok" : 1.0
     }
    

    统计就此结束.... 稍等,似乎有点太粗糙了,虽然统计出来的,但是根本没法看,同一个省份的科目都不在一起。囧

    接下来进行下加强,

    支线任务: 将同一省份的科目成绩统计到一起( 即,期望 'province':'xxxxx', avgscores:[ {'xxx':xxx}, ....] 这样的形式)

    要做的有一件事,在前面的统计结果的基础上,先用 $project 将平均分和成绩揉到一起,即形如下面的样子

    { "subjinfo" : { "subjname" : "英语" ,"AvgScores" : 58.1 } ,"province" : "海南" }
    

    再按省份group,将各科目的平均分push到一块,命令如下:

    $project 重构group结果

    {$project:{province:"$_id.province", subjinfo:{"subjname":"$_id.subjname", "avgscore":"$AvgScore"}}
    

    $使用 group 再次分组

    {$group:{_id:"$province", avginfo:{$push:"$subjinfo"}}}
    

    java 代码如下:

    Mongo m = new Mongo("localhost", 27017);
    DB db = m.getDB("test");
    DBCollection coll = db.getCollection("student");
           
    /* 创建 $unwind 操作, 用于切分数组*/
    DBObject unwind = new BasicDBObject("$unwind", "$subjects");
           
    /* Group操作*/
    DBObject groupFields = new BasicDBObject("_id", new BasicDBObject("subjname", "$subjects.name").append("province", "$province"));
    groupFields.put("AvgScore", new BasicDBObject("$avg", "$subjects.scores"));
    DBObject group = new BasicDBObject("$group", groupFields);
           
    /* Reshape Group Result*/
    DBObject projectFields = new BasicDBObject();
    projectFields.put("province", "$_id.province");
    projectFields.put("subjinfo", new BasicDBObject("subjname","$_id.subjname").append("avgscore", "$AvgScore"));
    DBObject project = new BasicDBObject("$project", projectFields);
           
    /* 将结果push到一起*/
    DBObject groupAgainFields = new BasicDBObject("_id", "$province");
    groupAgainFields.put("avginfo", new BasicDBObject("$push", "$subjinfo"));
    DBObject reshapeGroup = new BasicDBObject("$group", groupAgainFields);
     
    /* 查看Group结果 */
    AggregationOutput output = coll.aggregate(unwind, group, project, reshapeGroup);
    System.out.println(output.getCommandResult());
    

    结果如下:

    { "serverUsed" : "localhost/127.0.0.1:27017" , 
     "result" : [ 
        { "_id" : "辽宁" , "avginfo" : [ { "subjname" : "数学" , "avgscore" : 56.46666666666667} , { "subjname" : "英语" , "avgscore" : 52.093333333333334} , { "subjname" : "语文" , "avgscore" : 50.53333333333333}]} , 
        { "_id" : "四川" , "avginfo" : [ { "subjname" : "数学" , "avgscore" : 52.72727272727273} , { "subjname" : "英语" , "avgscore" : 55.90909090909091} , { "subjname" : "语文" , "avgscore" : 57.59090909090909}]} , 
        { "_id" : "重庆" , "avginfo" : [ { "subjname" : "语文" , "avgscore" : 56.077922077922075} , { "subjname" : "英语" , "avgscore" : 54.84415584415584} , { "subjname" : "数学" , "avgscore" : 55.33766233766234}]} , 
        { "_id" : "安徽" , "avginfo" : [ { "subjname" : "英语" , "avgscore" : 55.458333333333336} , { "subjname" : "数学" , "avgscore" : 54.47222222222222} , { "subjname" : "语文" , "avgscore" : 52.80555555555556}]} 
      .
      .
      .
      ] , "ok" : 1.0}
    

    您可能感兴趣的文章:
    • 基于Django统计博客文章阅读量
    • MongoDB 中聚合统计计算--$SUM表达式
    • django项目用higcharts统计最近七天文章点击量
    • 使用django的ORM框架按月统计近一年内的数据方法
    • Golang 函数执行时间统计装饰器的一个实现详解
    • Vue自定义指令上报Google Analytics事件统计的方法
    • Golang 统计字符串字数的方法示例
    • 利用Celery实现Django博客PV统计功能详解
    • Google 统计图表(Flash)小插件
    • go语言之给定英语文章统计单词数量(go语言小练习)
    上一篇:详解MongoDB管理命令
    下一篇:MongoDB最基本命令速查笔记
  • 相关文章
  • 

    © 2016-2020 巨人网络通讯 版权所有

    《增值电信业务经营许可证》 苏ICP备15040257号-8

    MongoDB中强大的统计框架Aggregation使用实例解析 MongoDB,中强,大的,统计,框架,