Skip to main content
 首页 » 编程设计

python之pymongo 保存嵌入的 objectIds,InvalidDocumentError

2025年05月04日18lvdongjie

使用pymongo driver bare 连接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”的值将代表该单个文档对特定问题的回答。

像这样应用 objectids 不会编码到 bson 然后失败的原因是什么?像这样在文档中嵌套 ObjectId 以进行交叉引用是不可能的吗?我是否误解了它们的用途?

请您参考如下方法:

BSON spec规定键必须是字符串,因此 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 进行高效搜索(依此类推)。