Вставка или обновление многих документов в MongoDB

есть ли способ вставить или обновить/заменить несколько документов в MongoDB одним запросом?

предположим, что следующая коллекция:

[
    {_id: 1, text: "something"},
    {_id: 4, text: "baz"}
]

теперь я хотел бы добавить несколько документов, некоторые из которых, возможно, уже в коллекции. Если документы уже находятся в коллекции, я хотел бы обновить / заменить их. Например, я хотел бы вставить следующие документы:

[
    {_id:1, text: "something else"},
    {_id:2, text: "foo"},
    {_id:3, text: "bar"}
]

запрос должен вставить документы с _id 2 и 3. Он также должен обновить / заменить документ на _id 1. После завершения процесса коллекция должна выглядеть следующим образом:

[
    {_id:1, text: "something else"},
    {_id:2, text: "foo"},
    {_id:3, text: "bar"},
    {_id:4, text: "baz"}
]

одним из подходов может быть использование insertMany:

db.collection.insertMany(
   [ {...}, {...}, {...} ],
   {
      ordered: false,
   }
)

если дубликаты происходят, этот запрос будет выдавать writeErrors содержащий массив объектов, содержащих индексы документов, которые не удалось вставить. Я мог бы просмотреть их и обновить.

но этот процесс обременительный. Есть ли способ вставить или обновить/заменить много документов в один запрос?

3 ответов


Как сказал здесь, чтобы сделать то, что вам нужно, вы можете поместить что-то вроде этого в

сценарий.js

(* предупреждение: непроверенный код)

use YOUR_DB
var bulk = db.collection.initializeUnorderedBulkOp();
bulk.find( { _id : 1 } ).upsert().update( { $set: { "text": "something else" } } );
bulk.find( { _id : 4 } ).upsert().update( { $set: { "text": "baz" } } );
bulk.find( { _id : 99 } ).upsert().update( { $set: { "text": "mrga" } } );
bulk.execute();

здесь

mongo

Я должен был сделать это таким образом, поскольку все, что я пытался обновить/вставить более 1000 документов, не работало из-за предела.

команды записи могут принимать не более 1000 операций. основная масса() операции в оболочке mongo и сопоставимые методы в драйверах не имеют этого ограничения.

источник


учитывая элемент данных следующим образом:

interface Item {
    id: string;         // unique key across collection
    someValue: string;
}

Если у вас есть элементы под пределом 1000, вы можете сделать массовую операцию записи следующим образом:

public async insertOrUpdateBulk(items: Item[]) {
        try {
            const bulkOperation = this._itemCollection.initializeUnorderedBulkOp();
            for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
                const item = items[itemIndex];
                bulkOperation.find({ id: item.id }).upsert().update(item);
            }
            await bulkOperation.execute();
            return true;
        } catch (err) {
            console.log(err);
            return false;
        }
    }

Если у вас есть предметы, которые превышают предел 1000, вы можете сделать одновременные обещания:

public async insertOrUpdate(items: Item[]) {
        try {
            const promises: Array<Promise<UpdateWriteOpResult>> = [];
            for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
                const item = items[itemIndex];
                const updatePromise = this.itemCollection.updateOne({ id: item.id }, item, { upsert: true });
                promises.push(updatePromise);
            }
            await Promise.all(promises);
            console.log('done...');
            return true;
        } catch (err) {
            console.log(err);
            return false;
        }
    }

вы также можете использовать bulkWrite api для обновления или вставки нескольких документов в один запрос, вот пример

var ops = []

items.forEach(item => {
    ops.push(
        {
            updateOne: {
                filter: { _id: unique_id },
                update: {
                    $set: { fields_to_update_if_exists },
                    $setOnInsert: { fileds_to_insert_if_does_not_exist }
                },
                upsert: true
            }
        }
    )
})

db.collections('collection_name').bulkWrite(ops, { ordered: false });