#javascript #node.js #json #mongodb #arguments
#javascript #node.js #json #mongodb #аргументы
Вопрос:
Я пишу код, который генерирует очень большой объект JSON, сохраняет его в файл, затем загружает файл и вставляет данные в коллекцию Mongo. Я хочу передать строку из командной строки при вызове скрипта, который я использую для установки имени файла, а также имени коллекции. Я называю это так: node --max-old-space-size=8192 data_generator.js foo 1000000
.
Код завершается ошибкой ENOENT: no such file or directory, open 'foo.json'
в третьей строке функции gen_collection()
, в которой я устанавливаю переменную data
. Эта ошибка не появляется, когда файл foo.json
уже существует, даже если он пуст. Перед сбоем код успешно создает файл foo.json
, но он содержит только пустой массив []
.
Код завершается ошибкой с той же точной ошибкой, когда я включаю любую ссылку на process.argv. Это включает в себя, когда я пытаюсь присвоить любой переменной значение из процесса.массив argv. Код работает, когда я устанавливаю переменные fname
as const fname = "foo"
и size
as const size = 0
. Однако, даже если единственная ссылка, которую я должен обработать.argv находится в консоли.войти, то есть добавление console.log(process.argv[2]
в main()
, завершается с той же ошибкой, что и выше.
Вот код, который я пытаюсь запустить:
const { MongoClient } = require("mongodb");
const fs = require('fs');
const bjson = require('big-json');
async function main() {
const uri = "my db uri";
const client = new MongoClient(uri);
const fname = process.argv[2];
const size = parseInt(process.argv[3]);
// const fname = 'small'
// const size = 1
try {
await client.connect({ useUnifiedTopology: true });
await write_data_to_disk(fname, size);
await gen_collection(client, fname);
} catch (e) {
console.error(e);
} finally {
await client.close();
}
};
// generate data as json aray and write to local file
async function write_data_to_disk(fname, size) {
let arr = [];
for (let i = 0; i < size; i ) {
let doc = gen_document();
arr.push(doc);
}
const strStream = bjson.createStringifyStream({
body: arr
})
let logger = fs.createWriteStream(`${fname}.json`);
strStream.on('data', (d) => {
logger.write(d);
})
};
async function gen_collection(client, fname) {
let db = client.db('test');
let collection = db.collection(fname);
let data = JSON.parse(fs.readFileSync(`${fname}.json`, 'utf8')); // ERROR APPEARS ON THIS LINE
bulkUpdateOps = [];
data.forEach((doc) => {
bulkUpdateOps.push({"insertOne": {"document": doc}});
if (bulkUpdateOps.length === 1000) {
collection.bulkWrite(bulkUpdateOps);
bulkUpdateOps = [];
}
})
if (bulkUpdateOps.length > 0) {
collection.bulkWrite(bulkUpdateOps);
}
};
function gen_document() {
// returns json object
};
Ответ №1:
Вы делаете
await write_data_to_disk(...)
но эта функция не возвращает обещание, к которому подключено, когда оно выполнено. Итак, вы пытаетесь прочитать результирующий файл ДО того, как он был создан или до того, как в нем есть допустимое содержимое, и, следовательно ENOENT
, ошибка, поскольку файл еще не существует, когда вы пытаетесь прочитать из него в следующей функции.
Потоки записи плохо сочетаются с обещаниями, если вы не обернете их в свое собственное обещание, которое разрешается, когда вы полностью закончите запись в поток и файл будет закрыт.
Кроме того, вы, вероятно, захотите просто .pipe()
передать strStream в поток регистратора. Намного проще, и затем вы можете просто отслеживать, когда выполняется эта операция pipe (), чтобы разрешить обещание, которое вы обертываете вокруг этой операции.
Вы можете пообещать write_data_to_disk()
так:
// generate data as json aray and write to local file
function write_data_to_disk(fname, size) {
return new Promise((resolve, reject) => {
const arr = [];
for (let i = 0; i < size; i ) {
let doc = gen_document();
arr.push(doc);
}
const strStream = bjson.createStringifyStream({ body: arr });
const dest = fs.createWriteStream(`${fname}.json`, {emitClose: true});
// monitor for completion and errors
dest.on('error', reject).on('close', resolve);
strStream.on('error', reject);
// pipe all the content from strStream to the dest writeStream
strStream.pipe(dest);
});
}
Поскольку это возвращает обещание, которое действительно привязано к моменту выполнения операции записи, вы можете использовать await write_data_to_disk(...)
.
Комментарии:
1. Спасибо за ваш ответ! Это определенно похоже на проблему. Я не совсем понимаю, что вы имеете в виду, используя .pipe(), не могли бы вы объяснить, как я мог бы использовать его в этой ситуации?
2. @jpc — Посмотрите, что я добавил к своему ответу.