如果我们在日常 *** 作中,将部分数据存储在了MongoDB中,但是有需求要求我们将存储进去的文档数据,按照一定的条件进行查询过滤,得到想要的结果便于二次利用,那么我们就可以尝试使用MongoDB的聚合框架。
前面我们在学习文档查询的过程中,也介绍过一些查询的 *** 作符,其中就有一部分是简单的查询聚合函数,例如 count 、 distinct 、 group 等,如果是简单的数据分析过滤,完全可以使用这些自带的聚合函数以及查询的 *** 作符来完成文档的过滤查询 *** 作
如果我们遇到了一些数据需要跨多个文本或者统计等 *** 作,这个时候可能文档自身也较为复杂,查询 *** 作符已经无法满足的时候,这个时候就需要使用MongoDB的聚合查询框架了。
使用聚合框架可以对集合中的文档进行变换和组合查询,基本上我们使用的时候,都是使用多个构件创建一个管道,用于对一连串的文档进行处理。这里的构件包括 筛选(filter) 、 投射(projecting) 、 分组(grouping) 、 排序(sorting) 、 限制(limiting) 以及 跳过(skipping)
MongoDB中需要使用聚合 *** 作,一般使用 aggregate 函数来完成多个聚合之间的连接,aggregate() 方法的基本语法格式如下 :
现在假设我们有个集合articles,里面存储了文章的集合,大致如下:
但这时我们需要查询出来每一个作者写的文章数量,需要使用aggregate()计算 ,大致如下:
输出的结果为:
通过这个简单的案例我们就能输出想要的数据和属性名,大概分析一下刚刚的聚合查询语句, by_user字段进行分组,代表每个用户一条数据,而num_tutorial则是定义了数值类型计算的结果字段,$sum则是计算总和,相当于每个用户出现一次,都会+1,最终计算出来的总和通过num_tutorial字段进行输出
注:如果管道没有给出预期的结果,就需要进行调试 *** 作,调试的时候,可以尝试先给一个管道 *** 作符的条件,如果这个时候查询出来的结果是我们想要的,那么我们需要再去指定第二个管道 *** 作符,依次 *** 作,最后就会定位到出了问题的 *** 作符
前面我们提到聚合查询会使用管道 *** 作符,而每一个 *** 作符就会接受一连串的文档,对这些文档进行一些类型转换,最后将转换以后的文档结果传递给下一个管道 *** 作符来执行后续的 *** 作,如果当前是最后一个管道 *** 作符,那么则会显示给用户最后的文档数据。不同的管道 *** 作符是可以按照顺序组合在一起使用,并且可以被重复执行多次,例如我们可以先使用$match然后再去、 match *** 作。
match管道 *** 作符可以使用$gt、$lt、$in等 *** 作符,进行过滤,不过需要注意的是不能在$match管道 *** 作符中使用空间地理 *** 作符。
在实际使用的过程中,尽可能的将 match *** 作符以后,再去投射或者执行分组 *** 作的话,是可以利用索引的。
相比较一般的查询 *** 作而言,使用管道 *** 作,尤其是其中的投射 *** 作更加强大。我们可以在查询文档结束以后利用 $project *** 作符从文档中进行字段的提取,甚至于我们可以重命名字段,将部分字段映射成我们想要展示出去的字段,也可以对一部分字段进行一些有意义的处理。需要注意的是, $project *** 作符可以传入两个参数,第一个是需要处理的属性名称,第二个则是0或者1,如果传入1,则代表当前的属性是需要显示出来的,如果是0或者不写,默认都是代表这个字段不需要显示出来
当然第二个参数也可以是一个表达式或者查询条件,满足当前表达式的数据也可以进行显示,接下来我们先准备一点数据:
接下来,我们来查询,条件是item字段为abc,quantity要大于5,并且我们只要item和price字段的结果,其他都排除掉:
可以看到结果为:
如果我们想要在原基础上改变某个字段的名称,例如将item改为item_code,可以利用$来完成,如下:
可以看到我们指定的名称item_code,而这个别名对应的字段item使用$作为前缀标记,代表将item字段映射为item_code,可以看到结果:
我们在投影的时候,除了可以将某个字段映射成其他字段以外,还可以针对某个字段进行一些简单的运算,最常见的就是 四则运算 ,即
加法( subtract )、乘法( divide )、求模( $mod ) ,
除此之外,还支持对字段进行 关系运算 (大小比较( " eq" )、大于( " gte" )、小于( " lte" )、不等于( " ifNull" ) )、
逻辑运算 (与( " or" )、非 ( " concat" )、截取( " toLower" ) )等
我们基于上面的需求,假设每一个价格是按照元为单位,现在要求输出W为单位,那么我们就需要对price进行除法运算,如下:
除此之外,我们也可以将计算完毕的price改名为priceW,即:
可以看到输出的结果为:
这时有一个需求,要求我们返回数据的同时还要yyyy-MM-dd格式的时间字符串,这个时候我们就需要对date字段进行时间函数和字符串混合处理了,如下:
这里需要注意的一点是, year:" substr函数将date字段的结果截取成字符串即可实现拼接
group的_id上,代表按照当前字段进行分组,例如,我们这里根据item进行分组:
在我们针对某个字段进行分组以后,我们可以针对每个分组进行一些 *** 作符的使用,常见的例如: $sum 、 $avg 、 $min 、 $max 、 $first 、 $last 。
$avg *** 作符用来返回每一个分组内的平均值
现在我们基于前面item的分组,我们想要算出来每个组内的平均价格是多少,如下:
$min 和 $max *** 作符用于返回分组内最大的值和最小的值
除了平均值以外,我们现在将最贵的和最便宜的价格也要列出来,这个时候就可以使用这两个 *** 作符了,如下:
$first 、 $last 则是可以获取当前分组中第一个或者最后一个的某个字段的结果,如下:
除此之外,我们还可以在分组的时候使用数组 *** 作符,例如 $addToSet 可以判断,当前数组如果不包含某个条件,就添加到当前数组中, $push 则不管元素是否存在,都直接添加到数组中
注意:大部分管道 *** 作符都是流式处理的,只要有新的文档进入,就可以对新的文档进行处理,但是 $group 代表必须收到全部文档以后才可以进行分组 *** 作,才会将结果传递给后续的管道 *** 作符,这就意味着,如果当前mongo是存在分片的,会先在每个分片上执行完毕以后,再把结果传递mongos进行统一的分组,剩下的管道 *** 作符也不会在每个分片,而是mongos上执行了
如果我们现在遇到一些文档比较复杂,比如存在内嵌文档的存在,某个属性里面嵌套了一个数组,但是我们需要对内嵌的数组文档进行分析过滤等查询处理,这个时候就可以使用 $unwind *** 作符将每一个文档中的嵌套数组文件拆分为一个个独立的文档便于进行后续的处理,例如我们需要将之前的set集合中关于请求的url以及ip的信息拆分出来,原始的格式如下:
我们可以使用命令进行拆分,如下:
结果为:
可以看到数据则是按照每一条信息的方式展示出来了,方便后续的计算以及输出,但是需要注意的一点是,这种方式,如果该文档中没有拆分的字段,或者是空数组,默认会直接排除,如果我们需要空数组等也输出计算出来,则可以指定 preserveNullAndEmptyArrays 参数,设置为true,则代表空数组或者不存在的文档也要拆分输出出来,即:
我们可以在管道查询的过程中,按照某个属性值或者多个属性的结果进行顺序排序,排序的方式与普通查询 *** 作符中的sort *** 作符表现一致,与其他管道 *** 作符一样,可以在任何阶段使用,但是,需要注意的一点是,建议在管道 *** 作符第一阶段进行排序,因为此时的排序是可以触发索引的,如果在后续阶段进行排序,会消耗大量内存,并且耗时会很久,尤其是在有 $group 的情况下,如果放在 $group *** 作符后面,会发现等到的时间很久,不仅仅是无法触发索引的问题,还和 $group *** 作符是等待所有数据完毕才会触发的特性有关,因此需要格外注意。
结果如下,按照我们想要的结果进行了排序:
limit,只返回前两条数据,如下:
结果如下:
除了 skip,与之前的查询 *** 作符作用也是一样的,用于在已经查询完毕的结果集中跳过前N条数据以后进行返回,我们将$skip加在刚刚的查询后面,如下:
这个时候可以看到返回的结果为空,什么结果都没有了,这是因为前一步管道已经限制了仅仅返回2条,而接着我们又跳过了前两条文档,因此返回的结果为空,我们将顺序调换一下,看看:
可以看到结果如下,与刚才的结果无异:
管道查询 *** 作符有很多,除了上面学习的常用的部分,还有几十个,需要了解全部的可以参考官网:
>
MongoDB显示数组N项,在_ongoDB中,我们在使用 find查询记录的数组字段时,如果我们想只返回数组的某一项到另一项之间的所有项,我们可以用 `$slice`修饰符加上数组。
以上就是关于mongoDB应用篇-mongo聚合查询全部的内容,包括:mongoDB应用篇-mongo聚合查询、mongodb 查询求助,嵌套数组里面查东西、mongodb表格怎么显示数组等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)