我刚刚进入了看似简单但非常复杂的搜索世界。对于应用程序,我需要构建一个搜索机制,以按名称搜索用户。
阅读了大量帖子和文章,包括:
How can I use Lucene for personal name (first name, last name) search?
http://dublincore.org/documents/1998/02/03/name-representation/
what's the best way to search a social network by prioritizing a users relationships first?
http://www.gossamer-threads.com/lists/lucene/java-user/120417
Lucene Index and Query Design Question - Searching People
Lucene Fuzzy Search for customer names and partial address
......以及其他一些我现在无法找到的人。在我的机器上进行至少索引和基本搜索工作我已经为用户搜索设计了以下方案:
1)拥有第一个,第二个和第三个名称字段,并用Solr作为索引
2)使用edismax作为多列搜索的requestParser
3)使用标准化过滤器的组合,例如:音译,拉丁语到ascii convesrion等。
4)最后使用模糊搜索
显然,对此我不熟悉,我不确定以上是否是最好的方法,并希望听到有经验的用户在这个领域有比我更好的想法。
我需要能够通过以下方式匹配名称:
1)口音折叠:Jorn与Jörn匹配,反之亦然
2)替代拼写:卡尔与卡尔匹配,反之亦然
3)缩短的陈述(我相信我使用SynonymFilterFactory):Sue匹配Susanne等。
4)Levenstein匹配:Jonn匹配John等。
5)Soundex匹配:Elin和Ellen
非常欢迎任何指导,批评或评论。如果可能的话请告诉我......或者我只是白日做梦。 :)
修改
我还必须补充一点,我也有一个全名字段以防有些人有长名字,例如其中一个帖子:Jon Paul或Del Carmen也应该与Jon Paul Del Carmen匹配
由于这是一个新项目,我可以按照我认为合适的方式修改架构和架构,因此限制非常有限。
答案 0 :(得分:9)
听起来你正在为一个你需要非常松散地匹配的搜索提供语料库?
如果你这样做,你会想要选择你的字段并设置不同的提升来对你的结果进行排名。
所以在solr中有单独的“复制”字段:
See Also: more non-english Soundex discussion
名称的同义词,我不知道是否有公共同义词db。
模糊搜索,我发现它没有用,它使用了Levenshtein Distance。
其他过滤器和索引可获得更优越的“搜索相关”结果。
名称中的Unicode字符可以使用ASCIIFoldingFilterFactory
进行处理您正在预先描述预期用例的解决方案。
如果您想获得高质量的结果,请计划调整Search Relevance
当尝试匹配同义词时,这种调整将特别有价值,例如MacDonald和McDonald(其Levenshtein距离比Carl和Karl更大)。
答案 1 :(得分:3)
找到一个昵称db,不知道有多好: http://www.peacockdata2.com/products/pdnickname/
请注意,它不是免费的。
答案 2 :(得分:1)
另一篇文章中的答案相当不错: Training solr to recognize nicknames or name variants
<fieldType name="name_en" class="solr.TextField" positionIncrementGap="100" autoGeneratePhraseQueries="true"> <analyzer type="index"> <tokenizer class="solr.WhitespaceTokenizerFactory"/> <filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" catenateWords="1" catenateNumbers="1" catenateAll="0" splitOnCaseChange="1"/> <filter class="solr.LowerCaseFilterFactory"/> <filter class="solr.ASCIIFoldingFilterFactory"/> </analyzer> <analyzer type="query"> <tokenizer class="solr.WhitespaceTokenizerFactory"/> <filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" catenateWords="0" catenateNumbers="0" catenateAll="0" splitOnCaseChange="1"/> <filter class="solr.LowerCaseFilterFactory"/> <filter class="solr.ASCIIFoldingFilterFactory"/> <filter class="solr.SynonymFilterFactory" synonyms="english_names.txt" ignoreCase="true" expand="true"/> </analyzer> </fieldType>
答案 3 :(得分:1)
对于语音名称搜索,您也可以尝试使用Beider-Morse Filter,如果您有来自不同国家/地区的混合名称,则可以正常运行。
如果要将其与预先输入功能一起使用,请将其与EdgeNGramFilter结合使用:
<fieldType name="phoneticNames" class="solr.TextField" positionIncrementGap="100" autoGeneratePhraseQueries="true">
<analyzer type="index">
<tokenizer class="solr.WhitespaceTokenizerFactory"/>
<filter class="solr.BeiderMorseFilterFactory" nameType="GENERIC" ruleType="APPROX" concat="true" languageSet="auto"/>
<filter class="solr.EdgeNGramFilterFactory" minGramSize="3" maxGramSize="15"/>
</analyzer>
<analyzer type="query">
<tokenizer class="solr.WhitespaceTokenizerFactory"/>
<filter class="solr.BeiderMorseFilterFactory" nameType="GENERIC" ruleType="APPROX" concat="true" languageSet="auto"/>
</analyzer>
</fieldType>
答案 4 :(得分:0)
我们创建了一个简单的名称&#39;字段类型,允许混合&#39;键&#39; (例如,SOUNDEX)和&#39;成对&#39;以上部分答案。
以下是概述:
这是其实施的核心......
List<IndexableField> createFields(SchemaField field, String name) {
Collection<FieldSpec> nameFields = deriveFieldsForName(name);
List<IndexableField> docFields = new ArrayList<>();
for (FieldSpec fs : nameFields) {
docFields.add(new Field(fs.getName(), fs.getStringValue(),
fs.getLuceneField()));
}
docFields.add(createDocValues(field.getName(), new Name(name)));
return docFields;
}
这个的核心是deriveFieldsForName(name),你可以在其中包含&#39;键&#39;来自PhoneticFilters,LowerCaseFolding等。
这是其实施的核心......
public Query getFieldQuery(QParser parser, SchemaField field, String val) {
Name name = parseNameString(externalVal, parser.getParams());
QuerySpec querySpec = buildQuery(name);
return querySpec.accept(new SolrQueryVisitor(field.getName()));
}
这是buildQuery(name)方法的核心,该方法应该生成一个知道上面的deriveFieldsForName(name)的查询,因此对于给定的查询名称,它将找到合适的候选名称。
以下是您查询中的内容......
&rq={!myRerank reRankQuery=$rrq} &rrq={!func}myMatch(fieldName, "John Doe")
myMatch的内容可能有成对的Levenstein或Jaro-Winkler实现。
N.B。我们自己的完整实现使用deriveFieldsForName,buildQuery和myMatch的专有代码(请参阅http://www.basistech.com/text-analytics/rosette/name-indexer/)来处理上面提到的更多种类的变体(例如,缺少空格,跨语言)。