Обновить записи новыми объектами

Скажем, у меня есть следующая коллекция MongoDB (я использую mongomock для этого примера, поэтому его легко воспроизвести):

import mongomock

collection = mongomock.MongoClient().db.collection

objects = [{'name': 'Alice', 'age': 21}, {'name': 'Bob', 'age': 20}]
collection.insert_many(objects)

Затем я хотел бы обновить свои существующие объекты полями из некоторых новых объектов:

new_objects = [{'name': 'Alice', 'height': 170}, {'name': 'Caroline', 'height': 160}]

Единственный способ, которым я мог это сделать, - это:

for record in new_objects:
    if collection.find_one({'name': record['name']}) is not None:
        collection.update_one({'name': record['name']}, {'$set': {'height': record['height']}})
    else:
        collection.insert_one(record)

Однако если new_objects очень большой, тогда этот метод становится медленным - есть ли способ использовать update_many для этого?

1 ответ

Решение

Вы не можете использовать update_many(), потому что для этого требуется один фильтр, который в вашем случае не будет работать, поскольку каждый фильтр отличается.

Более простая конструкция использует upsert=True чтобы избежать логики вставки / обновления, а также устанавливает все поля, указанные в записи, которая меньше кодирует:

for record in objects + new_objects:
    collection.update_one({'name': record.get('name')}, {'$set': record}, upsert=True)

Если он замедляется из-за большего количества обновлений, убедитесь, что у вас есть индекс на name поле с использованием (в оболочке mongo):

db.collection.createIndex( { "name": 1 } )

Вы можете немного увеличить производительность, используя операцию bulk_write. Пример работы:

from pymongo import MongoClient, UpdateOne

collection = MongoClient().db.collection

objects = [{'name': 'Alice', 'age': 21}, {'name': 'Bob', 'age': 20}]
new_objects = [{'name': 'Alice', 'height': 170}, {'name': 'Caroline', 'height': 160}]

updates = []

for record in objects + new_objects:
    updates.append(UpdateOne({'name': record.get('name')}, {'$set': record}, upsert=True))

collection.bulk_write(updates)

for record in collection.find({}, {'_id': 0}):
    print(record)

Дает:

{'name': 'Alice', 'age': 21, 'height': 170}
{'name': 'Bob', 'age': 20}
{'name': 'Caroline', 'height': 160}
Другие вопросы по тегам