说句不怕笑话的话,MongoDB使用也有6 7 8 年了,但对于聚合一般我是抗拒的,可能是MOGNODB 3.X落下的顽疾,一听到用MongoDB 做聚合操作,一般都不想听 不想听。但时代不一样,MongoDB已经走到了 8.0UP,聚合早就和之前不一样了。
所以怕也的上,还的学习。 以上学习基于MOGNODB7.0 ,聚合操作中首选的方案是聚合管道,或者使用单一聚合的方法。一般来说聚合操作中的管道操作,主要是通过多个阶段来处理数据,比如第一需要先过滤数据,然后对过滤的数据进行文档的分组并计算聚合操作后的结果。同时聚合还可以进行聚合后的数据更新,当然这需要在4.2后的版本才有此功能。
我们先产生测试数据,先简单产生 4万条数据
mongo7 [direct: primary] test> function insertData(dbName, colName, num) {
...
... var col = db.getSiblingDB(dbName).getCollection(colName);
...
... for (i = 0; i < num; i++) {
... col.insert({x:i});
... }
...
... print(col.count());
...
... }
[Function: insertData]
mongo7 [direct: primary] test> insertData("test", "testData", 40000)
DeprecationWarning: Collection.insert() is deprecated. Use insertOne, insertMany, or bulkWrite.
DeprecationWarning: Collection.count() is deprecated. Use countDocuments or estimatedDocumentCount.
40000
下面我们开始,假设一个需求,我需要计算插入数据中 10000 到 20000 ,20000 到 30000 之间的数据值的累加和并展示出来。
mongo7 [direct: primary] test> db.testData.aggregate([
... {
... $match: {
... x: { $gte: 10000, $lt: 30000 }
... }
... },
... {
... $group: {
... _id: null,
... sum1: { $sum: { $cond: [{ $and: [ { $gte: ["$x", 10000] }, { $lt: ["$x", 20000] } ] }, "$x", 0] } },
... sum2: { $sum: { $cond: [{ $and: [ { $gte: ["$x", 20000] }, { $lt: ["$x", 30000] } ] }, "$x", 0] } }
... }
... }
... ])
[ { _id: null, sum1: 149995000, sum2: 249995000 } ]
mongo7 [direct: primary] test>
上面的语句,直接将结果进行了计算和展示非常快。
首先这边语句分为两个部分,第一部分是限制数据进入,因为这里计算是大于等于10000 和 小于30000,所以我们需要根据match 来进行数据的排除,将9999以内的数字和30000以外的数据进行,排除。
$match: { x: { $gte: 10000, $lt: 30000}
然后留下的是我们的要处理的数据,进行数据的聚合操作。
$group: {
_id: null,
sum1: { $sum: { $cond: [{ $and: [ { $gte: ["$x", 10000] }, { $lt: ["$x", 20000] } ] }, "$x", 0] } },
sum2: { $sum: { $cond: [{ $and [ { $gte: ["$x", 20000] }, { $lt: ["$x", 30000] } } }, "$x", 0] } }
这里在过滤出我们要的数据后,首先我们遇到的是针对什么进行分组,如果是传统数据库,这里面一般就头疼了,分组是没有字段的,这里MongoDB 是可以针对没有分组的聚合数据进行分组的,上面就是一个案例,我们只有object_id , x 两个字段,我们怎么聚合分组我们的分组实际是值,这也是传统DBA 烧脑的开始。
我们这里根据过滤出的条件,分别对于符合条件的数据进行聚合。
sum1: { $sum: { $cond: [{ $and: [ { $gte: ["$x", 10000] }, { $lt: ["$x", 20000] } ] }, "$x", 0] } }
这条的意思是,首先要表达的是我们要进行 sum的操作,也就是累加和,然后 cond 的意思是在我们match后的数据还需要进行条件的筛选,也就是我这里只要大于等于10000 和小于20000的数,进行累加和,如果这里条件都不符合的话,我们就给一个默认的值 0
mongo7 [direct: primary] test> db.testData.aggregate([
... {
... $match: {
... x: { $gte: 10000, $lt: 30000 }
... }
... },
... {
... $group: {
... _id: null,
... sum1: { $sum: { $cond: [{ $and: [ { $gte: ["$x", 10000] }, { $lt: ["$x", 20000] } ] }, "$x", 0] } },
... sum2: { $sum: { $cond: [{ $and: [ { $gte: ["$x", 20000] }, { $lt: ["$x", 30000] } ] }, "$x", 0] } },
... sum3: { $sum: { $cond: [{ $and: [ { $gte: ["$x", 40000] }, { $lt: ["$x", 50000] } ] }, "$x", 0] } }
... }
... }
... ])
[ { _id: null, sum1: 149995000, sum2: 249995000, sum3: 0 } ]
mongo7 [direct: primary] test>
上面的例子中我们可以看到,最后一句明显就是找茬的,我们在顾虑数据的时候值包含了10000 到 30000的数据,而下面是要40000到50000的数据,这里cond 条件就产生效用了,如果符合条件则打印结果,如果不符合条件,则选择后面的给定的结果进行打印,很明不符合条件的为0
那么这样的语句还有其他的写法吗,有的,例如下面的写法
mongo7 [direct: primary] test> db.testData.aggregate( [
... {
... $match: { x: { $gte: 10000, $lt: 30000} }
... },
... {
... $group: {
... _id: null,
... sum1: { $sum: { $cond: [{ $and: [ { $gte: ["$x", 10000] }, { $lt: ["$x", 20000] } ] }, "$x", 0] } },
... sum2: { $sum: { $cond: { if: { $gte: ["$x", 20000] }, then: "$x", else: 0 } } }
... }
... }
... ] )
[ { _id: null, sum1: 149995000, sum2: 249995000 } ]
mongo7 [direct: primary] test>
我们可以看到,结果是一样的,但写法的确是不同,第二个我们采用了是条件的方式来撰写的,也就是最后一个20000到3000,所以用了另一种Mongodb的语句的写法。
sum2: { $sum: { $cond: { if: { $gte: ["$x", 20000] }, then: "$x", else: 0 } } }
这个写法的意思是,如果值大于等于20000的话,那么就取值,否则就是0
明显这里是一个判断的方式的表达,如果想用SQL 来表达类似的意思可以写成,下图方式
SELECT
NULL AS _id,
SUM(CASE WHEN x >= 10000 AND x < 20000 THEN x ELSE 0 END) AS sum1,
SUM(CASE WHEN x >= 20000 AND x < 30000 THEN x ELSE 0 END) AS sum2,
SUM(CASE WHEN x >= 40000 AND x < 50000 THEN x ELSE 0 END) AS sum3
FROM
testData
WHERE
x >= 10000 AND x < 30000;
最后在给传统数据库DBA 来一个烧脑的作为此次的结尾,这样的数据查询如果是在传统数据库,相比是有索引也走不了,作为传统的DBA 对于这样的语句,在X列加索引,是不会抱有希望的。
这里我们也比较一下,在对X key 加索引后的查询执行计划,是否有不同,答案是当然有不同。
1 不加索引,时从执行计划看,走了全collection扫描是没跑了
2 添加索引后
结果与传统数据库的思路不一样,传统思路这样的查询这样的量是无法走索引的,全表扫描是一定的,而在NOSQL数据库中,这样的情况添加了索引也可以运行并使用,后续还的学习和发现,目前写不下去了,需要散热
后记,随着文档型数据库的被熟知,并且步步紧逼传统数据库一些事务,跨表,跨库查询,以及聚合查询等方案的退出,以及天然的分布式存储方式,和传统数据库打死都没有的灵活性,文档数据库和传统型数据库PK 的还在后面。
本文分享自 AustinDatabases 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!