MongoDB 中由于一些误操作或者写入数据的问题可能会产生重复数据,下面以 testCollection 为例进行去重
testCollection 中使用 rid 作为唯一标识,所以这里根据 rid 对数据进行去重,同一个 rid 只保留一项
1、 方案一:将重复数据的 ObjectId 根据 rid 分组,每个分组中只保留一条,其余的数据都使用 ObjectId 删除
这里使用 aggregation 将所有的 doc 根据 rid 聚合,保存在 dups 参数,这样 aggregation 的结果的每一项都是同一个 rid 聚合的结果,每一项都有一个 dups 参数,表示这个 rid 对应的所有 doc 的 ObjectId
然后遍历 aggregation 的结果,对于同一个 rid 只保留 dups 中的第一个 ObjectId,其他的 ObjectId 都加入到 duplicates 数组中
最后将 duplicates 中 ObjectId 对应的所有 doc 依次删掉
var duplicates = []; // 用于存放需要删除的 doc 的 ObjectId 的数组
db.getCollection("testCollection").aggregate([
{ $match: {
title: { "$eq": 'placeholder' } // 匹配“placeholder”对应的评论
}},
{ $group: {
_id: { rid : "$rid "}, // 根据 rid 进行聚合,聚合结果的每一项都是同一个 rid
dups: { "$addToSet": "$_id" }, // 将同一个 rid 对应的所有 ObjectId 加入 dups 中
count: { "$sum": 1 } // 记录每一个 rid 对应的 ObjectId 的数量
}},
{ $match: {
count: { "$gt": 1 } // 通过前面的 count 将有重复评论的 group 结果筛选出,没有重复的评论不会出现在后面遍历的过程中
}}
],
{allowDiskUse: true} // 允许使用写临时文件来加速 aggregation 操作
)
.forEach(function(doc) {
doc.dups.shift(); // 对每一个 rid 保留第一个 ObjectId
doc.dups.forEach( function(dupId){
duplicates.push(dupId); // 将剩下的 ObjectId 都加入待删除 doc 的 ObjectId 数组
}
)
})
db.getCollection("testCollection").remove({_id:{$in:duplicates}}) // 将 duplicates 中 ObjectId 对应的 doc 删除
结果报错:
Document 0 is too large for the cluster. Document is 68310755 bytes, max is 16777216.
原因是这里重复数据过多,导致生成的 duplicates 太大,而 MongoDB 对于 BSON 文件大小的限制是 16MB
The maximum BSON document size is 16 megabytes. The maximum document size helps ensure that a single document cannot use excessive amount of RAM or, during transmission, excessive amount of bandwidth. To store documents larger than the maximum size, MongoDB provides the GridFS API. See
mongofiles
and the documentation for your driver for more information about GridFS.
1、 方案2:不再将 aggregation 操作结果的 ObjectId 保存到 duplicates 中,而是直接遍历,依次删除多余的 ObjectId 对应的 doc
db.getCollection("testCollection").aggregate([
{ $match: {
title: { "$eq": 'placeholder' }
}},
{ $group: {
_id: { rid: "$rid"},
dups: { "$addToSet": "$_id" },
count: { "$sum": 1 }
}},
{ $match: {
count: { "$gt": 1 }
}}
],
{allowDiskUse: true}
)
.forEach(function(doc) {
doc.dups.shift();
db.getCollection("testCollection").remove({_id : {$in: doc.dups }});
})
参考: