MongoDB中如何校验对象内元素日期与同对象参考元素日期的关系
MongoDB聚合查询:筛选与参考日期相差1年以内的数组元素
需求说明
校验文档内items数组的所有元素,以数组中第一个元素的日期为参考标准,保留与该参考日期相差1年以内的元素,移除超出范围的元素。
示例数据
[ { "_id": "102", "items": [ { "p_id": "102", "e_date": "2017-04-01T00:00:00.000", "h_val": 4 }, { "p_id1": "102", "e_date1": "2004-05-01T00:00:00.000", "s_val": 87, "d_val": 58 }, { "p_id1": "102", "e_date1": "2016-09-01T00:00:00.000", "s_val": 81, "d_val": 62 } ], "count": 3 } ]
示例结果要求:文档_id:102中,保留第一个元素(参考日期本身)和第三个元素(与参考日期2017-04-01相差约7个月,在1年以内),移除第二个元素(相差超过12年)。
原查询的问题
你提供的聚合查询逻辑存在错误:
- 错误地在单个元素内对比
e_date和e_date1,但实际需求是所有元素与第一个元素的e_date做对比; - 用固定400天作为阈值,不符合“1年以内”的准确要求;
- 未处理元素日期字段名不一致的情况(有的是
e_date,有的是e_date1)。
正确的聚合查询方案
db.collection.aggregate([ { $addFields: { // 提取参考日期:items数组第一个元素的e_date并转为日期类型 "referenceDate": { $dateFromString: { dateString: { $arrayElemAt: ["$items.e_date", 0] } } } } }, { $project: { _id: 1, count: 1, "items": { $filter: { input: "$items", as: "item", cond: { // 计算当前元素日期与参考日期的绝对毫秒差,判断是否≤1年的毫秒数 $lte: [ { $abs: { $subtract: [ // 处理元素的日期字段:优先取e_date,没有则取e_date1,转为日期类型 { $dateFromString: { dateString: { $ifNull: ["$$item.e_date", "$$item.e_date1"] } } }, "$referenceDate" ] } }, // 1年的毫秒数:365天×24小时×60分钟×60秒×1000毫秒 31536000000 ] } } } } }, // 可选:移除临时的referenceDate字段 { $unset: "referenceDate" } ])
查询解释
- $addFields阶段:提取
items数组第一个元素的e_date,转换为MongoDB日期类型,存储为临时字段referenceDate,作为后续对比的参考标准; - $project阶段:使用
$filter遍历items数组:- 对每个元素,用
$ifNull兼容e_date和e_date1两种字段名,将其转为日期类型; - 计算该日期与
referenceDate的毫秒差绝对值,判断是否小于等于1年的毫秒数(31536000000),满足条件的元素保留;
- 对每个元素,用
- $unset阶段:移除临时的
referenceDate字段,保持文档结构整洁。
如果需要更精确的“1年”(考虑闰年),可以用$dateAdd计算参考日期加1年后的日期,再判断当前元素日期是否在[referenceDate, referenceDate+1年]区间内,调整后的条件如下:
cond: { $and: [ { $gte: [ { $dateFromString: { dateString: { $ifNull: ["$$item.e_date", "$$item.e_date1"] } } }, "$referenceDate" ] }, { $lte: [ { $dateFromString: { dateString: { $ifNull: ["$$item.e_date", "$$item.e_date1"] } } }, { $dateAdd: { startDate: "$referenceDate", unit: "year", amount: 1 } } ] } ] }
内容的提问来源于stack exchange,提问作者Will Jue
相关产品推荐
相关产品推荐

