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

时间:2015-06-17 09:59:31

标签: mongodb mongodb-query pymongo aggregation-framework

我有一个mongodb集合。当我这样做。

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中实现这一目标。

4 个答案:

答案 0 :(得分:16)

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

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

$setUnion运算符是“set”运算符,因此要设置“set”,那么只会保留“唯一”项。

如果您仍在使用早于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()检索“光标”,但基本上遵循以下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 } }
    )
})

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

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

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

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

这至少涵盖了技术。

答案 1 :(得分:1)

你可以使用带有一些javascript的foreach循环:

for (int i = 0, count = container.Items.Count; i < count; i++)
{
    Application.Current.Dispatcher.BeginInvoke(new Action(delegate()
    {
        TreeViewItem subContainer = (TreeViewItem)container.ItemContainerGenerator.ContainerFromIndex(i);
        GetTotalNTreeViewItems(subContainer, sender);
        SetNodesProcessed(sender, GetNodesProcessed(sender) + 1); 
    }));
}

答案 2 :(得分:0)

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}}
])    

答案 3 :(得分:0)

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

一个例子:

数组集合:

{ "_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()也具有此功能。