从 MongoDB 中删除重复项
Remove Duplicates from MongoDB
嗨,我在 mongodb(复制)中有大约 500 万个文档,每个文档有 43 个字段。如何删除重复文档。我试过了
db.testkdd.ensureIndex({
duration : 1 , protocol_type : 1 , service : 1 ,
flag : 1 , src_bytes : 1 , dst_bytes : 1 ,
land : 1 , wrong_fragment : 1 , urgent : 1 ,
hot : 1 , num_failed_logins : 1 , logged_in : 1 ,
num_compromised : 1 , root_shell : 1 , su_attempted : 1 ,
num_root : 1 , num_file_creations : 1 , num_shells : 1 ,
num_access_files : 1 , num_outbound_cmds : 1 , is_host_login : 1 ,
is_guest_login : 1 , count : 1 , srv_count : 1 ,
serror_rate : 1 , srv_serror_rate : 1 , rerror_rate : 1 ,
srv_rerror_rate : 1 , same_srv_rate : 1 , diff_srv_rate : 1 ,
srv_diff_host_rate : 1 , dst_host_count : 1 , dst_host_srv_count : 1 ,
dst_host_same_srv_rate : 1 , dst_host_diff_srv_rate : 1 ,
dst_host_same_src_port_rate : 1 , dst_host_srv_diff_host_rate : 1 ,
dst_host_serror_rate : 1 , dst_host_srv_serror_rate : 1 ,
dst_host_rerror_rate : 1 , dst_host_srv_rerror_rate : 1 , lable : 1
},
{unique: true, dropDups: true}
)
运行 此代码出现错误 "errmsg":“从索引生成的命名空间名称 ..
{
"ok" : 0,
"errmsg" : "namespace name generated from index name \"project.testkdd.$duration_1_protocol_type_1_service_1_flag_1_src_bytes_1_dst_bytes_1_land_1_wrong_fragment_1_urgent_1_hot_1_num_failed_logins_1_logged_in_1_num_compromised_1_root_shell_1_su_attempted_1_num_root_1_num_file_creations_1_num_shells_1_num_access_files_1_num_outbound_cmds_1_is_host_login_1_is_guest_login_1_count_1_srv_count_1_serror_rate_1_srv_serror_rate_1_rerror_rate_1_srv_rerror_rate_1_same_srv_rate_1_diff_srv_rate_1_srv_diff_host_rate_1_dst_host_count_1_dst_host_srv_count_1_dst_host_same_srv_rate_1_dst_host_diff_srv_rate_1_dst_host_same_src_port_rate_1_dst_host_srv_diff_host_rate_1_dst_host_serror_rate_1_dst_host_srv_serror_rate_1_dst_host_rerror_rate_1_dst_host_srv_rerror_rate_1_lable_1\" is too long (127 byte max)",
"code" : 67
}
如何解决问题?
从 MongoDB 2.6 和 removed in MongoDB 3.0 开始,用于创建索引的 "dropDups" 语法已变为 "deprecated"。在大多数情况下使用它不是一个好主意,因为 "removal" 是任意的,任何 "duplicate" 都可以删除。这意味着 "removed" 可能不是您真正想要删除的内容。
无论如何,您 运行 会陷入 "index length" 错误,因为此处索引键的值会比允许的更长。一般来说,你不是"meant"在任何正常的应用程序中索引43个字段。
如果要从集合中删除 "duplicates",那么最好的办法是 运行 聚合查询以确定哪些文档包含 "duplicate" 数据,然后循环遍历该列表从目标集合中删除 "all but one" 个已经 "unique" _id
个值。这可以通过 "Bulk" 操作来实现,以获得最大效率。
注意:我确实很难相信您的文档实际上包含 43 个 "unique" 字段。很可能"all you need"只是简单地识别只有那些使文档"unique"的字段,然后按照流程如下所述:
var bulk = db.testkdd.initializeOrderedBulkOp(),
count = 0;
// List "all" fields that make a document "unique" in the `_id`
// I am only listing some for example purposes to follow
db.testkdd.aggregate([
{ "$group": {
"_id": {
"duration" : "$duration",
"protocol_type": "$protocol_type",
"service": "$service",
"flag": "$flag"
},
"ids": { "$push": "$_id" },
"count": { "$sum": 1 }
}},
{ "$match": { "count": { "$gt": 1 } } }
],{ "allowDiskUse": true}).forEach(function(doc) {
doc.ids.shift(); // remove first match
bulk.find({ "_id": { "$in": doc.ids } }).remove(); // removes all $in list
count++;
// Execute 1 in 1000 and re-init
if ( count % 1000 == 0 ) {
bulk.execute();
bulk = db.testkdd.initializeOrderedBulkOp();
}
});
if ( count % 1000 != 0 )
bulk.execute();
如果您的 MongoDB 版本 "lower" 高于 2.6,并且没有批量操作,那么您也可以尝试在循环内使用标准 .remove()
。还要注意 .aggregate()
不会 return 此处的光标并且循环必须更改为:
db.testkdd.aggregate([
// pipeline as above
]).result.forEach(function(doc) {
doc.ids.shift();
db.testkdd.remove({ "_id": { "$in": doc.ids } });
});
但请务必仔细查看您的文档,并且仅包含 "just" 您希望属于分组 _id
的 "unique" 字段。否则你最终什么都不会删除,因为那里没有重复项。
嗨,我在 mongodb(复制)中有大约 500 万个文档,每个文档有 43 个字段。如何删除重复文档。我试过了
db.testkdd.ensureIndex({
duration : 1 , protocol_type : 1 , service : 1 ,
flag : 1 , src_bytes : 1 , dst_bytes : 1 ,
land : 1 , wrong_fragment : 1 , urgent : 1 ,
hot : 1 , num_failed_logins : 1 , logged_in : 1 ,
num_compromised : 1 , root_shell : 1 , su_attempted : 1 ,
num_root : 1 , num_file_creations : 1 , num_shells : 1 ,
num_access_files : 1 , num_outbound_cmds : 1 , is_host_login : 1 ,
is_guest_login : 1 , count : 1 , srv_count : 1 ,
serror_rate : 1 , srv_serror_rate : 1 , rerror_rate : 1 ,
srv_rerror_rate : 1 , same_srv_rate : 1 , diff_srv_rate : 1 ,
srv_diff_host_rate : 1 , dst_host_count : 1 , dst_host_srv_count : 1 ,
dst_host_same_srv_rate : 1 , dst_host_diff_srv_rate : 1 ,
dst_host_same_src_port_rate : 1 , dst_host_srv_diff_host_rate : 1 ,
dst_host_serror_rate : 1 , dst_host_srv_serror_rate : 1 ,
dst_host_rerror_rate : 1 , dst_host_srv_rerror_rate : 1 , lable : 1
},
{unique: true, dropDups: true}
)
运行 此代码出现错误 "errmsg":“从索引生成的命名空间名称 ..
{
"ok" : 0,
"errmsg" : "namespace name generated from index name \"project.testkdd.$duration_1_protocol_type_1_service_1_flag_1_src_bytes_1_dst_bytes_1_land_1_wrong_fragment_1_urgent_1_hot_1_num_failed_logins_1_logged_in_1_num_compromised_1_root_shell_1_su_attempted_1_num_root_1_num_file_creations_1_num_shells_1_num_access_files_1_num_outbound_cmds_1_is_host_login_1_is_guest_login_1_count_1_srv_count_1_serror_rate_1_srv_serror_rate_1_rerror_rate_1_srv_rerror_rate_1_same_srv_rate_1_diff_srv_rate_1_srv_diff_host_rate_1_dst_host_count_1_dst_host_srv_count_1_dst_host_same_srv_rate_1_dst_host_diff_srv_rate_1_dst_host_same_src_port_rate_1_dst_host_srv_diff_host_rate_1_dst_host_serror_rate_1_dst_host_srv_serror_rate_1_dst_host_rerror_rate_1_dst_host_srv_rerror_rate_1_lable_1\" is too long (127 byte max)",
"code" : 67
}
如何解决问题?
从 MongoDB 2.6 和 removed in MongoDB 3.0 开始,用于创建索引的 "dropDups" 语法已变为 "deprecated"。在大多数情况下使用它不是一个好主意,因为 "removal" 是任意的,任何 "duplicate" 都可以删除。这意味着 "removed" 可能不是您真正想要删除的内容。
无论如何,您 运行 会陷入 "index length" 错误,因为此处索引键的值会比允许的更长。一般来说,你不是"meant"在任何正常的应用程序中索引43个字段。
如果要从集合中删除 "duplicates",那么最好的办法是 运行 聚合查询以确定哪些文档包含 "duplicate" 数据,然后循环遍历该列表从目标集合中删除 "all but one" 个已经 "unique" _id
个值。这可以通过 "Bulk" 操作来实现,以获得最大效率。
注意:我确实很难相信您的文档实际上包含 43 个 "unique" 字段。很可能"all you need"只是简单地识别只有那些使文档"unique"的字段,然后按照流程如下所述:
var bulk = db.testkdd.initializeOrderedBulkOp(),
count = 0;
// List "all" fields that make a document "unique" in the `_id`
// I am only listing some for example purposes to follow
db.testkdd.aggregate([
{ "$group": {
"_id": {
"duration" : "$duration",
"protocol_type": "$protocol_type",
"service": "$service",
"flag": "$flag"
},
"ids": { "$push": "$_id" },
"count": { "$sum": 1 }
}},
{ "$match": { "count": { "$gt": 1 } } }
],{ "allowDiskUse": true}).forEach(function(doc) {
doc.ids.shift(); // remove first match
bulk.find({ "_id": { "$in": doc.ids } }).remove(); // removes all $in list
count++;
// Execute 1 in 1000 and re-init
if ( count % 1000 == 0 ) {
bulk.execute();
bulk = db.testkdd.initializeOrderedBulkOp();
}
});
if ( count % 1000 != 0 )
bulk.execute();
如果您的 MongoDB 版本 "lower" 高于 2.6,并且没有批量操作,那么您也可以尝试在循环内使用标准 .remove()
。还要注意 .aggregate()
不会 return 此处的光标并且循环必须更改为:
db.testkdd.aggregate([
// pipeline as above
]).result.forEach(function(doc) {
doc.ids.shift();
db.testkdd.remove({ "_id": { "$in": doc.ids } });
});
但请务必仔细查看您的文档,并且仅包含 "just" 您希望属于分组 _id
的 "unique" 字段。否则你最终什么都不会删除,因为那里没有重复项。