基于时间和标签的复杂搜索引擎的数据库结构和搜索方法

时间:2013-03-12 17:48:15

标签: mysql mongodb database-design tags search-engine

我一直试图解决这个问题两个月了,经历了与其他开发人员无数的头脑风暴会议,但仍未能找到一个好的解决方案。

这个想法 我们正在为会议,公共活动等建立搜索引擎。

数据
我有数万个事件(包括未来和历史)的数据集,具有以下结构:

{
    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 Sc​​hmidt,Marissa Meyer,... [点击浏览所有人]
主题 [建议主题]网络营销,初创公司,... [点击浏览所有主题]

  • 单击菜单中的任何标记**必须**导致至少一个结果(菜单中没有死区标记)。
  • 每当用户点击菜单中的任何标签时,都会执行搜索,菜单应重新填充搜索产生的事件子集中的标签,以便用户可以继续点击
  • 在[点击浏览全部...]链接之前,只显示前五个标签(基于它们的重量)。
  • 点击[点击浏览全部...]链接会弹出一个层级菜单。对于地点,它将是一个大陆列表。点击一个大陆拉出一个国家列表。点击国家/地区会列出城市列表。这里没有加权,只是通过层次浏览

当前方法

鉴于我们提出的上述文档结构,如果非常简单,使用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相关的标签(而不是事件)。
  •     
  • 该查询的第二个问题是,对于大型数据集,它可能会耗尽内存

我们也尝试了 仅仅针对菜单问题,我们尝试从标记开始,而不是事件:

"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匹配​​)
  •     
  • 它不会返回每个代码的事件数,因此我们可以排除那些匹配所有结果事件的事件

你们有没有想过如何改进这两种方法中的任何一种,或者建议一种新方法?

我知道这不是一个快速的回复,我感谢你花了一百万次的时间来阅读和理解这个问题。

1 个答案:

答案 0 :(得分:0)

您遇到的一个问题是,您不可能总是拥有足够的数据来为所有用户选择组合提供至少一个结果。

如果是这样的话,不要让自己变得复杂,为什么不做其他网站所做的事情,只是显示'没有结果等等'然后提供建议。例如,您可以在删除其中一个过滤器时向他们显示用户选择的部分结果,或者您只需向他们提供一个链接即可删除(或汇总)其当前过滤器列表。