MongoDB:从数组列表中删除重复的条目

时间:2016-08-01 12:05:34

标签: arrays mongodb duplicates

我目前正在与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" : "info@seiko.nl"
         "web" : "http://www.seiko.nl/Default"
         "kind" : "Horloges",
         "country" : "",
         "brandsNetherlands" : [ ]
    }

这是我数据库中单个文档的示例。正如您所看到的,"品牌"中列出的品牌数组都被复制了,我需要一种摆脱它们的方法。这样做的最佳方式是什么?

3 个答案:

答案 0 :(得分:0)

对于相对较小的数据,您可以使用聚合框架来实现上述目的,您可以使用中的 $setUnion 运算符创建具有不同品牌价值的新数组。 $project 阶段。如果数组包含重复的条目, $setUnion 会忽略重复的条目以及元素的顺序。

获得新的不同品牌字段后,您将需要一个额外的字段来过滤整个集合中的文档,即它使用与 $setUnion <相同的概念检查给定数组是否具有唯一元素/ strong>运营商。使用聚合文档中的结果数组 通过使用 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() 循环来创建批次,我们可以按批量更新集合,如下所示。

在此演示中,MongoDB版本>= 2.6 and < 3.2中提供的 Bulk() API使用 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,并使用 {{3}提供了一套更新的api }

它使用与上面相同的游标,但使用相同的 bulkWrite() 游标方法创建具有批量操作的数组,以将每个批量写入文档推送到数组。因为写入命令可以接受不超过1000次操作,所以需要将操作分组以进行最多1000次操作,并在循环达到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); }

答案 1 :(得分: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)} });
})

当然,在更新文档之前,您可以检查独特品牌阵列是否比原始品牌列表中的项目少。但是因此它对数据库问题的快速修复我不会打扰性能调整。

答案 2 :(得分:0)

只需运行脚本(不要忘记在^ _ ^之前备份)

db.wholesalers.find().forEach(saler => {
  db.wholesalers.update(
    {_id: saler._id},
    {$set: { brands: [...new Set(saler.brands)] }})
});