MongoDB筛选lifeCycleInfo数组中completed早于created事件的文档
筛选MongoDB中payment.completed早于payment.created的文档
我正在用MongoDB Compass执行查询,需要从数千条文档里筛选特定记录。每条文档都包含lifeCycleInfo数组,部分文档中数组内的payment.completed事件发生在payment.created事件之前,我要找出这类记录。
之前尝试按数组索引查询没生效,用的语句是:
{"lifeCycleInfo[1].eventtype":"payment.completed"}
示例文档结构如下:
{ "lifeCycleInfo": [ { "eventId": "9b8b6adfae", "eventSubType": "SendTransfer_Receipt", "eventType": "SendTransfer", "odsTimestamp": { "$date": "2023-02-06T14:33:42.308Z" }, "payload": "{}", "timestamp": { "$date": "2023-02-06T14:33:42.271Z" } }, { "eventId": "06e8d144-531b02", "eventSubType": "payment.created", "eventType": "Notification", "odsTimestamp": { "$date": "2023-02-06T14:33:45.488Z" }, "payload": "{}", "timestamp": { "$date": "2023-02-06T14:33:45.479Z" } }, { "eventId": "9da54454d6", "eventSubType": "payment.completed", "eventType": "Notification", "odsTimestamp": { "$date": "2023-02-06T14:33:46.698Z" }, "payload": "{}", "timestamp": { "$date": "2023-02-06T14:33:46.689Z" } } ] }
问题分析
之前的查询失效有两个核心原因:
- 字段名错误:MongoDB字段名区分大小写,应该用
eventSubType而非eventtype - 固定索引不可靠:
lifeCycleInfo数组内的事件顺序不固定,不能硬指定索引位置判断事件先后
解决方案
方式一:使用find结合$expr直接查询
适合在MongoDB Compass的查询栏直接使用:
db.collection.find({ // 先过滤出同时包含两个目标事件的文档,避免无意义的比较 "lifeCycleInfo.eventSubType": { $all: ["payment.completed", "payment.created"] }, $expr: { $lt: [ // 提取payment.completed的最早时间戳 { $min: { $map: { input: { $filter: { input: "$lifeCycleInfo", cond: { $eq: ["$$this.eventSubType", "payment.completed"] } } }, as: "completed", in: "$$completed.timestamp" } } }, // 提取payment.created的最早时间戳 { $min: { $map: { input: { $filter: { input: "$lifeCycleInfo", cond: { $eq: ["$$this.eventSubType", "payment.created"] } } }, as: "created", in: "$$created.timestamp" } } } ] } })
方式二:使用聚合管道分步处理
如果需要查看中间过程,或后续要做更多数据处理,用聚合管道更清晰:
db.collection.aggregate([ // 第一步:过滤出同时包含两个事件的文档 { $match: { "lifeCycleInfo.eventSubType": { $all: ["payment.completed", "payment.created"] } } }, // 第二步:提取两个事件的时间戳到临时字段 { $addFields: { completedTime: { $min: { $map: { input: { $filter: { input: "$lifeCycleInfo", cond: { $eq: ["$$this.eventSubType", "payment.completed"] } } }, as: "item", in: "$$item.timestamp" } } }, createdTime: { $min: { $map: { input: { $filter: { input: "$lifeCycleInfo", cond: { $eq: ["$$this.eventSubType", "payment.created"] } } }, as: "item", in: "$$item.timestamp" } } } } }, // 第三步:筛选出completed时间早于created的文档 { $match: { $expr: { $lt: ["$completedTime", "$createdTime"] } } }, // 可选:移除临时字段,恢复原文档结构 { $project: { completedTime: 0, createdTime: 0 } } ])
说明
两种方式都通过$filter提取目标事件,用$min获取事件的最早时间戳(如果一个文档有多个同类型事件,取最早的那个比较),最后通过$lt判断时间先后。
内容的提问来源于stack exchange,提问作者user3100834
相关产品推荐
相关产品推荐

