2016-08-01 4 views
0

В настоящее время я работаю над интернет-магазином с MongoDB, но я случайно продублировал все бренды, перечисленные в коллекции «оптовиков». Я искал запрос удалить или обрезать эти повторяющиеся записи, но пока ничего не работает.MongoDB: Удаление дубликатов записей из списка массивов

db.wholesalers.find().pretty() 

{ 
     "_id" : ObjectId("..."), 
     "brands" : [ 
       "Seiko", 
       "Breil", 
       "Lorus", 
       "Seiko", 
       "Breil", 
       "Lorus", 
     ], 
     "name" : "Seiko Nederlands B.V.", 
     "address" : "Daniel Pichotstraat", 
     "housenr" : "17-31", 
     "postalcode" : "3115JB", 
     "city" : "Schiedam", 
     "phone" : "+31 (0)10 - 400 98 66" 
     "email" : "[email protected]" 
     "web" : "http://www.seiko.nl/Default" 
     "kind" : "Horloges", 
     "country" : "", 
     "brandsNetherlands" : [ ] 
    } 

Это пример одного документа в моей базе данных. Как вы можете видеть, бренды, перечисленные в массиве «бренды», были дублированы, и мне нужен способ избавиться от них. Каков наилучший способ сделать это?

+0

вы хотите сделать это в Монго Shell ли? – Mahdi

+0

Самый простой способ - написать скрипт, который обновит все ваши записи. И в будущем вы должны использовать оператор $ set для предотвращения дублирования. – Sabik

ответ

0

Для относительно небольших данных, вы можете достичь выше, используя структуру агрегации, где вы создаете новый массив с различными значениями бренда с помощью оператора в $setUnion в стадии $project. Если массив содержит повторяющиеся записи, $setUnion игнорирует дубликаты записей, а также порядок элементов.

После того, как вы получите новое поле различных марок, вам потребуется дополнительное поле для фильтрации документов во всей коллекции т.е. он проверяет, имеет ли данный массив уникальных элементов, используя ту же концепцию с оператором на $setUnion. Используйте массив результатов из агрегированных документов обновить свою коллекцию итерируя курсор результатов, используя его forEach() метода и обновление каждого документа следующим образом:

db.wholesalers.aggregate([ 
    { 
     "$project": { 
      "distinctBrands": { "$setUnion": [ "$brands", [] ] }, 
      "hasUniqueBrands": { 
       "$eq": [ 
        { "$size": "$brands" }, 
        { "$size": { "$setUnion": [ "$brands", [] ] } } 
       ] 
      } 
     } 
    }, 
    { "$match": { "hasUniqueBrands": false } } 
]).forEach(function(doc) { 
    db.wholesalers.update(
     { "_id": doc._id }, 
     { "$set": { "brands": doc.distinctBrands } } 
    ) 
}) 

Хотя это является оптимальным для небольших коллекций, производительность с большими коллекциями значительно сокращается, так как цикл через большой набор данных и отправка каждой операции обновления за запрос на сервер приводит к компьютерному штрафу.

Bulk() API приходит на помощь и значительно повышает производительность, поскольку операции записи отправляются на сервер только один раз навалом. Эффективность достигается, поскольку метод не отправляет каждый запрос на запись на сервер (как и в текущем операторе обновления в цикле forEach()), но только один раз в каждые 1000 запросов, что делает обновления более эффективными и быстрыми, чем в настоящее время.


Используя ту же концепцию выше с петлей forEach(), чтобы создать партии, мы можем обновить коллекцию навалом следующим образом.

В этой демонстрации Bulk() API доступен в MongoDB версии >= 2.6 and < 3.2 использует метод initializeUnorderedBulkOp() выполняться параллельно, а также в недетерминированном порядке, операции записи в партиях:

var bulk = db.wholesalers.initializeUnorderedBulkOp(), 
    counter = 0; // counter to keep track of the batch update size 

    db.wholesalers.aggregate([ 
     { 
      "$project": { 
       "distinctBrands": { "$setUnion": [ "$brands", [] ] }, 
       "hasUniqueBrands": { 
        "$eq": [ 
         { "$size": "$brands" }, 
         { "$size": { "$setUnion": [ "$brands", [] ] } } 
        ] 
       } 
      } 
     }, 
     { "$match": { "hasUniqueBrands": false } } 
    ]).forEach(function(doc) { 
     bulk.find({ "_id": doc._id }).updateOne({ 
      "$set": { "brands": doc.distinctBrands } 
     }); 

     counter++; // increment counter 
     if (counter % 1000 == 0) { 
      bulk.execute(); // Execute per 1000 operations 
      // and re-initialize every 1000 update statements 
      bulk = db.wholesalers.initializeUnorderedBulkOp(); 
     } 
    }); 

Следующий пример относится к новой версии MongoDB 3.2, которая с тех пор устарела Bulk() API и предоставил новый набор apis с использованием bulkWrite().

Он использует те же курсоры, как описаны выше, но и создает массивы с объемными операциями с использованием тех же forEach() метод курсора, чтобы подтолкнуть каждый объемный документ на запись в массив. Поскольку команда записи не могут принимать не более 1000 операций, там нужно группировать операции, чтобы иметь максимум 1000 операций и повторно intialise массива, когда цикл попадет в 1000 итерации:

var bulkUpdateOps = [];  
db.wholesalers.aggregate([ 
     { 
      "$project": { 
       "distinctBrands": { "$setUnion": [ "$brands", [] ] }, 
       "hasUniqueBrands": { 
        "$eq": [ 
         { "$size": "$brands" }, 
         { "$size": { "$setUnion": [ "$brands", [] ] } } 
        ] 
       } 
      } 
     }, 
     { "$match": { "hasUniqueBrands": false } } 
]).forEach(function(doc){ 
    bulkUpdateOps.push({ 
     "updateOne": { 
      "filter": { "_id": doc._id }, 
      "update": { "$set": { "brands": doc.distinctBrands } } 
     } 
    }); 

    if (bulkUpdateOps.length === 1000) { 
     db.wholesalers.bulkWrite(bulkUpdateOps); 
     bulkUpdateOps = []; 
    } 
});   

if (bulkUpdateOps.length > 0) { db.wholesalers.bulkWrite(bulkUpdateOps); } 
0

Вы также можете удалить дубликаты, применяя Javascript функция каждого документа (см How to get unique array items):

function unique(arr) { 
    var hash = {}, result = []; 
    for (var i = 0, l = arr.length; i < l; ++i) { 
     if (!hash.hasOwnProperty(arr[i])) { 
      hash[ arr[i] ] = true; 
      result.push(arr[i]); 
     } 
    } 
    return result; 
} 

db.wholesalers.find({}).forEach(function(doc){ 
    db.wholesalers.update({"_id" : doc._id}, {$set: {"brands": unique(doc.brands)} }); 
}) 

конечно, вы можете проверить, если уникальные бренды массив имеет меньше элементов, чем первоначальный список брендов перед обновлением документов и т.д. Но при этом его быстрое исправление проблем с базой данных, я бы не стал с настройкой производительности.

0

просто запустить скрипт (не забудьте сделать резервную копию, прежде чем^_ ^)

db.wholesalers.find().forEach(saler => { 
    db.wholesalers.update(
    {_id: saler._id}, 
    {$set: { brands: [...new Set(saler.brands)] }}) 
}); 
Смежные вопросы