Страницы

среда, 10 февраля 2016 г.

Node.js MongoDB Iterate Cursor

Три паттерна итерации по курсору MongoDB: первый - "классический" - на старых добрых функциях обратного вызова, второй - "на любителя" - на промисах, третий - "для извращенцев эстетов" - на промисах и генераторах с использованием co, второй и третий можно применять только со второй версией MongoDB Native NodeJS Driver и Node.js не меньше четвертой версии.

Первый:
const next = (cursor, fn, cb) => {
  function next(err) {
    if (err) return cb(err);
    cursor.nextObject(onNext);
  }
  function onNext(err, item) {
    if (err) return cb(err);
    if (item) return fn(item, next);
    cb();
  }
  next();
};

... упрощенный вариант:
const next = (cursor, fn, cb) => {
  const next = (err) => {
    if (err) return cb(err);
    cursor.nextObject((err, item) => {
      if (err) return cb(err);
      if (item) return fn(item, next);
      cb();
    });
  };
  next();
};

Тестируем:
require('mongodb').connect('mongodb://localhost/test', (err, db) => {
  if (err) throw err;
  const collection = db.collection('test');
  console.log('test');
  collection.deleteMany({}, (err) => {
    if (err) throw err;
    collection.insertMany([{ a: 1, b: 2 }, { a: 2, b: 3 }], (err) => {
      if (err) throw err;
      next(collection.find({}), (item, cb) => (console.log(item), cb()), (err) => {
        if (err) throw err;
        console.log('exit');
        db.close();
        process.exit(0);
      });
    });
  });
});


Второй:
const next = (cursor, fn) => new Promise((resolve, reject) => {
  function onNext(item) {
    if (item) return fn(item, next);
    resolve();
  }
  function next() {
    cursor.next().then(onNext, reject);
  }
  next();
});

... упрощенный вариант:
const next = (cursor, fn) => new Promise((resolve, reject) => {
  const onNext = (item) => {
    if (item) return fn(item, () => cursor.next().then(onNext, reject));
    resolve();
  };
  cursor.next().then(onNext, reject);
});

Тестируем:
require('mongodb').connect('mongodb://localhost/test')
  .then((db) => {
    const collection = db.collection('test');
    console.log('test');
    return collection.deleteMany({})
      .then(collection.insertMany([{ a: 1, b: 2 }, { a: 2, b: 3 }]))
      .then(() => next(collection.find({}), (item, cb) => (console.log(item), cb())))
      .then(() => db);
  })
  .then((db) => (console.log('exit'), db.close(), process.exit(0)))
  .catch((err) => (console.error(err), process.exit(1)));


Третий:
const next = (cursor, fn) => function* () {
  while (yield cursor.hasNext()) {
    fn(yield cursor.next());
  }
};

Тестируем:
require('co')(function* () {
  const db = yield require('mongodb').connect('mongodb://localhost/test');
  const collection = db.collection('test');
  console.log('test');
  yield collection.deleteMany({});
  yield collection.insertMany([{ a: 1, b: 2 }, { a: 2, b: 3 }]);
  yield next(collection.find({}), (item) => console.log(item));
  console.log('exit');
  db.close();
}).catch((err) => (console.error(err), process.exit(1)));


Хозяйкам на заметку.