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());

它运行良好,但前提是用户键入患者的完整名字和/或姓氏。

但是,即使用户键入名字或姓氏的一部分我也想使其工作

例如,如果有一个叫“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() 之前添加通配符() 函数的

但是,这会中断搜索并且不返回任何结果。我究竟做错了什么?

罗迪埃

简短回答:不要使用通配符查询,使用带有EdgeNGramFilterFactory. 另外,不要尝试自己分析查询(这是通过将查询拆分为术语所做的):Lucene 会做得更好(特别是使用 a WhitespaceTokenizerFactory、 anASCIIFoldingFilterFactory和 a 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”转换为以下标记列表:

  • 毛尔
  • 毛里
  • 毛里克
  • 毛里求斯
  • 毛里西奥
  • 乌布
  • ...
  • 乌比利亚
  • C
  • ...
  • 卡瓦哈尔

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

请注意,您显然必须将分析器分配给该字段。对于索引部分,它是使用@Analyzerannotation完成的对于查询部分,你将不得不使用overridesForField的查询生成器如图所示在这里

QueryBuilder queryBuilder = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(Hospital.class)
    .overridesForField( "name", "edgeNGram_query" )
    .get();
// Then it's business as usual

另请注意,在 Hibernate Search 5 中,Elasticsearch 分析器定义仅在实际分配给索引时才由 Hibernate Search 生成。因此默认情况下不会生成查询分析器定义,并且 Elasticsearch 会抱怨它不知道分析器。这是一个解决方法:https : //discourse.hibernate.org/t/cannot-find-the-overridden-analyzer-when-using-overridesforfield/1043/4?u=yrodiere

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章