MongoDB
 sql >> база данни >  >> NoSQL >> MongoDB

Итерация на асинхронен курсор с асинхронна подзадача

Cursor.hasNext() методът също е "асинхронен", така че трябва да await това също. Същото важи и за Cursor.next() . Следователно действителното използване на "loop" наистина трябва да бъде while :

async function dbanalyze(){

  let cursor = db.collection('randomcollection').find()
  while ( await cursor.hasNext() ) {  // will return false when there are no more results
    let doc = await cursor.next();    // actually gets the document
    // do something, possibly async with the current document
  }

}

Както е отбелязано в коментарите, в крайна сметка Cursor.hasNext() ще върне false когато курсорът действително е изчерпан, и Cursor.next() е нещото, което всъщност извлича всяка стойност от курсора. Бихте могли да направите други структури и да break цикълът, когато hasNext() е false , но по-естествено се поддава на while .

Те все още са "асинхронни", така че трябва да await резолюцията на обещанието за всеки и това беше основният факт, който пропускахте.

Що се отнася до Cursor.map() , тогава вероятно пропускате точката, че може да бъде маркирана с async флаг и на предоставената функция:

 cursor.map( async doc => {                   // We can mark as async
    let newDoc = await someAsyncMethod(doc);  // so you can then await inside
    return newDoc;
 })

Но вие все пак искате да го „повторите“ някъде, освен ако не можете да се разминете с помощта на .pipe() към друга изходна дестинация.

Също така async/await флаговете също правят Cursor.forEach() „по-практично отново“ , тъй като това е един често срещан недостатък, че не можете просто да се справите с "вътрешно" асинхронно повикване, но с тези флагове вече можете да го правите с лекота, макар че разбира се, тъй като трябва използвайте обратно извикване, вероятно искате да обвиете това в Promise :

await new Promise((resolve, reject) => 
  cursor.forEach(
    async doc => {                              // marked as async
      let newDoc = await someAsyncMethod(doc);  // so you can then await inside
      // do other things
    },
    err => {
      // await was respected, so we get here when done.
      if (err) reject(err);
      resolve();
    }
  )
);

Разбира се, винаги е имало начини това да се приложи или с обратно извикване, или с обикновени имплементации на Promise, но това е „захарта“ на async/await отколкото всъщност прави това да изглежда много по-чисто.

NodeJS v10.x и MongoDB Node драйвер 3.1.x и по-нова версия

А любимата версия използва AsyncIterator който вече е активиран в NodeJS v10 и нагоре. Това е много по-чист начин за повторение

async function dbanalyze(){

  let cursor = db.collection('randomcollection').find()
  for await ( let doc of cursor ) {
    // do something with the current document
  }    
}

Което „по някакъв начин“ се връща към това, което първоначално беше зададен въпросът относно използването на for цикъл, тъй като можем да направим for-await-of синтаксис тук, преди да поддържа iteable, който поддържа правилния интерфейс. И Cursor поддържа този интерфейс.

Ако сте любопитни, ето списък, който съставих преди време, за да демонстрирам различни техники за итерация на курсора. Той дори включва случай за асинхронни итератори от функция на генератор:

const Async = require('async'),
      { MongoClient, Cursor } = require('mongodb');

const testLen = 3;
(async function() {

  let db;

  try {
    let client = await MongoClient.connect('mongodb://localhost/');

    let db = client.db('test');
    let collection = db.collection('cursortest');

    await collection.remove();

    await collection.insertMany(
      Array(testLen).fill(1).map((e,i) => ({ i }))
    );

    // Cursor.forEach
    console.log('Cursor.forEach');
    await new Promise((resolve,reject) => {
      collection.find().forEach(
        console.log,
        err => {
          if (err) reject(err);
          resolve();
        }
      );
    });

    // Async.during awaits cursor.hasNext()
    console.log('Async.during');
    await new Promise((resolve,reject) => {

      let cursor = collection.find();

      Async.during(
        (callback) => Async.nextTick(() => cursor.hasNext(callback)),
        (callback) => {
          cursor.next((err,doc) => {
            if (err) callback(err);
            console.log(doc);
            callback();
          })
        },
        (err) => {
          if (err) reject(err);
          resolve();
        }
      );

    });

    // async/await allows while loop
    console.log('async/await while');
    await (async function() {

      let cursor = collection.find();

      while( await cursor.hasNext() ) {
        let doc = await cursor.next();
        console.log(doc);
      }

    })();

    // await event stream
    console.log('Event Stream');
    await new Promise((end,error) => {
      let cursor = collection.find();

      for ( let [k,v] of Object.entries({ end, error, data: console.log }) )
        cursor.on(k,v);
    });

    // Promise recursion
    console.log('Promise recursion');
    await (async function() {

      let cursor = collection.find();

      function iterate(cursor) {
        return cursor.hasNext().then( bool =>
          (bool) ? cursor.next().then( doc => {
            console.log(doc);
            return iterate(cursor);
          }) : Promise.resolve()
        )
      }

      await iterate(cursor);

    })();

    // Uncomment if node is run with async iteration enabled
    // --harmony_async_iteration


    console.log('Generator Async Iterator');
    await (async function() {

      async function* cursorAsyncIterator() {
        let cursor = collection.find();

        while (await cursor.hasNext() ) {
          yield cursor.next();
        }

      }

      for await (let doc of cursorAsyncIterator()) {
        console.log(doc);
      }

    })();


    // This is supported with Node v10.x and the 3.1 Series Driver
    await (async function() {

      for await (let doc of collection.find()) {
        console.log(doc);
      }

    })();

    client.close();

  } catch(e) {
    console.error(e);
  } finally {
    process.exit();
  }

})();



  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Как да наложа външни ключове в бази данни NoSql (MongoDB)?

  2. MongoDB .Net драйвер 2.0 Pull (премахване на елемент)

  3. Драйверът на Mongodb C# връща само съвпадащи поддокументи в масива

  4. как мога да видя какви портове mongo слуша от mongo shell?

  5. Урок за PyMongo:Тестване на отказ на MongoDB във вашето Python приложение