如何删除 mongodb 列表中的重复值

How to remove duplicate values inside a list in mongodb

我有一个 mongodb collection 。当我做的时候。

db.bill.find({})

我明白了,

{ 
    "_id" : ObjectId("55695ea145e8a960bef8b87a"),
    "name" : "ABC. Net", 
    "code" : "1-98tfv",
    "abbreviation" : "ABC",
    "bill_codes" : [  190215,  44124,  190215,  147708 ],
    "customer_name" : "abc"
}

我需要一个操作来删除 bill_codes 中的重复值。最后应该是

{ 
    "_id" : ObjectId("55695ea145e8a960bef8b87a"),
    "name" : "ABC. Net", 
    "code" : "1-98tfv",
    "abbreviation" : "ABC",
    "bill_codes" : [  190215,  44124,  147708 ],
    "customer_name" : "abc"
}

如何在 mongodb 中实现这一点。

您可以使用聚合框架执行此操作,如下所示:

collection.aggregate([
    { "$project": {
        "name": 1,
        "code": 1,
        "abbreviation": 1,
        "bill_codes": { "$setUnion": [ "$bill_codes", [] ] }
    }}
])

$setUnion 运算符是 "set" 运算符,因此要生成 "set",则只保留 "unique" 项。

如果您仍在使用早于 2.6 的 MongoDB 版本,那么您必须使用 $unwind 执行此操作$addToSet 改为:

collection.aggregate([
    { "$unwind": "$bill_codes" },
    { "$group": {
        "_id": "$_id",
        "name": { "$first": "$name" },
        "code": { "$first": "$code" },
        "abbreviation": { "$first": "$abbreviation" },
        "bill_codes": { "$addToSet": "$bill_codes" }
    }}
])

效率不高,但从 2.2 版开始支持运算符。

当然,如果您真的想永久修改您的集合文档,那么您可以对此进行扩展并相应地处理每个文档的更新。您可以从 .aggregate() 检索 "cursor",但基本上遵循此 shell 示例:

db.collection.aggregate([
    { "$project": {
        "bill_codes": { "$setUnion": [ "$bill_codes", [] ] },
        "same": { "$eq": [
            { "$size": "$bill_codes" },
            { "$size": { "$setUnion": [ "$bill_codes", [] ] } }
        ]}
    }},
    { "$match": { "same": false } }
]).forEach(function(doc) {
    db.collection.update(
        { "_id": doc._id },
        { "$set": { "bill_codes": doc.bill_codes } }
    )
})

早期版本涉及更多:

db.collection.aggregate([
    { "$unwind": "$bill_codes" },
    { "$group": {
        "_id": { 
            "_id": "$_id",
            "bill_code": "$bill_codes"
        },
        "origSize": { "$sum": 1 }
    }},
    { "$group": {
        "_id": "$_id._id",
        "bill_codes": { "$push": "$_id.bill_code" },
        "origSize": { "$sum": "$origSize" },
        "newSize": { "$sum": 1 }
    }},
    { "$project": {
        "bill_codes": 1,
        "same": { "$eq": [ "$origSize", "$newSize" ] }
    }},
    { "$match": { "same": false } }
]).forEach(function(doc) {
    db.collection.update(
        { "_id": doc._id },
        { "$set": { "bill_codes": doc.bill_codes } }
    )
})

在其中添加操作以比较 "de-duplicated" 数组是否与原始数组长度相同,并且仅 return 删除了 "duplicates" 的文档以进行处理更新。


可能还应在此处添加 "for python" 注释。如果您不关心 "identifying" 包含重复数组条目的文档并准备 "blast" 更新整个集合,那么只需在客户端中使用 python .set()删除重复项的代码:

for doc in collection.find():
    collection.update(
       { "_id": doc["_id"] },
       { "$set": { "bill_codes": list(set(doc["bill_codes"])) } }
    )

所以这很简单,这取决于哪个是更大的罪恶,查找具有重复的文档或更新每个文档的成本是否需要它。

这至少涵盖了技术。

您可以使用带有一些 javascript:

的 foreach 循环
db.bill.find().forEach(function(entry){
     var arr = entry.bill_codes;
     var uniqueArray = arr.filter(function(elem, pos) {
        return arr.indexOf(elem) == pos;
     }); 
     entry.bill_codes = uniqueArray;
     db.bill.save(entry);
})

Mongo 3.4+ 具有 $addFields 聚合阶段,这使您可以避免显式列出 $project:

中的所有其他字段
db.bill.aggregate([
    {"$addFields": {
        "bill_codes": {"$setUnion": ["$bill_codes", []]}
    }}
])

仅供参考,这是另一种(更冗长的)使用 replaceRoot 的方式,也不需要列出所有可能的字段:

db.bill.aggregate([
    {'$unwind': {
        'path': '$bill_codes',
        // output the document even if its list of books is empty
        'preserveNullAndEmptyArrays': true
    }},
    {'$group': {
        '_id': '$_id',
        'bill_codes': {'$addToSet': '$bill_codes'},
        // arbitrary name that doesn't exist on any document
        '_other_fields': {'$first': '$$ROOT'},
    }},
    {
      // the field, in the resulting document, has the value from the last document merged for the field. (c) docs
      // so the new deduped array value will be used
      '$replaceRoot': {'newRoot': {'$mergeObjects': ['$_other_fields', "$$ROOT"]}}
    },
    {'$project': {'_other_fields': 0}}
])    

MongoDB 4.2集合updateMany方法的update参数也可以是聚合pipeline(而不是文档)。管道支持 $set$unset$replaceWith 阶段。在 $set 阶段使用 $setIntersection 聚合管道运算符,您可以从数组字段中删除重复项并在单个操作中更新集合。

一个例子:

数组集合:

{ "_id" : 0, "a" : [ 3, 5, 5, 3 ] }
{ "_id" : 1, "a" : [ 1, 2, 3, 2, 4 ] }

来自mongo shell:

db.arrays.updateMany(
   {  },
   [
      { $set: { a: { $setIntersection: [ "$a", "$a" ] } } }
   ]
)

更新数组集合:

{ "_id" : 0, "a" : [ 3, 5 ] }
{ "_id" : 1, "a" : [ 1, 2, 3, 4 ] }

其他更新方法update()updateOne()findAndModify()也有此功能。