web-dev-qa-db-ja.com

ピモンゴの高速または一括アップサート

ピモンゴで一括アップサートを行うにはどうすればよいですか?一連のエントリを更新したいのですが、一度に1つずつ実行すると非常に時間がかかります。

ほとんど同じ質問に対する答えはここにあります: MongoDBの一括更新/アップサート?

受け入れられた答えは実際には質問に答えるものではありません。それは単にインポート/エクスポートを行うためのmongo CLIへのリンクを提供します。

一括アップサートを実行できない、またはベストプラクティスがない理由を説明してくれる人もいますが、この種の問題に対する推奨される解決策を説明してください。

MongoDB 2.6以降では、一括操作がサポートされています。これには、一括挿入、アップサート、更新などが含まれます。これのポイントは、レコードごとの操作(「ドキュメントごと」が正しい)を実行する際の往復遅延による遅延を削減/排除することです。

それで、これはどのように機能しますか? Pythonでの例。これは私が作業しているものだからです。

>>> import pymongo
>>> pymongo.version
'2.7rc0'

この機能を使用するには、「バルク」オブジェクトを作成し、それにドキュメントを追加してから、executeを呼び出すと、すべての更新が一度に送信されます。警告:収集された操作のBSONサイズ(bsonsizeの合計)は、ドキュメントサイズの制限である16 MBを超えることはできません。もちろん、このように操作の数は大幅に異なる可能性があり、マイレージは異なる場合があります。

一括アップサート操作のPymongoの例:

import pymongo
conn = pymongo.MongoClient('myserver', 8839)
db = conn['mydbname']
coll = db.myCollection
bulkop = coll.initialize_ordered_bulk_op()
retval = bulkop.find({'field1':1}).upsert().update({'$Push':{'vals':1}})
retval = bulkop.find({'field1':1}).upsert().update({'$Push':{'vals':2}})
retval = bulkop.find({'field1':1}).upsert().update({'$Push':{'vals':3}})
retval = bulkop.execute()

これは必須の方法です。詳細については、以下をご覧ください。

http://api.mongodb.org/python/2.7rc1/examples/bulk.html

編集:-pythonドライバのバージョン3.5以降、initialize_ordered_bulk_opは非推奨になりました。代わりにbulk_write()を使用してください。[ http://api.mongodb.com/python/current/api/ pymongo/collection.html#pymongo.collection.Collection.bulk_write ]

30
Kevin J. Rice

Pymongoの最新のリリース(3.x以上)では、サーバーのリリースが一括操作をサポートしていない場所をダウングレードする一貫したインターフェイスで一括操作をラップします。これは、MongoDBの公式にサポートされているドライバーで一貫しています。

したがって、コーディングに推奨される方法は、代わりに bulk_write() を使用することです。この場合、代わりに UpdateOne を使用します。そしてもちろん今では、特定のビルダーよりも自然言語リストを使用することが推奨されています

古いドキュメンテーションの直接翻訳:

from pymongo import UpdateOne

operations = [
    UpdateOne({ "field1": 1},{ "$Push": { "vals": 1 } },upsert=True),
    UpdateOne({ "field1": 1},{ "$Push": { "vals": 2 } },upsert=True),
    UpdateOne({ "field1": 1},{ "$Push": { "vals": 3 } },upsert=True)
]

result = collection.bulk_write(operations)

または、古典的なドキュメント変換ループ:

import random
from pymongo import UpdateOne

random.seed()

operations = []

for doc in collection.find():
    # Set a random number on every document update
    operations.append(
        UpdateOne({ "_id": doc["_id"] },{ "$set": { "random": random.randint(0,10) } })
    )

    # Send once every 1000 in batch
    if ( len(operations) == 1000 ):
        collection.bulk_write(operations,ordered=False)
        operations = []

if ( len(operations) > 0 ):
    collection.bulk_write(operations,ordered=False)

返される結果は BulkWriteResult であり、一致および更新されたドキュメントのカウンターと、発生した「アップサート」に対して返された_id値が含まれます。

一括操作配列のサイズについては、少し誤解があります。サーバーに送信される実際のリクエストは、16MBのBSON制限を超えることはできません。この制限は、BSON形式を使用しているサーバーに送信される「リクエスト」にも適用されるためです。

ただし、実際の操作は1000のバッチでのみ送信および処理されるため、ビルドできるリクエスト配列のサイズは影響を受けません。実際の唯一の制限は、これらの1000の操作命令自体が実際に16MBを超えるBSONドキュメントを作成しないことです。それは確かにかなり高い注文です。

バルクメソッドの一般的な概念は、一度に多くのものを送信し、1つのサーバー応答しか処理しないため、「トラフィックが少ない」ということです。すべての更新リクエストに付随するそのオーバーヘッドの削減により、多くの時間を節約できます。

29
Neil Lunn

答えは同じです。一括アップサートはサポートされていません。

5
Andreas Jung

Multi = Trueを使用して、クエリ仕様に一致するすべてのドキュメントを更新できます。

バグがあります ここ コマンドのバッチを希望どおりに実行することについて。

1
Bernie Hackett

データが多く、 "_ id"を使用してデータが存在するかどうかを判断する場合、

あなたが試すことができます...

import pymongo
from pymongo import UpdateOne
client = pymongo.MongoClient('localhost', 27017)
db=client['sampleDB']

collectionInfo = db.sample

#sample data
datas=[
    {"_id":123456,"name":"aaa","N":1,"comment":"first sample","lat":22,"lng":33},
    {"_id":234567,"name":"aaa","N":1,"comment":"second sample","lat":22,"lng":33},
    {"_id":345678,"name":"aaa","N":1,"comment":"xxx sample","lat":22,"lng":33},
    {"_id":456789,"name":"aaa","N":1,"comment":"yyy sample","lat":22,"lng":33},
    {"_id":123456,"name":"aaaaaaaaaaaaaaaaaa","N":1,"comment":"zzz sample","lat":22,"lng":33},
    {"_id":11111111,"name":"aaa","N":1,"comment":"zzz sample","lat":22,"lng":33}
]

#you should split judge item and other data 
ids=[data.pop("_id") for data in datas]

operations=[UpdateOne({"_id":idn},{'$set':data},upsert=True) for idn ,data in Zip(ids,datas)]

collectionInfo.bulk_write(operations)

私の英語はとても下手です。私が言っていることが理解できなければ、ごめんなさい

0
袁華谷-Andy

Python 3.5+、モーター、非同期を使用した最速の一括更新:

import asyncio
import datetime
import logging
import random
import time

import motor.motor_asyncio
import pymongo.errors


async def execute_bulk(bulk):
    try:
        await bulk.execute()
    except pymongo.errors.BulkWriteError as err:
        logging.error(err.details)


async def main():
    cnt = 0
    bulk = db.initialize_unordered_bulk_op()
    tasks = []
    async for document in db.find({}, {}, no_cursor_timeout=True):
        cnt += 1
        bulk.find({'_id': document['_id']}).update({'$set': {"random": random.randint(0,10)}})
        if not cnt % 1000:
            task = asyncio.ensure_future(execute_bulk(bulk))
            tasks.append(task)
            bulk = db.initialize_unordered_bulk_op()
    if cnt % 1000:
        task = asyncio.ensure_future(bulk.execute(bulk))
        tasks.append(task)
    logging.info('%s processed', cnt)
    await asyncio.gather(*tasks)


logging.basicConfig(level='INFO')    
db = motor.motor_asyncio.AsyncIOMotorClient()['database']['collection']
start_time = time.time()
loop = asyncio.get_event_loop()
try:
    loop.run_until_complete(main())
finally:
    execution_time = time.time() - start_time
    logging.info('Execution time: %s', datetime.timedelta(seconds=execution_time))
0
El Ruso