6

次の形式のドキュメントのコレクションがあります。

{ _id: ObjectId(...)
, title: "foo"
, tags: ["bar", "baz", "qux"] 
}

クエリは、これらのタグのいずれかを持つすべてのドキュメントを検索する必要があります。私は現在、このクエリを使用しています:

{ "tags": { "$in": ["bar", "hello"] } }

そしてそれは機能します。「bar」または「hello」とタグ付けされたすべてのドキュメントが返されます。

ただし、関連性で並べ替えたいと考えています。つまり、一致するタグが多いほど、ドキュメントが結果で早く出現するはずです。たとえば、タグ付けされたドキュメントは、クエリに対して["bar", "hello", "baz"]タグ付けされたドキュメントよりも結果の上位に表示される必要があります。どうすればこれを達成できますか?["bar", "baz", "boo"]["bar", "hello"]

4

3 に答える 3

12

MapReduce とそれをクライアント側で行うのは遅すぎるため、集約フレームワーク (MongoDB 2.2 の新機能) を使用する必要があります。

次のようになります。

db.collection.aggregate([
   { $match : { "tags": { "$in": ["bar", "hello"] } } },
   { $unwind : "$tags" },
   { $match : { "tags": { "$in": ["bar", "hello"] } } },
   { $group : { _id: "$title", numRelTags: { $sum:1 } } },
   { $sort : { numRelTags : -1 } }
   //  optionally
   , { $limit : 10 }
])

1 番目と 3 番目のパイプライン メンバーが同じに見えることに注意してください。これは意図的なものであり、必要です。手順は次のとおりです。

  1. タグ「bar」または「hello」を含むドキュメントのみを渡します。
  2. tags 配列をほどきます (つまり、tags 要素ごとに 1 つのドキュメントに分割します)
  3. 正確に「bar」または「hello」のタグのみを渡す (つまり、残りのタグを破棄する)
  4. タイトルごとにグループ化します (「$_id」または元のドキュメントのその他の組み合わせによって、(「bar」と「hello」の) タグの数を合計することもできます)。
  5. 関連するタグの数で降順​​に並べ替えます
  6. (オプション) 返されるセットを上位 10 に制限します。
于 2012-10-07T21:28:57.903 に答える
1

そのようなものに MapReduce を使用できる可能性があります。Map ステップで各ドキュメントを処理し、クエリに一致するタグの数を割り出し、スコアを割り当てます。次に、そのスコアに基づいて並べ替えることができます。

http://www.mongodb.org/display/DOCS/MapReduce

于 2012-10-07T19:02:25.343 に答える
0

複雑なことは、クエリの後に実行する必要があります。db.eval を介したサーバー側 (クライアントがこれをサポートしている場合) またはクライアント側のみ。探しているものの例を次に示します。

指定したタグが付いたすべての投稿を取得し、一致する数に応じて並べ替えます。

db.eva( 部分を削除し、クライアント側の効果を得るためにクライアントがクエリに使用する言語に翻訳します (

db.eval(function () {
    var tags = ["a","b","c"];
    return db.posts.find({tags:{$in:tags}}).toArray().sort(function(a,b){

        var matches_a = 0;
        var matches_b = 0;
        a.tags.forEach(function (tag) {
            for (t in tags) {
                if (tag == t) {
                    matches_a++;
                } else {
                    matches_b++;
                }
            }
        });

        b.tags.forEach(function(tag) {
            for (t in tags) {
                if (tag == t) {
                    matches_b++;
                } else {
                    matches_a++;
                }
            }
        });
        return matches_a - matches_b;
    });
});
于 2012-10-07T19:40:59.390 に答える