You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过单条聚合管道查找MongoDB指定时间范围缺失的小时文档

MongoDB单聚合管道查询指定时间范围缺失小时时段方案

以下方案完全适配Metabase的限制,不需要创建临时集合、不需要跨集合$lookup,单条聚合管道即可返回结果,前提是集合内有效记录的时间字段均为整点小时级,可根据实际业务替换字段名、时间范围。


核心逻辑

不需要预先生成全量小时序列,直接通过已有记录的相邻时间差判断数据缺口,同时自动补全校验查询起止边界的缺失时段,所有计算都在单聚合管道内完成。

可直接复用的聚合代码

注意替换代码里的3类自定义值:

  • your_collection:实际业务集合名
  • ts:实际存储小时级时间戳的字段名(默认是BSON Date类型,数字时间戳适配方案看后续说明)
  • 两处ISODate值:分别替换为你要查询的整点起始时间和整点结束时间
db.your_collection.aggregate([
  // 过滤指定时间范围内的文档,减少无效计算
  {
    $match: {
      ts: {
        $gte: ISODate("2024-05-01T00:00:00Z"),
        $lte: ISODate("2024-05-03T00:00:00Z")
      }
    }
  },
  // 按小时去重,每个小时仅保留一条标记记录
  {
    $group: {
      _id: { $dateTrunc: { date: "$ts", unit: "hour" } }
    }
  },
  // 按时间正序排列,取每条记录的上一个小时点
  {
    $setWindowFields: {
      sortBy: { _id: 1 },
      output: {
        prevHour: { $shift: { by: -1, output: "$_id" } }
      }
    }
  },
  // 筛选出相邻时间差大于1小时的缺口段
  {
    $match: {
      $expr: {
        $gt: [{ $dateDiff: { startDate: "$prevHour", endDate: "$_id", unit: "hour" } }, 1]
      }
    }
  },
  // 生成缺口段内所有缺失的整点小时
  {
    $project: {
      missingHours: {
        $map: {
          input: {
            $range: [
              0,
              { $subtract: [
                { $dateDiff: { startDate: "$prevHour", endDate: "$_id", unit: "hour" } },
                1
              ]}
            ]
          },
          as: "offset",
          in: { $dateAdd: { startDate: "$prevHour", unit: "hour", amount: { $add: ["$$offset", 1] } } }
        }
      }
    }
  },
  // 把缺失小时数组拆分为单条记录
  { $unwind: "$missingHours" },
  { $replaceRoot: { newRoot: { missingHour: "$missingHours" } } },
  // 补全校验查询首尾边界的缺失时段
  {
    $unionWith: {
      coll: "your_collection",
      pipeline: [
        {
          $match: {
            ts: {
              $gte: ISODate("2024-05-01T00:00:00Z"),
              $lte: ISODate("2024-05-03T00:00:00Z")
            }
          }
        },
        {
          $group: {
            _id: null,
            firstHour: { $min: { $dateTrunc: { date: "$ts", unit: "hour" } } },
            lastHour: { $max: { $dateTrunc: { date: "$ts", unit: "hour" } } }
          }
        },
        {
          $project: {
            missingHours: {
              $concatArrays: [
                // 计算查询起点到第一条有效记录之间的缺失小时
                {
                  $map: {
                    input: { $range: [0, { $dateDiff: { startDate: ISODate("2024-05-01T00:00:00Z"), endDate: "$firstHour", unit: "hour" } }] },
                    as: "offset",
                    in: { $dateAdd: { startDate: ISODate("2024-05-01T00:00:00Z"), unit: "hour", amount: "$$offset" } }
                  }
                },
                // 计算最后一条有效记录到查询终点之间的缺失小时
                {
                  $map: {
                    input: { $range: [0, { $dateDiff: { startDate: "$lastHour", endDate: ISODate("2024-05-03T00:00:00Z"), unit: "hour" } }] },
                    as: "offset",
                    in: { $dateAdd: { startDate: "$lastHour", unit: "hour", amount: { $add: ["$$offset", 1] } } }
                  }
                }
              ]
            }
          }
        },
        { $unwind: "$missingHours" },
        { $replaceRoot: { newRoot: { missingHour: "$missingHours" } } }
      ]
    }
  },
  // 最终按时间正序返回结果
  { $sort: { missingHour: 1 } }
])

适配说明

  • 版本兼容:上述代码用到的日期运算符、窗口函数均为MongoDB 5.0及以上版本原生支持,目前绝大多数云MongoDB实例、自建实例版本都满足要求。如果是4.x及以下版本,可将$dateTrunc替换为日期格式化截断的写法,$shift替换为数组滑动窗口的写法,核心逻辑不变。
  • 时间戳格式适配:如果你的时间字段存的是毫秒级数字而非Date类型,不需要改整体逻辑,只需把日期运算替换为数字计算即可:1小时对应3600000毫秒,小时截断逻辑为{ $subtract: ["$ts", { $mod: ["$ts", 3600000] }] },时间差直接做减法即可。
  • 结果校验:返回的missingHour字段就是所有缺失记录的整点小时时间,可直接在Metabase中做可视化展示。

内容的提问来源于stack exchange,提问作者Dyson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 00:36:20