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".