对于特定的医疗中心,我有以下查询以全名搜索患者:
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());
它工作正常,但只有当用户输入患者的完整名字和/或姓氏时才会有效。
但是,即使用户输入名字或姓氏的部分,我也希望能够工作。
例如,如果有一个名为“Bilbo Baggins”的病人,我希望搜索找到他,当用户键入“Bilbo Baggins”,Bilbo,“Baggins”,或者即使他只输入“Bil” “或”袋子“
为实现这一点,我修改了上述查询,如下所示:
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()函数然而,这会破坏搜索并返回无结果。我做错了什么?
答案 0 :(得分:5)
简答:不要使用通配符查询,请使用带EdgeNGramAnalyzerFactory
的自定义分析器。另外,不要试图自己分析查询(这是你通过将查询分成术语而做的):Lucene会做得更好(使用WhitespaceTokenizerFactory
,ASCIIFoldingFilterFactory
和{{1特别是)。
答案很长:
通配符查询可用作一次性问题的快速简便解决方案,但它们不是非常灵活,可以很快达到极限。特别是,正如@femtoRgon所提到的,这些查询不会被分析,因此例如,大写查询与小写名称不匹配。
Lucene世界中大多数问题的经典解决方案是在索引时和查询时使用特制的分析器(不一定相同)。在您的情况下,您将需要在索引时使用此类分析器:
LowercaseFilterFactory
查询时就是这种:
@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")
}
)
})
索引分析器会将“Mauricio Ubilla Carvajal”转换为此令牌列表:
查询分析器会将查询“mau UB”变为[“mau”,“ub”],这将匹配索引名称(两个标记都存在于索引中)。
请注意,您显然必须将分析仪分配到该字段。对于索引部分,使用@Analyzer
annotation完成。
对于查询部分,您必须在查询构建器上使用@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
})
,如图所示here