sql >> Database >  >> NoSQL >> MongoDB

Hoe herstart ik de belofteketen voorwaardelijk vanaf het begin?

Kortom, u hoeft dat in dit geval niet te doen. Maar er is een langere uitleg.

Als uw MongoDB-versie dit ondersteunt, kunt u eenvoudig de gebruiken $voorbeeld aggregatiepijplijn na uw initiële vraagvoorwaarden om de "willekeurige" selectie te krijgen.

Natuurlijk, in ieder geval, als iemand niet in aanmerking komt omdat ze al "gewonnen" hebben, markeer ze dan gewoon als zodanig, ofwel direct in een andere reeks getabelleerde resultaten. Maar het algemene geval van "uitsluiting" hier is om de zoekopdracht eenvoudig aan te passen om de "winnaars" uit te sluiten van mogelijke resultaten.

Ik zal echter in feite "een lus doorbreken" demonstreren, in ieder geval in een "moderne" zin, ook al heb je dat niet echt nodig voor wat je hier echt moet doen, namelijk de query wijzigen om in plaats daarvan uit te sluiten.

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()
  }
})()

En natuurlijk zijn bij beide benaderingen de resultaten elke keer willekeurig en worden eerdere "winnaars" uitgesloten van selectie in de eigenlijke zoekopdracht zelf. De "loop break" hier wordt alleen gebruikt om resultaten te blijven produceren totdat er geen mogelijke winnaars meer kunnen zijn.

Een opmerking over de "loop breaking"-methoden

De algemene aanbeveling in moderne node.js-omgevingen is de ingebouwde async/wait/yield functies die nu standaard zijn ingeschakeld in v8.x.x-releases. Deze versies zullen in oktober van dit jaar (op het moment van schrijven) op Long Term Support (LTS) komen en volgens mijn eigen persoonlijke "regel van drie maanden", dan zouden alle nieuwe werken gebaseerd moeten zijn op dingen die op dat moment actueel zouden zijn.

De alternatieve gevallen hier worden gepresenteerd via async.await als een afzonderlijke bibliotheekafhankelijkheid. Of anders als een afzonderlijke bibliotheekafhankelijkheid met behulp van "Bluebird" Promise.coroutine , waarbij het laatste geval is dat je afwisselend Promise.try , maar als je een bibliotheek gaat toevoegen om die functie te krijgen, dan kun je net zo goed de andere functie gebruiken die de modernere syntaxisbenadering implementeert.

Dus "terwijl" ( woordspeling niet bedoeld ) demonstreert "een belofte breken/terugbellen" lus, het belangrijkste dat hier echt moet worden weggenomen, is het andere zoekproces, dat de "uitsluiting" doet die werd geprobeerd te implementeren in een "lus" totdat de willekeurige winnaar werd geselecteerd.

Het werkelijke geval is dat de gegevens dit het beste bepalen. Maar het hele voorbeeld laat in ieder geval zien hoe "zowel" de selectie als de "loop break" kan worden toegepast.




  1. Externe mongodb gebruiken met meteoorontwikkeling

  2. Hoe een base64-afbeelding in reactie te tonen?

  3. Geografisch verspreide MongoDB-clusters op AWS in de EU-regio

  4. Hoe de gedumpte gegevens van mongodump te gebruiken?