web-dev-qa-db-ja.com

MongoDB:100のコレクションから10のランダムなドキュメントを見つける方法は?

MongoDBは、複数のクエリを実行することなく、多数のランダムドキュメントに資金を提供できますか?

例えばコレクション内のすべてのドキュメントを読み込んだ後、JS側で実装しましたが、これは無駄です。したがって、1つのdbクエリでこれをより適切に実行できるかどうかを確認したかっただけですか。

私がJS側でたどった道:

  • すべてのデータを取得する
  • iDの配列を作成します
  • iDのシャッフル配列(ランダムな順序)
  • アレイを必要なドキュメント数に接続します
  • コレクション全体から1つずつ、前の2つの操作の後に残したIDでドキュメントを選択して、ドキュメントのリストを作成します。

2つの大きな欠点は、すべてのデータを読み込んでいること、または複数のクエリを実行していることです。

どんな提案も大歓迎です

13
Iladarsda

これはずっと前に答え​​られ、それ以来、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は、コレクション内のドキュメント全体の5%未満です
  • コレクションには100を超えるドキュメントが含まれています

上記の条件のいずれかが満たされない場合、$ 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)

クエリに一致するすべての製品を同じ乱数で更新するため、うまく機能しません。最後のアプローチの方がうまく機能します(ドキュメントを取得するときに一部のドキュメントを更新します)

22
Enrique Fueyo

3.2以降、コレクションからドキュメントのランダムサンプルを取得する簡単な方法があります。

$ sampleバージョン3.2の新機能。

入力から指定された数のドキュメントをランダムに選択します。

$ sampleステージの構文は次のとおりです。

{ $sample: { size: <positive integer> } }

ソース:MongoDB Docs

この場合:

db.products.aggregate([{$sample: {size: 10}}]);
27
Daniel Budick

これが私が最後に思いついたものです:

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);

    });

});
2
Iladarsda

スキップは私にとってうまくいきませんでした。これが私が最終的に得たものです:

var randomDoc = db.getCollection("collectionName").aggregate([ {
    $match : {
// criteria to filter matches
    }
}, {
    $sample : {
        size : 1
    }
} ]).result[0];

基準に一致する単一のランダムな結果を取得します。

0
Marc