【发布时间】:2013-02-28 09:22:50
【问题描述】:
我已经尝试解决这个问题两个月了,与其他开发人员进行了无数次头脑风暴会议,但仍然无法提出一个好的解决方案。
想法 我们正在为会议、公共活动等构建搜索引擎。
数据
我有一个包含数万个事件(未来和历史)的数据集,其结构如下:
{
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, ... [点击浏览所有人]
主题: [建议的主题] Internet Marketing, Startups, ... [点击浏览所有主题]
- 点击菜单中的任何标签**必须**产生至少一个结果(菜单中没有死角标签)。
- 每当用户点击菜单中的任何标签时,都会执行搜索,菜单应重新填充来自搜索结果的事件子集的标签,以便用户可以继续点击
- 根据权重,只有前 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}}} );
- 该查询的第一个问题是最后一个条件应该忽略与匹配的所有事件相关的标签(因为在单击时显示不过滤结果的标签是没有意义的)。上面的查询目前会过滤掉与所有 INTERVALS(而不是 EVENTS)相关的标签。
- 该查询的第二个问题是,对于大型数据集,它可能会耗尽内存
我们也尝试过 仅针对菜单问题,我们尝试从标签而不是事件开始:
"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,尝试使用 SELF JOIN 查询它,确保间隔匹配:
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) )
但是有两个问题:
- 对于添加到选择中的每个额外标签,区间匹配都会增加复杂性(对于三个标签,我们会将区间 a 与 b、b 与 c 以及 a 与 c 进行匹配)
- 它不返回每个标签的事件数,以便我们可以排除匹配所有结果事件的那些
你们对如何改进这两种方法有任何想法,或者建议一种新方法吗?
我知道这不是一个快速的回复,我非常感谢你花时间阅读和理解这个问题。
【问题讨论】:
-
您的模型中似乎有很多复杂的关系。标签、父标签、日期、事件等。似乎还创建了动态结构。您想要最受欢迎的标签并且它们属于可以通过单击发现的某种结构?我还没有机会尝试这个,但我一直认为一旦关系变得复杂,图形数据库可能是一个不错的选择。它可以帮助您管理对象(节点)之间的显式复杂关系(边)。我不知道经验,但我想他们也有很好的遍历。
-
嘿,Ryan,实际上我直到现在才听说过图形数据库,但我肯定会阅读它们。感谢您的意见!
-
查看泰坦。 thinkaurelius.github.com/titan我是从对其创建者的视频采访中听说的。他还很好地描述了如何使用图形数据库:channel9.msdn.com/Shows/Going+Deep/…
-
再次感谢 Ryan,我会在确保我了解 Neo4j 后立即查看它,它似乎已经存在了一段时间。
标签: mysql mongodb database-design tags search-engine