如何使用 insert_many 安全地忽略重复的键错误

Posted

技术标签:

【中文标题】如何使用 insert_many 安全地忽略重复的键错误【英文标题】:How to Ignore Duplicate Key Errors Safely Using insert_many 【发布时间】:2017-12-03 22:20:36 【问题描述】:

在使用 pymongo 的 insert_many 时,我需要忽略重复插入,其中重复项基于索引。我在 *** 上看到过这个问题,但我没有看到有用的答案。

这是我的代码 sn-p:

try:
    results = mongo_connection[db][collection].insert_many(documents, ordered=False, bypass_document_validation=True)
except pymongo.errors.BulkWriteError as e:
    logger.error(e)

我希望 insert_many 忽略重复项而不抛出异常(这会填满我的错误日志)。或者,是否可以使用单独的异常处理程序,以便我可以忽略错误。我想念“w=0”...

谢谢

【问题讨论】:

即使使用ordered=False Bulk "inserts" 仍然会抛出错误,即使整个批次实际上都已提交。选项取决于您是否要 try .. except 并本质上“忽略”重复键错误,或者如果您真的不想这样做,那么请改用“upserts”。这确实需要在每个文档上有效地“查找”,但本质上是“不能”创建重复键。这就是它的工作原理。 如何忽略特定的“重复键”错误?我不想无意中忽略其他错误。 好吧 BuklWriteError 或 python 中的任何特定类(需要查找),并列出数组中的每个错误。这些条目有一个错误代码 E11000 在我的脑海中。简单地处理并忽略这些,当然对存在的任何其他代码都“扔/抱怨/记录/随便什么”。 这是错误字符串:“batch op errors occurred”,不是很具体。 亲爱的 S.M.Styvane, 是的,这个问题以前曾被问过,遗憾的是没有一个答案令人满意。因此,重新发布的原因。但在这种情况下,答案是正确且有用的。 【参考方案1】:

正确的解决方案是使用 w=0 和ordered=False 的WriteConcern:

import pymongo
from pymongo.write_concern import WriteConcern


mongodb_connection[db][collection].with_options(write_concern=WriteConcern(w=0)).insert_many(messages, ordered=False)

【讨论】:

【参考方案2】:

为 Neil 的解决方案添加更多内容。

拥有 'ordered=False, bypass_document_validation=True' 参数允许新的待处理插入发生,即使出现重复异常。

from pymongo import MongoClient, errors

DB_CLIENT = MongoClient()
MY_DB = DB_CLIENT['my_db']
TEST_COLL = MY_DB.dup_test_coll

doc_list = [
    
        "_id": "82aced0eeab2467c93d04a9f72bf91e1",
        "name": "shakeel"
    ,
    
        "_id": "82aced0eeab2467c93d04a9f72bf91e1",  # duplicate error: 11000
        "name": "shakeel"
    ,
    
        "_id": "fab9816677774ca6ab6d86fc7b40dc62",  # this new doc gets inserted
        "name": "abc"
    
]

try:
    # inserts new documents even on error
    TEST_COLL.insert_many(doc_list, ordered=False, bypass_document_validation=True)
except errors.BulkWriteError as e:
    print(f"Articles bulk insertion error e")

    panic_list = list(filter(lambda x: x['code'] != 11000, e.details['writeErrors']))
    if len(panic_list) > 0:
        print(f"these are not duplicate errors panic_list")

既然我们谈论的是重复,它也值得检查this solution。

【讨论】:

【参考方案3】:

您可以通过检查BulkWriteError 产生的错误来解决这个问题。这实际上是一个具有多个属性的“对象”。有趣的部分在details

import pymongo
from bson.json_util import dumps
from pymongo import MongoClient
client = MongoClient()
db = client.test

collection = db.duptest

docs = [ '_id': 1 ,  '_id': 1 , '_id': 2 ]


try:
  result = collection.insert_many(docs,ordered=False)

except pymongo.errors.BulkWriteError as e:
  print e.details['writeErrors']

在第一次运行时,这将给出e.details['writeErrors'] 下的错误列表:

[
   
    'index': 1,
    'code': 11000, 
    'errmsg': u'E11000 duplicate key error collection: test.duptest index: _id_ dup key:  : 1 ', 
    'op': '_id': 1
  
]

在第二次运行时,您会看到三个错误,因为所有项目都存在:

[
  
    "index": 0,
    "code": 11000,
    "errmsg": "E11000 duplicate key error collection: test.duptest index: _id_ dup key:  : 1 ", 
    "op": "_id": 1
   , 
   
     "index": 1,
     "code": 11000,
     "errmsg": "E11000 duplicate key error collection: test.duptest index: _id_ dup key:  : 1 ",
     "op": "_id": 1
   ,
   
     "index": 2,
     "code": 11000,
     "errmsg": "E11000 duplicate key error collection: test.duptest index: _id_ dup key:  : 2 ",
     "op": "_id": 2
   
]

所以你需要做的就是过滤数组中带有"code": 11000 的条目,然后只在有其他东西时“恐慌”

panic = filter(lambda x: x['code'] != 11000, e.details['writeErrors'])

if len(panic) > 0:
  print "really panic"

这为您提供了一种忽略重复键错误但当然要注意实际问题的机制。

【讨论】:

我不知道异常对象中的详细信息字段,谢谢! @vgoklani 这有点隐藏,甚至没有真正记录在案:(所以即使我也不得不去挖掘它,即使我“知道它在某个地方”。因此自从我上次 cmets 以来的延迟.

以上是关于如何使用 insert_many 安全地忽略重复的键错误的主要内容,如果未能解决你的问题,请参考以下文章

使用 Jackson 忽略 JSON 对象上的新字段 [重复]

Spring 安全忽略 url 不适用于我们的安全忽略方法 [重复]

如何在退出前安全地关闭所有线程[重复]

[pyMongo]insert_many的Bulkwrite实现机制

当我将它添加到指针时,我可以安全地忽略溢出检查的最小偏移量是多少?

我可以安全地 gitignore “._” 前缀文件吗