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

Как да рестартирам условно веригата обещания от началото?

Накратко, всъщност не е необходимо да правите това в този случай. Но има по-дълго обяснение.

Ако вашата версия на MongoDB го поддържа, можете просто да използвате $sample тръбопровод за агрегиране след първоначалните ви условия на заявка, за да получите „случайния“ избор.

Разбира се, във всеки случай, ако някой не отговаря на условията, защото вече е „спечелил“, тогава просто го маркирайте като такъв, или директно в друг набор от таблични резултати. Но общият случай на „изключване“ тук е просто да промените заявката, за да изключите „победителите“ от възможните резултати.

Въпреки това, аз всъщност ще демонстрирам "разбиване на цикъл" поне в "модерен" смисъл, въпреки че всъщност не се нуждаете от това за това, което всъщност трябва да направите тук, което всъщност е да промените заявката, за да изключите вместо това.

const MongoClient = require('mongodb').MongoClient,
      whilst = require('async').whilst,
      BPromise = require('bluebird');

const users = [
  'Bill',
  'Ted',
  'Fred',
  'Fleur',
  'Ginny',
  'Harry'
];

function log (data) {
  console.log(JSON.stringify(data,undefined,2))
}

const oneHour = ( 1000 * 60 * 60 );

(async function() {

  let db;

  try {
    db = await MongoClient.connect('mongodb://localhost/raffle');

    const collection = db.collection('users');

    // Clean data
    await collection.remove({});

    // Insert some data
    let inserted = await collection.insertMany(
      users.map( name =>
        Object.assign({ name },
          ( name !== 'Harry' )
            ? { updated: new Date() }
            : { updated: new Date( new Date() - (oneHour * 2) ) }
        )
      )
    );
    log(inserted);

    // Loop with aggregate $sample
    console.log("Aggregate $sample");

    while (true) {
      let winner = (await collection.aggregate([
        { "$match": {
          "updated": {
            "$gte": new Date( new Date() - oneHour ),
            "$lt": new Date()
          },
          "isWinner": { "$ne": true }
        }},
        { "$sample": { "size": 1 } }
      ]).toArray())[0];

      if ( winner !== undefined ) {
        log(winner);    // Picked winner
        await collection.update(
          { "_id": winner._id },
          { "$set": { "isWinner": true } }
        );
        continue;
      }
      break;
    }

    // Reset data state
    await collection.updateMany({},{ "$unset": { "isWinner": "" } });

    // Loop with random length
    console.log("Math random selection");
    while (true) {
      let winners = await collection.find({
        "updated": {
          "$gte": new Date( new Date() - oneHour ),
          "$lt": new Date()
        },
        "isWinner": { "$ne": true }
      }).toArray();

      if ( winners.length > 0 ) {
        let winner = winners[Math.floor(Math.random() * winners.length)];
        log(winner);
        await collection.update(
          { "_id": winner._id },
          { "$set": { "isWinner": true } }
        );
        continue;
      }
      break;
    }

    // Reset data state
    await collection.updateMany({},{ "$unset": { "isWinner": "" } });

    // Loop async.whilst
    console.log("async.whilst");

    // Wrap in a promise to await
    await new Promise((resolve,reject) => {
      var looping = true;
      whilst(
        () => looping,
        (callback) => {
          collection.find({
            "updated": {
              "$gte": new Date( new Date() - oneHour ),
              "$lt": new Date()
            },
            "isWinner": { "$ne": true }
          })
          .toArray()
          .then(winners => {
            if ( winners.length > 0 ) {
              let winner = winners[Math.floor(Math.random() * winners.length)];
              log(winner);
              return collection.update(
                { "_id": winner._id },
                { "$set": { "isWinner": true } }
              );
            } else {
              looping = false;
              return
            }
          })
          .then(() => callback())
          .catch(err => callback(err))
        },
        (err) => {
          if (err) reject(err);
          resolve();
        }
      );
    });

    // Reset data state
    await collection.updateMany({},{ "$unset": { "isWinner": "" } });

    // Or synatax for Bluebird coroutine where no async/await
    console.log("Bluebird coroutine");

    await BPromise.coroutine(function* () {
      while(true) {
        let winners = yield collection.find({
          "updated": {
            "$gte": new Date( new Date() - oneHour ),
            "$lt": new Date()
          },
          "isWinner": { "$ne": true }
        }).toArray();

        if ( winners.length > 0 ) {
          let winner = winners[Math.floor(Math.random() * winners.length)];
          log(winner);
          yield collection.update(
            { "_id": winner._id },
            { "$set": { "isWinner": true } }
          );
          continue;
        }
        break;
      }
    })();

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

И, разбира се, при двата подхода резултатите са произволни всеки път и предишните „победители“ се изключват от избора в самата заявка. „Прекъсването на цикъла“ тук се използва само за продължаване на извеждането на резултати, докато не изчезнат възможните победители.

Бележка относно методите за "разбиване на цикъл"

Общата препоръка в съвременните среди на node.js би била вграденият async/await/yield функции, които вече са включени като включени по подразбиране във версии v8.x.x. Тези версии ще достигнат дългосрочна поддръжка (LTS) през октомври тази година (към момента на писане) и според моето лично „правило за три месеца“, тогава всички нови произведения трябва да се основават на неща, които биха били актуални към този момент.

Алтернативните случаи тук са представени чрез async.await като отделна зависимост от библиотеката. Или по друг начин като отделна зависимост от библиотека с помощта на "Bluebird" Promise.coroutine , като последният случай е, че можете алтернативно да използвате Promise.try , но ако ще включите библиотека, за да получите тази функция, тогава можете също да използвате другата функция, която прилага по-модерния синтаксисен подход.

Така че „докато“ (игра на думи не е предназначена), демонстрираща „нарушаване на обещание/обратно повикване“ цикъл, основното нещо, което наистина трябва да бъде премахнато от тук, е различният процес на заявка, който всъщност прави „изключването“, което се опитваше да бъде приложено в „цикл“, докато не бъде избран произволният победител.

Действителният случай е, че данните определят това най-добре. Но целият пример поне показва начини, по които могат да бъдат приложени "както" селекцията, така и "прекъсването на цикъла".




  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Мога ли все още да имам достъп до фасет за контакт на ядрото на сайта, след като сесията бъде изчистена?

  2. Spring Data MongoDB - $eq в рамките на $project поддръжка

  3. Mongoose вградени документи / DocumentsArrays id

  4. Как да изключа полета от вграден документ в Mongoid?

  5. Визуализация на вашата клъстерна топология в ClusterControl