插入流式XML数据库

时间:2017-08-05 22:48:48

标签: javascript node.js xml mongodb

我试图有效地插入大量数据(XML文件大小超过70GB)而不会崩溃我的MongoDB服务器。目前,这就是我在NodeJS中使用xml-stream所做的事情:

xml.on()

当我调用db.collection().insertOne()时,它基本上会返回我当前所在的树/元素。由于这是直接的JSON,我可以将它作为参数提供给我的insertMany()函数,它会将它完全插入到我想要的数据库中。

所有代码实际上都可以正常工作,但在大约3000次插入(大约需要10秒)后停止。我怀疑它是因为我打开数据库连接,插入数据,然后每次在XML文件中看到一棵树时关闭连接,在这种情况下大约3000次。

我可以以某种方式合并theano函数并以100s(或更多)的块来执行,但我不太确定如何将它与流式和异步一起使用。< / p>

所以我的问题是:如何在没有崩溃的情况下将大量XML(到JSON)插入我的MongoDB数据库?

1 个答案:

答案 0 :(得分:3)

你认为.insertMany()比每次写作更好是正确的,所以这只是收集"stream"上的数据的问题。

由于执行是“异步”,您通常希望避免在堆栈上有太多活动调用,因此通常在调用.pause()之前"stream" .insertMany()然后{ {3}}回调完成后:

var fs = require('fs'),
    path = require('path'),
    XmlStream = require('xml-stream'),
    MongoClient = require('mongodb').MongoClient,
    url = 'mongodb://username:password@my.server:27017/mydatabase',
    amount = 0;

MongoClient.connect(url, function(err, db) {

    var stream = fs.createReadStream(path.join(__dirname, 'motor.xml'));
    var xml = new XmlStream(stream);

    var docs = [];
    //xml.collect('ns:Statistik');

    // This is your event for the element matches
    xml.on('endElement: ns:Statistik', function(item) {
        docs.push(item);           // collect to array for insertMany
        amount++;

        if ( amount % 1000 === 0 ) { 
          xml.pause();             // pause the stream events
          db.collection('vehicles').insertMany(docs, function(err, result) {
            if (err) throw err;
            docs = [];             // clear the array
            xml.resume();          // resume the stream events
          });
        }
    });

    // End stream handler - insert remaining and close connection
    xml.on("end",function() {
      if ( amount % 1000 !== 0 ) {
        db.collection('vehicles').insertMany(docs, function(err, result) {
          if (err) throw err;
          db.close();
        });
      } else {
        db.close();
      }
    });

});

或者甚至对它进行现代化改造:

const fs = require('fs'),
      path = require('path'),
      XmlStream = require('xml-stream'),
      MongoClient = require('mongodb').MongoClient;

const uri = 'mongodb://username:password@my.server:27017/mydatabase';

(async function() {

  let amount = 0,
      docs = [],
      db;

  try {

    db = await MongoClient.connect(uri);

    const stream = fs.createReadStream(path.join(__dirname, 'motor.xml')),
          xml = new XmlStream(stream);

    await Promise((resolve,reject) => {
      xml.on('endElement: ns:Statistik', async (item) => {
        docs.push(item);
        amount++;

        if ( amount % 1000 === 0 ) {
          try {
            xml.pause();
            await db.collection('vehicle').insertMany(docs);
            docs = [];
            xml.resume();
          } catch(e) {
            reject(e)
          }
        }

      });

      xml.on('end',resolve);

      xml.on('error',reject);
    });

    if ( amount % 1000 !== 0 ) {
      await db.collection('vehicle').insertMany(docs);
    }

  } catch(e) {
    console.error(e);
  } finally {
    db.close();
  }

})();

请注意,MongoClient连接实际上包含了所有其他操作。您只想连接一次,其他操作发生在.resume()的事件处理程序上。

因此,对于XMLStream,在表达式匹配时触发事件处理程序,并将数据提取并收集到数组中。 "stream"调用的每1000个项目用于插入文档,“暂停”和“恢复”“异步”调用。

完成后,.insertMany()会触发“结束”事件。这是关闭数据库连接的地方,事件循环将被释放并结束程序。

虽然可以通过允许一次发出各种"stream"调用来获得某种程度的“并行性”(并且通常为“汇集大小”以便不会超出调用堆栈),这基本上是如何通过简单地暂停等待其他异步I / O完成,该过程以最简单的形式查找。

  

注意:根据.insertMany()注释原始代码中的.collect()方法,这似乎没有必要,实际上是将节点保留在内存中每次写入数据库后都应该丢弃。