MongoDB查询:获取时序数据中每日首个true及后续false的子文档
搞定每日首个True状态时序文档的查询需求
嘿,我来帮你解决这个问题!首先得对齐下数据结构,我先基于你描述的信息假设一个典型的时序文档结构(如果和实际不符,你直接调整字段名就行):
{ "_id": ObjectId("..."), "data": [2024, 5, 20, "09:00-09:15"], // 依次是年份、月份、日期、15分钟时间区间 "status": true // 你提到的那个true/false状态字段 }
核心思路是用聚合管道按日期分组,把当天的文档按时间排序后,筛选出首个status: true的文档,再顺手抓它之后第一个status: false的文档。下面是具体的实现步骤:
第一步:预处理文档,做好分组和排序的准备
首先得把data里的日期信息提取出来作为分组键,同时把15分钟区间转成可排序的数值(方便按时间先后排序):
db.yourCollectionName.aggregate([ // 新增日期标识和时间排序字段 { $addFields: { // 把年/月/日转成YYYY-MM-DD格式的字符串,用来按天分组 "dateKey": { $dateToString: { format: "%Y-%m-%d", date: { $dateFromParts: { year: { $arrayElemAt: ["$data", 0] }, month: { $arrayElemAt: ["$data", 1] }, day: { $arrayElemAt: ["$data", 2] } } } } }, // 把15分钟区间转成当日的总分钟数(比如"09:00-09:15"转成540),方便排序 "timeSort": { $let: { vars: { startTime: { $split: [{ $arrayElemAt: ["$data", 3] }, "-"] } }, in: { $sum: [ { $multiply: [{ $toInt: { $split: ["$$startTime", ":"] } }, 60] }, { $toInt: { $arrayElemAt: [{ $split: ["$$startTime", ":"] }, 1] } } ] } } } } }, // 按日期分组,把当天所有文档存进一个数组 { $group: { _id: "$dateKey", dailyDocs: { $push: { data: "$data", status: "$status", timeSort: "$timeSort" } } } }, // 把每组内的文档按时间先后排序 { $addFields: { sortedDailyDocs: { $sortArray: { input: "$dailyDocs", sortBy: { timeSort: 1 } } } } } ])
第二步:筛选目标文档
接下来就在排序好的每日文档数组里,精准定位我们要的内容:
// 接上面的聚合管道,继续添加这些阶段 { $addFields: { // 找到第一个status为true的文档的索引 firstTrueIndex: { $indexOfArray: ["$sortedDailyDocs.status", true] }, // 提取第一个true的文档 firstTrueDoc: { $cond: { if: { $ne: ["$firstTrueIndex", -1] }, // 检查当天有没有true的文档 then: { $arrayElemAt: ["$sortedDailyDocs", "$firstTrueIndex"] }, else: null } } } }, { $addFields: { // 如果找到了第一个true文档,找它之后第一个false的文档 followingFalseDoc: { $cond: { if: { $ne: ["$firstTrueIndex", -1] }, then: { $let: { vars: { // 截取第一个true之后的所有文档 docsAfterFirstTrue: { $slice: ["$sortedDailyDocs", { $add: ["$firstTrueIndex", 1] }, { $size: "$sortedDailyDocs" }] } }, in: { $arrayElemAt: ["$$docsAfterFirstTrue", { $indexOfArray: ["$$docsAfterFirstTrue.status", false] }] } } }, else: null } } } }, // 整理输出格式,只保留我们需要的字段 { $project: { _id: 0, date: "$_id", firstTrueDocument: "$firstTrueDoc.data", followingFalseDocument: { $ifNull: ["$followingFalseDoc.data", null] } } }
一些注意事项
- 如果你的状态字段不是
status,或者data数组的结构有差异(比如区间是数字而不是字符串),直接替换对应的字段名或调整时间转换逻辑就行。 - 如果某一天没有
status: true的文档,firstTrueDocument和followingFalseDocument都会返回null。 - 要是你的数据库不是MongoDB,你可以告诉我具体的数据库类型,我再帮你调整查询语句~
内容的提问来源于stack exchange,提问作者Aaron Gayle
相关产品推荐
相关产品推荐

