在休眠搜索中搜索内部子串

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() 中有空格,您就不会得到任何结果。但是,如果我理解正确的话,索引文本中可以有空格,这正是您想要的。