在使用mongo数据库时,简单的查询基本上可以满足大多数的业务场景,但是试想一下,如果要统计某一荐在指定的数据中出现了多少次该怎么查询呢?笨的方法是使用find 将数据查询出来,再使用count() 方法进行数据统计,这个场景还好,但是如果要求其中某个字段的和呢?是不是就非得遍历出相应的数据然后再进行求和运算呢?
在mysql中我们经常会用到countgroup by 等查询,在mongodb中我们也可以使用聚合查询。

假设有这样的一组数据
价格

里面记录了每种水果的价格,现在我要统计一下,各种水果在这张表中出现的次数,如果不用聚合查询的话,思路应该是这样,先把表中所有的数据都取出来,然后初始化一个字典,然后再遍历每一行的数据,获取它的fName ,然后再更新字典中的计数,这种方法的时间复杂度是O(N)的,如果数据量很大的话不是很好,下面来看一下使用聚合是怎么查询的。

聚合查询使用的是aggregate函数,它的参数是 pipeline 管道,管道的概念是用于将当前命令的输出结果作为下一个命令的参数,管道是有顺序的,比如通过第一个管道操作以后没有符合的数据那么之后的管道操作也就不会有输入,所以一定得要注意管道操作的顺序。由于对于上述问题,我们要的是所的数据统计,所以这里就不需要$match

1
2
3
4
5
6
7
8
9
10
from pymongo import MongoClient

client = MongoClient(host=['%s:%s'%(mongoDBhost,mongoDBport)])
G_mongo = client[mongoDBname]['FruitPrice'] pipeline = [
{'$group': {'_id': "$fName", 'count': {'$sum': 1}}},
]
for i in G_mongo['test'].aggregate(pipeline):
print i

数据大家可以自已构造,这里主要是看aggregate的用法。
得到的结果是

1
2
3
{u'count': 8, u'_id': u'banana'}
{u'count': 9, u'_id': u'pear'}
{u'count': 14, u'_id': u'apple'}

可以看到,一步操作就可以得到相应的统计了。

如果想要获取价格在50以上的各种统计呢?
这时有pipeline应该再$group 之前加上$match 操作

1
2
3
4
pipeline = [
{'$match':{'price':{'$gte':50}}},
{'$group': {'_id': "$fName", 'count': {'$sum': 1}}},
]

一定要注意顺序

$match里的条件其实就和使用find函数里是一样的。

下面重点来说说$group操作,group意为分组,指数据根据哪个字段进行分组,上面使用的{'$group': {'_id': "$fName", 'count': {'$sum': 1},_id为所要分的组,这里是以fName字段分的,后面的'count': {'$sum': 1},这里的$sum就是求和的意思,后面的值是1,也就是说每出现一次就加1,这样就能达到计数的目的了,如果要计算价格 price 的和,那么这里就应该写成这样

1
{'$group': {'_id': "$fName", 'count': {'$sum': '$price'}}}

注意这里的字段要有$ 的,如果我想要求价格的平均值呢?也就是先要求出价格的总数,再除以商品的个数,但是这里有一个$avg 操作

1
2
3
4
pipeline = [
{'$match':{'price':{'$gte':50}}},
{'$group': {'_id': "$fName", 'avg': {'$avg': '$price'}}},
]

得到的结果

1
2
3
{u'_id': u'banana', u'avg': 66.200000000000003}
{u'_id': u'pear', u'avg': 77.0}
{u'_id': u'apple', u'avg': 74.0}

类似于$ave的操作还有很多,比较常用的是$min(求最小值),$max(求最大值)

1
2
3
4
5
6
7
8
9
10
11
12
13
pipeline = [
{'$match':{'price':{'$gte':50}}},
{'$group': {'_id': "$fName",
'count':{'$sum':1},
'priceAll':{'$sum':'$price'},
'avg': {'$avg': '$price'},
'min': {'$min':'$price'},
'max': {'$max':'$price'}
}
},
]
for i in G_mongo['test'].aggregate(pipeline):
print i

所有支持的操作可以参考官方文档:group 支持的操作

以哪个字段进行分组时必须使用_id

接下来看一下多键分组。
以上在使用group 进行分组查询的时候,用到的_id都是单一字段,比如我的数据库中有如下数据
带用户的数据

带有一个user 字段了,那如果我要根据userfName进行分组该如何操作呢?
这里可以传一个字典进去

1
2
3
4
5
6
7
8
9
10
11
12
13
pipeline = [
{'$match':{'price':{'$gte':50}}},
{'$group': {'_id': {'fName':'$fName','user':'$user'},
'count':{'$sum':1},
'priceAll':{'$sum':'$price'},
'avg': {'$avg': '$price'},
'min': {'$min':'$price'},
'max': {'$max':'$price'}
}
},
]
for i in G_mongo['test2'].aggregate(pipeline):
print i

得到的结果如下:

1
2
3
4
5
6
7
8
9
{u'count': 1, u'avg': 93.0, u'min': 93, u'max': 93, u'_id': {u'user': u'fanjieying', u'fName': u'pear'}, u'priceAll': 93}
{u'count': 2, u'avg': 88.0, u'min': 87, u'max': 89, u'_id': {u'user': u'yangyanxing', u'fName': u'banana'}, u'priceAll': 176}
{u'count': 2, u'avg': 70.0, u'min': 69, u'max': 71, u'_id': {u'user': u'yangyanxing', u'fName': u'pear'}, u'priceAll': 140}
{u'count': 2, u'avg': 65.5, u'min': 58, u'max': 73, u'_id': {u'user': u'fanjieying', u'fName': u'banana'}, u'priceAll': 131}
{u'count': 3, u'avg': 92.333333333333329, u'min': 86, u'max': 97, u'_id': {u'user': u'fantuan', u'fName': u'banana'}, u'priceAll': 277}
{u'count': 2, u'avg': 78.5, u'min': 73, u'max': 84, u'_id': {u'user': u'yangyanxing', u'fName': u'apple'}, u'priceAll': 157}
{u'count': 3, u'avg': 56.666666666666664, u'min': 51, u'max': 60, u'_id': {u'user': u'fantuan', u'fName': u'pear'}, u'priceAll': 170}
{u'count': 2, u'avg': 81.5, u'min': 73, u'max': 90, u'_id': {u'user': u'fanjieying', u'fName': u'apple'}, u'priceAll': 163}
{u'count': 2, u'avg': 69.5, u'min': 53, u'max': 86, u'_id': {u'user': u'fantuan', u'fName': u'apple'}, u'priceAll': 139}

这里的结果显示出每个用户买了哪个商品,一共花了多少钱,最大最小平均值等都可以一次性的展示了,如果要是使用for循环自已遍历的话这种时间复杂度相当高。

这里只是简单的说了下$group$match 的用法,聚合查询支持很多种操作(称为stages),可以通官方文档进行查看
pymongo 中pipeline中的stages

参考文章
pymongo 的 group by 方法

pymongo中使用聚合查询的更多相关文章

  1. 关于在elasticSearch中使用聚合查询后只显示10个bucket的问题

    先看下面es查询语句 { "size": 0, "aggs" : { "all_articleId" : { "terms&quo ...

  2. ThinkPHP中使用聚合查询去重求和

    我使用的是TP5.1 首先去model类里面设置failed条件: 想要的效果: 数据库展示: 代码: eturn self::alias('gr') ->join('gs_staff gs', ...

  3. mongo客户端升级导致pymongo中使用聚合函数时出现异常

    一.异常信息 The 'cursor' option is required, except for aggregate with the explain argument 二.解决办法 #部分源代码 ...

  4. Mongoose中的关联表查询 && 聚合查询

    注:阅读此篇文章,需要有一定的Mongo基础.基本的不会再重复介绍. 例:  有两张表,一张是博客列表,另外一张是博客的标签表.现在我们要做两张表的插入和关联查询. 创建两张表的Schema 主表bl ...

  5. Rafy 中的 Linq 查询支持(根据聚合子条件查询聚合父)

    为了提高开发者的易用性,Rafy 领域实体框架在很早开始就已经支持使用 Linq 语法来查询实体了.但是只支持了一些简单的.常用的条件查询,支持的力度很有限.特别是遇到对聚合对象的查询时,就不能再使用 ...

  6. 开发中使用mongoTemplate进行Aggregation聚合查询

    笔记:使用mongo聚合查询(一开始根本没接触过mongo,一点一点慢慢的查资料完成了工作需求) 需求:在订单表中,根据buyerNick分组,统计每个buyerNick的电话.地址.支付总金额以及总 ...

  7. django系列5.5--分组查询,聚合查询,F查询,Q查询,脚本中调用django环境

    一.聚合查询 aggregate(*args, **args) 先引入需要的包,再使用聚合查询 #计算所有图书的平均价格 from django.db.models import Avg Book.o ...

  8. django 中的聚合和分组 F查询 Q查询 事务cookies和sessions 066

    1 聚合和分组 聚合:对一些数据进行整理分析 进而得到结果(mysql中的聚合函数) 1aggregate(*args,**kwargs) : 通过对QuerySet进行计算 ,返回一个聚合值的字典. ...

  9. Django中多表的增删改查操作及聚合查询、F、Q查询

    一.创建表 创建四个表:书籍,出版社,作者,作者详细信息 四个表之间关系:书籍和作者多对多,作者和作者详细信息一对一,出版社和书籍一对多 创建一对一的关系:OneToOne("要绑定关系的表 ...

随机推荐

  1. 7.Jmeter 快速入门教程--录制复杂web测试脚本

    Jmeter的功能简单,不需要有脚本语言的编写经验,纯图形界面添加测试场景, 用起来上手很快.但是如果手动添加每一个web(http/https)请求,费时又费力.而且有可能最后手动编写的和实际发的请 ...

  2. C#变量1

    |   版权声明:本文为博主原创文章,未经博主允许不得转载. 1.变量:代表这内存(RAM,保存正在运行程序的数据,断电RAM中的数据将会丢失)中的一块空间,我们可以通过变量的名称存/取数据, 因此我 ...

  3. 转 loadrunner11 录制 chrome 浏览器

    chrome不设置代理的原始状态 图1 [LoadRunner]解决LR11无法录制Chrome浏览器脚本问题   LoadRunner录制脚本时,遇到高版本的IE.FireFox,或者Chrome浏 ...

  4. org-mode记录总结

    org-mode记录总结 */--> code {color: #FF0000} pre.src {background-color: #002b36; color: #839496;} cod ...

  5. 【Java学习笔记之一】 java关键字及作用

    Java关键字及其作用 一. 总览: 访问控制 private protected public 类,方法和变量修饰符 abstract class extends final implements ...

  6. 分布式-技术专区-Redis分布式锁原理实现

    在很多场景中,我们为了保证数据的最终一致性,需要很多的技术方案来支持,比如分布式事务.分布式锁等.那具体什么是分布式锁,分布式锁应用在哪些业务场景.如何来实现分布式锁呢?今天来探讨分布式锁这个话题. ...

  7. Raid,分区表与LVM逻辑卷

    1.组Raid 磁盘前512K组合 前446K:MBR引导程序 中64K:分区表 后2K:校检码 Raid0:极致的读写速度,一个数据分散到多个盘去读写.但是极不安全,一旦坏了一块盘那么所有盘都将无法 ...

  8. 分布式锁实现(一):Redis

    前言 单机环境下我们可以通过JAVA的Synchronized和Lock来实现进程内部的锁,但是随着分布式应用和集群环境的出现,系统资源的竞争从单进程多线程的竞争变成了多进程的竞争,这时候就需要分布式 ...

  9. leetcode-163周赛-1260-二维网格迁移

    题目描述: 自己的提交: class Solution: def shiftGrid(self, grid: List[List[int]], k: int) -> List[List[int] ...

  10. Stream(Java 8)

    Java 8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利.高效的聚合操作(aggregate operation),或者大批量数据操作 (b ...