Hibernate Search:在索引时搜索字段的任何部分而不会丢失字段的内容
Hibernate Search: Search any part of the field without losing field's content while indexing
我希望能够根据其索引字段的 任何部分 找到一个实体,并且字段 不能 松散索引时的任何内容。
假设我有以下示例实体 class:
@Entity
public class E {
private String f;
// ...
}
如果一个实体中 f
的值为 "This is a nice field!"
,我希望能够通过以下任何查询找到它:
- "this"
- "a"
- "IC"
- "!"
- "This is a nice field!"
最明显的决定是以这种方式注释实体:
@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
并使用 [=23= 组合搜索每个子字符串].
据我所知,在这种方法中,如果 f
中包含的字符串的长度是 E.MAX_LENGTH
,那么我将构建更少的 n-gram,这对性能来说一定是好的。而且我也将能够通过例如 "hi ield" 查询找到前面描述的实体。那将是理想的。
那么解决我的问题的最佳方法是什么?还是我的想法都不好?
P.S。使用 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 中查询时(第一个解决方案)还是在存储 space 中(第二个解决方案)。请注意,根据 E.MAX_LENGTH
的值,第三种解决方案在存储方面可能仍然相当昂贵 space。一般建议minGramSize
和maxGramSize
只相差一两,避免克数过多的索引。
只需定义另一个分析器,将其命名为 "ngram_query",当您需要构建查询时,像这样创建查询构建器:
QueryBuilder queryBuilder = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(EPCAsset.class)
.overridesForField( "f" /* name of the field */, "ngram_query" )
.get();
然后照常创建查询。
请注意,如果您依靠 Hibernate Search 将索引模式和分析器推送到 Elasticsearch,则必须使用 hack 才能推送 query-only 分析器:默认情况下只有分析器在索引期间实际使用的被推送。参见 https://discourse.hibernate.org/t/cannot-find-the-overridden-analyzer-when-using-overridesforfield/1043/4
我希望能够根据其索引字段的 任何部分 找到一个实体,并且字段 不能 松散索引时的任何内容。
假设我有以下示例实体 class:
@Entity
public class E {
private String f;
// ...
}
如果一个实体中 f
的值为 "This is a nice field!"
,我希望能够通过以下任何查询找到它:
- "this"
- "a"
- "IC"
- "!"
- "This is a nice field!"
最明显的决定是以这种方式注释实体:
@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
并使用 [=23= 组合搜索每个子字符串].
据我所知,在这种方法中,如果 f
中包含的字符串的长度是 E.MAX_LENGTH
,那么我将构建更少的 n-gram,这对性能来说一定是好的。而且我也将能够通过例如 "hi ield" 查询找到前面描述的实体。那将是理想的。
那么解决我的问题的最佳方法是什么?还是我的想法都不好?
P.S。使用 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 中查询时(第一个解决方案)还是在存储 space 中(第二个解决方案)。请注意,根据 E.MAX_LENGTH
的值,第三种解决方案在存储方面可能仍然相当昂贵 space。一般建议minGramSize
和maxGramSize
只相差一两,避免克数过多的索引。
只需定义另一个分析器,将其命名为 "ngram_query",当您需要构建查询时,像这样创建查询构建器:
QueryBuilder queryBuilder = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(EPCAsset.class)
.overridesForField( "f" /* name of the field */, "ngram_query" )
.get();
然后照常创建查询。
请注意,如果您依靠 Hibernate Search 将索引模式和分析器推送到 Elasticsearch,则必须使用 hack 才能推送 query-only 分析器:默认情况下只有分析器在索引期间实际使用的被推送。参见 https://discourse.hibernate.org/t/cannot-find-the-overridden-analyzer-when-using-overridesforfield/1043/4