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

sta*_*ion 6 mongodb pymongo mongodb-query aggregation-framework

我有一个mongodb系列.当我做.

db.bill.find({})
Run Code Online (Sandbox Code Playgroud)

我明白了

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

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

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

如何在mongodb中实现这一目标.

小智 16

那你可以使用聚合框架做到这一点,如下所示:

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

$setUnion运营商是一个"套"操作,因此做一个"套",则只有"唯一"的项目被保留后面.

如果你还在使用MongoDB的版本比2.6更旧,那么你就必须做这个操作与$unwind$addToSet替代:

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

它没有那么高效,但自2.2版以来就支持运营商.

当然,如果您确实要永久修改收集文档,则可以对此进行扩展并相应地处理每个文档的更新.您可以从中检索"光标" .aggregate(),但基本上遵循以下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 } }
    )
})
Run Code Online (Sandbox Code Playgroud)

早期版本更多涉及:

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 } }
    )
})
Run Code Online (Sandbox Code Playgroud)

使用添加的操作来比较"重复数据删除"数组是否与原始数组长度相同,并且只返回那些已删除"重复"的文档以便在更新时进行处理.


也许应该在这里添加"for python"注释.如果你不关心"识别"包含重复数组条目的文档并准备用更新"爆炸"整个集合,那么只需.set()在客户端代码中使用python 来删除重复项:

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

所以这很简单,它取决于哪个是更大的邪恶,找到带有重复的文件或更新每个文件的成本是否需要它.

这至少涵盖了技术.