Как «передать» данные oracle-db из события «on data» - PullRequest
0 голосов
/ 14 мая 2018

Я использую node-oracledb в течение нескольких месяцев, и мне удалось достичь того, что мне нужно до сих пор.

В настоящее время я работаю над поисковым приложением, которое потенциально может вернуть около2м строки данных за один звонок.Чтобы убедиться, что я не получаю отключение от браузера и сервера, я подумал, что я бы попробовал queryStream, чтобы обеспечить постоянный поток данных обратно клиенту.

Я реализовал queryStream пример как есть, и это работало нормально для нескольких сотен тысяч строк.Однако, когда количество возвращаемых строк превышает миллион, узлу не хватает памяти.Записывая и просматривая события журнала клиента и сервера, я вижу, что клиент сильно отстает от сервера с точки зрения количества отправленных и полученных строк.Итак, похоже, что Node падает, потому что он буферизует так много данных.

Стоит отметить, что на данный момент моя реализация selectstream находится в функции req / res, вызываемой через Express.

Чтобы вернуть данные, я делаю что-то вроде ....

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

    rowcount++;

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

});

Я читал о том, как потоки и труба могут помочь с потоком, поэтому я хотел бы иметь возможность передавать результаты запроса в: а) помощь с потоком иб) чтобы можно было передать результаты другим функциям перед отправкой обратно клиенту.

Например,

function getData(req, res){

    var stream = myQueryStream(connection, query);

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

}

Я потратил несколько часов, пытаясь найти решение или пример, которыйпозволяет мне передавать результаты, но я застрял и нуждаюсь в некоторой помощи.

Извиняюсь, если упускаю что-то очевидное, но я все еще пытаюсь справиться с Node и особенно с потоками.

Заранее спасибо.

1 Ответ

0 голосов
/ 18 мая 2018

Здесь есть небольшое несоответствие импеданса. API queryStream генерирует строки объектов JavaScript, но для потоковой передачи клиенту требуется массив JSON. По сути, вам нужно добавить открытую скобку в начало, запятую после каждой строки и закрывающую скобку в конец.

Я покажу вам, как это сделать в контроллере, который использует драйвер напрямую, как вы, вместо использования отдельных модулей базы данных, как я рекомендую в этой серии .

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, который позволяет вам использовать pipe в логике контроллера:

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;
...