【MongoDB】MapReduceとMath.random()でランダムな値を取得するクエリの書き方
MapReduceの仕組みを活用すれば、MongoDBのコレクションからランダムな値を取得できます。ポイントは、mapReduce()とMath.random()を組み合わせることです。この記事では、サンプルコレクションの作成から、ランダムにドキュメントを抽出するクエリの実行まで、手順を追って解説します。
サンプルコレクションの作成
まず、テスト用のコレクション「demo651」を作成し、複数のドキュメントを挿入しておきます。
> db.demo651.insertOne({Value:10});
{
"acknowledged" : true,
"insertedId" : ObjectId("5e9f0330e3c3cd0dcff36a57")
}
> db.demo651.insertOne({Value:20});
{
"acknowledged" : true,
"insertedId" : ObjectId("5e9f0332e3c3cd0dcff36a58")
}
> db.demo651.insertOne({Value:30});
{
"acknowledged" : true,
"insertedId" : ObjectId("5e9f0335e3c3cd0dcff36a59")
}
> db.demo651.insertOne({Value:40});
{
"acknowledged" : true,
"insertedId" : ObjectId("5e9f0337e3c3cd0dcff36a5a")
}
> db.demo651.insertOne({Value:50});
{
"acknowledged" : true,
"insertedId" : ObjectId("5e9f0339e3c3cd0dcff36a5b")
}
> db.demo651.insertOne({Value:60});
{
"acknowledged" : true,
"insertedId" : ObjectId("5e9f033be3c3cd0dcff36a5c")
}
> db.demo651.insertOne({Value:70});
{
"acknowledged" : true,
"insertedId" : ObjectId("5e9f033ee3c3cd0dcff36a5d")
}
> db.demo651.insertOne({Value:80});
{
"acknowledged" : true,
"insertedId" : ObjectId("5e9f0340e3c3cd0dcff36a5e")
}
登録済みドキュメントの確認
find()メソッドを使えば、コレクション内の全ドキュメントを一覧表示できます。
> db.demo651.find();
実行すると、以下のような出力が得られます。
{ "_id" : ObjectId("5e9f0330e3c3cd0dcff36a57"), "Value" : 10 }
{ "_id" : ObjectId("5e9f0332e3c3cd0dcff36a58"), "Value" : 20 }
{ "_id" : ObjectId("5e9f0335e3c3cd0dcff36a59"), "Value" : 30 }
{ "_id" : ObjectId("5e9f0337e3c3cd0dcff36a5a"), "Value" : 40 }
{ "_id" : ObjectId("5e9f0339e3c3cd0dcff36a5b"), "Value" : 50 }
{ "_id" : ObjectId("5e9f033be3c3cd0dcff36a5c"), "Value" : 60 }
{ "_id" : ObjectId("5e9f033ee3c3cd0dcff36a5d"), "Value" : 70 }
{ "_id" : ObjectId("5e9f0340e3c3cd0dcff36a5e"), "Value" : 80 }
MapReduceでランダムな値をフェッチするクエリ
それでは本題である、ランダムにデータを取得するクエリを見ていきましょう。map関数の中でMath.random()を使い、生成された乱数が0.1未満の場合(約10%の確率)にだけドキュメントをemitします。reduce関数は、受け取った値をそのまま返すシンプルな実装で問題ありません。
> map = function() {
...
... if (Math.random() < 0.1) {
... emit(this._id, this);
... }
... }
function () {
if (Math.random() < 0.1) {
emit(this._id, this);
}
}
>
> reduce = function(key, values) {
... return values;
... }
function (key, values) {
return values;
}
>
> db.demo651.mapReduce( map, reduce, { out: 'demo_651' } );
{
"result" : "demo_651",
"timeMillis" : 1104,
"counts" : {
"input" : 8,
"emit" : 1,
"reduce" : 0,
"output" : 1
},
"ok" : 1
}
> db.demo_651.find();
結果として、ランダムに選ばれた1件のドキュメントが出力されます。実行するたびに選ばれるドキュメントは変わる点に注目してください。
{ "_id" : ObjectId("5e9f033be3c3cd0dcff36a5c"), "value" : { "_id" : ObjectId("5e9f033be3c3cd0dcff36a5c"), "Value" : 60 } }
補足:$sample演算子という選択肢
MongoDB 4.4以降ではmapReduce()は非推奨となっており、集計フレームワークの$sampleステージを使う方法が公式に推奨されています。例えば、db.demo651.aggregate([{$sample:{size:1}}])と書けば、同様にランダムなドキュメントを1件取得でき、よりシンプルかつ高速です。ただし、既存システムとの互換性維持やMapReduceの学習目的であれば、今回紹介したアプローチも依然として有効です。
まとめ
mapReduce()とMath.random()を組み合わせれば、MongoDBのコレクションからランダムなドキュメントを簡単に抽出できます。emitする確率(乱数の閾値)を調整することで、取得件数をコントロールできるのも大きな魅力です。新規プロジェクトでは、より効率的な$sample演算子の活用もあわせて検討してみてください。
-
単純なクエリを使ってMongoDBのドキュメントをフィルタリングする方法
$match演算子によるドキュメントのフィルタリングMongoDBで単純なクエリを使ってドキュメントをフィルタリングするには、集計パイプライン(aggregate)の中で$match演算子を使用します。$matchは、指定された条件に一致するドキュメントだけを次のパイプラインステージへ通過させる役割を持つステージです。まずは、ドキュメントを含むコレクションを作成してみましょう。> db.demo629.insertOne(... {... "Subje
-
MongoDBで配列をクエリして特定の値を取得する方法
配列から特定の値を取得するには、aggregate()メソッドと$projectステージを組み合わせて使用します。まずは、ドキュメントを含むコレクションを作成してみましょう。 > db.demo761.insertOne( ... { ... "details": [ ... { ... "