Как добавить много записей в 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 ответов
по состоянию на mongodb
2.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 *()
использование генераторов).