【问题标题】:PyMongo updating array records with calculated fields via cursorPyMongo 通过游标使用计算字段更新数组记录
【发布时间】:2014-01-28 06:02:05
【问题描述】:

一个非常大的数据集的精细聚合管道的收集输出基本上类似于以下内容:

{
    "_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 数组,而无需额外开销。

感谢您的宝贵时间

【问题讨论】:

    标签: mongodb numpy pymongo


    【解决方案1】:

    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 中实际存储计算的字段。

    【讨论】:

    • 有趣,感谢您提供的信息。我搜索了 JIRA,但错过了那些伟大的发现。所以看起来我必须将这些对象放入内存中进行计算并写回常量。
    • 您对使用 Python 的可扩展解决方案有什么建议吗?我无法链接一些 $unwind 运算符。
    • 遗憾的是,Monary 的作者一直是 MIA。似乎没有办法从聚合()中倒回光标 find()只是cursor.rewind()。尝试使用聚合得到“InvalidOperation:无法在命令光标上调用倒带”
    • 是的——为什么需要回退聚合光标?
    • 倒带并不特别;这与在循环中重新创建光标完全相同。回退游标只是将查询再次发送到服务器,并再次从服务器检索结果。 (查看the code in PyMongo。)因此,您应该按照 Bernie 在 Google Groups 中的建议进行操作(获取一次结果并处理每个字段),或者创建一个新光标来检索每个字段。
    猜你喜欢
    • 1970-01-01
    • 2017-09-05
    • 1970-01-01
    • 1970-01-01
    • 2018-05-28
    • 2017-05-04
    • 1970-01-01
    • 2020-05-26
    • 1970-01-01
    相关资源
    最近更新 更多