MongoDB $lt查询返回无目标字段文档的原因及处理方案
问题解答
行为是否正常?
这个行为是正常的。在MongoDB的比较逻辑里,当访问一个不存在的字段(比如last_event.last_update_submitted)时,该字段会被视为null。而null和日期类型值做$lt比较时,结果会返回true,所以那些最后一个事件不含last_update_submitted字段的文档会被错误匹配出来。
如何处理?需要校验字段存在性吗?
必须添加字段存在性校验,才能排除这类不符合要求的文档。你可以在$match阶段的$expr中,用$and同时组合两个条件:
- 最后一个事件的
last_update_submitted字段确实存在 - 该字段的日期早于30天前
修改后的聚合查询如下:
[ { $project: { events: "$events", last_event: { $last: "$events" }, thirty_days_ago: { $dateSubtract: { startDate: new ISODate(), unit: "day", amount: 30 } } } }, { $match: { $expr: { $and: [ { $exists: ["$last_event.last_update_submitted", true] }, { $lt: ["$last_event.last_update_submitted", "$thirty_days_ago"] } ] } } } ]
另外,也可以把字段存在性处理提前到$project阶段,用$ifNull给不存在的字段设置一个不可能满足条件的默认值(比如未来日期),同样能过滤掉这类文档:
[ { $project: { events: "$events", last_event_date: { $ifNull: [ { $last: "$events.last_update_submitted" }, // 设置为未来日期,确保不会被$lt匹配 { $dateAdd: { startDate: new ISODate(), unit: "day", amount: 1 } } ] }, thirty_days_ago: { $dateSubtract: { startDate: new ISODate(), unit: "day", amount: 30 } } } }, { $match: { $expr: { $lt: ["$last_event_date", "$thirty_days_ago"] } } } ]
这种方式直接提取最后一个事件的目标日期,不存在则替换为未来日期,后续匹配时自然会被排除。
内容的提问来源于stack exchange,提问作者ScriptSurfer
相关产品推荐
相关产品推荐

