我正在尝试理解(并修复)一个不属于我的代码,它使用 PyMongo。
我们要查找带有
_id==_id
的文档,在其列表 comments
中,带有 id==comment_id
或 id==PyObjectId(comment_id)
的评论。
对于该评论,我们想添加一个答案。
在代码中:
await db_collection.update_one(
filter=query, update=update, array_filters=array_filters
)
在哪里
query = {
"_id": _id,
"$or": [
{"comments": {"$elemMatch": {"id": comment_id}}},
{"comments": {"$elemMatch": {"id": PyObjectId(comment_id)}}},
],
}
update = {
"$set": {
"comments.$[cmt].answer": jsonable_encoder(reply)
}
}
array_filters = [{"cmt.id": comment_id}]
我的问题是
array_filters
只检查是否id==comment_id
而不是id == PyObjectId(comment_id)
,就像查询一样。
这样当我有一个PyObjectId
作为id时,没有项目被更新。
我想我应该用类似的东西修改
array_filters
array_filters = [{"cmt.id": comment_id}, {"cmt.id": PyObjectId(comment_id)}]
或
array_filters = [{"$or$": [{ "cmt.id": comment_id}, {"cmt.id": PyObjectId(comment_id)}]}
但遗憾的是,我只能在生产环境中测试我的代码,并且我试图在破坏之前了解它的实际工作原理。
谢谢大家!
让我们看看这里发生了什么......
来自原始问题:
query = {
"_id": _id,
"$or": [
{"comments": {"$elemMatch": {"id": comment_id}}},
{"comments": {"$elemMatch": {"id": PyObjectId(comment_id)}}},
],
}
update = {
"$set": {
"comments.$[cmt].answer": jsonable_encoder(reply)
}
}
array_filters = [{"cmt.id": comment_id}]
查询使用的是
"$elemMatch"
,但由于单个字段只有一个条件,因此可以简化为:
query = {
"_id": _id,
"comments.id": {
"$in": [comment_id, PyObjectId(comment_id)]
},
}
如果我们假设(这里似乎有道理,但我们应该小心)
"id"
在"comments"
数组中是唯一的,那么"arrayFilters"
也不是必要的。所以update
可以简化为:
update = {
"$set": {
"comments.$.answer": jsonable_encoder(reply)
}
}
这里
$
使用query
中的元素“匹配”并替换第一个,仅第一个,匹配"comments"
中的元素为update
。如果 "comments"
数组的多个元素应该更新,这将不起作用(如果应该更新多个元素,请参见下面的 "arrayFilters"
的使用,因为 "id"
是 not 唯一的)。
因此,将所有这些与我们所做的假设放在一起:
query = {
"_id": _id,
"comments.id": {
"$in": [comment_id, PyObjectId(comment_id)]
},
}
update = {
"$set": {
"comments.$.answer": jsonable_encoder(reply)
}
}
# possibly some other code here from the original app ...
await db_collection.update_one(
filter=query, update=update
)
在 mongoplayground.net 上查看它如何处理伪造的文档。仅更新一个元素,即使存在重复的
"id"
值。
"arrayFilters"
如果
"id"
在"comments"
数组中不是唯一的并且应该更新数组中的多个对象,那么可以使用"arrayFilters"
来做到这一点。
query = {
"_id": _id,
"comments.id": {
"$in": [comment_id, PyObjectId(comment_id)]
},
}
update = {
"$set": {
"comments.$[cmt].answer": jsonable_encoder(reply)
}
}
array_filters = [
{
"cmt.id": {
"$in": [comment_id, PyObjectId(comment_id)]
}
}
]
# possibly some other code here from the original app ...
await db_collection.update_one(
filter=query, update=update, array_filters=array_filters
)
在这里,任何与
"comments"
“匹配”的"arrayFilter"
数组元素都将被更新。在 mongoplayground.net 上查看它是如何工作的。所有与"arrayFilter"
“匹配”的元素都会更新。