如何管道' oracle-db数据来自'数据'事件

时间:2018-05-14 09:27:03

标签: node.js oracledb

我已经使用node-oracledb几个月了,而且我已经成功实现了迄今为止我所需要的。

我目前正在开发一款搜索应用,可能会从一次通话中返回约2百万行数据。为了确保我不会断开与浏览器和服务器的连接,我想我会尝试使用queryStream,以便有一个持续的数据流回到客户端。

我按原样实现了queryStream示例,这适用于几十万行。但是,当返回的行大于一百万时,节点内存不足。通过记录和观察客户端和服务器日志事件,我可以看到客户端在发送和接收的行方面远远落后于服务器。因此,看起来Node正在倒下,因为它缓冲了如此多的数据。

值得注意的是,此时,我的selectstream实现位于通过Express调用的req / res函数内。

要返回数据,我会做类似......

stream.on('data', function (data) {

    rowcount++;

    let obj = new myObjectConstructor(data);
    res.write(JSON.stringify(obj.getJson());

});

我一直在阅读关于流和管道如何帮助流程的知识,所以我希望能够做的是能够将查询的结果传递给a)帮助流程b)在发送回客户端之前能够将结果传递给其他函数。

E.g。

function getData(req, res){

    var stream = myQueryStream(connection, query);

    stream
        .pipe(toSomeOtherFunction)
        .pipe(yetAnotherFunction)
        .pipe(res);

}

我花了几个小时试图找到一个允许我输出结果的解决方案或示例,但我陷入困境并需要一些帮助。

道歉,如果我错过了一些明显的东西,但我仍然要掌握Node,尤其是溪流。

提前致谢。

1 个答案:

答案 0 :(得分:1)

这里有一点阻抗不匹配。 queryStream API会发出JavaScript对象的行,但是要传递给客户端的是JSON数组。你基本上必须在开头添加一个开括号,在每行后添加一个逗号,并在末尾添加一个括号。

我将告诉您如何在直接使用驱动程序的控制器中执行此操作,而不是像我在this series中提倡的那样使用单独的数据库模块。

const oracledb = require('oracledb');

async function get(req, res, next) {
  try {
    const conn = await oracledb.getConnection();

    const stream = await conn.queryStream('select * from employees', [], {outFormat: oracledb.OBJECT});

    res.writeHead(200, {'Content-Type': 'application/json'});

    res.write('[');

    stream.on('data', (row) => {
      res.write(JSON.stringify(row));
      res.write(',');
    });

    stream.on('end', () => {
      res.end(']');
    });

    stream.on('close', async () => {
      try {
        await conn.close();
      } catch (err) {
        console.log(err);
      }
    });

    stream.on('error', async (err) => {
      next(err);

      try {
        await conn.close();
      } catch (err) {
        console.log(err);
      }
    });
  } catch (err) {
    next(err);
  }
}

module.exports.get = get;

获得概念之后,您可以使用可重用的Transform类简化一些事情,它允许您在控制器逻辑中使用管道:

const oracledb = require('oracledb');
const { Transform } = require('stream');

class ToJSONArray extends Transform {
  constructor() {
    super({objectMode: true});

    this.push('[');
  }

  _transform (row, encoding, callback) {
    if (this._prevRow) {
      this.push(JSON.stringify(this._prevRow));
      this.push(',');
    }

    this._prevRow = row;

    callback(null);
  }

  _flush (done) {
    if (this._prevRow) {
      this.push(JSON.stringify(this._prevRow));
    }

    this.push(']');

    delete this._prevRow;

    done();
  }
}

async function get(req, res, next) {
  try {
    const toJSONArray = new ToJSONArray();
    const conn = await oracledb.getConnection();

    const stream = await conn.queryStream('select * from employees', [], {outFormat: oracledb.OBJECT});

    res.writeHead(200, {'Content-Type': 'application/json'});

    stream.pipe(toJSONArray).pipe(res);

    stream.on('close', async () => {
      try {
        await conn.close();
      } catch (err) {
        console.log(err);
      }
    });

    stream.on('error', async (err) => {
      next(err);

      try {
        await conn.close();
      } catch (err) {
        console.log(err);
      }
    });
  } catch (err) {
    next(err);
  }
}

module.exports.get = get;