MongoDB聚合查询实现按Symbol分组获取指定日期最新单条记录
获取MongoDB中指定日期每个Symbol的最新记录
没问题,我来帮你搞定这个聚合查询。要实现每个symbol在指定日期的最新记录,我们可以用MongoDB的聚合框架,结合几个关键阶段来完成,还能利用你现有的索引提升性能。
完整聚合查询语句
db.yourCollectionName.aggregate([ // 筛选2021-10-17当天的所有记录 { $match: { "times.event_ts_utc": { $gte: ISODate("2021-10-17T00:00:00.000Z"), $lt: ISODate("2021-10-18T00:00:00.000Z") } } }, // 按symbol分组前先排序,确保每个symbol的最新记录排在最前面 { $sort: { "symbol": 1, "times.event_ts_utc": -1 } }, // 按symbol分组,取每个组的第一条(最新)记录 { $group: { _id: "$symbol", latestRecord: { $first: "$$ROOT" } } }, // 将嵌套的最新记录展开为顶层文档,匹配预期输出格式 { $replaceRoot: { newRoot: "$latestRecord" } } ])
各阶段详细解释
- $match阶段:精准过滤出2021-10-17当天的所有数据,用
$gte(当日零点)和$lt(次日零点)的范围查询比直接匹配日期更准确,还能触发你已有的idx_times.event_ts_utc索引,大幅减少后续需要处理的数据量。 - $sort阶段:先按
symbol升序排列,再按times.event_ts_utc降序排列。这样同一个symbol下的记录,时间最晚的会排在该组的最前面。如果数据量特别大,建议创建{symbol:1, times.event_ts_utc:-1}的复合索引,能让排序操作完全走索引,避免内存排序的性能瓶颈。 - $group阶段:以
symbol作为分组键,用$first操作符提取每个组的第一条记录(也就是刚才排序后最新的那条)。$$ROOT代表当前完整的文档对象,这样我们就能把整条最新记录保存到latestRecord字段中。 - $replaceRoot阶段:把
latestRecord字段里的内容替换为顶层文档结构,让输出格式和你预期的完全一致——每个文档都是独立的、对应symbol的最新记录。
动态获取“昨日”数据的优化
如果你的需求不是固定日期,而是要动态获取“昨日”的最新记录,可以用JavaScript生成日期范围:
// 生成昨日的开始和结束时间 const yesterdayStart = new Date(); yesterdayStart.setDate(yesterdayStart.getDate() - 1); yesterdayStart.setHours(0, 0, 0, 0); const yesterdayEnd = new Date(yesterdayStart); yesterdayEnd.setDate(yesterdayEnd.getDate() + 1); // 将上述变量代入$match阶段 db.yourCollectionName.aggregate([ { $match: { "times.event_ts_utc": { $gte: yesterdayStart, $lt: yesterdayEnd } } }, // 后续阶段和之前一致 { $sort: { "symbol": 1, "times.event_ts_utc": -1 } }, { $group: { _id: "$symbol", latestRecord: { $first: "$$ROOT" } } }, { $replaceRoot: { newRoot: "$latestRecord" } } ])
索引优化建议
虽然你现有的idx_symbol和idx_times.event_ts_utc单字段索引能支持查询,但创建{symbol:1, times.event_ts_utc:-1}的复合索引会让整个聚合流程更高效——它能同时满足$match和$sort的需求,避免MongoDB在内存中进行大量排序操作,尤其适合你提到的“数据量庞大”的场景。
内容的提问来源于stack exchange,提问作者funtus
相关产品推荐
相关产品推荐

