为啥 db.insert(dict) 在使用 pymongo 时将 _id 键添加到 dict 对象

Posted

技术标签:

【中文标题】为啥 db.insert(dict) 在使用 pymongo 时将 _id 键添加到 dict 对象【英文标题】:Why does db.insert(dict) add _id key to the dict object while using pymongo为什么 db.insert(dict) 在使用 pymongo 时将 _id 键添加到 dict 对象 【发布时间】:2014-08-18 13:51:33 【问题描述】:

我正在通过以下方式使用 pymongo:

from pymongo import *
a = 'key1':'value1'
db1.collection1.insert(a)
print a

打印出来

'_id': ObjectId('53ad61aa06998f07cee687c3'), 'key1': 'value1'

在控制台上。 我了解 _id 已添加到 mongo 文档中。但是为什么这也被添加到我的 python 字典中呢?我不打算这样做。我想知道这样做的目的是什么?我可以将此字典用于其他目的,并且字典会作为将其插入文档的副作用而更新?如果我不得不将这个字典序列化为一个 json 对象,我会得到一个

ObjectId('53ad610106998f0772adc6cb') is not JSON serializable

错误。在将文档插入数据库时​​,插入函数不应该保持字典的值相同。

【问题讨论】:

_id 是文档的主键,是必填字段。如果_id不存在,MongoDB会自动创建一个ObjectId作为_id。 docs.mongodb.org/manual/core/crud-introduction/…。实际上对象不是 json 它是 bson 我的问题不是为什么将_id添加到mongodb文档中。我的问题是为什么将这个键添加到我的 python 字典中,因为我不打算更改我的字典对象。我正在更新我的问题以更清楚地说明这一点。 key被添加是因为它是一个新的文档并且你发送到insert的参数是引用的。如果您不想要它,只需将其取消设置 我不明白。为什么和python团队有关?是 pymongo 将该 _id 添加到内存字典中(在插入之后)。据我所知,没有必要这样做。 【参考方案1】:

与许多其他数据库系统一样,Pymongo 将添加必要的唯一标识符,以便在插入数据库后立即从数据库中检索数据(如果您在数据库中插入两个具有相同内容 'key1':'value1' 的字典会发生什么情况?你如何区分你想要 this 一个而不是 那个 一个?)

这在Pymongo docs中有解释:

插入文档时,如果文档尚未包含“_id”键,则会自动添加一个特殊键“_id”。 “_id”的值在整个集合中必须是唯一的。

如果你想改变这种行为,你可以在插入之前给对象一个_id 属性。在我看来,这是一个坏主意。这很容易导致冲突,并且您会丢失存储在“真实”ObjectId 中的多汁信息,例如 creation time,这对于排序和类似的事情非常有用。

>>> a = '_id': 'hello', 'key1':'value1'
>>> collection.insert(a)
'hello'
>>> collection.find_one('_id': 'hello')
u'key1': u'value1', u'_id': u'hello'

或者如果您在序列化为 Json 时出现问题,您可以在 BSON 模块中使用 utilities:

>>> a = 'key1':'value1'
>>> collection.insert(a)
ObjectId('53ad6d59867b2d0d15746b34')
>>> from bson import json_util
>>> json_util.dumps(collection.find_one('_id': ObjectId('53ad6d59867b2d0d15746b34')))
'"key1": "value1", "_id": "$oid": "53ad6d59867b2d0d15746b34"'

(您可以在jsonlint.com等页面中验证这是有效的json)

【讨论】:

【参考方案2】:

_id 充当文档的主键,与 SQL 数据库不同,它在 mongodb 中是必需的。

要使_id 可序列化,您有两种选择:

    在插入文档之前将_id 设置为可序列化的JSON 数据类型(例如intstr),但请记住,每个文档它必须是唯一的。

    使用自定义 BSON 序列化编码器/解码器类:

    from bson.json_util import default as bson_default
    from bson.json_util import object_hook as bson_object_hook
    
    class BSONJSONEncoder(json.JSONEncoder):
        def default(self, o):
            return bson_default(o)
    
    
    class BSONJSONDecoder(json.JSONDecoder):
        def __init__(self, **kwrgs):
            JSONDecoder.__init__(self, object_hook=bson_object_hook)
    

【讨论】:

【参考方案3】:

作为@BorrajaX 回答已经想添加更多。 _id 是一个唯一标识符,当将文档插入到集合中时,它会生成一些随机数。您可以设置自己的 id,也可以使用 MongoDB 为您创建的。

正如documentation 提到的那样。

对于您的情况,您可以使用 del 关键字 del a["_id"] 忽略此键。

如果您需要_id 进行进一步操作,您可以使用来自 bson 模块的转储。

import json
from bson.json_util import loads as bson_loads, dumps as bson_dumps 

a["_id"]=json.loads(bson_dumps(a["_id"]))

在插入文档之前,您可以添加您的自定义_id,您不需要序列化您的字典

a["_id"] = "some_id"

db1.collection1.insert(a)

【讨论】:

【参考方案4】:

可以使用copy 模块规避此行为。这会将字典的副本传递给 pymongo,保持原件不变。根据您示例中的代码 sn-p ,应该像这样修改它:

import copy
from pymongo import *
a = 'key1':'value1'
db1.collection1.insert(copy.copy(a))
print a

【讨论】:

【参考方案5】:

docs 显然回答了你的问题

MongoDB 以BSON 序列化格式将文档存储在磁盘上。 BSONJSON 文档的二进制表示,尽管它包含比 JSON 更多的数据类型。

字段的值可以是任何 BSON 数据类型,包括其他文档、数组和文档数组。以下文档包含不同类型的值:

var mydoc = 
               _id: ObjectId("5099803df3f4948bd2f98391"),
               name:  first: "Alan", last: "Turing" ,
               birth: new Date('Jun 23, 1912'),
               death: new Date('Jun 07, 1954'),
               contribs: [ "Turing machine", "Turing test", "Turingery" ],
               views : NumberLong(1250000)
            

了解更多关于BSON

【讨论】:

您所指的文档是mongodb的。我在这里谈论 pymongo,我的问题不是关于 mongo 文档,而是关于 python 字典对象。

以上是关于为啥 db.insert(dict) 在使用 pymongo 时将 _id 键添加到 dict 对象的主要内容,如果未能解决你的问题,请参考以下文章

为啥我不能在 python 中使用列表作为 dict 键?

pybind11:使用嵌套的 py::dict 和 py::list?

Discord.py 我不能在 python 中使用 dict 来保存用户数据

廖雪峰py笔记5之dict

如何将 py_func 与返回 dict 的函数一起使用

为啥 dict(k=4, z=2).update(dict(l=1)) 在 Python 中返回 None?