如何从MongoDB每10秒一条的集合中按1小时间隔提取单条记录
MongoDB按小时间隔提取单条记录方案
一、现有普通集合的聚合实现
1. 精确匹配指定分秒的记录
如果需要严格保留每个小时中08分20秒的记录(如示例中的2021-11-26T15:08:20.000+00:00),可以通过以下聚合管道实现:
db.collection1.aggregate([ // 筛选分钟=8、秒=20的记录 { $match: { $expr: { $and: [ { $eq: [{ $minute: "$datetime" }, 8] }, { $eq: [{ $second: "$datetime" }, 20] } ] } } }, // 按年-月-日-小时分组,确保每个小时仅保留一条记录(避免毫秒级重复) { $group: { _id: { year: { $year: "$datetime" }, month: { $month: "$datetime" }, day: { $dayOfMonth: "$datetime" }, hour: { $hour: "$datetime" } }, record: { $first: "$$ROOT" } } }, // 还原完整记录结构 { $replaceRoot: { newRoot: "$record" } }, // 按时间升序排序 { $sort: { datetime: 1 } } ])
2. 提取每小时最接近指定分秒的记录
如果部分小时没有刚好08:20的记录,可改为提取该小时内与目标时间最接近的记录:
db.collection1.aggregate([ // 计算每条记录对应小时的目标时间(08:20),并计算时间差绝对值 { $addFields: { targetDatetime: { $dateFromParts: { year: { $year: "$datetime" }, month: { $month: "$datetime" }, day: { $dayOfMonth: "$datetime" }, hour: { $hour: "$datetime" }, minute: 8, second: 20, timezone: "UTC" // 根据业务时区调整 } }, timeDiff: { $abs: { $subtract: ["$datetime", "$targetDatetime"] } } } }, // 按小时分组,找出每组内的最小时间差及对应记录 { $group: { _id: { year: { $year: "$datetime" }, month: { $month: "$datetime" }, day: { $dayOfMonth: "$datetime" }, hour: { $hour: "$datetime" } }, closestRecord: { $first: "$$ROOT" }, minDiff: { $min: "$timeDiff" } } }, // 过滤出时间差等于最小差的记录 { $match: { $expr: { $eq: ["$closestRecord.timeDiff", "$minDiff"] } } }, // 移除临时字段,还原记录结构 { $replaceRoot: { newRoot: { $mergeObjects: [ "$closestRecord", { targetDatetime: "$$REMOVE", timeDiff: "$$REMOVE" } ] } } }, { $sort: { datetime: 1 } } ])
二、转为时间序列集合后的优化方案
时间序列集合针对时间型数据做了存储和查询优化,转换步骤如下:
// 将普通集合转为时间序列集合(datetime为时间字段) db.collection1.aggregate([ { $out: { db: "your_database", coll: "collection1_ts", timeseries: { timeField: "datetime" } } } ])
转换后,按小时提取记录的聚合操作性能会显著提升,示例如下:
db.collection1_ts.aggregate([ // 按小时截断时间作为分组键,取每组第一条记录 { $group: { _id: { $dateTrunc: { date: "$datetime", unit: "hour", timezone: "UTC" } }, record: { $first: "$$ROOT" } } }, { $replaceRoot: { newRoot: "$record" } }, { $sort: { datetime: 1 } } ])
如果仍需保留指定分秒的记录,直接复用普通集合的$match逻辑即可,时间序列集合会自动利用时间索引加速查询。
内容的提问来源于stack exchange,提问作者Aislan Diego
相关产品推荐
相关产品推荐

