MongoDB
 sql >> Cơ Sở Dữ Liệu >  >> NoSQL >> MongoDB

Làm cách nào để khởi động lại chuỗi hứa hẹn từ đầu một cách có điều kiện?

Tóm lại, bạn thực sự không cần phải làm điều đó trong trường hợp này. Nhưng có một lời giải thích dài hơn.

Nếu phiên bản MongoDB của bạn hỗ trợ nó, thì bạn chỉ cần sử dụng $ mẫu quy trình tổng hợp sau các điều kiện truy vấn ban đầu của bạn để có được lựa chọn "ngẫu nhiên".

Tất nhiên trong mọi trường hợp, nếu ai đó không đủ điều kiện vì họ đã "thắng" thì chỉ cần đánh dấu họ là như vậy, hoặc trực tiếp vào một tập hợp kết quả được lập bảng khác. Nhưng trường hợp chung của "loại trừ" ở đây là chỉ cần sửa đổi truy vấn để loại trừ "người chiến thắng" khỏi các kết quả có thể có.

Tuy nhiên, tôi sẽ thực sự chứng minh "phá vỡ vòng lặp" ít nhất theo nghĩa "hiện đại" mặc dù bạn không thực sự cần điều đó cho những gì bạn thực sự cần làm ở đây, thực sự là sửa đổi truy vấn để loại trừ thay thế.

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

Và tất nhiên với một trong hai cách tiếp cận, kết quả là ngẫu nhiên mỗi lần và "người chiến thắng" trước đó bị loại khỏi lựa chọn trong chính truy vấn thực tế. "Vòng lặp ngắt" ở đây chỉ được sử dụng để duy trì kết quả xuất ra cho đến khi không thể có thêm người chiến thắng.

Lưu ý về các phương pháp "phá vòng lặp"

Đề xuất chung trong môi trường node.js hiện đại sẽ được tích hợp sẵn async / await / output các tính năng hiện được bật theo mặc định trong các bản phát hành v8.x.x. Các phiên bản này sẽ ra mắt Hỗ trợ dài hạn (LTS) vào tháng 10 năm nay (tính đến thời điểm viết bài) và tuân theo "quy tắc ba tháng" của cá nhân tôi, sau đó bất kỳ tác phẩm mới nào đều phải dựa trên những thứ hiện tại tại thời điểm đó.

Các trường hợp thay thế ở đây được trình bày qua async.await như một phụ thuộc thư viện riêng biệt. Hoặc dưới dạng phụ thuộc thư viện riêng biệt bằng cách sử dụng "Bluebird" Promise.coroutine , với trường hợp thứ hai là bạn có thể luân phiên sử dụng Promise.try , nhưng nếu bạn định bao gồm một thư viện để có được hàm đó, thì bạn cũng có thể sử dụng hàm khác triển khai cách tiếp cận cú pháp hiện đại hơn.

Vì vậy, "whilst" (không có ý định chơi chữ) thể hiện "thất hứa / gọi lại" vòng lặp, điều chính thực sự cần được loại bỏ khỏi đây là quy trình truy vấn khác, thực sự thực hiện "loại trừ" đang được cố gắng thực hiện trong một "vòng lặp" cho đến khi người chiến thắng ngẫu nhiên được chọn.

Trường hợp thực tế là dữ liệu xác định điều này tốt nhất. Nhưng toàn bộ ví dụ ít nhất cũng chỉ ra những cách có thể áp dụng "cả" lựa chọn và "ngắt vòng lặp".




  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. pymongo.errors.CursorNotFound:id con trỏ '...' không hợp lệ tại máy chủ

  2. Cách tốt nhất để lập mô hình hệ thống bỏ phiếu trong MongoDB

  3. Chèn dữ liệu trong MongoDB bằng PHP

  4. Mảng Mongo db thay đổi chuỗi thành float

  5. MongoDB cực kỳ chậm so với MySQL với 10 triệu bản ghi