我有以下查询,可以通过全名搜索特定医疗中心的患者:
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会做得更好(特别是使用WhitespaceTokenizerFactory,an ASCIIFoldingFilterFactory和a LowercaseFilterFactory)。
EdgeNGramFilterFactory
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注释完成的。对于查询部分,你将不得不使用overridesForField的查询生成器如图所示在这里:
@Analyzer
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