java - hibernate 搜索 : Search any part of the field without losing field's content while indexing

标签 java hibernate search full-text-search hibernate-search

我希望能够根据其索引字段的任何部分找到一个实体,并且这些字段在索引时不得丢失任何内容。

假设我有以下示例实体类:

@Entity
public class E {
    private String f;
    // ...
}

如果一个实体中的 f 值是 “这是一个不错的字段!”,我希望能够通过任何这些查询找到它:

  • “这个”
  • “一个”
  • “IC”
  • “!”
  • “这是一个不错的领域!”

最明显的决定是以这种方式注释实体:

@Entity
@Indexed
@AnalyzerDef(name = "a",
        tokenizer = @TokenizerDef(factory = KeywordTokenizerFactory.class),
        filters = @TokenFilterDef(factory = LowerCaseFilterFactory.class)
)
@Analyzer(definition = "a")
public class E {
    @Field
    private String f;
    // ...
}

然后按以下方式搜索:

String queryString;
// ...
org.apache.lucene.search.Query query = queryBuilder
        .keyword()
        .wildcard()
        .onField("f")
        .matching("*" + queryString.toLowerCase() + "*")
        .createQuery();

但文档中指出 for performance purposes, it is recommended that the query does not start with either ? or * .

据我了解,这个方法是无效的。

另一个想法是使用像这样的 n-gram:

@Entity
@Indexed
@AnalyzerDef(name = "a",
        tokenizer = @TokenizerDef(factory = KeywordTokenizerFactory.class),
        filters = {
                @TokenFilterDef(factory = LowerCaseFilterFactory.class),
                @TokenFilterDef(factory = NGramFilterFactory.class,
                        params = {
                                @Parameter(name = "minGramSize", value = "1"),
                                @Parameter(name = "maxGramSize", value = E.MAX_LENGTH)
                        })
        }
)
@Analyzer(definition = "a")
public class E {
    static final String MAX_LENGTH = "42";
    @Field
    private String f;
    // ...
}

并以这种方式创建查询:

String queryString;
// ...
org.apache.lucene.search.Query query = queryBuilder
                .keyword()
                .onField("f")
                .ignoreAnalyzer()
                .matching(queryString.toLowerCase())
                .createQuery();

这次没有使用通配符查询,并且查询中的分析器被忽略。我不确定忽略分析器是好是坏,但它可以在忽略分析器的情况下工作。

其他可能的解决方案是在使用 n-gram 时使用 WhitespaceTokenizerFactory 而不是 KeywordTokenizerFactory,然后用空格分割 queryString 并合并搜索每个子字符串使用 MUST 。 据我了解,在这种方法中,如果 f 中包含的字符串的长度是 E.MAX_LENGTH,那么我构建的 n-gram 会少很多,那么必须是有利于性能。我还可以通过例如“hi ield”查询来查找前面描述的实体。那将是理想的。

那么解决我的问题的最佳方法是什么?还是我的想法都不好?

附注使用 n-gram 时是否应该忽略查询中的分析器?

最佳答案

Other possible solution would be to use WhitespaceTokenizerFactory instead of KeywordTokenizerFactory when using n-grams, then split queryString by spaces and combine searches for each substring using MUST. In this approach, as I understand, I will get a lot less n-grams built, if the length of the string contained in f is E.MAX_LENGTH, what must be good for performance. And I will also be able to find the previously described entity by, for example, "hi ield" query. And that would be ideal.

这或多或少是理想的解决方案,除了一件事:查询时不应该忽略分析器。您应该做的是定义另一个不带 ngram 过滤器但带有分词器、小写过滤器等的分析器,并明确指示 Hibernate Search 在查询时使用该分析器。

其他解决方案都太昂贵,无论是查询时的 I/O 和 CPU(第一个解决方案)还是存储空间(第二个解决方案)。请注意,第三种解决方案在存储空间方面可能仍然相当昂贵,具体取决于 E.MAX_LENGTH 的值。通常建议 minGramSizemaxGramSize 之间仅相差一到两个,以避免索引过多的克。

只需定义另一个分析器,将其命名为“ngram_query”,当您需要构建查询时,创建查询构建器,如下所示:

    QueryBuilder queryBuilder = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(EPCAsset.class)
        .overridesForField( "f" /* name of the field */, "ngram_query" )
        .get();

然后照常创建您的查询。

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

关于java - hibernate 搜索 : Search any part of the field without losing field's content while indexing,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56083137/

相关文章:

java - 使用 IN 子句优化 Oracle 查询

java - Hibernate 保存或更新覆盖旧数据

c# - 匹配两个列表

search - Google Chrome DevTools 元素搜索不再有效?

php - 顺序 strpos() 比具有一个 preg_match 的函数更快?

java - 2D 游戏方 block 放置算法

java - 为什么对 WeakRef 对象的 WeakReference 没有被垃圾回收?

java - JScrollpane 之上的 JLabel

java - 无法使用 HibernateOGM 连接到 Mongodb

java - 如何拦截 JTA 事务事件并获取对与事务关联的当前 EntityManager 的引用