MongoDB基于xCorrelationId查重复数据:modifiedBy匹配最大modifiedDate问题
问题解决:MongoDB聚合关联maxModifiedDate对应的modifiedBy字段
需求背景
需要在MongoDB的audit集合中,基于xCorrelationId字段识别重复数据,现有聚合已实现统计重复数、合并events、获取最小creationDate和最大modifiedDate,但无法让modifiedBy字段对应拥有最大modifiedDate的记录。
集合样例数据
[ { "_id": ObjectId("60d21b4667d0d8992e610c85"), "xCorrelationId": "CORR-123", "events": ["CREATE"], "creationDate": ISODate("2021-06-23T10:00:00Z"), "modifiedDate": ISODate("2021-06-23T10:00:00Z"), "modifiedBy": "user1" }, { "_id": ObjectId("60d21b4667d0d8992e610c86"), "xCorrelationId": "CORR-123", "events": ["UPDATE"], "creationDate": ISODate("2021-06-23T10:00:00Z"), "modifiedDate": ISODate("2021-06-23T10:30:00Z"), "modifiedBy": "user2" }, { "_id": ObjectId("60d21b4667d0d8992e610c87"), "xCorrelationId": "CORR-456", "events": ["CREATE"], "creationDate": ISODate("2021-06-24T09:00:00Z"), "modifiedDate": ISODate("2021-06-24T09:00:00Z"), "modifiedBy": "user3" } ]
现有聚合查询(问题版)
db.audit.aggregate([ { $group: { _id: "$xCorrelationId", count: { $sum: 1 }, mergedEvents: { $addToSet: "$events" }, minCreationDate: { $min: "$creationDate" }, maxModifiedDate: { $max: "$modifiedDate" }, // 此处modifiedBy为随机取的第一条,不对应maxModifiedDate modifiedBy: { $first: "$modifiedBy" } } }, { $match: { count: { $gt: 1 } } } ])
期望输出
[ { "_id": "CORR-123", "count": 2, "mergedEvents": [["CREATE"], ["UPDATE"]], "minCreationDate": ISODate("2021-06-23T10:00:00Z"), "maxModifiedDate": ISODate("2021-06-23T10:30:00Z"), "modifiedBy": "user2" // 对应最大modifiedDate的记录的modifiedBy } ]
解决方案:两种MongoDB聚合实现方式
方式一:预排序后分组(高效推荐)
利用排序+$last操作符,确保同一xCorrelationId组内最后一条记录就是modifiedDate最大的记录,直接取其modifiedBy:
db.audit.aggregate([ // 按xCorrelationId分组前,先按xCorrelationId+modifiedDate升序排序 // 若有索引{xCorrelationId:1, modifiedDate:1},此步骤性能最优 { $sort: { xCorrelationId: 1, modifiedDate: 1 } }, { $group: { _id: "$xCorrelationId", count: { $sum: 1 }, mergedEvents: { $addToSet: "$events" }, minCreationDate: { $min: "$creationDate" }, maxModifiedDate: { $last: "$modifiedDate" }, modifiedBy: { $last: "$modifiedBy" } } }, { $match: { count: { $gt: 1 } } } ])
方式二:分组后筛选匹配(无需预排序)
通过分组收集所有modifiedDate与modifiedBy的组合,再筛选出对应最大modifiedDate的modifiedBy:
db.audit.aggregate([ { $group: { _id: "$xCorrelationId", count: { $sum: 1 }, mergedEvents: { $addToSet: "$events" }, minCreationDate: { $min: "$creationDate" }, // 收集所有modifiedDate和modifiedBy的关联记录 modifiedPairs: { $push: { date: "$modifiedDate", by: "$modifiedBy" } } } }, { $match: { count: { $gt: 1 } } }, { $addFields: { maxModifiedDate: { $max: "$modifiedPairs.date" }, // 筛选出date等于maxModifiedDate的记录,提取对应的modifiedBy modifiedBy: { $first: { $map: { input: { $filter: { input: "$modifiedPairs", cond: { $eq: ["$$this.date", { $max: "$modifiedPairs.date" }] } } }, as: "pair", in: "$$pair.by" } } } } }, // 移除临时字段modifiedPairs { $project: { modifiedPairs: 0 } } ])
方案对比
- 方式一:性能更优,尤其适合大数据量场景,若存在复合索引可避免全表排序。
- 方式二:无需预排序,逻辑直观,适合数据量较小或无法创建索引的场景。
内容的提问来源于stack exchange,提问作者Poonam Gupta
相关产品推荐
相关产品推荐

