MongoDB
 sql >> Baza danych >  >> NoSQL >> MongoDB

Jak warunkowo zrestartować łańcuch obietnic od początku?

Krótko mówiąc, w tym przypadku nie musisz tego robić. Ale jest dłuższe wyjaśnienie.

Jeśli Twoja wersja MongoDB to obsługuje, możesz po prostu użyć $przykład potok agregacji po początkowych warunkach zapytania w celu uzyskania „losowego” wyboru.

Oczywiście w każdym przypadku, jeśli ktoś nie kwalifikuje się ponieważ już "wygrał" to po prostu oznacz go jako takiego, albo bezpośrednio w innym zestawie wyników tabelarycznych. Ale ogólnym przypadkiem „wykluczenia” jest tutaj po prostu zmodyfikowanie zapytania, aby wykluczyć „zwycięzców” z możliwych wyników.

Jednak faktycznie zademonstruję „przerywanie pętli” przynajmniej w „nowoczesnym” sensie, mimo że tak naprawdę nie potrzebujesz tego do tego, co faktycznie musisz tutaj zrobić, czyli w rzeczywistości zmodyfikuj zapytanie, aby zamiast tego wykluczyć.

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

I oczywiście przy każdym podejściu wyniki są za każdym razem losowe, a poprzedni „zwycięzcy” są wykluczani z wyboru w samym zapytaniu. „Przerwa w pętli” jest tutaj używana jedynie do utrzymywania wyników, dopóki nie będzie więcej możliwych zwycięzców.

Uwaga na temat metod „przerywania pętli”

Ogólne zalecenie w nowoczesnych środowiskach node.js to wbudowany async/await/yield funkcje są teraz włączone jako domyślnie włączone w wersjach v8.x.x. Te wersje trafią do Long Term Support (LTS) w październiku tego roku (od momentu pisania) i będą podlegać mojej osobistej „zasadzie trzech miesięcy”, więc wszelkie nowe prace powinny opierać się na rzeczach, które byłyby aktualne w tym momencie.

Alternatywne przypadki tutaj są przedstawione za pośrednictwem async.await jako oddzielna zależność biblioteki. Lub inaczej jako oddzielna zależność biblioteki przy użyciu „Bluebird” Promise.coroutine , przy czym w drugim przypadku można naprzemiennie używać Promise.try , ale jeśli zamierzasz dołączyć bibliotekę, aby uzyskać tę funkcję, możesz równie dobrze użyć innej funkcji, która implementuje bardziej nowoczesne podejście do składni.

Tak więc „podczas” (gra słów niezamierzona) demonstruje „złamanie obietnicy/oddzwanianie” pętla, najważniejszą rzeczą, którą naprawdę należy tutaj odjąć, jest inny proces zapytania, który faktycznie wykonuje „wykluczenie”, które próbowano zaimplementować w „pętli”, dopóki nie zostanie wybrany losowy zwycięzca.

Rzeczywisty przypadek jest taki, że najlepiej to określają dane. Ale cały przykład pokazuje przynajmniej, w jaki sposób można zastosować „zarówno” zaznaczenie, jak i „przerwanie pętli”.




  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Wyszukiwanie pełnotekstowe MongoDB ze sterownikiem haskell

  2. Jaki jest odpowiednik metody AbstractMongoEventListener dla mongooperacji updateMulti?

  3. MongoDB dropIndex()

  4. Aktualizacja MongoDBOne()

  5. Jaka jest najlepsza praktyka dla połączeń MongoDB w Node.js?