从MongoDB中删除重复项

4

您好,我在mongodb(复制)中有大约五百万个文档,每个文档包含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
}

如何解决问题?
1个回答

7
“dropDups”语法用于索引创建,在MongoDB 2.6中已被“弃用”,并在MongoDB 3.0中移除。在大多数情况下,使用此语法不是一个好主意,因为“删除”是任意的,“重复项”可能会被删除,这意味着被“删除”的内容可能不是您真正想要删除的内容。
无论如何,由于索引键的值超过了允许的长度,您会遇到“索引长度”错误。一般来说,在任何普通应用程序中都不应该对43个字段进行索引。
如果您想从集合中删除“重复项”,则最好运行聚合查询以确定哪些文档包含“重复数据”,然后循环遍历该列表,从目标集合中删除已经“唯一”的_id值之外的所有内容。这可以通过批量操作来实现最大效率。

注意:我确实很难相信你的文档实际上包含43个“独特”的字段。很可能“你所需要的”只是简单地识别使文档“独特”的那些字段,然后按照下面概述的过程进行:

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版本低于2.6且没有批量操作,则可以尝试在循环中使用标准的.remove()。同时请注意,.aggregate()在此处不会返回游标,循环必须更改为:

db.testkdd.aggregate([
   // pipeline as above
]).result.forEach(function(doc) {
    doc.ids.shift();  
    db.testkdd.remove({ "_id": { "$in": doc.ids } });
});

请务必仔细查看您的文档,并仅包括您期望成为分组_id的“唯一”字段。否则,您最终将删除任何内容,因为那里没有重复项。

@mohamedzajith 好的,错误实际上告诉了你该怎么做。在管道中添加"allowDiskUse"。这在聚合方法中有记录。我还建议你仍然传入了比实际构成"唯一"组合更多的字段。 - Blakes Seven
如何在Mongo shell中添加allowDiskUse - mohamedzajith
@mohamedzajith 在聚合选项的文档中有明确的示例。它们在管道参数数组之后指定。上面包含了一个示例。 - Blakes Seven

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接