Hibernate Search:如何正确使用通配符?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Hibernate Search:如何正确使用通配符?相关的知识,希望对你有一定的参考价值。

对于特定的医疗中心,我有以下查询以全名搜索患者:

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会做得更好(特别是WhitespaceTokenizerFactoryASCIIFoldingFilterFactoryLowercaseFilterFactory)。

答案很长:

通配符查询可用作一次性问题的快速简便解决方案,但它们不是非常灵活,可以很快达到极限。特别是,正如@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”转换为这个令牌列表:

  • 想要
  • 圣莫尔
  • 马利
  • mauric
  • 毛里求斯
  • UB
  • ...
  • ubilla
  • C
  • ...
  • 卡瓦哈尔

并且查询分析器将查询“mau UB”变为[“mau”,“ub”],这将匹配索引名称(两个标记都存在于索引中)。

请注意,您显然必须将分析仪分配到该字段。对于索引部分,使用@Analyzer annotation完成。对于查询部分,您必须在查询构建器上使用overridesForField,如图所示here

以上是关于Hibernate Search:如何正确使用通配符?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用gradle获取Hibernate Search Lucene的所有依赖项?

如何使用Hibernate Search查询DSL匹配多个术语?

如何在 Hibernate Search/Lucene 中禁用默认评分/提升?

如何在输入输出中正确使用通配符

如何使用 SQL 在 python 中使用 %x% 通配符

SQL按LIKE(带通配符)进行分组,如何实现正确的求和