对于特定的医疗中心,我有以下查询以全名搜索患者:
MustJunction mj = qb.bool().must(qb.keyword()
.onField("medicalCenter.id")
.matching(medicalCenter.getId())
.createQuery());
for(String term: terms)
if(!term.equals(""))
mj.must(qb.keyword()
.onField("fullName")
.matching(term+"*")
.createQuery());
并且它工作得很好,但前提是用户键入患者的完整名字和/或姓氏。
但是,即使用户键入firstname或lastname的一部分,我也希望能够工作。
例如,如果有一个名为“Bilbo Baggins”的病人,我希望搜索找到他,当用户输入“Bilbo Baggins”,Bilbo,“Baggins”时,或者即使他只输入“Bil”或“Bag”
为此,我修改了上面的查询,如下所示:
MustJunction mj = qb.bool().must(qb.keyword()
.onField("medicalCenter.id")
.matching(medicalCenter.getId())
.createQuery());
for(String term: terms)
if(!term.equals(""))
mj.must(qb.keyword()
.wildcard()
.onField("fullName")
.matching(term+"*")
.createQuery());
注意我在调用onField()之前如何添加wildcard()函数
但是,这会中断搜索并返回无结果。我究竟做错了什么?
简短回答:不要使用通配符查询,使用带有EdgeNGramAnalyzerFactory
的自定义分析器。另外,不要试图自己分析查询(这是你通过将查询分成术语而做的):Lucene会做得更好(特别是WhitespaceTokenizerFactory
,ASCIIFoldingFilterFactory
和LowercaseFilterFactory
)。
答案很长:
通配符查询可用作一次性问题的快速简便解决方案,但它们不是非常灵活,可以很快达到极限。特别是,正如@femtoRgon所提到的,这些查询不会被分析,因此大写查询不会与小写名称匹配。
Lucene世界中大多数问题的经典解决方案是在索引时和查询时使用特制的分析器(不一定相同)。在您的情况下,您将需要在索引时使用此类分析器:
@AnalyzerDef(name = "edgeNgram",
tokenizer = @TokenizerDef(factory = WhitespaceTokenizerFactory.class),
filters = {
@TokenFilterDef(factory = ASCIIFoldingFilterFactory.class), // Replace accented characeters by their simpler counterpart (è => e, etc.)
@TokenFilterDef(factory = LowerCaseFilterFactory.class), // Lowercase all characters
@TokenFilterDef(
factory = EdgeNGramFilterFactory.class, // Generate prefix tokens
params = {
@Parameter(name = "minGramSize", value = "1"),
@Parameter(name = "maxGramSize", value = "10")
}
)
})
在查询时这种类型:
@AnalyzerDef(name = "edgeNGram_query",
tokenizer = @TokenizerDef(factory = WhitespaceTokenizerFactory.class),
filters = {
@TokenFilterDef(factory = ASCIIFoldingFilterFactory.class), // Replace accented characeters by their simpler counterpart (è => e, etc.)
@TokenFilterDef(factory = LowerCaseFilterFactory.class) // Lowercase all characters
})
索引分析器将“Mauricio Ubilla Carvajal”转换为这个令牌列表:
并且查询分析器将查询“mau UB”变为[“mau”,“ub”],这将匹配索引名称(两个标记都存在于索引中)。
请注意,您显然必须将分析仪分配到该字段。对于索引部分,使用@Analyzer
annotation完成。对于查询部分,您必须在查询构建器上使用overridesForField
,如图所示here