【问题标题】:Bulk insert mapped array using pymongo fails due to BulkWriteError由于 BulkWriteError,使用 pymongo 批量插入映射数组失败
【发布时间】:2025-12-09 04:10:01
【问题描述】:

我正在尝试使用 python 库 pymongo 在 MongoDB 中批量插入文档。

import pymongo
def tryManyInsert():
    p = {'x' : 1, 'y' : True, 'z': None}
    mongoColl = pymongo.MongoClient('localhost', 27017)['test']['multiIn']
    mongoColl.insert_many([p for i in range(10)])
tryManyInsert()

但由于BulkWriteError,我一直失败。

Traceback (most recent call last):
    File "/prog_path/testMongoCon.py", line 9, in <module>
    tryManyInsert();
    File "/prog_path/testMongoCon.py", line 7, in tryManyInsert
mongoColl.insert_many([p for i in range(10)])
    File "/myenv_path/lib/python3.6/site-packages/pymongo/collection.py", line 724, in insert_many
blk.execute(self.write_concern.document)
    File "/myenv_path/lib/python3.6/site-packages/pymongo/bulk.py", line 493, in execute
return self.execute_command(sock_info, generator, write_concern)
    File "/myenv_path/lib/python3.6/site-packages/pymongo/bulk.py", line 331, in execute_command
raise BulkWriteError(full_result)
    pymongo.errors.BulkWriteError: batch op errors occurred

我试图在没有_id 的情况下按顺序仅插入 10 个文档,因此answer / discussion 中的条件不适用于此处。类似的question 没有答案。

我试过pymongo 3.4pymongo 3.5.1,都给出了同样的错误。我在python3.6mongodb 3.2.10。 我在这里做错了什么?

【问题讨论】:

    标签: python mongodb python-3.x pymongo


    【解决方案1】:

    Python 仍然将 p 称为每个数组成员的相同事物。对于每个数组成员,您需要 copy()p

    import pymongo
    from copy import copy
    def tryManyInsert():
        p = {'x' : 1, 'y' : True, 'z': None}
        mongoColl = pymongo.MongoClient('localhost', 27017)['test']['multiIn']
        mongoColl.insert_many([copy(p) for i in range(10)])
    tryManyInsert()
    

    甚至简单地说:

        mongoColl.insert_many([{ 'x': 1, 'y': True, 'z': None } for i in range(10)])
    

    除非您这样做,否则 _id 只会被分配 一次,并且您只是在 insert_many() 的参数中使用相同的 _id 重复“相同的文档”。因此重复键的错误。

    作为一个快速演示:

    from bson import ObjectId
    
    p = { 'a': 1 }
    
    def addId(obj):
      obj['_id'] = ObjectId()
      return obj
    
    docs = map(addId,[p for i in range(2)])
    print docs
    

    给你:

    [
      {'a': 1, '_id': ObjectId('59fbc4a16cb6b30bdb3de0fd')}, 
      {'a': 1, '_id': ObjectId('59fbc4a16cb6b30bdb3de0fd')}
    ]
    

    或者更简洁:

    p = { 'a': 1 }
    
    def addKey(x):
      x[0]['b'] = x[1]
      return x[0]
    
    docs = map(addKey,[[p,i] for i,p in enumerate([p for i in range(3)])])
    print docs
    

    给予:

    [{'a': 1, 'b': 2}, {'a': 1, 'b': 2}, {'a': 1, 'b': 2}]
    

    这清楚地表明传递的索引值覆盖了传入的相同值。

    但是使用copy() 来获取值的副本:

    from bson import ObjectId
    
    p = { 'a': 1 }
    
    def addId(obj):
      obj['_id'] = ObjectId()
      return obj
    
    docs = map(addId,[copy(p) for i in range(2)])
    print docs
    

    给你:

    [
      {'a': 1, '_id': ObjectId('59fbc5466cb6b30be4d0fc00')},
      {'a': 1, '_id': ObjectId('59fbc5466cb6b30be4d0fc01')}
    ]
    

    或者我们的基础演示:

    p = { 'a': 1 }
    
    def addKey(x):
      x[0]['b'] = x[1]
      return x[0]
    
    docs = map(addKey,[[p,i] for i,p in enumerate([copy(p) for i in range(3)])])
    print docs
    

    返回:

    [{'a': 1, 'b': 0}, {'a': 1, 'b': 1}, {'a': 1, 'b': 2}]
    

    所以这基本上就是 python 的工作方式。如果您实际上并没有刻意分配一个新值,那么您所做的只是返回相同的引用值并简单地更新循环中的每个引用值,而不是生成一个“新值”。

    【讨论】:

    • 是的,谢谢您的解释。我应该更仔细地阅读docs 上面写着When a document is inserted a special key, "_id", is automatically added if the document doesn’t already contain an "_id" key.
    • @parthasarathy 嗯,这是其中的一部分,但这里真正的重点是 python 如何处理对象分配。所以这里的关键区别是“复制”而不是“相同的引用”被映射到数组中。