我在MongoDB中有一个集合,其中有大约(约300万条记录)。我的样本记录看起来像,
{ "_id" = ObjectId("50731xxxxxxxxxxxxxxxxxxxx"),
"source_references" : [
"_id" : ObjectId("5045xxxxxxxxxxxxxx"),
"name" : "xxx",
"key" : 123
]
}
我在集合中有很多重复记录,具有相同的source_references.key
。 (重复我的意思是,source_references.key
不是_id
)。
我想删除基于source_references.key
的重复记录,我正在考虑编写一些PHP代码来遍历每条记录并删除记录(如果存在)。
有没有办法删除Mongo Internal命令行中的重复项?
答案 0 :(得分:76)
如果您确定source_references.key
标识了重复记录,则可以确保在MongoDB 2.6或更早版本中使用dropDups:true
索引创建选项的唯一索引:
db.things.ensureIndex({'source_references.key' : 1}, {unique : true, dropDups : true})
这将保留每个source_references.key
值的第一个唯一文档,并删除任何可能导致重复密钥违规的后续文档。
重要提示:
dropDups
选项为removed in MongoDB 3.0,因此需要采用不同的方法。例如,您可以按照MongoDB duplicate documents even after adding unique key上的建议使用聚合。 source_references.key
字段的文档都将被视为具有 null 值,因此缺少关键字段的后续文档将被删除。您可以添加sparse:true
索引创建选项,以便索引仅适用于具有source_references.key
字段的文档。明显谨慎:备份数据库,如果您担心意外数据丢失,请先在暂存环境中尝试此操作。
答案 1 :(得分:47)
这是我在MongoDB 3.2上使用的最简单的查询
db.myCollection.find({}, {myCustomKey:1}).sort({_id:1}).forEach(function(doc){
db.myCollection.remove({_id:{$gt:doc._id}, myCustomKey:doc.myCustomKey});
})
在运行此customKey
之前将其编入索引以提高速度
答案 2 :(得分:29)
按aggregation framework删除重复项。
一个。如果你想一次删除。
var duplicates = [];
db.collectionName.aggregate([
// discard selection criteria, You can remove "$match" section if you want
{ $match: {
source_references.key: { "$ne": '' }
}},
{ $group: {
_id: { source_references.key: "$source_references.key"}, // can be grouped on multiple properties
dups: { "$addToSet": "$_id" },
count: { "$sum": 1 }
}},
{ $match: {
count: { "$gt": 1 } // Duplicates considered as count greater than one
}}
]) // You can display result until this and check duplicates
.forEach(function(doc) {
doc.dups.shift(); // First element skipped for deleting
doc.dups.forEach( function(dupId){
duplicates.push(dupId); // Getting all duplicate ids
}
)
})
// If you want to Check all "_id" which you are deleting else print statement not needed
printjson(duplicates);
// Remove all duplicates in one go
db.collectionName.remove({_id:{$in:duplicates}})
湾您可以逐个删除文档。
db.collectionName.aggregate([
// discard selection criteria, You can remove "$match" section if you want
{ $match: {
source_references.key: { "$ne": '' }
}},
{ $group: {
_id: { source_references.key: "$source_references.key"}, // can be grouped on multiple properties
dups: { "$addToSet": "$_id" },
count: { "$sum": 1 }
}},
{ $match: {
count: { "$gt": 1 } // Duplicates considered as count greater than one
}}
]) // You can display result until this and check duplicates
.forEach(function(doc) {
doc.dups.shift(); // First element skipped for deleting
db.collectionName.remove({_id : {$in: doc.dups }}); // Delete remaining duplicates
})
答案 3 :(得分:8)
虽然@ Stennie是一个有效的答案,但这不是唯一的方法。事实上,MongoDB手册要求您在执行此操作时要非常谨慎。还有两个选择
答案 4 :(得分:3)
在Fernando's answer上扩展时,我发现它花费的时间太长,因此我对其进行了修改。
var x = 0;
db.collection.distinct("field").forEach(fieldValue => {
var i = 0;
db.collection.find({ "field": fieldValue }).forEach(doc => {
if (i) {
db.collection.remove({ _id: doc._id });
}
i++;
x += 1;
if (x % 100 === 0) {
print(x); // Every time we process 100 docs.
}
});
});
改进基本上是使用文档ID删除,它应该更快,并且还添加了操作进度,您可以将迭代值更改为所需的数量。
此外,在操作之前对字段进行索引会有所帮助。
答案 5 :(得分:2)
我有类似的要求,但我想保留最新的条目。以下查询可用于我的具有数百万条记录和重复项的集合。
/** Create a array to store all duplicate records ids*/
var duplicates = [];
/** Start Aggregation pipeline*/
db.collection.aggregate([
{
$match: { /** Add any filter here. Add index for filter keys*/
filterKey: {
$exists: false
}
}
},
{
$sort: { /** Sort it in such a way that you want to retain first element*/
createdAt: -1
}
},
{
$group: {
_id: {
key1: "$key1", key2:"$key2" /** These are the keys which define the duplicate. Here document with same value for key1 and key2 will be considered duplicate*/
},
dups: {
$push: {
_id: "$_id"
}
},
count: {
$sum: 1
}
}
},
{
$match: {
count: {
"$gt": 1
}
}
}
],
{
allowDiskUse: true
}).forEach(function(doc){
doc.dups.shift();
doc.dups.forEach(function(dupId){
duplicates.push(dupId._id);
})
})
/** Delete the duplicates*/
var i,j,temparray,chunk = 100000;
for (i=0,j=duplicates.length; i<j; i+=chunk) {
temparray = duplicates.slice(i,i+chunk);
db.collection.bulkWrite([{deleteMany:{"filter":{"_id":{"$in":temparray}}}}])
}
答案 6 :(得分:1)
这是一种稍微“手动”的方式:
基本上,首先,获取您感兴趣的所有唯一键的列表。
然后使用其中每个键执行搜索,如果搜索返回大于1,则删除。
var Electron = require('electron')
答案 7 :(得分:0)
pip install mongo_remove_duplicate_indexes
查看mongo_remove_duplicate_indexes的软件包源代码,以便更好地理解
答案 8 :(得分:0)
如果你有足够的内存,你可以在scala中做类似的事情:
cole.find().groupBy(_.customField).filter(_._2.size>1).map(_._2.tail).flatten.map(_.id)
.foreach(x=>cole.remove({id $eq x})