使用 nGram 进行 Hibernate 搜索 |如何指示 nGram 在搜索期间不生成克

Hibernate Search with nGram | How to instruct that nGram do no make grams during search time

我已将我的分析器定义如下

@AnalyzerDefs({
@AnalyzerDef(name = "ngram",
            tokenizer = @TokenizerDef(factory = KeywordTokenizerFactory.class),
            filters = {
                    //@TokenFilterDef(factory = StandardFilterFactory.class),
                    @TokenFilterDef(factory = LowerCaseFilterFactory.class),
                    @TokenFilterDef(factory = NGramFilterFactory.class, params = {
                            @Parameter(name = "minGramSize", value = "3"),
                            @Parameter(name = "maxGramSize", value = "255") }) }),
//-----------------------------------------------------------------------
    @AnalyzerDef(name = "ngram_query",
            tokenizer = @TokenizerDef(factory = KeywordTokenizerFactory.class),
            filters = {
                    //@TokenFilterDef(factory = StandardFilterFactory.class),
                    @TokenFilterDef(factory = LowerCaseFilterFactory.class)
                    }) 
})

@Analyzer(definition = "ngram")
public class EPCAsset extends Asset {
    @Field
    private String obturatorMaterial;

}

它在索引时间内完美地生成了 n-gram 术语向量。但它也会在搜索期间生成 n-gram 搜索查询。

我想要的是一种搜索查询使用 n-gram 索引进行搜索而不将搜索词分解为克的方式。

注意:我这里必须使用n-gram,因为要求是在文本的任何地方进行搜索。开始或中间。所以 edge-n-gram 对我来说不是一个选择。

示例: Input Data to be index ICQ 234

然后在索引时间内它的词向量是

    "234"
    " 23"
    " 234"
    "cq "
    "cq 2"
    "cq 23"
    "cq 234"
    "icq"
    "icq "
    "icq 2"
    "icq 23"
    "icq 234"
    "q 2"
    "q 23"
    "q 234"

现在,当我搜索 icq 时,它工作得很好。但它也适用于 icqabc 因为在搜索期间它会生成 n-gram 的搜索查询。那么有没有一种方法可以在搜索期间不破坏搜索词而是使用n-gram索引进行搜索。

这是我的搜索查询构建

FullTextEntityManager fullTextEntityManager = Search
            .getFullTextEntityManager(entityManager);

QueryBuilder qb = fullTextEntityManager.getSearchFactory().buildQueryBuilder()
            .forEntity(entityClass).get();
Query query = qb.phrase().onField("obturatorMaterial").sentence("icqabc").createQuery();
FullTextQuery fullTextQuery = fullTextEntityManager.createFullTextQuery(query,
            entityClass);
fullTextQuery.getResultList()

我正在使用弹性搜索作为 Hibernate 搜索的后端。

编辑: 我还根据@yrodiere 的回答应用了查询时间分析器,但它给了我错误。

QueryBuilder qb = fullTextEntityManager.getSearchFactory().buildQueryBuilder()
            .forEntity(entityClass).overridesForField("obturatorMaterial","ngram_query").get();

org.hibernate.search.exception.SearchException: HSEARCH000353: Unknown analyzer: 'ngram_query'. Make sure you defined this analyzer.

编辑

按照这个 link overriderForField when using elasticsearch backed hibernate search

我现在可以定义一个查询时间第二个分析器,它解决了这个问题。

要么您需要使用 search time analyzer 并且很有可能在搜索时使用关键字分析器。或者需要使用 term 查询而不是 match 查询,这意味着它使用相同的分析器使用索引时间。

阅读有关 term query and match query 的更多信息以了解更多信息。

Edit :- https://www.elastic.co/guide/en/elasticsearch/reference/current/search-analyzer.html 说的很清楚search_analyzer的使用,如果是edgeNGram tokenizer自动完成搜索 这正是您的用例。

首先,您应该仔细检查 ngram 过滤器是否真的是您想要的。我提到这个是因为 ngram 分析器通常用于索引和查询,因此它提供模糊匹配。这就是这个分析器的全部意义所在。

当用户键入 cq 2 时,您真的需要匹配吗?是否有意义?在实现自动完成时,人们通常更喜欢只匹配包含 start 与用户输入的单词的文档,因此 i 会匹配 icicq也会,但不会 cq 2。如果这似乎是您想要的,您应该看看 "edge_ngram" 过滤器。它往往会提高匹配的相关性,并且不需要那么多磁盘 space.

现在,即使使用 "edge_ngram" 过滤器,您也需要在查询时禁用 ngram。在 Hibernate Search 中,这是由 "overriding" 分析器完成的。

  1. 首先,定义第二个分析器,与您在索引期间使用的分析器相同,但没有 "ngram" 或 "edge_ngram" 过滤器。将其命名为 "ngram_query".
  2. 然后,使用它来创建您的查询构建器:

    QueryBuilder queryBuilder = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(EPCAsset.class)
        .overridesForField( "obturatorMaterial", "ngram_query" )
        .get();
    
  3. 像往常一样使用查询生成器创建查询。

请注意,如果您依靠 Hibernate Search 将索引模式和分析器推送到 Elasticsearch,则必须使用 hack 才能推送 query-only 分析器:默认情况下只有分析器在索引期间实际使用的被推送。参见 https://discourse.hibernate.org/t/cannot-find-the-overridden-analyzer-when-using-overridesforfield/1043/4