Python 在pymongo快速或批量插入

Python 在pymongo快速或批量插入,python,mongodb,pymongo,upsert,nosql,Python,Mongodb,Pymongo,Upsert,Nosql,我如何在pymongo进行批量升级?我想更新一堆条目,一次更新一个条目的速度非常慢 一个几乎相同的问题的答案如下: 被接受的答案实际上并没有回答这个问题。它只是提供了一个到mongo CLI的链接,用于执行导入/导出 我也愿意有人解释为什么批量升级不可能/不是最佳做法,但请解释这类问题的首选解决方案。答案仍然是一样的:不支持批量升级。您可以使用multi=True更新所有符合查询规范的文档 按您希望的方式执行一批命令时存在错误。MongoDB 2.6+支持批量操作。这包括批量插入、升级、更新等。

我如何在pymongo进行批量升级?我想更新一堆条目,一次更新一个条目的速度非常慢

一个几乎相同的问题的答案如下:

被接受的答案实际上并没有回答这个问题。它只是提供了一个到mongo CLI的链接,用于执行导入/导出


我也愿意有人解释为什么批量升级不可能/不是最佳做法,但请解释这类问题的首选解决方案。

答案仍然是一样的:不支持批量升级。

您可以使用multi=True更新所有符合查询规范的文档


按您希望的方式执行一批命令时存在错误。

MongoDB 2.6+支持批量操作。这包括批量插入、升级、更新等。其目的是减少/消除逐条记录操作的往返延迟(“逐条记录”是正确的)

那么,这是如何工作的呢?Python中的示例,因为这就是我的工作

>>> import pymongo
>>> pymongo.version
'2.7rc0'
要使用此功能,我们创建一个“批量”对象,向其添加文档,然后对其调用execute,它将立即发送所有更新。注意:收集的操作的BSONsize(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()
这是最基本的方法。有关更多信息,请访问:


编辑:-由于python驱动程序版本3.5,不推荐使用initialize\u ordered\u bulk\u op。改为使用bulk_write()。[]

pymongo的现代版本(大于3.x)将批量操作包装在一个一致的界面中,当服务器版本不支持批量操作时,该界面会降级。这在MongoDB官方支持的驱动程序中是一致的

因此,编码的首选方法是使用其他适当的操作操作。当然,现在更倾向于使用自然语言列表,而不是特定的生成器

旧文件的直接翻译:

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)
返回的结果将包含匹配和更新文档的计数器以及发生的任何“upserts”的返回的
\u id

对于批量操作数组的大小有一点误解。发送到服务器的实际请求不能超过16MB BSON限制,因为该限制也适用于发送到使用BSON格式的服务器的“请求”

但是,这并不控制您可以构建的请求数组的大小,因为实际操作无论如何只能以1000个批的形式发送和处理。唯一真正的限制是,这1000条操作指令本身实际上并不创建大于16MB的BSON文档。这的确是一个相当高的要求


批量方法的一般概念是“更少的通信量”,这是一次发送多个内容并只处理一个服务器响应的结果。减少每个更新请求的开销可以节省大量时间。

使用Python 3.5+、motor和asyncio进行最快的批量更新:

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))

如果你有很多数据,你想用“_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)

我的英语很差,如果你听不懂我说的话,我很抱歉

情况已经不是这样了。查看Kevin的答案。Bulk.find.upsert()是否有任何方法可以避免每次操作都执行查找?考虑一下,我想列出一个嵌入在文档中的JSON对象的列表,这样,我必须每次都找到文档,这看起来不太有效。这只是MungDB写东西的方式,而不是我的想法。在任何upsert或update中,您都必须找到要更新的文档,该查找指定要对哪些文档进行操作。此外,它应该对大量操作有效,但不一定适用于您的特定应用程序,因此,正如他们所说,您的里程可能会有所不同。
initialize\u ordered\u bulk\u op()
语法现在已被弃用:为什么使用模1000?我以为mongo批量操作会在2000次操作中自动执行…@duhaime问得好。我可以参考,但看起来会更精确的值。不幸的是,我不知道如何衡量这些请求size@duhaime顺便说一句,根据最新的汽车,我们不能担心它在allExcuse我,但我不明白,为什么你在你的循环中划分你的批次,如果你知道你的司机自己做。我的意思是,在您的示例中,
UpdateOne({“\u id”:doc[“\u id”]},{“$set”:{“random”:random.randint(0,10)}})
的1000个文档少于16MB,很明显,pymongo可以分割您的数据,请求将成功。为什么要用手分割数据?@Budulianin因为它是在迭代一个光标,将整个集合加载到内存中是非常复杂和反模式的。这就是为什么它们的批量大小合理。此外,这还需要经过一个网络,特别是考虑到异步环境(其中基本技术保持不变),您基本上可以在等待确认的同时通过网络发送数据并构建另一批。如果你认为你通过“一个大批量”而不是“几个合理的批量”获得了一些东西,那么在大多数情况下,我会说这是不可能的。我知道这个答案现在已经有4年了,但是
update\u many
会是一个有效的选择吗?