“每天计数”的 MongoDB 聚合查询(第 1 部分)

一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / Java 学习路线 / 一对一提问 / 学习打卡/ 赠书活动

目前,正在 星球 内带小伙伴们做第一个项目:全栈前后端分离博客项目,采用技术栈 Spring Boot + Mybatis Plus + Vue 3.x + Vite 4手把手,前端 + 后端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,陪伴式直到项目上线,目前已更新了 204 小节,累计 32w+ 字,讲解图:1416 张,还在持续爆肝中,后续还会上新更多项目,目标是将 Java 领域典型的项目都整上,如秒杀系统、在线商城、IM 即时通讯、权限管理等等,已有 870+ 小伙伴加入,欢迎点击围观

我需要一个 MongoDB 查询来检索每天的文档计数,以便为我的业余无线电接收信号历史可视化服务 SpotViz 提供热图显示(使用 https://kamisama.github.io/cal-heatmap/ )。

提供给 Cal-heatmap 的数据如下所示:


 {
  "946721039":4,
  "946706853":2,
  "946706340":7,
  ...
}

这个数据结构的有趣之处在于属性名称是可变的,我不确定如何将结果投影到 MongoDB 查询中的属性名称中。我在 StackOverflow 上问了这个问题: “Return a computed value as field name in MongoDB query?” – 到目前为止我还没有得到任何答案或建议,所以我不确定这是否可行。

似乎没有办法完全满足我的需要,所以我的下一个挑战是如何每天对文档进行分组(忽略日期的时间部分),并返回每天的计数。

我从 shell 中的工作聚合查询开始,然后使用 MongoDB Java api 实现它。此查询的挑战在于,似乎没有任何开箱即用的功能允许您根据日期选择匹配文档并排除 new Date() 的时间部分。我需要的是等同于“查找按同一天分组的文档数”。要注意的是不要按完全相同的 yyyy/MM/dd hh:mm:ss 值对文档进行分组,而是仅按相同的 yyyy/MM/dd 值进行分组。

由于有一种方法可以使用聚合 $year、$month、$dayOfMonth 运算符从日期中提取年、月和日值,这些可用于获取我需要的结果(每天的计数),但这种格式不能帮助我获取 1970 年 1 月 1 日之后几秒内计数的属性名称,例如“946721039”。

使用这种方法的查询如下所示:


 {
  "946721039":4,
  "946706853":2,
  "946706340":7,
  ...
}

…此方法遵循 此 SO 帖子 的建议。

这种按天对文档计数进行分组的方法很好,但它不会返回自 1/1/1970 以来每天以秒表示的格式的文档。

更好的方法是按日期分组,然后返回该值。然而,将 mongo 中的日期转换为另一种格式似乎有些挑战——我可能花了太多时间来计算一个查询来执行此操作,接近了,但仍然不是我想要的,并以这个相当复杂的查询结束:


 {
  "946721039":4,
  "946706853":2,
  "946706340":7,
  ...
}

我试图用这种方法做的是使用 $project 阶段从每个时间戳值中减去转换为毫秒的 $hour、$minute 和 $second 值,以获得 yyyy/MM/dd 的毫秒值但忽略时间部分。这与我得到的差不多,但我无法让数学工作,或者至少无法在类型之间进行转换,因此计算将按照我想要的方式进行。

我的下一次尝试是基于 这篇 SO 帖子 中的建议。这是解决问题的一种更简单的方法——我的新查询如下所示:


 {
  "946721039":4,
  "946706853":2,
  "946706340":7,
  ...
}

如果我尝试将其分解为文字,那么我正在做的是:

– 对于日期 x,计算自 1/1/1970(纪元日期)以来的毫秒数

– 从中减去自一天开始以来的毫秒数(这是自 1/1/1970 以来的毫秒数 mod 一天中的毫秒数,余数除以另一个)

...结果是每个日期在午夜的毫秒数,即不包括时间部分。

好的,差不多了!我如何处理这个查询并将其转换为 MongoDB Java Drvier API 将在第 2 部分中介绍。



如果您喜欢这篇文章并想了解有关 MongoDB 的更多信息,请查看有关 MongoDB 的所有 教程和文章的集合