为什么使用pymongo时db.insert(dict)会在字典中添加_id键

15 投票
5 回答
13833 浏览
提问于 2025-04-18 11:19

我在使用pymongo,代码是这样的:

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

这段代码在控制台上打印出:

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

我知道_mongo会自动给文档加上一个_id字段。但是,为什么这个_id也会出现在我的Python字典里呢?我并没有想这样做。我想知道这样做的目的是什么?我可能还会用这个字典做其他事情,而在插入文档的时候,它却被更新了?如果我想把这个字典转换成一个json对象,我会遇到一个

ObjectId('53ad610106998f0772adc6cb') is not JSON serializable

错误。插入函数难道不应该在把文档放进数据库时保持字典的值不变吗?

5 个回答

-2

很明显,文档已经回答了你的问题。

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的信息。

0

这个问题可以通过使用 copy 模块来解决。这样可以把字典的一个副本传给 pymongo,而不改变原来的字典。根据你例子中的代码片段,可以这样修改:

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

正如@BorrajaX已经回答的那样,我想再补充一些内容。_id是一个唯一的标识符,当你把一个文档插入到集合中时,它会生成一些随机的数字。你可以选择自己设置一个id,或者使用MongoDB为你生成的那个。

关于这个,文档中也有提到。

在你的情况下,如果你想忽略这个键,可以使用del关键字,像这样:del a["_id"]

或者

如果你需要_id来进行后续操作,可以使用bson模块中的dumps。

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)
0

_id 在文档中充当主键,这和SQL数据库不一样,在mongodb中这是必须的。

为了让 _id 可以被序列化,你有两个选择:

  1. 在插入文档之前,把 _id 设置为可以被JSON序列化的数据类型(比如 intstr),但要记住,每个文档的 _id 必须是唯一的。

  2. 使用自定义的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)
    
1

就像其他很多数据库系统一样,Pymongo在你把数据插入数据库时,会自动添加一个唯一的标识符,这样你就可以从数据库中检索到这些数据。想象一下,如果你插入了两个内容完全相同的字典,比如 {'key1':'value1'},那么你怎么知道你想要的是哪个呢?

这个过程在 Pymongo的文档中有解释:

当一个文档被插入时,如果这个文档里没有"_id"这个键,系统会自动添加一个特殊的键"_id"。这个"_id"的值在整个集合中必须是唯一的。

如果你想改变这个行为,可以在插入之前给对象设置一个 _id 属性。但我认为这样做不好,因为这很容易导致冲突,你可能会失去一些重要的信息,比如一个“真实”的 ObjectId 中存储的 创建时间,这些信息对于排序等操作非常有用。

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

如果你的问题出现在将数据转换为Json格式时,你可以使用BSON模块中的 工具

>>> 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)

撰写回答