Как добавить много записей в mongoDB из каталога JSON-файлов?

у меня есть около миллиона файлов JSON, сохраненных во многих подкаталогах каталога "D:/njs/nodetest1/imports/source1/" и я хочу импортировать их в коллекцию "пользователи" в моей базе данных mongoDB.

следующий код правильно проходит через файловую систему. Как вы можете видеть, он читает каждый элемент в каталоге, и если этот элемент является каталогом, он читает каждый элемент в нем. Для каждого элемента, который не является каталогом, он выполняет некоторые операции над ним перед отправкой переменная, удерживающая функцию.

function traverseFS (path){
     var files = fs.readdirSync(path);
     for (var i in files){
          var currentFile = path + '/' + files[i];
          var stats = fs.statSync(currentFile);
          if (stats.isFile())
               runOnFile(currentFile);
          else
               traverseFS(currentFile);
     }
}
traverseFS("D:/njs/nodetest1/imports/source1/")

далее я выполняю несколько операций над кодом (см. ниже). Это считывает файл, анализирует его в объект JSON, считывает два атрибута этого объекта в переменные, создает объект в переменной "entry" и передает переменную другой функции.

function runOnFile(currentFile){
    var fileText = fs.readFileSync(currentFile,'utf8');
    var generatedJSON = JSON.parse(fileText);
    var recordID = generatedJSON.recordID;
    var recordText = generatedJSON.recordTexts;
    var entry = {recordID:recordID, recordText:recordText};
    insertRecord(entry);
}

конечная функция затем должна использоваться для вставки данных в mongoDB. Я думаю, что здесь все идет не так.

function insertRecord(entry){
    var MongoClient = mongodb.MongoClient;
    var MongoURL = 'mongodb://localhost:27017/my_database_name';
    MongoClient.connect(MongoURL, function (err, db) {
         var collection = db.collection('users');
         collection.insert([entry], function (err, result) {
             db.close();
         });
     });
 }

Я ожидал, что это будет работать через файловую структуру, чтение файлов JSON в объекты, а затем вставка этих объектов в мой mongoDB. Вместо этого он читает первый файл в базу данных, а затем останавливается/зависает.

Примечания:

  • я не хочу использовать mongoimport, потому что я не хочу вставлять все данные из этих файлов в мою базу данных MongoDB. Однако я не привязан ни к одному аспекту этого подхода. Если существует какое-то другое решение, я открыт для него.
  • это связано с база данных в порядке. Для каждого элемента в каталоге это успешно создает объект "запись" и передает его функции insertRecord. Другими словами, проблема должна возникать в разделе insertRecord. Но это, очевидно, может быть вызвано чем-то в начале процесса.
  • если я добавлю обработку ошибок, ошибки не производятся. Я оставил обработку ошибок из этого сообщения, потому что она загромождает читаемость кода фрагменты.

3 ответов


по состоянию на mongodb2.2 (свежую) документация, insert - это устаревший

устарел

используйте insertOne, insertMany или bulkWrite

таким образом, короткий ответ, вероятно, изменить collection.insert([entry], ...) до collection.insertOne(entry, ...) и вы сделали.


затем для длинного ответа вы говорите "около миллиона файлов json", которые обычно заслуживают полного асинхронного подхода с наименьшим количеством накладные расходы.

в образце кода есть два (потенциальных) узких места:

  • fs.readFileSync, это блокирующая операция
  • подключение, вставка записи и закрытие подключения к базе данных

оба выполнены "около миллиона раз". Конечно, импорт обычно не выполняется снова и снова и (надеюсь) не на машине, которая нуждается в своей производительности для других важных задач. Тем не менее, пример кода может легко быть более жесткой.

рассмотрите возможность использования glob модуль для получения списка файлов json.

glob('imports/**/*.json', function(error, files) {...})

это предоставляет вам полный список файлов легко в асинхронном режиме.

затем рассмотрите возможность подключения к базе данных только один раз, вставьте все и закройте один раз.

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

var glob = require('glob'),
    mongodb = require('mongodb'),
    fs = require('fs'),
    MongoClient = mongodb.MongoClient,
    mongoDSN = 'mongodb://localhost:27017/my_database_name',
    collection; //  moved this to the "global" scope so we can do it only once

function insertRecord(json, done) {
    var recordID = json.recordID || null,
        recordText = json.recordText || null;

    //  the question implies some kind of validation/sanitation/preparation..
    if (recordID && recordText) {
        //  NOTE: insert was changed to insertOne
        return collection.insertOne({recordID: recordID, recordText: recordText}, done);
    }

    done('No recordID and/or recordText');
}

function runOnFile(file, done) {
    //  moved to be async
    fs.readFile(file, function(error, data) {
        if (error) {
            return done(error);
        }

        var json = JSON.parse(data);

        if (!json) {
            return done('Unable to parse JSON: ' + file);
        }

        insertRecord(json, done);
    });
}

function processFiles(files, done) {
    var next = files.length ? files.shift() : null;

    if (next) {
        return runOnFile(next, function(error) {
            if (error) {
                console.error(error);
                // you may or may not want to stop here by throwing an Error
            }

            processFiles(files, done);
        });
    }

    done();
}

MongoClient.connect(mongoDSN, function(error, db) {
    if (error) {
        throw new Error(error);
    }

    collection = db.collection('users');

    glob('imports/**/*.json', function(error, files) {
        if (error) {
            throw new Error(error);
        }

        processFiles(files, function() {
            console.log('all done');
            db.close();
        });
    });
});

Примечание: Вы можете собрать несколько "запись" - записи, чтобы использовать прирост производительности нескольких вставок с помощью insertMany, хотя у меня такое чувство, что вставленные записи сложнее, чем описано, и это может дать некоторые проблемы с памятью, если они не обрабатываются правильно.


просто структурируйте свои данные в один большой массив объектов, затем запустите db.коллекция.insertMany.


Я предлагаю вам сделать это, используя обещания:

const Bluebird = require('bluebird');
const glob = Bluebird.promisify(require('glob'));
const mongodb = require('mongodb');
const fs = Bluebird.promisifyAll(require('fs'));
const Path = require('path');
const MongoClient = mongodb.MongoClient;

const insertMillionsFromPath = Bluebird.coroutine(function *(path, mongoConnString) {
    const db = yield MongoClient.connect(mongoConnString);
    try {
        const collection = db.collection('users');
        const files = yield glob(Path.join(path, "*.json"));
        yield Bluebird.map(
            files,
            Bluebird.coroutine(function *(filename) {
                console.log("reading", filename);
                const fileContent = yield fs.readFileAsync(filename);
                const obj = JSON.parse(fileContent);

                console.log("inserting", filename);
                yield collection.insertOne(obj);
            }),
            {concurrency: 10} // You can increase concurrency here
        );
    } finally {
        yield db.close();
    }
});

insertMillionsFromPath("./myFiles", "mongodb://localhost:27017/database")
    .then(()=>console.log("OK"))
    .catch((err)=>console.log("ERROR", err));

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

npm install --save mongodb bluebird glob

и нужно использовать узел.js версии 6 или выше, в противном случае вам нужно будет транспилировать javascript (из-за function *() использование генераторов).