我将mongoengine
与具有Document
属性的EmbeddedDocumentListField
一起使用。
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
和版本“ current”来更新每个child_type
文档,并将其更改为版本“ old”。然后,将Child
的“新”版本更改为“当前”版本。
这是我到目前为止尝试过的:
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查询,我想对其进行“翻译”以在mongoengine中使用它:
db.parent.updateMany(
{"children.child_type": "type1", "children.version": "current"},
{"$set": {"children.$[element].version": "old"}},
{arrayFilters: [{"element.child_type": "type1", "element.version": "current"}]}
)
NB:我不认为这是重复的,因为我发现的大多数问题都与给定id的特定EmbeddedDocument更新有关。在这里,我要更新每个EmbeddedDocument,而不对父项进行任何过滤。
答案 0 :(得分:0)
找不到一种方法来处理单个查询,因此我通过逐个更新每个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")
编辑:查看我的其他答案,以获得更高效(但有点怪癖)的解决方案
答案 1 :(得分:0)
比我之前建议的解决方案更有效的解决方案是通过获取"~/Dropbox/sample/music".replace("~", os.homedir)
的链接collection
对象来运行原始查询:
Parent
与逐个更新每个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")
实例相比,这是执行方法更快的方法!
对应的是,尽管有一天might be made public,但我正在使用未记录的Child
方法。