使用 MongoEngine 批量更新文档的最有效方法

问题描述 投票:0回答:2

所以,我有一个以这种方式构造的文件集合(例如

Person
):

class Person(Document):
    name = StringField(max_length=200, required=True)
    nationality = StringField(max_length=200, required=True)
    earning = ListField(IntField())

当我保存文档时,我只输入

name
nationality
字段,因为这是信息。

然后,时不时地,我想更新特定国籍的每个人的收入。让我们想象一下,有一些公式可以让我计算收入领域(例如,我查询了一些名为

EarningAPI
的神奇api,它返回给定
earning
的人的
name
)。

要更新它们,我会做类似的事情:

japanese_people = Person.objects(Q(nationality='Japanese'))).all()
for japanese_person in japanese_people:
    japanese_person.earning.append(EarningAPI(japanese_person.name))

Person.objects.insert(japanese_people, load_bulk=False) 

EarningAPI 也可以批量工作,这样我就可以给出一个名字列表,它会返回一个收入列表(每个名字一个)。这种方法速度更快,成本更低。

一条一条的正确吗?利用批次的最佳方式是什么?

谢谢

python mongodb orm batch-processing mongoengine
2个回答
1
投票

使用 Mongoengine 批量更新的方法,无需对象。更新()

from pymongo import UpdateOne
from mongoengine import Document, ValidationError

class Person(Document):
    name = StringField(max_length=200, required=True)
    nationality = StringField(max_length=200, required=True)
    earning = ListField(IntField())

japanese_people = Person.objects(Q(nationality='Japanese')).all()

japanese_ids = [person.id for person in japanese_people]
earnings = EarningAPI(japanese_ids) 
# I'm assuming it takes a list of id's as input and returns a list of earnings. 

bulk_operatons = [
    UpdateOne(
        {'_id': j_id},
        {'$set': {'earning': earn}},
        upsert=True
    ),
    for j_id, earn in zip(japanese_ids, earnings)
]

result = Person._get_collection().bulk_write(bulk_operations, ordered=False)

我不能确定这是否比一个一个的方法更快,因为我无法访问你的神奇 API 来进行基准测试,但这应该是批量进行的方法。


-1
投票

一个一个地调用和更新API是正确的方法,是的,但是分批进行也是正确的。这两种方法各有利弊,即一对一方法需要更简单的代码,更易于阅读和维护,但性能较慢,而批处理方法将更复杂,更难编写,但会通过减少 API 请求开销来提高性能(对于 n 的批量大小,您的 API 请求开销减少到一对一方法开销的大约 1/n)。

您应该采用哪种方法取决于多种因素,包括您的问题集的大小(考虑问题的大小是否会随着时间的推移而增加以及增加多少),您是否正在获取 API来自的数据有配额或速率限制,您的应用程序可以同时处理多少数据,也许还有许多其他因素。通过考虑这些因素,您可以确定您是否甚至需要进行批量调用以及您应该使用的批量大小。

一个简单的伪代码示例看起来像这样:

people = Person.get({nationality: 'Japanese'})
for(i = 0; i < people.length; i += batch_size) {
    people_batch = []
    names = []
    for(j = i; j < min(i + batch_size, people.length); j++) {
        people_batch.append(people[j])
        names.append(people[j].name)
    }

    earnings = EarningAPI(names)

    for(k = 0; k < people_batch.length; k++) {
        people_batch[k].earning.append(earnings[k])
    }

    Person.update(people_batch)
}

上面的例子是一个更通用的解决方案,可以处理可能更大的数据量。另请注意,批量大小为 1 的功能与您的一对一代码大致相同。

如果你的数据集足够小,那么你可以简单地批量拉取API数据并同时更新所有

Person
条目:

people = Person.get({nationality: 'Japanese'})
for(i = 0; i < people.length; i += batch_size) {
    names = []
    for(j = i; j < min(i + batch_size, people.length); j++) {
        names.append(people[j].name)
    }

    earnings = EarningAPI(names)

    for(k = 0; k < names.length; k++) {
        people[i + k].earning.append(earnings[k])
    }
}

Person.update(people)

由于我不精通Python,而且这个问题的未知数太多,我无法写出Python特定的解决方案,但我希望以上解决方案结构足以帮助您实现最适合您用例的解决方案。

© www.soinside.com 2019 - 2024. All rights reserved.