如果符合条件,则更新 EmbeddedDocumentListField 中的所有 EmbeddedDocuments
Update all EmbeddedDocuments in EmbeddedDocumentListField if it matches criteria
我正在使用 mongoengine
和具有 EmbeddedDocumentListField
属性的 Document
。
class Child(mongoengine.EmbeddedDocument):
value = mongoengine.IntField(required=True)
child_type = mongoengine.StringField(required=True, choices=["type1", "type2", "type3"], unique_with=["version"])
version = mongoengine.StringField(required=True, choices=["old", "current", "new"])
class Parent(mongoengine.Document):
children = mongoengine.EmbeddedDocumentListField(Child)
我正在用这种方式填充我的数据库:
def populate():
# for each child_type
for child_type in ["type1", "type2", "type3"]:
for parent_id, value in compute_stuff(child_type):
# create a new Child embedded document with version "new" and append it to the corresponding Parent
parent = Parent.get(parent_id)
child = Child(value=value, child_type=child_type, version="new")
parent.children.append(child)
parent.save()
update_versions(child_type)
现在,我遇到的问题是我的 update_versions
函数。基本上,我想用当前 child_type
和版本 "current" 更新每个 Child
文档,并将其更改为版本 "old"。然后,通过将版本 "new" 的 Child
更改为版本 "current".
来执行相同的操作
这是我目前尝试过的方法:
def update_versions(child_type):
# update "current" to "old"
Parent.objects(
children__version="current",
children__child_type=child_type
).update(set__children__S__version="old")
# update "new" to "current"
Parent.objects(
children__version="new",
children__child_type=child_type
).update(set__children__S__version="current")
不幸的是,更新没有正确完成,因为我尝试制作的 child_type
上的过滤器似乎没有完成。这是我在数据库中得到的结果:
> // 1. before first populating -> OK
> db.parent.find({"_id": 1}).pretty()
{
"_id" : 1,
"children" : [ ]
}
> // 2. after first populating of type1 -> OK
> db.parent.find({"_id": 1}).pretty()
{
"_id" : 1,
"children" : [
{
"value" : 1,
"child_type": "type1",
"version": "new"
}
]
}
> // 3. after updating versions -> OK
> db.parent.find({"_id": 1}).pretty()
{
"_id" : 1,
"children" : [
{
"value" : 1,
"child_type": "type1",
"version": "current" // <- this is OK
}
]
}
> // 4. after first populating of type2 -> OK
> db.parent.find({"_id": 1}).pretty()
{
"_id" : 1,
"children" : [
{
"value" : 1,
"child_type": "type1",
"version": "current" // <- this is OK
},
{
"value" : 17,
"child_type": "type2",
"version": "new" // <- this is OK
}
]
}
> // 5. after updating versions (only "current" to "old") -> NOT OK
> db.parent.find({"_id": 1}).pretty()
{
"_id" : 1,
"children" : [
{
"value" : 1,
"child_type": "type1",
"version": "old" // <- this is NOT OK, expecting to stay "current"
},
{
"value" : 17,
"child_type": "type2",
"version": "new" // <- this is OK
}
]
}
我错过了什么?
编辑:这个查询似乎符合我的要求,但这是一个原始的 Mongo 查询,我想 "translate it" 将它与 mongoengine 一起使用:
db.parent.updateMany(
{"children.child_type": "type1", "children.version": "current"},
{"$set": {"children.$[element].version": "old"}},
{arrayFilters: [{"element.child_type": "type1", "element.version": "current"}]}
)
注意:我不认为这是重复的,因为我发现的大多数问题都是关于更新特定的 EmbeddedDocument,给定它的 ID。在这里,我想更新每个 EmbeddedDocument,而不对父级进行任何过滤。
没有找到让它与单个查询一起工作的方法,所以我通过一个一个地更新每个 Child
实例来做到这一点:
def update_versions(child_type):
def _update_from_to(current_version, new_version):
# find all the Parents with a matching Child
parents_to_update = Parent.objects(
children__version=current_version,
children__child_type=child_type
)
for parent in parents_to_update:
# find the matching Child in the children list
for child in parent.children:
if (child.version == current_version and
child.child_type == child_type):
# and update it
child.version = new_version
break
# each parent is updated one by one, this is not efficient...
parent.save()
_update_from_to("current", "old")
_update_from_to("new", "current")
编辑:查看我的其他答案以获得更有效(但有点老套)的解决方案
比我之前建议的解决方案更有效的解决方案是 运行 通过获取 Parent
的链接 collection
对象的原始查询:
def update_versions(child_type):
def _update_from_to(current_version, new_version):
Parent._get_collection().update_many(
filter={
"children.child_type": child_type,
"children.version": current_version
},
update={
"$set": {"children.$[element].version": new_version}
},
array_filters=[{
"element.child_type": child_type,
"element.version": current_version
}],
upsert=False
)
_update_from_to("current", "old")
_update_from_to("new", "current")
这比一个一个地更新每个 Child
实例要快得多!
对应的是我正在使用未记录的 _get_collection
方法,尽管它有一天 might be made public。
我正在使用 mongoengine
和具有 EmbeddedDocumentListField
属性的 Document
。
class Child(mongoengine.EmbeddedDocument):
value = mongoengine.IntField(required=True)
child_type = mongoengine.StringField(required=True, choices=["type1", "type2", "type3"], unique_with=["version"])
version = mongoengine.StringField(required=True, choices=["old", "current", "new"])
class Parent(mongoengine.Document):
children = mongoengine.EmbeddedDocumentListField(Child)
我正在用这种方式填充我的数据库:
def populate():
# for each child_type
for child_type in ["type1", "type2", "type3"]:
for parent_id, value in compute_stuff(child_type):
# create a new Child embedded document with version "new" and append it to the corresponding Parent
parent = Parent.get(parent_id)
child = Child(value=value, child_type=child_type, version="new")
parent.children.append(child)
parent.save()
update_versions(child_type)
现在,我遇到的问题是我的 update_versions
函数。基本上,我想用当前 child_type
和版本 "current" 更新每个 Child
文档,并将其更改为版本 "old"。然后,通过将版本 "new" 的 Child
更改为版本 "current".
这是我目前尝试过的方法:
def update_versions(child_type):
# update "current" to "old"
Parent.objects(
children__version="current",
children__child_type=child_type
).update(set__children__S__version="old")
# update "new" to "current"
Parent.objects(
children__version="new",
children__child_type=child_type
).update(set__children__S__version="current")
不幸的是,更新没有正确完成,因为我尝试制作的 child_type
上的过滤器似乎没有完成。这是我在数据库中得到的结果:
> // 1. before first populating -> OK
> db.parent.find({"_id": 1}).pretty()
{
"_id" : 1,
"children" : [ ]
}
> // 2. after first populating of type1 -> OK
> db.parent.find({"_id": 1}).pretty()
{
"_id" : 1,
"children" : [
{
"value" : 1,
"child_type": "type1",
"version": "new"
}
]
}
> // 3. after updating versions -> OK
> db.parent.find({"_id": 1}).pretty()
{
"_id" : 1,
"children" : [
{
"value" : 1,
"child_type": "type1",
"version": "current" // <- this is OK
}
]
}
> // 4. after first populating of type2 -> OK
> db.parent.find({"_id": 1}).pretty()
{
"_id" : 1,
"children" : [
{
"value" : 1,
"child_type": "type1",
"version": "current" // <- this is OK
},
{
"value" : 17,
"child_type": "type2",
"version": "new" // <- this is OK
}
]
}
> // 5. after updating versions (only "current" to "old") -> NOT OK
> db.parent.find({"_id": 1}).pretty()
{
"_id" : 1,
"children" : [
{
"value" : 1,
"child_type": "type1",
"version": "old" // <- this is NOT OK, expecting to stay "current"
},
{
"value" : 17,
"child_type": "type2",
"version": "new" // <- this is OK
}
]
}
我错过了什么?
编辑:这个查询似乎符合我的要求,但这是一个原始的 Mongo 查询,我想 "translate it" 将它与 mongoengine 一起使用:
db.parent.updateMany(
{"children.child_type": "type1", "children.version": "current"},
{"$set": {"children.$[element].version": "old"}},
{arrayFilters: [{"element.child_type": "type1", "element.version": "current"}]}
)
注意:我不认为这是重复的,因为我发现的大多数问题都是关于更新特定的 EmbeddedDocument,给定它的 ID。在这里,我想更新每个 EmbeddedDocument,而不对父级进行任何过滤。
没有找到让它与单个查询一起工作的方法,所以我通过一个一个地更新每个 Child
实例来做到这一点:
def update_versions(child_type):
def _update_from_to(current_version, new_version):
# find all the Parents with a matching Child
parents_to_update = Parent.objects(
children__version=current_version,
children__child_type=child_type
)
for parent in parents_to_update:
# find the matching Child in the children list
for child in parent.children:
if (child.version == current_version and
child.child_type == child_type):
# and update it
child.version = new_version
break
# each parent is updated one by one, this is not efficient...
parent.save()
_update_from_to("current", "old")
_update_from_to("new", "current")
编辑:查看我的其他答案以获得更有效(但有点老套)的解决方案
比我之前建议的解决方案更有效的解决方案是 运行 通过获取 Parent
的链接 collection
对象的原始查询:
def update_versions(child_type):
def _update_from_to(current_version, new_version):
Parent._get_collection().update_many(
filter={
"children.child_type": child_type,
"children.version": current_version
},
update={
"$set": {"children.$[element].version": new_version}
},
array_filters=[{
"element.child_type": child_type,
"element.version": current_version
}],
upsert=False
)
_update_from_to("current", "old")
_update_from_to("new", "current")
这比一个一个地更新每个 Child
实例要快得多!
对应的是我正在使用未记录的 _get_collection
方法,尽管它有一天 might be made public。