搜索人名是我们在许多应用程序中经常用到的功能。比如对书店来说,按作者名检索的功能就相当重要。虽然很难起一个完美的名字,但是我们可以使用Solr的一些功能,使绝大多数英文名搜索达到绝佳的效果。
还记得“作者阿尔法”的辉煌时代吗?
我们可以从这样的假设出发,即除了人名中的差异之外,我们作者域中的一个名字很像单个域中的一小部分标记。我们要避免把这些名字中的姓,名和中间部分分开(假设这个规则适用于所有文化背景)。我们先看看作者域中的一些样本名称:
作者:
- Doug Turnbull
- Turnbull, Douglas
- Turnbull, Douglas G.
- Turnbull, D. G.
- D. Graeme Turnbull
好了,您已经可以清楚看出我们在表示英语人名时的差异,这让我们有了检索方法。首先,为了记录,我们在作者域中使用这个非常基本的分析链,它将完成删除标点符号和统一小写字母的工作:
代码语言:javascript复制<fieldType name="AuthorsType" class="solr.TextField" positionIncrementGap="100">
<analyzer>
<tokenizer class="solr.StandardTokenizerFactory"/>
<filter class="solr.LowerCaseFilterFactory" />
</analyzer>
</fieldType>
好吧,回到正题。如果我们能够解决两个主要问题,人名搜索的问题就解决一大半了。
- 作者姓名重排,无论是在文档还是查询中,有些部分都被省略了:(Doug Turnbull, D. Turnbull, D. G. Turnbull, Douglas G. Turnbull)
- 许多名字缩写:(Doug Turnbull, D. Turnbull, D. G. Turnbull, Douglas G. Turnbull)
重排的名字
作者名字标记的重新排序是Lucene邻近搜索中一个相当直接的操作。Lucene语法查询的特性让我们能够处理用户的查询和相似度P:
- Douglas Turnbull
然后搜索用户输入或与之相似度在P之内的词组或短语,在Lucene语法查询中表现为:
- 作者:“Douglas Turnbull”〜2
两个带有Douglas和Turnbull字符的人名作为结果返回(不论顺序),接受以下匹配:
- Douglas Turnbull
- Turnbull Douglas
考虑到中间名字会缩写,我们可以将相似度设置为3,因此查询:
- 作者:“Douglas Turnbull”〜3
现在匹配:
- Douglas G. Turnbull
- Turnbull,Douglas G.
太好了,我们已经解决了这个问题!实际上,这在大多数而非全部情况下可以用。你能发现其中的小错误吗?提示:它与使用短语查询有关。这种方法不适用于哪类查询?
缩写形式
当用户搜索Doug Turnbull时,所有Solr已编索引得出的结果都是Douglas Turnbull怎么办?为达到高效的前缀查询,Solr为我们提供了EdgeNGramFilterFactory操作。EdgeNGramFilterFactory接受一个指令,例如Douglas,并从字符串的前面或后面切分字符串来生成标记。例如,在minGramSize = 1和side =“front”的情况下,标记“Douglas”将产生以下标记:
代码语言:javascript复制Input: douglas
Tokens: [d] [do] [dou] [doug] [dougl] [dougla] [douglas]
有关此过滤器(以及Solr中的许多其他过滤器)需要注意的是,每个生成的标记最终在索引文档中占据相同的位置。实际上,我们可以将该文件设想为二维的:
代码语言:javascript复制Position N: Position N 1
[d] -> [t]
[do] [tu]
... ...
[douglas] [turnbull]
因此,在文档的相同位置上,查询短语“do turnbull”将得出“douglas turnbull”这个结果。太棒了!我们可以匹配缩写为“D.Turnbull”的名字,简单地在我们的分析链中使用如下过滤器:
域:
代码语言:javascript复制<field name="AuthorsPre" type="AuthorsPrefix" indexed="true" multiValued="true"/>
复制字段:
代码语言:javascript复制<copyField source="Authors" dest="AuthorsPre"/>
字段类型:
代码语言:javascript复制<fieldType name="AuthorsPrefix" class="solr.TextField" positionIncrementGap="100">
<analyzer type="index">
<tokenizer class="solr.StandardTokenizerFactory"/>
<filter class="solr.LowerCaseFilterFactory" />
<filter class="solr.EdgeNGramFilterFactory" minGramSize="1" maxGramSize="200" side="front"/>
</analyzer>
<analyzer type="query">
<tokenizer class="solr.StandardTokenizerFactory"/>
<filter class="solr.LowerCaseFilterFactory" />
</analyzer>
</fieldType>
让我们通过AuthorsPre字段上的示例查询来分析这个链,看它是如何工作的。人名“Douglas G. Turnbull”的索引如下:
代码语言:javascript复制Position N N 1 N 2
Standard Tokenizer: [Douglas] [G] [Turnbull]
Lower Case Filter: [douglas] [G] [turnbull]
NGram Filter: [d] [g] [t]
[do] [tu]
…(etc)… …(etc)…
[douglas] [turnbull]
现在我们来看一个用户查询:“DG Turnbull”。这样就可以简单地使用查询分析链对[d] [g] [turnbull]进行标记。结果将出现索引名称Douglas G. Turnbull出现的每一处(以及有David G. Turnbull的地方)!
结合
好的,进入下一环节。现在用户在搜索框中输入“Turnbull,D.”。然后呢?只需重复之前的操作,而不是重新搜索:
- AuthorsPre:“Turnbull,D.”
把它变成一个相似查询:
- AuthorsPre:“Turnbull D.”〜3
有很多碎片化信息,看看它们如何作用。首先,如上所述,所有生成的标记在标记流中共享位置。所以[D.]和[Douglas]在索引文档中处于相同的位置。这意味着,当位置重要时(如在词组查询中)“D. Turnbull“和”Douglas Turnbull“都将匹配包含”Douglas Turnbull“的文档。另一方面,我们的相似搜索为Solr提供了一些自由度,可以重新排列标记以满足匹配需要,从而给了自由组合的可能 - 所以会搜到许多重排和缩写的人名。
路还很长
这是一个很好的开始,但搜索是一条改进空间巨大的探索之路。要让这个搜索系统无懈可击,还有很多工作要做。除了我所违反的文化习惯之外,还有很多问题留给读者:
来Solr培训解决这些问题!
- 您如何完全匹配前缀名称?
- 您怎么确定哪些查询标记是用于中间名,姓和名的?
- 标准标记器打破了有连字符的名字,您如何将连字符人名保存为一个标记?
- 许多名称缩写不是原始名称的前缀。例如,当用户输入“Tom?”时,你会如何匹配“Thomas”?
所以,在你的Solr之旅中还有一些有趣的谜题!如果你想要解决这些问题,一定要查看我们的Solr培训!
来分享您的意见吧!希望这篇文章能帮助你开始建立一个合理的人名搜索系统。您过去是否遇到过此类问题?您如何用Solr解决这些问题?请联系我们以获取问题帮助!
lucene/solr搜索引擎lucene/solr搜索引擎