对今天解决不了的事情,也不要着急。因为明天也可能还是解决不了。
前提
开发找我问MongoDB
的 aggregate 聚合
用过不😰 。
我说查查资料吧,发现是aggregate
聚合管道 😨 。
把SQL 与 Aggergation 对比下😂
SQL Terms, Functions, and Concepts | MongoDB Aggregation Operators |
---|---|
WHERE | $match |
HAVING | $match |
SELECT | $project |
ORDER BY | $sort |
LIMIT | $limit |
SUM() | $sum |
COUNT() | $sum |
COUNT() | $sortByCount |
join | $lookup |
开发的需求是:
- 按照天分组,统计一下数量
经过查看MongoDB官网
的aggregate资料实现开发的需求
实现
查询数据的状态
1 | db.antispam_image.find({"moduleId":5,createTs:{$gte:1554048000},createTs:{$lt:1556640000}}).pretty().limit(1); |
1 | { |
开发给的聚合的查询
1 | db.antispam_image.aggregate([ |
1 | mgset-11469021:SECONDARY> db.antispam_image.aggregate([{$match: {"moduleId":5, createTs:{$gte:1554048000},createTs:{$lt:1556640000}}}, {$group: {_id: {$dateToString: {format: "%Y-%m-%d", date: "$createTs"}}, count: {$sum: 1}}}]); |
报错了,问开发。开发又给了一个查询语句
1 | db.antispam_image.aggregate([ |
1 | { "_id" : "1970-01-19", "count" : 15131 } |
发现时间戳转化不对
于是查看官网资料,继续改写先把UNIX时间戳转化为日期。可是MongoDB又没有MySQL
那种FROM_UNIXTIME()
与UNIX_TIMESTAMP()
函数。只能自己造
通过将值乘以1000将createTs字段转换为毫秒时间戳
1 | { "$multiply": [1000, "$createTs"]} |
$multiply 将数字相乘以返回产品。接受任意数量的参数表达式。
然后转换为日期
1 | "$add": [ new Date(0), { "$multiply": [1000, "$createTs"]} ] |
继续组装查询
1 | {"$group": { "_id": { "year": { "$year": { "$add": [ new Date(0), { "$multiply": [1000, "$createTs"] } ]}}, "mmonth": { "$month": { "$add": [ new Date(0), { "$multiply": [1000, "$createTs"] } ]}}, "day": { "$dayOfMonth": { "$add": [ new Date(0), { "$multiply": [1000, "$createTs"] } ]}}}, "count" : { "$sum" : 1 }}} |
在
$project
管道中完成,方法是将毫秒时间
添加到零毫秒Date(0)
对象,然后从转换后的日期
中提取$year
,$month
,$dayOfMonth
个零件,可以在$group
管道中使用这些零件对文档进行分组
完整的查询语句拼接出来
1 | db.antispam_image.aggregate([{ $match: {"moduleId":5,createTs:{$gte:1554048000},createTs:{$lt:1556640000}}}, {"$group": { "_id": { "year": { "$year": { "$add": [ new Date(0), { "$multiply": [1000, "$createTs"] } ]}}, "mmonth": { "$month": { "$add": [ new Date(0), { "$multiply": [1000, "$createTs"] } ]}}, "day": { "$dayOfMonth": { "$add": [ new Date(0), { "$multiply": [1000, "$createTs"] } ] }}}, "count" : { "$sum" : 1 }}}]); |
1 | { "_id" : { "year" : 2019, "mmonth" : 4, "day" : 30 }, "count" : 624 } |
拿这样查询出来的数据问开发是否是这样、开发确认这样可以。需求解决。
开发写了一个查询
1 | db.antispam_report.aggregate( |
添加了时区时间28800000
1 | { "_id" : ObjectId("5bd41d96e870a1daab7a0d6d"), "createTs" : NumberLong(1540627862), "date1Str" : "2018-10-27" } |
结论
针对DBA这个岗位来说。大多数都是从事MongoDB 运维工作 😂。
很少贴近开发需求,这次开发问了我这个问题。我当然无法立马给出答案。只能不断的查询。拼接,才能马马虎虎的满足了开发的需求 ✅。
刚才问一个架构师,架构师说有一个更简单的方式:
- 查出总结出一天的数据,放到管道中临时保存起来
- 在用前一次查询的结束时间作为第二天的开始时间,在加上一天的时间(86400s)得出结尾时间。
- 查询完成在统一显示打印出来
这种方式就需要使用MongoDB forEach
方式实现了。
开发又说不能按时间排序😱 , 妹的😂哪里来的这么多要求😱
参考
- MongoDB 官方资料: aggregate资料
- MongoDB 聚合查询 - 按时间分组统计