PyMongo通过游标更新带有计算字段的数组记录

时间:2014-01-21 16:28:12

标签: mongodb numpy pymongo

基本上,非常大的数据集的复杂聚合管道的集合输出类似于以下内容:

{
    "_id" : {
        "clienta" : NumberLong(460011766),
        "clientb" : NumberLong(2886729962)
    },
    "states" : [ 
        [ 
            "fixed", "fixed.rotated","fixed.rotated.off"

        ]
    ],
    "VBPP" : [ 
        244, 
        182,
        184,
        11,
        299,

    ],
    "PPF" : 72.4,   
}

在转换为数组之前,使用PyMongo更新这些字段以计算其以前的自身(数组的长度和方差)的直观(尽管很慢)方法如下:

records_list = []

cursor = db.clientAgg.find({}, {'_id' : 0,
                                      'states' : 1, 
                                      'VBPP' : 1, 
                                      'PPF': 1})
for record in cursor:
    records_list.append(record)

for dicts in records_list:
        dicts['states'] = len(dicts['states'])
        dicts['VBPP']  = np.var(dicts['VBPP'])

我已经编写了这种基本流程的各种形式来优化速度,但是在将它们转换为数组以通过机器学习估算器之前在内存中引入500k字典来修改它们是昂贵的。我已经尝试了各种方法直接通过游标更新记录,其中包含以下变体但没有成功:

cursor = db.clientAgg.find().skip(0).limit(50000)

def iter():
    for item in cursor:
        yield item

l = []
for x in iter():
    x['VBPP']  = np.var(x['VBPP']) 
    # Or    
    # db.clientAgg.update({'_id':x['_id']},{'$set':{'x.VBPS': somefunction as above }},upsert=False, multi=True)

我也没有尝试使用Mongo的常用运算符,因为方差就像从数组的每个元素中减去均值一样简单,对结果求平方,然后对结果求平均值。

如果我可以直接成功修改集合,那么我可以使用像Monary或IOPro这样的非常快速的东西来直接从Mongo加载数据并进入一个numpy数组,而无需额外的开销。

感谢您的时间

1 个答案:

答案 0 :(得分:3)

MongoDB无法使用从文档字段计算的值更新文档;目前,您只能使用update将值设置为从应用程序传入的常量。因此,您可以将document.x设置为2,但不能将document.x设置为document.y + document.z或任何其他计算值。

有关未来可能的功能,请参阅https://jira.mongodb.org/browse/SERVER-11345https://jira.mongodb.org/browse/SERVER-458

在不久的将来,PyMongo将发布bulk API,允许您在单个网络往返中发送一批不同的更新操作,这将提高您的性能。

<强>附录:

我还有另外两个想法。首先,运行一些Javascript服务器端。例如,将所有文档的b字段设置为2 * a

db.eval(function() {
    var collection = db.test_collection;
    collection.find().forEach(function(doc) {
        var b = 2 * doc.a;
        collection.update({_id: doc._id}, {$set: {b: b}});
    });
});

第二个想法是使用the aggregation framework's $out operator, new in MongoDB 2.5.2将集合转换为包含计算字段的第二个集合:

db.test_collection.aggregate({
    $project: {
        a: '$a',
        b: {$multiply: [2, '$a']}
    }
}, {
    $out: 'test_collection2'
});

请注意,$project必须明确包含您想要的所有字段;默认情况下仅包含_id

对于我机器上的一百万份文件,前一种方法需要2.5分钟,后一种方法需要9秒。因此,您可以使用聚合框架将数据从其源复制到目标,并包含计算字段。然后,如果需要,将原始集合和rename the target collection删除到源名称。

我最后的想法是,MongoDB 2.5.3及更高版本可以使用游标从聚合管道流式传输大型结果集。没有理由Monary无法使用该功能,因此您可以在那里提交功能请求。这样您就可以通过Monary以您想要的形式从集合中获取文档,而无需在MongoDB中实际存储计算字段。

相关问题