在休眠搜索中搜索内部子串
Search internal substrings in hibernate search
我将我的实体定义如下。
@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
然后我尝试覆盖在索引期间使用的默认分析器,以便在另一个非实体的 class 中进行查询。
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: Unknown analyzer: 'ngram_query'. Make sure you defined this analyzer.",…}
异常:"RuntimeException"
消息:"HSEARCH000353: Unknown analyzer: 'ngram_query'. Make sure you defined this analyzer."
这是我从官方文档中找到的。
您可以在任何对象上使用@AnalyzerDef:
@Indexed entity 不管分析器应用到哪里;
@Indexed 实体的父级 class;
package-info.java 包含 @Indexed 实体的包。
因为我看到了未知的分析器,我想我试图用 "ngram_query" 分析器覆盖的 class 在这个分析器上没有可见性?
是的,您可以为每个单词创建 ngram:使用 WhitespaceTokenizerFactory
作为分词器,并将 NGramFilterFactory
添加到分词过滤器(请注意,这与您提到的 class 不同: 它是一个标记过滤器,而不是一个标记器。
您还需要在查询时使用不同的分析器,一个不创建 ngram 的分析器。否则,用户键入 "manhantan" 可能会匹配包含 "man" 的文档,例如。
有关如何执行此操作的信息,请参阅 。
请注意,ngrams 可能会导致非常大的索引,特别是如果您不注意 "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()
中有空格,您就不会得到任何结果。但是,如果我理解正确的话,索引文本中可以有空格,这正是您想要的。
我将我的实体定义如下。
@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
然后我尝试覆盖在索引期间使用的默认分析器,以便在另一个非实体的 class 中进行查询。
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: Unknown analyzer: 'ngram_query'. Make sure you defined this analyzer.",…} 异常:"RuntimeException" 消息:"HSEARCH000353: Unknown analyzer: 'ngram_query'. Make sure you defined this analyzer."
这是我从官方文档中找到的。
您可以在任何对象上使用@AnalyzerDef:
@Indexed entity 不管分析器应用到哪里;
@Indexed 实体的父级 class;
package-info.java 包含 @Indexed 实体的包。
因为我看到了未知的分析器,我想我试图用 "ngram_query" 分析器覆盖的 class 在这个分析器上没有可见性?
是的,您可以为每个单词创建 ngram:使用 WhitespaceTokenizerFactory
作为分词器,并将 NGramFilterFactory
添加到分词过滤器(请注意,这与您提到的 class 不同: 它是一个标记过滤器,而不是一个标记器。
您还需要在查询时使用不同的分析器,一个不创建 ngram 的分析器。否则,用户键入 "manhantan" 可能会匹配包含 "man" 的文档,例如。
有关如何执行此操作的信息,请参阅
请注意,ngrams 可能会导致非常大的索引,特别是如果您不注意 "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()
中有空格,您就不会得到任何结果。但是,如果我理解正确的话,索引文本中可以有空格,这正是您想要的。