在集合Mongo DB中的每个文档上调用自定义python函数

时间:2016-06-12 21:19:32

标签: mongodb pymongo pymongo-3.x

我想在整个集合中的每个文档的某些现有属性上调用自定义python函数将结果存储为该(相同)文档中的新键值对。我可以知道是否有任何方法可以做到这一点(因为每个电话都独立于其他电话)?

我注意到cursor.forEach但是不能有效地使用python吗?

一个简单的例子是将字符串拆分为text并存储号码。单词作为新属性。

def split_count(text):
    # some complex preprocessing...

    return len(text.split())

# Need something like this...
db.collection.update_many({}, {'$set': {"split": split_count('$text') }}, upsert=True)

但似乎在基于同一文档中另一个属性的值在文档中设置新属性的方式是not possible。这篇文章很老,但问题似乎仍然存在。

2 个答案:

答案 0 :(得分:3)

我找到了一种在PyMongo中使用parallel_scan在集合上调用任何自定义python函数的方法。

def process_text(cursor):
    for row in cursor.batch_size(200):
        # Any complex preprocessing here...
        split_text = row['text'].split()

        db.collection.update_one({'_id': row['_id']}, 
                                 {'$set': {'split_text': split_text, 
                                           'num_words': len(split_text) }},
                                 upsert=True)


def preprocess(num_threads=4):

    # Get up to max 'num_threads' cursors.
    cursors = db.collection.parallel_scan(num_threads)
    threads = [threading.Thread(target=process_text, args=(cursor,)) for cursor in cursors]

    for thread in threads:
        thread.start()

    for thread in threads:
        thread.join()

这并不比cursor.forEach快(但也不是那么慢),但它可以帮助我执行任何任意复杂的python代码并从Python本身保存结果。

此外,如果我在其中一个属性中有一个ints数组,那么cursor.forEach会将它们转换为我不想要的floats。所以我更喜欢这种方式。

但我很高兴知道是否有比这更好的方法:)

答案 1 :(得分:0)

在python中做这种事情是不可能有效的。这是因为文档必须进行往返并通过客户端计算机上的python函数。

在您的示例代码中,您将函数的结果传递给mongodb update查询,该查询无法正常工作。您无法在db服务器上的mongodb查询中运行任何python代码。

正如answer to you linked question建议的那样,这种类型的动作必须在mongo shell中执行。 e.g:

db.collection.find().snapshot().forEach(
    function (elem) {
        splitLength = elem.text.split(" ").length
        db.collection.update(

            {
                _id: elem._id
            },
            {
                $set: {
                    split: splitLength 
                }
            }
        );
    }
);