この質問は、この「ファセット検索、複数の多値フィールド、カウントではなく重みでソート」に要約されます。
データベース
約 1,000 万のイベントがあり、それぞれに複数のエディションがあり、各エディションはタグで記述されています。5 つのタグ タイプ (場所、スピーカー、参加者、トピック、業界) があります。
{
title: "CES",
editions: [
{
date: "2013-02-01",
tags: [ {label: "Eric Schmidt", type: "speaker", "popularity": 50}, {label: "Paris", type: "place", "popularity": 30} ]
},
{
date: "2012-01-23",
tags: [ ... ]
}
]
}
データロジック
- タグは階層化されており、たとえば「Eric Schmidt」は Google の下にあり、Tech company の下にはあります。そのため、Eric がイベントに参加するたびに、3 つのタグすべてがイベントに関連付けられます。
- タグごとに人気度が異なる場合があります。つまり、「Eric Schmidt」の人気度は 100 ですが、「Eileen Naughton」の人気度は「10」になります。
- 人気は階層的に適用されません。つまり、「Eric Schmidt」が Google を離れて Foursquare に移行したとしても、彼の人気は 100 のままで、Foursquare の人気は 50 のままです。
- ある時点で、別の「参加者」が参加していることがわかった場合、たとえば、彼をタグとして追加できるようにする必要があります
検索要件
ここで、4 つのセクションがある左側のメニューを想像してください。
Places
------------
Paris
London
New York
[more]
Speakers
----------
Google
Facebook
Marc Zuckerberg
[more]
等々。
ユーザーがタグをクリックするたびに、メニューに結果が反映され、さらにドリルダウンできるようにしたい (ファセット検索)。ひねりを加えたのは、各セクションの最初の 3 つのタグで「Google」、「Eric Schmidt」、「Foursquare」を表示することを決定するとき、[一致するイベントの数] に基づいて、最も人気のあるタグがより高く表示されるようにしたいということです。 】 ★【タグ人気】。つまり、「Foursquare」に 3 つの一致するイベントがあり、「Eric Schmidt」に 1 つしかない場合、Foursquare が最初に表示され、スコアは 3*50 = 150 vs Schmidt の 1 * 100 になります。
また、理想的には、「スピーカー」セクションで「Google」を選択した場合、一致するイベントに「ザッカーバーグ」がリストされていても、システムは Google 以外のスピーカーを返すべきではなく、200 という非常に人気があります。返されたタグは、各セクションの現在の選択の「下」に存在する必要があり、それらの並べ替えは上記のスコアリング ロジックに基づく必要があります。
現在の MongoDB ソリューション
エディションごとに個別のドキュメントを保存します。
{
event: "CES",
date: "2013-02-01",
tags: [ {label: "Eric Schmidt", type: "speaker", "popularity": 50, path: ",Tech Companies,Google,"}, {label: "Paris", type: "place", "popularity": 30, path: ",Europe,France,"} ]
},
{
event: "CES",
date: "2012-01-23",
tags: [ ... ]
}
集計フレームワークを使用する
*タグの種類ごとに 1 つのクエリ (リクエストごとに 5 つのクエリ) *
db.events.aggregate(
{
'$match': {'tags.label': {'$all': ["selected tag 1", "selected tag2", ...]}}
},
{
'$unwind': '$tags'
},
// group by events, so we can later sum each tag's popularity only once per event, not per event edition
{
'$group': {
'_id': '$event',
'taglistUnqiue': {
'$addToSet': {
'label': '$tags.label',
'type': '$tags.type',
'popularity': '$tags.popularity'
}
}
}
},
{
'$unwind': '$taglist'
},
{
'$match': {
'taglist.type': "speaker",
/* haven't tested this path-matching, but it should work
to only get the tags that are in the bottom tree
of the current selected speaker tag */
'taglist.path': /^,selected speaker tag,/,
}
},
{
'$group': {
'_id': '$taglist.label',
'score': {
'$sum': '$taglist.popularity'
}
}
});
わかりました、これはアルゴリズム的には機能するはずですが、パフォーマンスに関しては、それぞれに数千の可能なタグがある 5,000 万のイベント エディションでは確実に機能しません。
誰でも別のアプローチを考えることができますか? このアプローチは、「マップ/リデュース」を使用する以外の方法で最適化できますか?これは、ユーザーごとにオンザフライで実行するには遅すぎることを理解していますか?