【问题标题】:Why do pymongo generate a duplicate _id value when using insert_one for different objects?为什么 pymongo 在对不同对象使用 insert_one 时会生成重复的 _id 值?
【发布时间】:2020-04-22 20:57:33
【问题描述】:

我正在尝试使用 pymongo 将对象保存到 mongodb。我对第一个对象没有任何问题,但是在尝试保存第二个对象时我得到pymongo.errors.DuplicateKeyError: E11000 duplicate key error collection: km_tracker.entries index: _id_ dup key: { : ObjectId('5b8ce80ebb822e06c8ecf1c7') }

我的保存功能:

def save_entries(entries):
    entries['save_date'] = str(datetime.datetime.now())
    db.entries.insert_one(entries)

回溯:

Traceback (most recent call last):
  File "app.py", line 182, in <module>
    main();
  File "app.py", line 22, in main
    new_entry()
  File "app.py", line 77, in new_entry
    review_information(entries)
  File "app.py", line 178, in review_information
    save_entries(entries)
  File "app.py", line 93, in save_entries
    db.entries.insert_one(entries)
  File "C:\Users\someuser\AppData\Local\Programs\Python\Python37\lib\site-packages\pymongo\collection.py", line 693, in insert_one
    session=session),
  File "C:\Users\someuser\AppData\Local\Programs\Python\Python37\lib\site-packages\pymongo\collection.py", line 607, in _insert
    bypass_doc_val, session)
  File "C:\Users\someuser\AppData\Local\Programs\Python\Python37\lib\site-packages\pymongo\collection.py", line 595, in _insert_one
    acknowledged, _insert_command, session)
  File "C:\Users\someuser\AppData\Local\Programs\Python\Python37\lib\site-packages\pymongo\mongo_client.py", line 1243, in _retryable_write
    return self._retry_with_session(retryable, func, s, None)
  File "C:\Users\someuser\AppData\Local\Programs\Python\Python37\lib\site-packages\pymongo\mongo_client.py", line 1196, in _retry_with_session
    return func(session, sock_info, retryable)
  File "C:\Users\someuser\AppData\Local\Programs\Python\Python37\lib\site-packages\pymongo\collection.py", line 592, in _insert_command
    _check_write_command_response(result)
  File "C:\Users\someuser\AppData\Local\Programs\Python\Python37\lib\site-packages\pymongo\helpers.py", line 217, in _check_write_command_response
    _raise_last_write_error(write_errors)
  File "C:\Users\someuser\AppData\Local\Programs\Python\Python37\lib\site-packages\pymongo\helpers.py", line 198, in _raise_last_write_error
    raise DuplicateKeyError(error.get("errmsg"), 11000, error)
pymongo.errors.DuplicateKeyError: E11000 duplicate key error collection: km_tracker.entries index: _id_ dup key: { : ObjectId('5b8ce6adbb822e40d431d444') }

第一个对象,成功保存到数据库中:

{
"_id" : ObjectId("5b8ce6adbb822e40d431d444"),
"reg_number" : "dfg",
"date" : "dfg",
"b_meter_indication" : "dfg",
"end_meter_indication" : "dfg",
"trip" : "dfg",
"start_address" : "dfg",
"stop_address" : "dfg",
"reason" : "dfg",
"driver" : "dfg",
"other" : "dfg",
"save_date" : "2018-09-03 09:45:49.340871"
}

第二个对象,由于key重复而没有保存:

{'_id': ObjectId('5b8ce6adbb822e40d431d444'),
 'b_meter_indication': 'rty',
 'date': 'rty',
 'driver': 'rty',
 'end_meter_indication': 'rty',
 'other': 'rty',
 'reason': 'rty',
 'reg_number': 'try',
 'save_date': '2018-09-03 09:46:02.246101',
 'start_address': 'rty',
 'stop_address': 'rty',
 'trip': 'rty'
}

由于我没有明确定义 _id 的值,而是让 pymongo 为我执行此操作,我不明白为什么它会将 _id 的先前值分配给我当前的对象。会不会是 pymongo 出于某种原因在这种情况下认为第二个对象与第一个对象相同,从而赋予它相同的 _id 值?

Python 版本:3.7.0 MongoDB 4.0 版 PyMongo 版本:3.7.1

编辑:使用 save_entries() 函数添加的函数

def edit_entry(entries):
    print("Editing: {}".format(entries))
    entry = input()
    return entry

def review_information(entries):
    print("Do you wish to edit something? (y/n)")
    while edit != False or edit != False:
        edit = input()

        if edit == "Y" or edit == "y":
            edit_entry(entries)
        elif edit == "N" or edit == "n":
            break
        else:
            print("Please provide a valid input")
            continue
    save_entries(entries)

【问题讨论】:

  • 那是因为您无法使用相同的ObjectId 保存对象,如果您想保存现有对象,请尝试更新它。
  • 正如我在帖子中所写:“因为我没有明确定义 _id 的值,而是让 pymongo 为我做这件事……”
  • 你能分享你调用save_entries的代码吗?我不相信 mongo 可重复生成相同的密钥。
  • 使用save_entries更新了代码

标签: python mongodb pymongo


【解决方案1】:

我没有明确定义_id的值,而是让pymongo为我做这件事

这是因为当使用 insert_one()insert_many()bulk_write() 将文档插入 MongoDB 时,并且该文档不包含 _id 字段,PyMongo 会自动为您添加一个,设置为ObjectId 的一个实例。

另见FAQ: Why does PyMongo add an _id field to all of my documents?

我对第一个对象没有任何问题,但是在尝试保存第二个对象时,我得到了 pymongo.errors.DuplicateKeyError

Python 没有显式的按值传递和按引用传递语义,而是按名称传递值。列表和字典是可变对象,数字、字符串和元组是不可变对象。

在您的情况下,基本上您将相同的 entries 对象传递给 save_entries() 函数。 PyMongo insert_one() 通过添加 _id 字段修改了对象,然后您尝试再次保存。然而,第二次保存已经包含 _id 字段,导致重复错误。

有几种方法可以处理这种情况:

  • 在传递给save_entries()之前明确copy()对象。
  • 在每次调用save_entries() 后从对象中删除_id 键。即del entries["_id"]

【讨论】:

  • 感谢您的回复。我目前没有机会尝试您的建议,但我会尽快回复。我也在考虑它仍然是同一个对象,但是交换了值。至于“我没有明确定义 _id 的值,而是让 pymongo 为我做这件事”部分,对我来说这不是问题,而是我期望 PyMongo 的行为方式。
【解决方案2】:

我也有同样的问题,但我现在解决了。

就我而言,我在 for 循环中创建了一个新的 dict。首先在循环判断条件中,如果日期是我需要的,我将 insert_one 插入 MongoDB 数据库,但是 Pymongo 认为它是同一个对象,即使文档中完全不同,因为 dict 具有相同的 memeory id 所以它没有t 创建一个新的“_id”。所以我需要把 dict 从循环的地方放到 if 的地方。

for itemin items:
i = 1
while i < len(item['a']):
    if (item['a'][i-1]['date'] < item['a'][i]['date']:
        temp= {}     #this one
        ...
        db.collection.insert_one(temp)
    i +=1

【讨论】:

    猜你喜欢
    • 2014-08-18
    • 2015-03-21
    • 1970-01-01
    • 2018-12-31
    • 1970-01-01
    • 1970-01-01
    • 2020-01-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多