我确实有一个有几个集合的数据库(总共有1500万个文档),文档看起来是这样(简化的):
{'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版本中不再有效,尽管有人建议这样做。
db.collection.ensureIndex( { ID: 1 }, { unique: true, dropDups: true } )所以,现在我正在尝试创建一个地图减少解决方案,但我不知道自己在做什么,尤其是在使用另一个字段(而不是数据库_id)查找和删除副本时遇到了困难。下面是我糟糕的第一种方法(从一些不同的来源中采用):
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字段(留下一个条目),将非常多地使用;)谢谢!
发布于 2016-01-11 14:23:26
另一种方法是使用聚合框架,它的性能优于map-reduce。考虑以下聚合管道,作为聚合管道的第一阶段,$group操作符按ID字段对文档进行分组,并使用$addToSet运算符将分组记录的每个_id值存储在unique_ids字段中。$sum累加器运算符将传递给它的字段的值相加,在本例中为常量1,从而计算归组到计数字段中的记录数。另一个管道步骤$match过滤至少2的文档,即重复的文档。
一旦从聚合中获得结果,就迭代游标以删除unique_ids字段中的第一个unique_ids,然后将其余部分推入一个数组中,该数组稍后将用于删除重复项(减去一个条目):
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}})发布于 2016-01-12 08:16:34
首先,我尝试了一种非常手动的方法,在列表和删除之后,但是DB看起来太大了,花费了很长时间,而且不实用。
最好的选择是使用.aggregate()方法,该方法提供对聚合管道的访问,以查找重复的文档。管道中的第一个阶段是$group阶段,您将文档按复制的键分组,然后使用$push和$sum累加器操作符,分别返回每个组的所有_id数组和组中元素的计数。管道中的下一个也是最后一个阶段是$match阶段,它只返回那些存在重复"ID“的结果。然后,您将迭代游标,并使用“散装”操作更新每个文档。
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()方法来执行请求。
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中删除具有特定条件的副本?的接受答案中所示
发布于 2020-11-14 18:56:09
我的解决方案也是使用聚合。您可以选择为聚合复制的字段。结果将是一个重复集合的列表。每个职位都将保持一组副本。您在列表上插入,忽略每个组的第一个元素来保存它,并删除其余元素。你为每一组副本都这么做。见下文:
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“来提高性能。
https://stackoverflow.com/questions/34722866
复制相似问题