MongoDB
 sql >> Teknologi Basis Data >  >> NoSQL >> MongoDB

Bagaimana cara memulai kembali rantai janji secara kondisional dari awal?

Singkatnya, Anda sebenarnya tidak perlu melakukan itu dalam kasus ini. Tapi ada penjelasan yang lebih panjang.

Jika versi MongoDB Anda mendukungnya, maka Anda cukup menggunakan $sample pipa agregasi setelah kondisi kueri awal Anda untuk mendapatkan pilihan "acak".

Tentu saja, jika seseorang tidak memenuhi syarat karena mereka sudah "menang" maka cukup tandai mereka seperti itu, baik secara langsung di kumpulan hasil tabulasi lainnya. Tetapi kasus umum "pengecualian" di sini adalah dengan hanya memodifikasi kueri untuk mengecualikan "pemenang" dari kemungkinan hasil.

Namun, saya benar-benar akan menunjukkan "melanggar lingkaran" setidaknya dalam arti "modern" meskipun Anda sebenarnya tidak membutuhkannya untuk apa yang sebenarnya perlu Anda lakukan di sini, yang sebenarnya adalah memodifikasi kueri untuk dikecualikan.

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

Dan tentu saja dengan pendekatan mana pun, hasilnya acak setiap kali dan "pemenang" sebelumnya dikeluarkan dari seleksi dalam kueri aktual itu sendiri. "Loop break" di sini hanya digunakan untuk terus mengeluarkan hasil sampai tidak ada lagi pemenang yang mungkin.

Catatan tentang metode "pemutusan loop"

Rekomendasi umum di lingkungan node.js modern adalah async/await/yield bawaan fitur sekarang disertakan sebagai diaktifkan secara default dalam rilis v8.x.x. Versi-versi ini akan mencapai Dukungan Jangka Panjang (LTS) pada bulan Oktober tahun ini ( pada saat penulisan ) dan mengikuti "aturan tiga bulan" pribadi saya sendiri, maka setiap karya baru harus didasarkan pada hal-hal yang akan terkini pada saat itu.

Kasus alternatif di sini disajikan melalui async.await sebagai ketergantungan perpustakaan yang terpisah. Atau sebaliknya sebagai dependensi perpustakaan terpisah menggunakan "Bluebird" Promise.coroutine , dengan kasus terakhir adalah Anda dapat menggunakan Promise.try , tetapi jika Anda akan menyertakan pustaka untuk mendapatkan fungsi tersebut, sebaiknya gunakan fungsi lain yang mengimplementasikan pendekatan sintaksis yang lebih modern.

Jadi "sementara" ( permainan kata-kata tidak dimaksudkan ) menunjukkan "melanggar janji/panggilan balik" loop, hal utama yang benar-benar harus diambil dari sini adalah proses kueri yang berbeda, yang sebenarnya melakukan "pengecualian" yang dicoba untuk diterapkan dalam "loop" hingga pemenang acak dipilih.

Kasus sebenarnya adalah data menentukan ini yang terbaik. Namun seluruh contoh setidaknya menunjukkan cara "baik" seleksi dan "loop break" dapat diterapkan.




  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Cara Menginstal Edisi Komunitas MongoDB di Ubuntu

  2. Kebocoran koneksi mongo dengan morphia

  3. Membuat Data Uji untuk MongoDB

  4. Fungsi agregat menduplikasi item dalam ng-repeat pada penyegaran halaman. Perlu mencari cara untuk menghentikan duplikasi. Angularjs Mongodb luwak

  5. Mongoose - Pembaruan yang efisien pada array yang diindeks dari luwak.Schema.Types.Mixed