首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >pymongo:移除重复(地图减少?)

pymongo:移除重复(地图减少?)
EN

Stack Overflow用户
提问于 2016-01-11 13:34:16
回答 3查看 8.2K关注 0票数 11

我确实有一个有几个集合的数据库(总共有1500万个文档),文档看起来是这样(简化的):

代码语言:javascript
运行
复制
{'Text': 'blabla', 'ID': 101}
{'Text': 'Whuppppyyy', 'ID': 102}
{'Text': 'Abrakadabraaa', 'ID': 103}
{'Text': 'olalalaal', 'ID': 104}
{'Text': 'test1234545', 'ID': 104}
{'Text': 'whapwhapwhap', 'ID': 104}

它们都有一个唯一的_id字段,但我想删除另一个字段(外部ID字段)的副本。

首先,我尝试了一种非常手动的方法,在列表和删除之后,但是DB看起来太大了,花费了很长时间,而且不实用。

第二,以下内容在当前的MongoDB版本中不再有效,尽管有人建议这样做。

代码语言:javascript
运行
复制
db.collection.ensureIndex( { ID: 1 }, { unique: true, dropDups: true } )

所以,现在我正在尝试创建一个地图减少解决方案,但我不知道自己在做什么,尤其是在使用另一个字段(而不是数据库_id)查找和删除副本时遇到了困难。下面是我糟糕的第一种方法(从一些不同的来源中采用):

代码语言:javascript
运行
复制
map = Code("function(){ if(this.fieldName){emit(this.fieldName,1);}}")
reduce = Code("function(key,values) {return Array.sum(values);}")
res = coll.map_reduce(map,reduce,"my_results");

response = []
for doc in res.find():
    if(doc['value'] > 1):
        count = int(doc['value']) - 1
        docs = col.find({"fieldName":doc['ID']},{'ID':1}).limit(count)
        for i in docs:
            response.append(i['ID'])

coll.remove({"ID": {"$in": response}})

任何帮助,以减少任何重复在外部ID字段(留下一个条目),将非常多地使用;)谢谢!

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2016-01-11 14:23:26

另一种方法是使用聚合框架,它的性能优于map-reduce。考虑以下聚合管道,作为聚合管道的第一阶段,$group操作符按ID字段对文档进行分组,并使用$addToSet运算符将分组记录的每个_id值存储在unique_ids字段中。$sum累加器运算符将传递给它的字段的值相加,在本例中为常量1,从而计算归组到计数字段中的记录数。另一个管道步骤$match过滤至少2的文档,即重复的文档。

一旦从聚合中获得结果,就迭代游标以删除unique_ids字段中的第一个unique_ids,然后将其余部分推入一个数组中,该数组稍后将用于删除重复项(减去一个条目):

代码语言:javascript
运行
复制
cursor = db.coll.aggregate(
    [
        {"$group": {"_id": "$ID", "unique_ids": {"$addToSet": "$_id"}, "count": {"$sum": 1}}},
        {"$match": {"count": { "$gte": 2 }}}
    ]
)

response = []
for doc in cursor:
    del doc["unique_ids"][0]
    for id in doc["unique_ids"]:
        response.append(id)

coll.remove({"_id": {"$in": response}})
票数 10
EN

Stack Overflow用户

发布于 2016-01-12 08:16:34

首先,我尝试了一种非常手动的方法,在列表和删除之后,但是DB看起来太大了,花费了很长时间,而且不实用。

最好的选择是使用.aggregate()方法,该方法提供对聚合管道的访问,以查找重复的文档。管道中的第一个阶段是$group阶段,您将文档按复制的键分组,然后使用$push$sum累加器操作符,分别返回每个组的所有_id数组和组中元素的计数。管道中的下一个也是最后一个阶段是$match阶段,它只返回那些存在重复"ID“的结果。然后,您将迭代游标,并使用“散装”操作更新每个文档。

代码语言:javascript
运行
复制
pipeline = [{'$group': {'_id': '$ID', 'count': {'$sum': 1}, 'ids': {'$push': '$_id'}}},
    {'$match': {'count': {'$gte': 2}}}]

bulk = db.collection.initialize_ordered_bulk_op()
count = 0
for document in db.collection.aggregate(pipeline):
    it = iter(document['ids'])
    next(it)
    for id in it:
        bulk.find({'_id': id}).remove_one({'_id': id})
        count = count + 1
        if count % 1000 == 0:
            bulk.execute()
    if count > 0:
        bulk.execute()

MongoDB 3.2不支持Bulk()及其相关方法,因此您需要使用bulk_write()方法来执行请求。

代码语言:javascript
运行
复制
from pymongo import DeleteOne

request = []
for document in db.collection.aggregate(pipeline):
    it = iter(document['ids'])
    next(it)
    for id in it:
        requests.append(DeleteOne({'_id': id}))
db.collection.bulk_write(requests)

您也可以在shell中这样做,如对从mongodb中删除dups如何在mongodb中删除具有特定条件的副本?的接受答案中所示

票数 4
EN

Stack Overflow用户

发布于 2020-11-14 18:56:09

我的解决方案也是使用聚合。您可以选择为聚合复制的字段。结果将是一个重复集合的列表。每个职位都将保持一组副本。您在列表上插入,忽略每个组的第一个元素来保存它,并删除其余元素。你为每一组副本都这么做。见下文:

代码语言:javascript
运行
复制
replic = db.<YOUR_COLLECTION>.aggregate([            # Cursor with all duplicated documents
    {'$group': {
        '_id': {'<FIELD_DUPLICATED>': '$<FIELD_DUPLICATED>'},     # Duplicated field
        'idsUnicos': {'$addToSet': '$_id'},
        'total': {'$sum': 1}
        }
    },
    {'$match': { 
        'total': {'$gt': 1}    # Holds how many duplicates for each group, if you need it.
        }
    }
])
                          # Result is a list of lists of ObjectsIds
for i in replic:
    for idx, j in enumerate(i['idsUnicos']):             # It holds the ids of all duplicates 
        if idx != 0:                                     # Jump over first element to keep it
            <YOUR_COLLECTION>.delete_one({'_id': j})     # Remove the rest

您可以尝试"delete_many“来提高性能。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/34722866

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档