pymongo 保存嵌入对象ID,InvalidDocumentError
使用pymongo这个库来连接Python和MongoDB时,为什么用ObjectId实例作为嵌入文档的键会引发InvalidDocument错误呢?
我想用ObjectId来连接文档,但不明白为什么要把它们转换成字符串,因为驱动程序自动生成的就是ObjectId
实例。
item = collection.find({'x':'foo'})
item['otherstuff'] = {pymongo.objectid.ObjectId() : 'data about this link'}
collection.update({'x':'foo'}, item)
bson.errors.InvalidDocument: documents must have only string keys, key was ObjectId('4f0b5d4e764df61c67000000')
实际上,这些链接的ID代表的是包含问题的文档,而这里字典中以'otherstuff'为键的值则代表这个特定文档对那个问题的回答。
有没有什么原因导致像这样使用ObjectId不能编码成bson,然后就失败了?难道在文档中嵌套ObjectId以进行交叉引用是不可能的吗?我是不是误解了它们的用途?
1 个回答
7
BSON规范规定,键必须是字符串,所以PyMongo拒绝这种情况是正确的(无论ObjectId作为键出现在什么层级,顶层还是嵌套文档中,都是如此)。这样做的原因之一是为了确保查询语言不会产生歧义。想象一下,如果你有这样一个文档(并且它是一个有效的BSON文档):
{ _id: ...,
"4f0cbe6d7f40d36b24a5c4d7": true,
ObjectId("4f0cbe6d7f40d36b24a5c4d7"): false
}
然后你尝试用以下方式查询:
db.foo.find({"4f0cbe6d7f40d36b24a5c4d7": false})
这样应该返回这个文档吗?这个字符串应该自动转换成ObjectId吗?Mongo是怎么知道什么时候可以自动转换的,以及在像这个文档这样的情况下如何避免混淆的呢?
一个可能的解决方案是使用一个嵌套文档的数组,比如:
{ answers: [
{ answer_id: ObjectId("..."), summary: "Good answer to this question" },
{ answer_id: ObjectId("..."), summary: "Bad answer to this question" }
]
}
这也是有效的BSON,并且在索引方面会更高效。如果你在answers
上添加索引,就可以高效地搜索这些子文档的精确匹配;如果你在answers.answer_id
上添加索引,那么你就可以根据你想要的答案的ObjectId高效搜索(等等)。