数据库和搜索专家:对多值字段进行加权分面搜索,并进行扭曲

时间:2013-03-21 16:05:08

标签: mongodb search database-design search-engine faceted-search

这个问题几乎归结为这个“分面搜索,多个多值字段,按重量排序而非计数”。

数据库

我有大约10M个事件,每个事件都有多个版本,每个版本都由标签描述。有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 Sc​​hmidt”是根据Google提交的,归属于Tech公司。因此,每当Eric参加活动时,所有三个标签都与事件相关联。
  • 不同的标签可以有不同的流行度,意味着“Eric Sc​​hmidt”的流行度为100,但“Eileen Naughton”的流行度为“10”。
  • 流行度不适用于分层次。这意味着,如果“埃里克施密特”将离开谷歌进入Foursquare,他的受欢迎程度仍然是100,而Foursquare仍然会有人气50。
  • 如果在给定时间,我们发现另一个“参与者”参加了,例如,我们需要能够将他添加为标签

搜索要求

现在,想象一下包含4个部分的左手菜单:

Places
------------
Paris
London
New York
[more]

Speakers
----------
Google
Facebook
Marc Zuckerberg
[more]

等等。

每当用户点击标签时,我希望菜单反映结果并允许他进一步向下钻取(分面搜索)。扭曲的是,当决定在每个部分的前三个标签中显示“Google”vs“Eric Sc​​hmidt”vs“Foursquare”时,我想确保根据[匹配事件的数量]显示最流行的标签更高] * [标签流行度]。这意味着,如果“Foursquare”有3个匹配事件,而“Eric Sc​​hmidt”只有一个匹配事件,它应首先显示Foursquare,得分为3 * 50 = 150,而Schmidt为1 * 100。

另外,理想情况下,如果我选择“Google”,那么对于“发言人”部分,系统不应该返回Google以外的发言人,即使匹配的事件也列出了“Zuckerberg”,其中人气极高,为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: [ ... ] 
}

使用聚合框架

* 每个标记类型一个查询(每个请求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'
        }
    }
});

好的,这应该在算法上有效,但是在性能方面,它肯定不适用于50M事件版本,每个版本都有数千个可能的标签。

有人能想到另一种方法吗?除了使用“Map / Reduce”之外,这种方法是否可以以任何方式进行优化,我理解这种方法对于每个用户来说都是无法动态执行的?

1 个答案:

答案 0 :(得分:0)

取决于您的搜索需要“实时”的方式,您是否考虑过使用增量map / reduce?

http://docs.mongodb.org/manual/tutorial/perform-incremental-map-reduce/