MongoDB查询每日各ID首个时间点起1小时内的对应数据
MongoDB按ID分组查询每日首小时记录实现方案
实现逻辑
我们可以通过MongoDB聚合框架完成需求,核心思路是先按id+自然日期分组拿到每日的首个时间点,再筛选出时间落在「首时间点~首时间点+1小时」区间内的所有记录。MongoDB 5.0+版本可使用窗口函数实现更高性能的查询,低版本可使用通用聚合流程实现。
推荐方案(MongoDB 5.0及以上)
使用$setWindowFields窗口函数,无需多表关联,性能更高:
db.collection.aggregate([ // 1. 新增辅助字段:记录所属自然日期(精确到天) { $addFields: { day: { $dateToString: { format: "%Y-%m-%d", date: "$date" } } } }, // 2. 窗口函数:按id+天分区,取每个分区的最小date作为当日首时间 { $setWindowFields: { partitionBy: { id: "$id", day: "$day" }, sortBy: { date: 1 }, output: { firstDateOfDay: { $min: "$date", window: { documents: ["unbounded", "unbounded"] } } } } }, // 3. 筛选出时间差<=1小时(3600*1000毫秒)的记录 { $match: { $expr: { $lte: [ { $subtract: ["$date", "$firstDateOfDay"] }, 3600000 ] } } }, // 4. 移除辅助字段,还原原始数据结构 { $unset: ["day", "firstDateOfDay"] } ])
低版本兼容方案(MongoDB 4.4及以下)
如果使用的MongoDB版本不支持窗口函数,可使用分组+关联的方式实现:
db.collection.aggregate([ // 1. 新增自然日期字段 { $addFields: { day: { $dateToString: { format: "%Y-%m-%d", date: "$date" } } } }, // 2. 按id+天分组,拿到每日首时间 { $group: { _id: { id: "$id", day: "$day" }, firstDateOfDay: { $min: "$date" } } }, // 3. 关联原集合,拉取同id同天的所有原始记录 { $lookup: { from: "collection", // 替换为你的实际集合名 let: { groupId: "$_id.id", groupDay: "$_id.day", groupFirstDate: "$firstDateOfDay" }, pipeline: [ { $match: { $expr: { $and: [ { $eq: ["$id", "$$groupId"] }, { $eq: [ { $dateToString: { format: "%Y-%m-%d", date: "$date" } }, "$$groupDay" ] }, { $lte: [ { $subtract: ["$date", "$$groupFirstDate"] }, 3600000 ] } ] } } } ], as: "records" } }, // 4. 展开记录数组 { $unwind: "$records" }, // 5. 还原原始数据结构 { $replaceRoot: { newRoot: "$records" } } ])
结果验证
以上查询返回的结果和你提供的预期输出完全一致:
- id为1的2021-07-01首时间为08:00,筛选出08:00~09:00的3条记录
- id为2的2021-07-02首时间为07:00,筛选出07:00~08:00的3条记录
- id为2的2021-07-03首时间为08:00,筛选出08:00~09:00的2条记录
内容的提问来源于stack exchange,提问作者user12392864
相关产品推荐
相关产品推荐

