MongoDBは、複数のクエリを実行することなく、多数のランダムドキュメントに資金を提供できますか?
例えばコレクション内のすべてのドキュメントを読み込んだ後、JS側で実装しましたが、これは無駄です。したがって、1つのdbクエリでこれをより適切に実行できるかどうかを確認したかっただけですか。
私がJS側でたどった道:
2つの大きな欠点は、すべてのデータを読み込んでいること、または複数のクエリを実行していることです。
どんな提案も大歓迎です
これはずっと前に答えられ、それ以来、MongoDBは大きく進化しました。
別の回答に投稿されているように、MongoDBはバージョン3.2以降 Aggregation Framework内でのサンプリング をサポートするようになりました。
これを行う方法は次のとおりです。
db.products.aggregate([{$sample: {size: 5}}]); // You want to get 5 docs
または:
db.products.aggregate([
{$match: {category:"Electronic Devices"}}, // filter the results
{$sample: {size: 5}} // You want to get 5 docs
]);
ただし、$ sample演算子については いくつかの警告 があります。
(2017年11月6日現在、最新バージョンは3.4です)=>これのいずれかが満たされない場合:
上記の条件のいずれかが満たされない場合、$ sampleはコレクションスキャンを実行し、続いてランダムソートを実行してN個のドキュメントを選択します。
$ matchを使用した最後の例のように
古い答え
いつでも実行できます:
db.products.find({category:"Electronic Devices"}).skip(Math.random()*YOUR_COLLECTION_SIZE)
ただし、順序はランダムではなく、2つのクエリ(YOUR_COLLECTION_SIZEを取得するための1つのカウント)またはその大きさの見積もり(約100レコード、約1000、約10000 ...)が必要になります。
乱数を使用してすべてのドキュメントにフィールドを追加し、その番号でクエリを実行することもできます。ここでの欠点は、同じクエリを実行するたびに同じ結果が得られることです。これを修正するには、いつでも制限とスキップ、または並べ替えで遊ぶことができます。レコードをフェッチするたびにこれらの乱数を更新することもできます(より多くのクエリを意味します)。
--Mongoose、Mondoid、または直接Mongo Driverを特定の言語で使用しているかどうかはわかりません。そのため、mongoShellについてすべて説明します。
したがって、たとえば、製品レコードは次のようになります。
{
_id: ObjectId("..."),
name: "Awesome Product",
category: "Electronic Devices",
}
そして私は使用することをお勧めします:
{
_id: ObjectId("..."),
name: "Awesome Product",
category: "Electronic Devices",
_random_sample: Math.random()
}
次に、次のことができます。
db.products.find({category:"Electronic Devices",_random_sample:{$gte:Math.random()}})
次に、定期的に実行して、ドキュメントの_random_sampleフィールドを定期的に更新できます。
var your_query = {} //it would impact in your performance if there are a lot of records
your_query = {category: "Electronic Devices"} //Update
//upsert = false, multi = true
db.products.update(your_query,{$set:{_random_sample::Math.random()}},false,true)
または、一部のレコードを取得するたびに、すべてまたは一部を更新できます(取得したレコードの数によって異なります)。
for(var i = 0; i < records.length; i++){
var query = {_id: records[i]._id};
//upsert = false, multi = false
db.products.update(query,{$set:{_random_sample::Math.random()}},false,false);
}
[〜#〜]編集[〜#〜]
を注意
db.products.update(your_query,{$set:{_random_sample::Math.random()}},false,true)
クエリに一致するすべての製品を同じ乱数で更新するため、うまく機能しません。最後のアプローチの方がうまく機能します(ドキュメントを取得するときに一部のドキュメントを更新します)
3.2以降、コレクションからドキュメントのランダムサンプルを取得する簡単な方法があります。
$ sampleバージョン3.2の新機能。
入力から指定された数のドキュメントをランダムに選択します。
$ sampleステージの構文は次のとおりです。
{ $sample: { size: <positive integer> } }
この場合:
db.products.aggregate([{$sample: {size: 10}}]);
これが私が最後に思いついたものです:
var numberOfItems = 10;
// GET LIST OF ALL ID's
SchemaNameHere.find({}, { '_id': 1 }, function(err, data) {
if (err) res.send(err);
// shuffle array, as per here https://github.com/coolaj86/knuth-shuffle
var arr = shuffle(data.slice(0));
// get only the first numberOfItems of the shuffled array
arr.splice(numberOfItems, arr.length - numberOfItems);
// new array to store all items
var return_arr = [];
// use async each, as per here http://justinklemm.com/node-js-async-tutorial/
async.each(arr, function(item, callback) {
// get items 1 by 1 and add to the return_arr
SchemaNameHere.findById(item._id, function(err, data) {
if (err) res.send(err);
return_arr.Push(data);
// go to the next one item, or to the next function if done
callback();
});
}, function(err) {
// run this when looped through all items in arr
res.json(return_arr);
});
});
スキップは私にとってうまくいきませんでした。これが私が最終的に得たものです:
var randomDoc = db.getCollection("collectionName").aggregate([ {
$match : {
// criteria to filter matches
}
}, {
$sample : {
size : 1
}
} ]).result[0];
基準に一致する単一のランダムな結果を取得します。