sql >> Database teknologi >  >> NoSQL >> MongoDB

Hvordan genstarter jeg betinget løftekæden fra begyndelsen?

Kort sagt, du behøver faktisk ikke at gøre det i dette tilfælde. Men der er en længere forklaring.

Hvis din MongoDB-version understøtter det, kan du blot bruge $sample aggregeringspipeline efter dine indledende forespørgselsbetingelser for at få det "tilfældige" valg.

Selvfølgelig under alle omstændigheder, hvis nogen ikke er berettiget, fordi de allerede har "vundet", skal du blot markere dem som sådan, enten direkte på i et andet sæt af tabelresultater. Men det generelle tilfælde af "udelukkelse" her er blot at ændre forespørgslen for at udelukke "vinderne" fra mulige resultater.

Men jeg vil faktisk demonstrere "breaking a loop" i det mindste i en "moderne" forstand, selvom du faktisk ikke har brug for det for det, du faktisk skal gøre her, som faktisk er at ændre forespørgslen til at ekskludere i stedet.

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

Og selvfølgelig med begge tilgange er resultaterne tilfældige hver gang, og tidligere "vindere" er udelukket fra udvælgelsen i selve forespørgslen. "loop break" her bruges blot til at blive ved med at udskrive resultater, indtil der ikke kan være flere mulige vindere.

En note om "loop breaking"-metoderne

Den generelle anbefaling i moderne node.js-miljøer ville være den indbyggede async/await/yield funktioner inkluderet nu som aktiveret som standard i v8.x.x-udgivelser. Disse versioner vil ramme Long Term Support (LTS) i oktober i år (i skrivende stund) og i henhold til min egen personlige "tre måneders regel", så bør alle nye værker være baseret på ting, der ville være aktuelle på det tidspunkt.

De alternative tilfælde her præsenteres via async.await som en separat biblioteksafhængighed. Eller på anden måde som en separat biblioteksafhængighed ved hjælp af "Bluebird" Promise.coroutine , hvor sidstnævnte tilfælde er, at du alternativt kunne bruge Promise.try , men hvis du vil inkludere et bibliotek for at få den funktion, så kan du lige så godt bruge den anden funktion, som implementerer den mere moderne syntakstilgang.

Så "mens" (ordspil ikke tilsigtet) demonstrerer "at bryde et løfte/tilbagekald" loop, det vigtigste, der virkelig bør tages væk herfra, er den anderledes forespørgselsproces, som faktisk udfører den "udelukkelse", som blev forsøgt implementeret i en "loop", indtil den tilfældige vinder blev valgt.

Det faktiske tilfælde er, at dataene bestemmer dette bedst. Men hele eksemplet viser i det mindste måder, hvorpå "både" markeringen og "loop break" kan anvendes.




  1. NodeJS forbindelsesfejl med mongoDB

  2. Unik begrænsning med to felter i MongoDB

  3. Hvordan gemmer og opdateres binære data effektivt i Mongodb?

  4. Hvordan indlæses 100 millioner poster i MongoDB med Scala til præstationstest?