Проблема с итерированием по коллекциям в MongoDB из mongo shell

Вопрос или проблема

Я пытаюсь создать скрипт, который подключается к экземпляру MongoDB, выбирает базу данных, перечисляет все коллекции из базы данных, а затем создает схему для использования валидации на основе образца документов. Вторая половина этого задания у меня уже получается, но я совершенно не понимаю, как выполнить первую часть. Вот что у меня есть на данный момент, это файл runner.js, который я загружаю из mongosh, запуская load(“runner.js”) в командной строке:

async function run() {
    const db = connect('mongodb://localhost/mainDB')

    // const collections = db.runCommand({
    //     listCollections: 1.0,
    //     nameOnly: true
    // });
    const coll = db.listCollections();

    console.log(collections);

    for await (const doc of collections) {
        console.log(doc);
    }
}

run().catch(reason => console.error('ОШИБКА В РАБОТЕ: ', reason));

Когда я запускаю вышеуказанное, я получаю следующую ошибку:
ОШИБКА В РАБОТЕ: TypeError: db.listCollections is not a function

Черт, в документации API сказано, что это метод https://mongodb.github.io/node-mongodb-native/6.9/classes/Db.html#listCollections

Но ничего, когда я раскомментирую закомментированный раздел выше и использую метод runCommand, я получаю следующую ошибку:

{
  cursor: {
    id: Long('0'),
    ns: 'mainDB.$cmd.listCollections',
    firstBatch: [
      { name: 'purchaseOrderLineItems', type: 'collection' },
      { name: 'transactionCost', type: 'collection' },
      { name: 'addresses', type: 'collection' },
      ...<больше, что я здесь не перечисляю>
    ]
  },
  ok: 1
}
ОШИБКА В РАБОТЕ: TypeError: collections is not async iterable

Это также не имеет смысла. Документация здесь https://www.mongodb.com/docs/drivers/node/current/fundamentals/crud/read-operations/cursor/ говорит, что курсор, (который это и есть, правильно?) реализует интерфейс async iterable, так что это должно работать.

Не мог бы кто-то прояснить, что здесь происходит?

Ответ или решение

Проблема с итерацией по коллекциям в MongoDB из mongo shell

Ваша проблема заключается в том, что вы пытаетесь получить доступ к методам и свойствам, которые не существуют в используемом вами контексте, когда обращаетесь к MongoDB из mongo shell. Давайте разберем ваш код и ошибки, с которыми вы столкнулись.

Ошибка 1: TypeError: db.listCollections is not a function

Причина этой ошибки в том, что метод listCollections() не доступен в контексте mongo shell. Вместо этого вам нужно использовать метод runCommand() для получения списка коллекций в базе данных.

Вы можете использовать следующий код для получения всех коллекций:

async function run() {
    const db = connect('mongodb://localhost/mainDB');

    const collectionsCommand = db.runCommand({ listCollections: 1 });
    if (collectionsCommand.ok !== 1) {
        throw new Error("Failed to retrieve collections: " + collectionsCommand.errmsg);
    }

    const collections = collectionsCommand.cursor.firstBatch;

    collections.forEach(collection => {
        console.log(collection.name);
    });
}

run().catch(reason => console.error('ERROR RUNNING: ', reason));

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

Ошибка 2: TypeError: collections is not async iterable

Ваша вторая ошибка возникает из-за того, что вы пытались использовать оператор for await...of на массиве обычных объектов (который не является асинхронным итерируемым). Метод runCommand() возвращает обычный объект, содержащий фрагмент (batch) коллекций, который доступен в свойстве firstBatch. Это массив, и его нельзя использовать с for await...of.

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

Полный исправленный скрипт

Принимая во внимание все детали, ваш конечный код может выглядеть следующим образом:

async function run() {
    const db = connect('mongodb://localhost/mainDB');

    const collectionsCommand = db.runCommand({ listCollections: 1 });
    if (collectionsCommand.ok !== 1) {
        throw new Error("Failed to retrieve collections: " + collectionsCommand.errmsg);
    }

    const collections = collectionsCommand.cursor.firstBatch;

    // Итерация по коллекциям и вывод их имен
    collections.forEach(collection => {
        console.log(collection.name);
    });

    // Дополнительная логика для создания схемы на основании образцов документов...
}

run().catch(reason => console.error('ERROR RUNNING: ', reason));

Заключение

Часто в mongo shell нужно учитывать, что доступные методы могут отличаться от тех, что доступны в программных библиотеках MongoDB, например, в Node.js. Мы использовали runCommand() для получения коллекций и исправили итерацию по результату с помощью стандартного цикла forEach(). Если у вас есть дополнительные вопросы, не стесняйтесь спрашивать!

Оцените материал
Добавить комментарий

Капча загружается...