8

次のMongoDBドキュメントコレクションがあるとします。

{
 title : 'shirt one'
 tags : [
  'shirt',
  'cotton',
  't-shirt',
  'black'
 ]
},
{
 title : 'shirt two'
 tags : [
  'shirt',
  'white',
  'button down collar'
 ]
},
{
 title : 'shirt three'
 tags : [
  'shirt',
  'cotton',
  'red'
 ]
},
...

一致したタグの総数順に、タグのリストに一致するアイテムのリストをどのように取得しますか?たとえば、次のタグのリストを入力として指定します。

['shirt', 'cotton', 'black']

一致するタグの総数で説明順にランク付けされたアイテムを取得したい:

item          total matches
--------      --------------
Shirt One     3 (matched shirt + cotton + black)
Shirt Three   2 (matched shirt + cotton)
Shirt Two     1 (matched shirt)

リレーショナルスキーマでは、タグは別のテーブルになり、そのテーブルに対して結合し、一致をカウントし、カウント順に並べ替えることができます。

しかし、モンゴでは...?

このアプローチはうまくいくようですが、

  • 入力タグを複数の「IN」ステートメントに分割します
  • タグ入力を「OR」してアイテムをクエリします
    • つまり、where('shirt' IN items.tags)OR('cotton' IN items.tags)
    • これにより、たとえば、「シャツ1」のインスタンスが3つ、「シャツ3」のインスタンスが2つ返されます。
  • その出力をマップ/リデュース
    • マップ:emit(this._id、{...});
    • 削減:_idの合計発生数をカウントします
    • ファイナライズ:カウントされた合計で並べ替え

しかし、これをMongoクエリとして実装する方法、またはこれが最も効率的なアプローチであるかどうかはわかりません。

4

3 に答える 3

9

In MongoDBで答えたように、配列で検索し、一致数で並べ替えます

Aggregation Framework を使用して可能です。

仮定

  • tags属性はセットです (繰り返し要素はありません)

クエリ

このアプローチでは、結果をアンワインドし、アンワインドされた結果で一致述語を再評価する必要があるため、非常に非効率的です。

db.test_col.aggregate(
    {$match: {tags: {$in: ["shirt","cotton","black"]}}}, 
    {$unwind: "$tags"}, 
    {$match: {tags: {$in: ["shirt","cotton","black"]}}},
    {$group: {
        _id:{"_id":1}, 
        matches:{$sum:1}
    }}, 
    {$sort:{matches:-1}}
);

予想された結果

{
    "result" : [
        {
            "_id" : {
                "_id" : ObjectId("5051f1786a64bd2c54918b26")
            },
            "matches" : 3
        },
        {
            "_id" : {
                "_id" : ObjectId("5051f1726a64bd2c54918b24")
            },
            "matches" : 2
        },
        {
            "_id" : {
                "_id" : ObjectId("5051f1756a64bd2c54918b25")
            },
            "matches" : 1
        }
    ],
    "ok" : 1
}
于 2012-09-13T15:23:12.353 に答える
5

今のところ、MapReduce を使わないとできません。MapReduce の唯一の問題は、(通常のクエリと比較して) 遅いことです。

集約フレームワークは 2.2 向けに予定されており (2.1 dev リリースで利用可能になるはずです)、MapReduce なしでこの種のことをより簡単に実行できるはずです。

個人的には、M/R を使用するのは効率的ではないと思います。すべてのドキュメントに対してクエリを実行し、アプリケーション側でそれらの計算を実行したいと思います。データベース サーバーをスケーリングするよりも、アプリ サーバーをスケーリングする方が簡単でコストもかからないため、アプリ サーバーに計算処理を任せることができます。これらのうち、データ アクセスのパターンと要件を考えると、このアプローチはうまくいかない場合があります。

さらに簡単な方法は、各タグ オブジェクトにプロパティを含めるだけで、配列に新しいタグを付けるcountたびにプロパティも含めることができます。これは、少なくとも集計フレームワークまでは、MongoDB の世界では一般的なパターンです。$push$inccount

于 2011-12-23T14:58:35.210 に答える
1

私は @Bryan に次ぐ、MapReduce が現時点で唯一可能な方法であると述べます (そして、それは完全には程遠いです)。しかし、あなたが必死にそれを必要とする場合は、ここに行きます:-)

    var m = function() {
        var searchTerms = ['shirt', 'cotton', 'black'];
        var me = this;
        this.tags.forEach(function(t) {
            searchTerms.forEach(function(st) {
                if(t == st) {
                    emit(me._id, {matches : 1});
                }
            })
        })
    };

    var r = function(k, vals) {
        var result = {matches : 0};
        vals.forEach(function(v) {
            result.matches += v.matches;
        })
        return result;
    };

    db.shirts.mapReduce(m, r, {out: 'found01'});

    db.found01.find();
于 2011-12-23T15:08:57.743 に答える