如何使用Lucene.NET帮助在Stack Overflow等网站上实现搜索?

cas*_*One 62 lucene search lucene.net sql-server-2008

我已经问了关于Meta Stack Overflow的一个类似问题,但这个问题专门讨论了Lucene.NET是否用于Stack Overflow.

这里的问题的目的,更多的是一种hypotetical的,至于是什么方法,如果他们是在一个站点使用Lucene.NET,以此为基础在现场搜索和其他因素之一会使堆栈溢出[SO].

根据Stack Overflow博客上标题为" SQL 2008全文搜索问题 " 的条目,有一个强烈的迹象表明Lucene.NET在某些时候正在被考虑,但似乎绝对不是这样,根据评论杰夫达尔加斯于2010年2月19日:

Lucene.NET没有用于Stack Overflow - 我们正在使用SQL Server全文索引.搜索是我们继续进行细微调整的领域.

所以我的问题是,如何将Lucene.NET用于具有与Stack Overflow相同语义的站点?

这是一些背景知识以及我到目前为止所做的/思考的内容(是的,我已经实现了大部分内容并且搜索是我必须完成的最后一个方面):

技术:

当然,这个节目的明星,Lucene.NET.

目的也是尽快转向.NET/C#4.0.虽然我不认为这是改变游戏规则的,但应该注意.

在深入了解Lucene.NET的各个方面之前,重要的是要指出它的SQL Server 2008方面以及所涉及的模型.

楷模

与Stack Overflow相比,此系统具有多个主要模型类型.这些模型的一些例子是:

  • 问题:这些是人们可以提出的问题.人们可以回复问题,就像在Stack Overflow上一样.
  • 注意:这些是单向预测,因此与问题相反,您正在对内容进行陈述.人们无法对此发表回复.
  • 事件:这是关于实时事件的数据.它有位置信息,日期/时间信息.

关于这些模型需要注意的重要事项:

  • 它们都具有Name/Title(文本)属性和Body(HTML)属性(格式无关紧要,因为内容将被适当地解析以供分析).
  • 模型的每个实例在网站上都有唯一的URL

然后有Stack Overflow提供的东西,IMO是模型的装饰者.这些装饰者可以有不同的基数,可以是一对一或一对多:

  • 投票:关键用户
  • 回复:可选,作为示例,请参阅上面的Notes案例
  • 收藏:该型号是否被列为用户的最爱?
  • 评论:(可选)
  • 标记关联:标记位于单独的表中,以便不复制每个模型的标记.模型和标记关联表之间存在链接,然后从标记关联表到标记表.

并且有支持的标签,它们本身就是以相同的方式键入它们的模型的一对一装饰器(通常通过模型id类型和模型id):

  • 投票结果:总积极,负投票,威尔逊得分间隔(这很重要,它将根据条目的投票确定置信水平,在大多数情况下,假设威尔逊区间的下限).

回复(答案)是具有大多数模型具有的大多数装饰器的模型,它们只是没有标题或URL,并且模型是否具有回复是可选的.如果允许回复,那当然是一对多的关系.

SQL Server 2008

这些表几乎遵循上面模型的布局,为装饰器提供单独的表,以及一些支持表和视图,存储过程等.

应该注意的是,不使用全文搜索的决定主要基于它不像Lucene.NET那样规范化分数这一事实.我对如何利用基于文本的搜索的建议持开放态度,但我将不得不跨多种模型类型执行搜索,因此请记住,我需要以某种方式对分数进行标准化.

Lucene.NET

这就是大问号所在.到目前为止,我对Stack Overflow功能的想法以及我已经完成的方式和内容.

索引

问题/型号

我相信每个模型都应该有一个自己的索引,其中包含一个唯一的id,以便根据该id的Term实例(索引,未分析)快速查找它.

在这方面,我考虑让Lucene.NET分析每个问题/模型,并分别单独回复.因此,如果有一个问题和五个答案,则问题和每个答案将分别作为一个单元编入索引.

这里的想法是Lucene.NET返回的相关性得分将更容易比较以不同方式投影的模型(比如没有回复的东西).

作为一个例子,一个问题设定了主题,然后答案阐述了主题.

对于没有回复的注释,它处理呈现主题的问题,然后详细说明.

我相信这有助于使相关性得分更加相关.

标签

最初,我认为这些应保存在具有多个字段的单独索引中,这些字段具有适当模型索引中的文档的ID.或者,如果它太大,则只有标记的索引和另一个维护标记索引与它们应用的问题之间关系的索引.这样,当您单击标签(或使用URL结构)时,很容易以渐进的方式看到,如果您成功,您只需"买入":

  • 如果标签存在
  • 标签与哪些问题相关联
  • 问题本身

但是,在实践中,使用SQL Server 2008对基于标记的所有项进行查询(例如单击Stack Overflow中的标记)非常容易.基于上面的模型,它只需要一个查询,例如:

select
     m.Name, m.Body
from
    Models as m
        left outer join TagAssociations as ta on
            ta.ModelTypeId = <fixed model type id> and
            ta.ModelId = m.Id
        left outer join Tags as t on t.Id = ta.TagId
where
    t.Name = <tag>
Run Code Online (Sandbox Code Playgroud)

由于某些属性在所有模型中共享,因此UNION在不同的模型类型/表之间进行操作并生成一组一致的结果非常容易.

TermQuery与Lucene.NET中的类似(我引用了Java文档,因为它是全面的,Lucene.NET是Lucene的逐行翻译,因此所有文档都是相同的).

这里使用Lucene.NET的问题是排序顺序.关于标签,TermQuery的相关性得分无关紧要.它是1或0(它有或没有).

此时,置信度得分(Wilson得分间隔)起到了对结果进行排序的作用.

这个分数可以存储在Lucene.NET中,但是为了在这个字段上对结果进行排序,它将依赖于存储在字段缓存中的值,这是我真正想要避免的.对于大量文档,字段缓存可能会变得非常大(Wilson分数是一个双倍,每个文档需要一个双精度,可以是一个大型数组).

鉴于我可以根据Wilson得分间隔将SQL语句更改为订单,如下所示:

select
     m.Name, m.Body
from
    Models as m
        left outer join TagAssociations as ta on
            ta.ModelTypeId = <fixed model type id> and
            ta.ModelId = m.Id
        left outer join Tags as t on t.Id = ta.TagId
        left outer join VoteTallyStatistics as s on
            s.ModelTypeId = ta.ModelTypeId and
            s.ModelId = ta.ModelId
where
    t.Name = <tag>
order by
    --- Use Id to break ties.
    s.WilsonIntervalLowerBound desc, m.Id
Run Code Online (Sandbox Code Playgroud)

使用它来处理Stack Overflow功能"使用<tag>标记所有项目"似乎是一个简单的选择.

回复

最初,我认为这是在它自己的一个单独的索引中,一个键回到Questions索引.

我认为应该有每个模型和每个回复(如果有的话)的组合,以便相互比较时不同模型的相关性得分更"平等".

这当然会使指数膨胀.我现在对此感到有点舒服.

或者,有没有办法将说明,模型和回复存储为Lucene.NET中的单个文档,然后同时获取两者并能够获得将两个文档视为一个查询的查询的相关性分数?如果是这样,那么这将是理想的.

当然存在哪些字段将被存储,索引,分析(所有操作可以是单独的操作,还是混合匹配)的问题?一个指数多少钱?

如何使用特殊的词干分析器/搬运工来解决拼写错误(使用Metaphone)以及同义词(我将服务的社区中有术语,对于某些具有多种表示形式的东西,它有自己的俚语/术语)?

促进

这当然与索引有关,但我认为这是它自己的部分.

你在推广字段和/或文件吗?如果是这样,你如何提升他们?某些字段的提升是否恒定?或者是否可以对投票/查看/收藏/外部数据适用的字段重新计算.

例如,在文档中,标题是否会提升身体?如果是这样,您认为哪些助推因素效果很好?标签怎么样?

这里的思路与Stack Overflow的思路相同.文档中的术语具有相关性,但如果文档标记有术语,或者它在标题中,那么应该提升它.

Shashikant Kore建议使用这样的文档结构:

  • 标题
  • 已接受的答案(如果没有接受的答案,则为高度投票的答案)
  • 所有答案相结合

然后使用提升但不是基于原始投票值.我相信我已经覆盖了威尔逊分数区间.

问题是,如果将提升应用于整个文档吗?我倾向于拒绝这一点,因为这意味着每次用户投票选择模型时我都必须重新索引文档.

搜索标记的项目

我原本以为在查询标签时(通过专门点击一个标签或使用URL结构查找标记内容),这是一个针对标签的标签索引的简单TermQuery,然后是关联索引(如果需要)然后返回对于问题,Lucene.NET非常快速地处理这个问题.

但是,考虑到上面关于在SQL Server中执行此操作是多么容易的注释,我在搜索标记项时选择了该路由.

一般搜索

那么现在,最突出的问题是,在对内容进行一般性短语或术语搜索时,您将如何整合其他信息(例如投票)以及如何以正确的顺序确定结果?例如,在Stack Overflow上的ASP.NET MVC上执行此搜索时,这些是前五个结果的计数(使用相关性选项卡时):

    q votes answers accepted answer votes asp.net highlights mvc highlights
    ------- ------- --------------------- ------------------ --------------
         21      26                    51                  2              2
         58      23                    70                  2              5
         29      24                    40                  3              4
         37      15                    25                  1              2
         59      23                    47                  2              2
Run Code Online (Sandbox Code Playgroud)

请注意,突出显示仅在结果页面上的标题和摘要中,并且仅是关于文档,标题,标记,回复中真实术语频率的小指标(但是它们被应用,这是另一个好问题).

这一切是如何结合在一起的?

在这一点上,我知道Lucene.NET将返回一个标准化的相关性分数,投票数据将给我一个Wilson分数间隔,我可以用它来确定置信度分数.

我应该如何结合使用两个分数来表示基于相关性和置信度的结果集的排序顺序?

对我来说很明显,两者之间应该存在某种关系,但这种关系应该在这一点上避开我.我知道随着时间的推移我必须改进它,但我真的迷失了这一部分.

我最初的想法是,如果相关性得分在0和1之间且置信度得分在0和1之间,那么我可以这样做:

1 / ((e ^ cs) * (e ^ rs))
Run Code Online (Sandbox Code Playgroud)

通过这种方式,可以获得接近0的标准化值,结果越相关且越自信,并且可以对其进行排序.

与此相关的主要问题是,如果对标签和/或标题字段执行提升,则相关性得分超出0到1的范围(上端变得无限制,我不知道如何处理).

此外,我相信我必须调整置信度得分以解决完全否定的投票结果.由于完全否定的投票结果导致威尔逊得分区间的下限为0,因此具有-500票的东西具有与具有-1投票或0投票的投票相同的置信度得分.

幸运的是,由于负投票结果上升,上限从1降至0.我可以将置信度分数更改为-1到1的范围,如下所示:

confidence score = votetally < 0 ? 
    -(1 - wilson score interval upper bound) :
    wilson score interval lower bound
Run Code Online (Sandbox Code Playgroud)

这样做的问题在于,在等式中插入0将使得所有项目的评分都低于那些具有负投票结果的项目.

为此,我在考虑是否将在上面的倒数等式中使用置信度得分(我显然关注溢出),然后需要重新设计以始终为正.实现这一目标的一种方法是:

confidence score = 0.5 + 
    (votetally < 0 ? 
        -(1 - wilson score interval upper bound) :
        wilson score interval lower bound) / 2
Run Code Online (Sandbox Code Playgroud)

我的另一个问题是如何在给定Lucene.NET和SQL Server的情况下实际执行计算.我对将信心分数放在Lucene索引中犹豫不决,因为它需要使用字段缓存,这会对内存消耗产生巨大影响(如前所述).

我的想法是从Lucene.NET获取相关性得分,然后使用表值参数将得分流式传输到SQL Server(以及要选择的项目的ID),此时我将执行计算用置信度得分然后正确地返回数据.

如前所述,我对此有很多其他问题,答案已经开始构建,并且随着问题和答案的推移,将继续扩展.

Mik*_*enn 10

单独使用lucene无法找到您正在寻找的答案.您需要排名和分组算法来过滤和理解数据及其相关性.Lucene可以帮助您获得标准化数据,但之后您需要正确的算法.

我建议您查看以下一本或所有书籍,它们将帮助您进行数学计算并让您指出正确的方向:

智能Web的算法

行动中的集体智慧

编程集体智慧


Pau*_*aul 2

对于这个问题,你可能比大多数试图回答你的人思考得更多(我想这也是为什么这一天我是你的第一个回应的部分原因)。我只是想尝试解决你的最后三个问题,因为有很多问题我没有时间讨论,而且我认为这三个问题是最有趣的(物理实现问题可能正在解决)最终成为“选择一些东西,然后随着你了解更多而调整它”)。

投票数据 不确定投票是否会使某些内容与搜索更相关,坦率地说,只会使它们更受欢迎。如果这是有道理的,我想说的是,某个特定的帖子是否与您的问题相关,很大程度上取决于它是否与其他人相关。也就是说,有趣的问题和人们想要找到的问题之间至少可能存在微弱的相关性。投票数据可能在纯粹基于数据进行搜索时最有用,例如“最受欢迎”类型的搜索。在基于文本的通用搜索中,我一开始可能不会为投票提供任何权重,但会考虑开发一种算法,该算法可能为排序提供轻微的权重(因此,不是返回的结果,而是对排序的轻微提升)其中)。

我同意你的方法,但需要进行一些测试;请记住,这必须是一个基于用户反馈的迭代过程(因此您需要收集有关搜索是否为搜索者返回成功结果的指标)

其他也不要忘记用户的分数。因此,用户也会在 SO 上获得积分,这会影响他们在回答的每个问题的答案中的默认排名(看起来主要是为了在具有相同数量的颠簸的回复中进行决胜)