簡単に言うと、この場合、実際にそれを行う必要はありません。しかし、もっと長い説明があります。
MongoDBのバージョンでサポートされている場合は、を使用できます。 $ sample
「ランダムな」選択を取得するための、最初のクエリ条件の後の集約パイプライン。
もちろん、いずれにせよ、誰かがすでに「勝った」ために資格がない場合は、別の表形式の結果のセットで直接、そのようにマークするだけです。ただし、ここでの「除外」の一般的なケースは、クエリを変更して「勝者」を可能な結果から除外することです。
ただし、ここで実際に行う必要があること、つまりクエリを変更して除外することは実際には必要ない場合でも、少なくとも「モダン」な意味で「ループを壊す」ことを実際に示します。
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()
}
})()
そしてもちろん、どちらのアプローチでも、結果は毎回ランダムであり、以前の「勝者」は実際のクエリ自体の選択から除外されます。ここでの「ループブレーク」は、勝者がなくなるまで結果を出力し続けるために使用されます。
「ループブレイク」メソッドに関する注意
最新のnode.js環境での一般的な推奨事項は、組み込みの async / await / yield
です。 v8.x.xリリースでデフォルトでオンになっている機能が含まれるようになりました。これらのバージョンは、今年10月にロングタームサポート(LTS)にヒットし(執筆時点)、私自身の「3か月ルール」に従っています。新しい作品は、その時点での最新のものに基づいている必要があります。
ここでの代替ケースは、 async.await
を介して提示されます。
別のライブラリ依存関係として。または、「Bluebird」を使用した個別のライブラリ依存関係として Promise.coroutine コード>
、後者の場合は、 Promise.try コード>
、ただし、その関数を取得するためにライブラリを含める場合は、より最新の構文アプローチを実装する他の関数を使用することもできます。
したがって、「ながら」(しゃれは意図されていません)は、「約束/コールバックを破る」ことを示します ループ、ここから実際に取り除く必要がある主なことは、ランダムな勝者が選択されるまで「ループ」で実装しようとしていた「除外」を実際に実行する別のクエリプロセスです。
実際のケースは、データがこれを最もよく決定するということです。しかし、例全体は、少なくとも選択と「ループブレーク」の「両方」を適用できる方法を示しています。