복붙노트

[MONGODB] MongoDB의 중복 문서를 제거하는 가장 빠른 방법

MONGODB

MongoDB의 중복 문서를 제거하는 가장 빠른 방법

나는 (미래 10m의 +에서) MongoDB를 약 1.7M 문서가 있습니다. 그들 중 일부는 내가 원하지 않는 중복 항목을 나타냅니다. 문서의 구조는 다음과 같이이다 :

{
    _id: 14124412,
    nodes: [
        12345,
        54321
        ],
    name: "Some beauty"
}

이 같은 이름을 가진 다른 문서와 같은 적어도 하나 개의 노드가있는 경우 문서는 중복입니다. 중복을 제거하는 가장 빠른 방법은 무엇입니까?

해결법

  1. ==============================

    1.당신이 컬렉션에서 중복 된 이름 + 노드 항목을 포함 영구적으로 삭제 문서에 원하는 가정하면, 당신은 dropDups와 고유 인덱스를 추가 할 수 있습니다 진정한 옵션 :

    당신이 컬렉션에서 중복 된 이름 + 노드 항목을 포함 영구적으로 삭제 문서에 원하는 가정하면, 당신은 dropDups와 고유 인덱스를 추가 할 수 있습니다 진정한 옵션 :

    db.test.ensureIndex({name: 1, nodes: 1}, {unique: true, dropDups: true}) 
    

    워드 프로세서 말하는 것처럼 그것은 당신의 데이터베이스에서 데이터를 삭제,이 극단적 인주의하십시오. 당신이 기대하고 정확하게하지 않는 경우에는 먼저 데이터베이스를 백업하십시오.

    최신 정보

    dropDups 옵션은 3.0 (문서)에서 더 이상 사용할 수로이 솔루션은 MongoDB를 2.x를 통해서만 유효하지 않습니다.

  2. ==============================

    2.dropDups : 사실 옵션은 3.0에서 사용할 수 없습니다.

    dropDups : 사실 옵션은 3.0에서 사용할 수 없습니다.

    나는 중복을 수집 한 후 한 번에 제거하기위한 통합 프레임 워크 솔루션을 가지고있다.

    그것은 다소 느린 시스템 레벨 "인덱스"변화보다는 수 있습니다. 그러나 중복 문서를 제거 할 방법을 고려하여 좋다.

    에이. 한 번에 모든 문서를 제거

    var duplicates = [];
    
    db.collectionName.aggregate([
      { $match: { 
        name: { "$ne": '' }  // discard selection criteria
      }},
      { $group: { 
        _id: { name: "$name"}, // can be grouped on multiple properties 
        dups: { "$addToSet": "$_id" }, 
        count: { "$sum": 1 } 
      }}, 
      { $match: { 
        count: { "$gt": 1 }    // Duplicates considered as count greater than one
      }}
    ],
    {allowDiskUse: true}       // For faster processing if set is larger
    )               // 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
      }}
    ],
    {allowDiskUse: true}       // For faster processing if set is larger
    )               // 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. ==============================

    3.mongodump와 수집 덤프 만들기

    mongodump와 수집 덤프 만들기

    클리어 모음

    고유 인덱스를 추가

    mongorestore으로 수집 복원

  4. ==============================

    4.나는 MongoDB를 3.4와 함께 작동이 솔루션을 발견 : 나는 중복과 필드가 fieldX이라고 가정합니다

    나는 MongoDB를 3.4와 함께 작동이 솔루션을 발견 : 나는 중복과 필드가 fieldX이라고 가정합니다

    db.collection.aggregate([
    {
        // only match documents that have this field
        // you can omit this stage if you don't have missing fieldX
        $match: {"fieldX": {$nin:[null]}}  
    },
    {
        $group: { "_id": "$fieldX", "doc" : {"$first": "$$ROOT"}}
    },
    {
        $replaceRoot: { "newRoot": "$doc"}
    }
    ],
    {allowDiskUse:true})
    

    MongoDB의 새로운이기 때문에, 나는 많은 시간을 소비하고 찾아 삭제 중복 다른 긴 솔루션을 사용했다. 그러나, 나는이 솔루션은 단정하고 이해하기 쉬운 생각합니다.

    그것은 fieldX (나는이 분야없이 일부 문서가 있고, 나는 하나 개의 여분의 빈 결과를 얻었다)를 포함 일치하는 첫 번째 문서에 의해 작동합니다.

    fieldX에 의해 다음 단계 그룹 문서, 만 $$ ROOT를 사용하여 각 그룹의 $ 첫 번째 문서를 삽입합니다. 마지막으로,이 문서에 의해 전체 집계 그룹이 제 $$ ROOT $를 사용하여 발견 대체합니다.

    나는 내 컬렉션이 크기 때문에 allowDiskUse를 추가했다.

    당신은 파이프 라인의 숫자 후이를 추가 할 수 있으며 $에 대한 문서가 전에 먼저 첫번째 $를 사용하여 정렬 단계를 언급하지만, 그것없이 나를 위해 일했습니다. "여기에 링크를 게시 couldnt는, 내 명성 적은 10보다 :("

    당신은 단계를 벗어난 $를 추가하여 새로운 컬렉션에 결과를 저장할 수 있습니다 ...

    또한, 하나는 몇 분야에 관심이있는 경우는 예를 들어, 필드 1, FIELD2, 그리고 전체 문서, replaceRoot없이 조별 예선에서 :

    db.collection.aggregate([
    {
        // only match documents that have this field
        $match: {"fieldX": {$nin:[null]}}  
    },
    {
        $group: { "_id": "$fieldX", "field1": {"$first": "$$ROOT.field1"}, "field2": { "$first": "$field2" }}
    }
    ],
    {allowDiskUse:true})
    
  5. ==============================

    5.당신이 pymongo에서 일을하려고하는 경우는 다음과 같이 할 수 있습니다.

    당신이 pymongo에서 일을하려고하는 경우는 다음과 같이 할 수 있습니다.

    def _run_query():
    
            try:
    
                for record in (aggregate_based_on_field(collection)):
                    if not record:
                        continue
                    _logger.info("Working on Record %s", record)
    
                    try:
                        retain = db.collection.find_one(find_one({'fie1d1': 'x',  'field2':'y'}, {'_id': 1}))
                        _logger.info("_id to retain from duplicates %s", retain['_id'])
    
                        db.collection.remove({'fie1d1': 'x',  'field2':'y', '_id': {'$ne': retain['_id']}})
    
                    except Exception as ex:
                        _logger.error(" Error when retaining the record :%s Exception: %s", x, str(ex))
    
            except Exception as e:
                _logger.error("Mongo error when deleting duplicates %s", str(e))
    
    
    def aggregate_based_on_field(collection):
        return collection.aggregate([{'$group' : {'_id': "$fieldX"}}])
    

    쉘에서 :

  6. ==============================

    6.단지를 복제하지 않고 고유의 노드를 유지하면서 다음 방법은 같은 이름을 가진 문서를 병합합니다.

    단지를 복제하지 않고 고유의 노드를 유지하면서 다음 방법은 같은 이름을 가진 문서를 병합합니다.

    나는 간단한 방법으로 운영자 밖으로 $를 사용하여 발견했다. 나는 세트에 추가하여 배열 한 다음 그룹을 긴장. 운영자 밖으로 $는 [워드 프로세서] 지속 집계 결과를 허용한다. 당신은 콜렉션 자체의 이름을 넣어 경우 새로운 데이터 수집을 대체합니다. 이름이 존재하지 않는 경우는 새로운 컬렉션을 만들 것입니다.

    희망이 도움이됩니다.

    allowDiskUse는 파이프 라인에 추가 할 수 있습니다.

    db.collectionName.aggregate([
      {
        $unwind:{path:"$nodes"},
      },
      {
        $group:{
          _id:"$name",
          nodes:{
            $addToSet:"$nodes"
          }
      },
      {
        $project:{
          _id:0,
          name:"$_id.name",
          nodes:1
        }
      },
      {
        $out:"collectionNameWithoutDuplicates"
      }
    ])
    
  7. ==============================

    7.이 pymongo 사용하여 작동합니다.

    이 pymongo 사용하여 작동합니다.

    필요 unique_field의 수집을위한 고유해야하는 필드를 추가

    unique_field = {"field1":"$field1","field2":"$field2"}
    
    cursor = DB.COL.aggregate([{"$group":{"_id":unique_field, "dups":{"$push":"$uuid"}, "count": {"$sum": 1}}},{"$match":{"count": {"$gt": 1}}},{"$group":"_id":None,"dups":{"$addToSet":{"$arrayElemAt":["$dups",1]}}}}],allowDiskUse=True)
    

    카운트 중복에 따라 DUPS 배열 슬라이스 (여기 내가 모두를위한 하나의 여분의 중복을했다)

    items = list(cursor)
    removeIds = items[0]['dups']
    hold.remove({"uuid":{"$in":removeIds}})
    
  8. ==============================

    8.여기 일을 조금 더 '매뉴얼'방법입니다 :

    여기 일을 조금 더 '매뉴얼'방법입니다 :

    기본적으로, 첫째, 당신이 관심이있는 모든 고유 키의 목록을 가져옵니다.

    그리고 그 각각의 키를 사용하여 검색을 수행하고보다 경우가 해당 검색 반환 더 큰 삭제합니다.

      db.collection.distinct("key").forEach((num)=>{
        var i = 0;
        db.collection.find({key: num}).forEach((doc)=>{
          if (i)   db.collection.remove({key: num}, { justOne: true })
          i++
        })
      });
    
  9. from https://stackoverflow.com/questions/14184099/fastest-way-to-remove-duplicate-documents-in-mongodb by cc-by-sa and MIT license