私はこれを2か月間理解しようとしてきましたが、他の開発者との数え切れないほどのブレインストーミングセッションを経ましたが、それでも良い解決策を思い付くことができませんでした。
アイデア 私たちは会議や公開イベントなどのための検索エンジンを構築しています。
データ
私は、次の構造を持つ数万のイベント(将来と履歴の両方)のデータセットを持っています。
{
id: 10,
name: "CES",
intervals: [
{
interval_start: "2013-01-01 08:00",
interval_end: "2013-01-15 10:00",
tags_by_type: {
people: [{name: "Eric Schmidt", weight: 20}, ...]
companies: [{name: "Google", weight: 100}, {name: "Microsoft", weight: 100}, ...],
topics: [{name: "Social Networking", weight: 80}, {name: "Internet marketing", weight: 95}, ...],
places: [{name: "Cannes Palace Hotel", weight: 100}, {name: "Cannes", weight: 100}, {name: "France", weight: 100}]
},
tags: ["Eric Schmidt", "Google", "Microsoft", "Social Networking", "Internet Marketing", "Cannes Palace Hotel", "Cannes", "France"]
},
{
interval_start: "2011-01-01 10:00",
interval_end: "2011-01-15 12:00",
tags_by_type: {
people: [{name: "Marissa Meyer", weight: 20}, ...]
companies: [{name: "Yahoo", weight: 100}, {name: "Facebook", weight: 100}, ...],
topics: [{name: "Recruiting", weight: 80}, {name: "Internet marketing", weight: 15}, ...],
places: [{name: "New york", weight: 100}, {name: "USA", weight: 100}]
},
tags: ["Marissa Mayer", "Yahoo", "Facebook", "Recruiting", "Internet marketing", "New york", "USA"]
},
...
],
}
正規化されたMySQLデータベースを使用してイベントとタグを追加/更新/削除してから、さまざまな検索シナリオ用にさまざまな形式(上記のドキュメントなど)でデータをコンパイルします。
- タグ間には階層があります(マーケティングはインターネットマーケティングの親であるため、インターネットマーケティングがタグである場合は常に、マーケティングもタグになります)
- 重みの数値は、それぞれのタグがそれぞれの時間枠に対してどれほど重要/関連性があるかを表します
問題次の ようなイベントをクリックしてフィルタリングするために使用できるメニューをユーザーに提供したいと考えています。
場所: [おすすめの場所]アメリカ、フランス、... [クリックしてすべての場所を閲覧]
人: [おすすめの人] Eric Schmidt、Marissa Meyer、... [クリックしてすべての人を閲覧]
トピック: [おすすめのトピック]インターネットマーケティング、スタートアップ、...[クリックしてすべてのトピックを閲覧]
- メニュー内の任意のタグをクリックすると、少なくとも1つの結果が得られます(メニューに行き止まりのタグはありません)。
- ユーザーがメニュー内のタグのいずれかをクリックするたびに、検索が実行されます。メニューには、検索結果のイベントのサブセットからのタグが再入力されるため、ユーザーはクリックを続けることができます。
- [クリックしてすべてを参照...]リンクの前に表示されるのは、重みに基づく上位5つのタグのみです。
- [クリックしてすべてを参照...]リンクをクリックすると、階層メニューがポップアップ表示されます。場所の場合は、大陸のリストになります。大陸をクリックすると、国のリストが表示されます。国をクリックすると、都市のリストが表示されます。ここでは重み付けを行わず、階層的なブラウジングのみを行います
現在のアプローチ
私たちが思いついた上記のドキュメント構造を考えると、非常に単純な場合は、MongoDbを使用してイベントを検索します。
{"intervals.tags": { $in: [selectedtag1, selectedtag2, selectedtag3]}}
ただし、タグメニューでユーザーにさらに表示するタグを見つけるのは面倒です:)重みを無視して、最も一般的なタグを見つけようとすると、次のようになります。
db.events.aggregate( { $unwind: "$intervals" }, {$unwind: "$intervals.tags"}, {$group: {"_id": "$intervals.tags", "evCount": {$sum:1}}}, {$match: {"evCount": {$lt: TOTAL_COUNT_OF_EVENTS_MATCHING_OUR_SEARCH}}} );
- そのクエリの最初の問題は、最後の条件が、一致したすべてのイベントに関連するタグを無視する必要があることです(クリックしたときに結果をフィルタリングしないタグを表示する意味がないため)。上記のクエリは現在、(EVENTSではなく)すべてのINTERVALSに関連するタグを除外しています。
- そのクエリの2番目の問題は、大きなデータセットの場合、おそらくメモリが不足することです。
また 、メニューの問題のためだけに、イベントではなくタグから始めようとしました。
"Eric Schmidt" relates to "Google", "Microsoft", "Social Networking", "Internet Marketing", "Cannes Palace Hotel" ... in the interval "2013-01-01 08:00" and "2013-01-01 10:00"
"Google" relates to "Eric Schmidt", "Microsoft" ... in the interval "2013-01-01 08:00" and "2013-01-01 10:00"
...
次に、これらの関係をMySQLテーブルにマッピングしました。
| tag | related tag | event | start time | end time |
----------------------------------------------------------------------------
| Eric Schmidt | Google | CES | 2013-01-01 08:00 | 2013-01-01 10:00 |
| Eric Schmidt | Microsoft | CES | 2013-01-01 08:00 | 2013-01-01 10:00 |
...
そして、ユーザーがメニューからSELECTED_TAG_1とSELECTED_TAG_2を選択したと仮定して、間隔が一致することを確認しながら、SELFJOINを使用してクエリを実行しようとしました。
SELECT a.related_tag FROM tag_relations a JOIN tag_relations b
ON a.related_tag = b.related_tag
AND a.tag = SELECTED_TAG_1 AND b.tag = SELECTED_TAG_2
AND ( (a.start_time < b.start_time AND a.end_time > b.start_time) OR (a.start_time > b.start_time AND a.start_time < b.end_time) )
しかし、2つの問題があります。
- 選択範囲に追加された追加のタグごとに、間隔の一致により複雑さが増します(3つのタグの場合、aとb、bとc、およびaとcの間隔を一致させます)
- 各タグのイベント数は返されないため、結果のすべてのイベントに一致するイベントを除外できます。
これら2つのアプローチのいずれかを改善する方法について何か考えがありますか、それとも新しいアプローチを提案しますか?
私はこれが迅速な返事ではないことを知っています、そして私はこの問題を読んで理解するために時間を割いてくれて何百万回もありがとうございます。