我的实体定义如下。
@Entity
@Indexed
@AnalyzerDef(name = "ngram_index", tokenizer = @TokenizerDef(factory = WhitespaceTokenizerFactory.class),
filters = {
@TokenFilterDef(factory = LowerCaseFilterFactory.class),
@TokenFilterDef(factory = ASCIIFoldingFilterFactory.class),
@TokenFilterDef(factory = NGramFilterFactory.class,
params = {
@Parameter(name = SearchConstants.MIN_GRAM_SIZE_NAME, value = SearchConstants.MIN_GRAM_SIZE_VALUE),
@Parameter(name = SearchConstants.MAX_GRAM_SIZE_NAME, value = SearchConstants.MAX_GRAM_SIZE_VALUE)
})
})
@AnalyzerDef(name = "ngram_query", tokenizer = @TokenizerDef(factory = WhitespaceTokenizerFactory.class),
filters = {
@TokenFilterDef(factory = LowerCaseFilterFactory.class),
@TokenFilterDef(factory = ASCIIFoldingFilterFactory.class),
})
@NormalizerDef(name = "lowercase",
filters = {
@TokenFilterDef(factory = ASCIIFoldingFilterFactory.class),
@TokenFilterDef(factory = LowerCaseFilterFactory.class)
}
)
@Table(name = "ORDER")
public class Order {
@Id
@DocumentId
@Column(name = "ID")
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Field(analyzer = @Analyzer(definition = "ngram_index"))
@Field(name = "name_Sort", store = Store.YES, normalizer= @Normalizer(definition="lowercase"))
@SortableField(forField = "name_Sort")
@Column(name = "NAME")
private String name;
//other fields, getters and setters omitted for brevity
然后,我尝试覆盖在索引中用于查询不是实体的另一个类中的默认分析器。
public abstract class AbstractHibernateSearcher<S extends SearchableEntity> {
// other fields and methods omitted here
protected Query buildInputSearchQuery(String[] searchableFields) {
if(Strings.isNullOrEmpty(searchRequest.getQuery()) || searchableFields.length == 0) {
return null;
}
SimpleQueryStringMatchingContext simpleQueryStringMatchingContext = queryBuilder.simpleQueryString().onField(searchableFields[0]);
for(int i = 1; i < searchableFields.length; i++) {
simpleQueryStringMatchingContext = simpleQueryStringMatchingContext.andField(searchableFields[i]);
}
Query inputSearchQuery = simpleQueryStringMatchingContext
.withAndAsDefaultOperator()
.matching((searchRequest.getQuery()).toLowerCase()).createQuery();
QueryBuilder queryBuilder = getNGramQueryBuilder(searchableFields);
return queryBuilder.bool().must(inputSearchQuery).createQuery();
}
protected QueryBuilder getNGramQueryBuilder(String[] searchFields) {
if (searchFields.length == 0) {
return null;
}
EntityContext entityContext = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(clazz);
for(String field : searchFields) {
entityContext = entityContext.overridesForField(field, "ngram_query");
}
return entityContext.get();
}
}
当我执行查询搜索时,这给了我以下错误。
{消息:“ HSEARCH000353:未知分析器:'ngram_query'。请确保已定义此分析器。”,…} 异常:“ RuntimeException” 消息:“ HSEARCH000353:未知分析器:'ngram_query'。请确保已定义此分析器。”
我从官方文档中发现了这一点。
您可以在任何以下位置使用@AnalyzerDef:
@带索引的实体,无论将分析器应用于何处;
@Indexed实体的父类;
包含@Indexed实体的软件包的package-info.java。
由于我看到了未知的分析器,所以我想尝试用“ ngram_query”分析器覆盖的类对此分析器没有可见性吗?
答案 0 :(得分:1)
是的,您可以为每个单词创建ngram:将WhitespaceTokenizerFactory
用作令牌生成器,并将NGramFilterFactory
添加到令牌过滤器中(请注意,它与您提到的类别不同:它是令牌过滤器,不是令牌生成器。
在查询时,您还需要使用其他分析器,该分析器不会创建ngram。否则,例如,键入“ manhantan”的用户可能会获得包含“ man”的文档的匹配项。 有关如何执行此操作的信息,请参见https://stackoverflow.com/a/56107399/6692043。
请注意,ngram可以导致非常大的索引,尤其是如果您对“ minGramSize”和“ maxGramSize”参数的值不小心的话。
另一种解决方案是使用原始分析器和通配符查询,但是不幸的是,它忽略了分析,并且在使用前导通配符时可能会很慢(这是您在这里需要的)。
protected Query inputFilterBuilder() {
String[] searchableFields = getSearchableFields();
if(searchableFields.length == 0) {
return null;
}
TermMatchingContext termMatchingContext = queryBuilder.keyword().wildcard().onField(searchableFields[0]);
for(int i = 1; i < searchableFields.length; i++) {
termMatchingContext = termMatchingContext.andField(searchableFields[i]);
}
return termMatchingContext
.matching(("*" + searchRequest.getQuery() + "*").toLowerCase()).createQuery();
}
请注意,上面的代码仅在存在单个搜索字词时才有效。 searchRequest.getQuery()
中一旦有空格,您将不会获得任何结果。但是,如果我理解正确的话,索引文本中可能会有空格,这正是您想要的。