我有一个包含字段的文档的集合,称之为field1
,我想在每个fxn
条目上调用(复杂的)python函数field1
并将其存储在一个新的field2
。我的集合非常大,fxn
需要几秒钟才能运行,所以我希望在一些工作中将其并行化。到目前为止,这是我的方法:
for i, entry in enumerate(collection.find().sort('_id')):
if i % nJobs != jobID: continue
field1 = entry['field1']
field2 = fxn(field1)
collection.update({'_id': entry['_id']}, {'$set': {'field2':field2})
其中nJobs
是作业总数,jobID
是当前作业的索引(例如,我说我并行运行此脚本5次,然后nJobs=5
和{{1 }范围从0到4)
是否有更快或更可靠的方法来实现它?我希望将所有内容保存在python中,因为jobID
需要保存在python中。
答案 0 :(得分:1)
您基本上需要使用 Bulk API ,在for循环中,您可以利用写入命令Bulk API来执行批量更新操作,这些操作很简单服务器顶部的抽象,以便轻松构建批量操作。这些批量操作主要有两种形式:
这非常有效,因为您没有发送"每个"请求到服务器,但每1000个请求只有一次,api实际上为你排除了这个问题。请注意,对于比2.6更旧的服务器,API将下转换操作。但是,不可能将100%下转换,因此可能存在一些无法正确报告正确数字的边缘情况。
在非分片群集上实施此功能需要使用 snapshot
参数,以便您可以将查找光标与更新后的相同文档隔离开来:
bulk = db.collection.initialize_ordered_bulk_op()
counter = 0;
for entry in collection.find(snapshot = True):
# process in bulk
# calc field2 value first
field2 = fxn(entry.field1)
bulk.find({ '_id': entry._id }).update({ '$set': { 'field2': field2 } })
counter++
if ( counter % 1000 == 0 ):
bulk.execute()
bulk = db.collection.initialize_ordered_bulk_op()
if (counter % 1000 != 0):
bulk.execute()