Mongo聚合操作中带限制条件的去重方法
嘿,针对你的问题——在MongoDB聚合操作里结合筛选条件实现去重,我结合你提供的AIS船舶数据场景,整理了几种实用方案:
方案一:先过滤条件,再分组去重(性能优先推荐)
先通过$match把符合限制条件的数据筛选出来,再用$group按目标字段(比如你的场景里的MMSI)聚合去重,同时保留你需要的关联字段。
举个例子:假设你要筛选STATUS='0'、时间在2013年12月内的数据,然后按MMSI去重,保留每个船舶最新的那条定位记录:
db.yourCollection.aggregate([ // 第一步:先过滤出符合要求的数据 { $match: { "properties.STATUS": "0", "properties.TIMESTAMP": { $gte: "2013-12-01 00:00:00.000", $lte: "2013-12-31 23:59:59.999" } } }, // 第二步:按时间倒序排序,确保最新的记录排在前面 { $sort: { "properties.TIMESTAMP": -1 } }, // 第三步:按MMSI分组去重,保留最新记录的字段 { $group: { _id: "$properties.MMSI", // 按MMSI作为分组键实现去重 latestTimestamp: { $max: "$properties.TIMESTAMP" }, latestGeometry: { $first: "$geometry" }, latestPosId: { $first: "$properties.POS_ID" }, originalDocId: { $first: "$_id" } } }, // 可选:把分组后的结果还原成类似原文档的结构 { $project: { _id: "$originalDocId", type: "Feature", geometry: "$latestGeometry", properties: { MMSI: "$_id", TIMESTAMP: "$latestTimestamp", POS_ID: "$latestPosId", STATUS: "0" } } } ])
方案二:先分组去重,再过滤结果
如果你的限制条件是针对分组后的结果(比如只保留最新定位时间在某个时段的船舶),可以先完成分组去重,再用$match过滤:
db.yourCollection.aggregate([ { $sort: { "properties.TIMESTAMP": -1 } }, { $group: { _id: "$properties.MMSI", latestTimestamp: { $max: "$properties.TIMESTAMP" }, latestGeometry: { $first: "$geometry" }, status: { $first: "$properties.STATUS" } } }, // 分组后再过滤符合条件的结果 { $match: { status: "0", latestTimestamp: { $gte: "2013-12-31 18:00:00.000" } } } ])
方案三:快速获取去重字段集合
如果你只需要拿到某个字段的去重列表(比如所有符合条件的MMSI),可以用$addToSet:
db.yourCollection.aggregate([ { $match: { "properties.STATUS": "0" } }, { $group: { _id: null, uniqueMMSIs: { $addToSet: "$properties.MMSI" } } } ])
几个关键提醒
- 性能优化:一定要把
$match放在聚合管道的最前面,先过滤掉无关数据,能大幅减少后续分组的计算量。 - 排序必要性:如果需要保留特定顺序的记录(比如最新/最早),必须在分组前做排序,否则
$first/$last的结果是随机的。 - 结构还原:分组后的结果结构会变化,用
$project或$replaceRoot可以把输出调整成你需要的格式。
内容的提问来源于stack exchange,提问作者e7lT2P
相关产品推荐
相关产品推荐

