tenemos una colección en MongoDB Atlas con 3 millones de documentos y usando NodeJS necesitamos exportarlos a CSV. No podemos usar MONGOEXPORT o MONGODUMP, es un proceso que debe desarrollarse como una API.
Para ello estamos trabajando con la librería fast-csv, pero tenemos el problema de que debemos pasar un array al método fastcsv.write() como dato de entrada para la creación del CSV.
El problema es que la transformación a una matriz de los 3 millones de documentos devueltos por la consulta a Mongo consume mucho tiempo y memoria.
¿Podrías darnos una mano para saber cómo desarrollar esto para que funcione de la manera más eficiente posible?
Aquí hay una muestra del código que estamos probando.
PD Preguntas que también nos pueden ayudar:
Gracias.
const aggCursor = colInventarioInstalaciones.aggregate(pipeline, pipelineOptions) .toArray((err, data) => { //<<===== taking a lot of time //console.log("se ejecuta punto 3 del metodo"); if (err) throw err; const ws = fs.createWriteStream("pruebaTC1.csv"); fastcsv.write(data, { headers: true }) //<<===== need to be array for write data in the file .on("finish", function() { var datetime = new Date(); console.log(datetime); console.log("Write to pruebaTC1.csv successfully!"); }).pipe(ws); });```Personalmente, NUNCA recomendaría usar .toArray() para consultar un número indefinido/muy grande. de documentos, ya que le pide a nodejs que obtenga 3 millones de documentos de su base de datos y almacene todo en una matriz de humongosaurus en la memoria. Espero que tengas una computadora de alta especificación. Además, no escribirá nada en un archivo a menos que todo esté en la memoria, si eso sucede alguna vez.
En su lugar, estaría iterando el cursor para que, en un momento dado, solo tenga 1 documento en la memoria y lo canalice al flujo de escritura.
Dado que está leyendo 3 millones de documentos, llevará tiempo, solo es cuestión de cuánto. Al menos con este enfoque, escribirá los documentos en el archivo csv a medida que se leen, en lugar de escribir todo de una vez desde la memoria.
Escribí un pequeño script para iterar sobre todo a través cursor.next() , documento por documento, y lo probé.
const { MongoClient } = require('mongodb'); const csv = require('fast-csv'); const fs = require('fs'); // or as an es module: // import { MongoClient } from 'mongodb' // Connection URL const url = 'supersecretmongourlhere'; const client = new MongoClient(url); // Database Name const dbName = 'db_name_here'; const run = async() => { try{ await client.connect(); console.log("db connected"); const db = client.db(dbName); console.time("X") const pipelineStages = [{ $match: { // your pipeline here } },{ $sort: { _id: -1 } },{ $limit: 50000 }] const cursor = db.collection('collection_name_here').aggregate(pipelineStages) const csvStream = csv.format({ headers: true }); const writeStream = fs.createWriteStream('./myfile.csv'); csvStream.pipe(writeStream).on('end',() => { console.log("DONE"); }).on('error',err => console.error(err)); while(await cursor.hasNext()){ const doc = await cursor.next(); csvStream.write(doc); console.log(`${doc._id} written`); } console.log('done') console.timeEnd("X") csvStream.end(); writeStream.end(); } catch(e) { console.error(e); } } run();Para 50000 documentos, 1:17,987s = 77987ms, 50000/77987 = 1,56ms/documento. 1.56*3000000 = 4680s = ~78mins
¿Es eso aceptable en su caso de uso?
Sí, sé que esta no es una solución muy rápida, pero funcionará.
Puede haber alguna forma de acelerarlo aún más obteniendo documentos en lotes de la base de datos, lo investigaré si puedo.