Hibernate 搜索前缀
Hibernate search on prefixes
现在,我已经成功配置了一个基本的 Hibernate 搜索索引,以便能够在我的 JPA 实体的各个字段上搜索完整的单词:
@Entity
@Indexed
class Talk {
@Field String title
@Field String summary
}
我的查询看起来像这样:
List<Talk> search(String text) {
FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(entityManager)
QueryBuilder queryBuilder = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(Talk).get()
Query query = queryBuilder
.keyword()
.onFields("title", "summary")
.matching(text)
.createQuery()
FullTextQuery jpaQuery = fullTextEntityManager.createFullTextQuery(query, Talk)
return jpaQuery.getResultList()
}
现在我想 fine-tune 这个设置,这样当我搜索 "test" 时它仍然会找到标题或摘要包含 "test" 甚至作为另一个词的前缀的演讲。因此,标题为 "unit testing" 或摘要包含 "testicle" 的演讲仍应出现在搜索结果中,而不仅仅是标题或摘要包含 "test" 作为完整单词的演讲。
我试图查看文档,但我无法确定是否应该更改实体的索引方式,或者它是否与查询有关。请注意,我想执行如下操作,但很难在多个字段上进行搜索:
Query query = queryBuilder
.keyword().wildcard()
.onField("title")
.matching(text + "*")
.createQuery()
编辑:
根据 Hardy 的回答,我这样配置了我的实体:
@Indexed
@Entity
@AnalyzerDefs([
@AnalyzerDef(name = "ngram",
tokenizer = @TokenizerDef(factory = StandardTokenizerFactory.class),
filters = [
@TokenFilterDef(factory = LowerCaseFilterFactory.class),
@TokenFilterDef(factory = NGramFilterFactory.class,
params = [
@Parameter(name = "minGramSize",value = "3"),
@Parameter(name = "maxGramSize",value = "3")
])
])
])
class Talk {
@Field(analyzer=@Analyzer(definition="ngram")) String title
@Field(analyzer=@Analyzer(definition="ngram")) String summary
}
由于该配置,当我搜索 'arti' 时,我得到标题或摘要包含 'arti' 是(艺术家、工匠等)的子词的演讲。不幸的是,在这些之后,我还得到了标题或摘要包含包含我的搜索词的子词(艺术、放屁等)的词的演讲。可能有一些 fine-tuning 可以消除这些,但至少我现在可以更快地得到结果,而且它们的顺序合理。
您可以在这里做很多事情。通过在索引时间进行适当的分析,可以做很多事情。
例如,您想要应用适合您的语言的词干提取器。对于英语,这通常是 Snowball stemmer.The 的想法是在索引期间所有单词都减少到它们的词干,testing 和 tested 到 _test例如。这会让您有所收获。
您可以研究的另一件事是 ngramm 索引。根据您的描述,您还想在不相关的词中找到匹配项。这里的想法是索引每个单词 "subwords",以便以后可以找到它们。
关于您想要查看 Hibernate Search 文档的 named analyzers 部分的分析器。这里的关键是 @AnalyzerDef
注释。
在查询端你也可以应用一些"tricks"。实际上,您可以使用通配符查询,但是,如果您使用的是 Hibernate Search 查询 DSL,则不能使用 keyword
查询,但需要使用 wildcard
查询。再次检查 Hibernate Search 文档。
在 Lucene 4.9 版中,我为此使用了 EnglishAnalyzer
。我认为这是 SnowballAnalyzer 的纯英文实现,但不是 100% 确定。我用它来创建和搜索索引。没有什么特别需要使用它。
Analyzer analyzer = new EnglishAnalyzer(Version.LUCENE_4_9);
IndexWriterConfig iwc = new IndexWriterConfig(Version.LUCENE_4_9, analyzer);
和
analyzer = new EnglishAnalyzer(Version.LUCENE_4_9);
parser = new StandardQueryParser(analyzer);
您可以在 Guided Code Search 上看到它的实际效果。这仅在 Lucene 上运行。
Lucene 可以集成到 Hibernate 搜索中,但我自己还没有尝试这样做。好像很厉害,不知道:见Apache Lucene™ Integration.
我还读到可以将 lucene 修补到 SQL 引擎中,但我也没有尝试过。示例:Indexing Databases with Lucene。
您应该使用 Ngram
或 EdgeNGram
过滤器作为索引输入,正如您在回答中正确指出的那样。但是您应该按照 lucene 文档中的建议对您的查询使用不同的分析器(参见 search_analyzer
):
https://www.elastic.co/guide/en/elasticsearch/guide/current/_index_time_search_as_you_type.html
这样您的搜索查询就不会被标记为 ngram,您的结果将更像是 SQL 中的 %text%
或 text%
。
不幸的是,由于未知原因,Hibernate Search 目前不支持 search_analyzer
字段规范。您只能将特定的分析器用于索引,这也将用于搜索查询分析。
我打算自己实现这个功能。
编辑:
您可以像这样指定搜索时间分析器 (search_analyzer
):
List<Talk> search(String text) {
FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(entityManager)
EntityContext entityContext = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(Talk);
entityContext.overridesForField("myField", "myNamedAnalyzerDef");
QueryBuilder queryBuilder = ec.get()
Query query = queryBuilder
.keyword()
.onFields("title", "summary")
.matching(text)
.createQuery()
FullTextQuery jpaQuery = fullTextEntityManager.createFullTextQuery(query, Talk)
return jpaQuery.getResultList()
}
我已经使用这种技术有效地模拟了 Lucene search_analyzer
属性.
现在,我已经成功配置了一个基本的 Hibernate 搜索索引,以便能够在我的 JPA 实体的各个字段上搜索完整的单词:
@Entity
@Indexed
class Talk {
@Field String title
@Field String summary
}
我的查询看起来像这样:
List<Talk> search(String text) {
FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(entityManager)
QueryBuilder queryBuilder = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(Talk).get()
Query query = queryBuilder
.keyword()
.onFields("title", "summary")
.matching(text)
.createQuery()
FullTextQuery jpaQuery = fullTextEntityManager.createFullTextQuery(query, Talk)
return jpaQuery.getResultList()
}
现在我想 fine-tune 这个设置,这样当我搜索 "test" 时它仍然会找到标题或摘要包含 "test" 甚至作为另一个词的前缀的演讲。因此,标题为 "unit testing" 或摘要包含 "testicle" 的演讲仍应出现在搜索结果中,而不仅仅是标题或摘要包含 "test" 作为完整单词的演讲。
我试图查看文档,但我无法确定是否应该更改实体的索引方式,或者它是否与查询有关。请注意,我想执行如下操作,但很难在多个字段上进行搜索:
Query query = queryBuilder
.keyword().wildcard()
.onField("title")
.matching(text + "*")
.createQuery()
编辑: 根据 Hardy 的回答,我这样配置了我的实体:
@Indexed
@Entity
@AnalyzerDefs([
@AnalyzerDef(name = "ngram",
tokenizer = @TokenizerDef(factory = StandardTokenizerFactory.class),
filters = [
@TokenFilterDef(factory = LowerCaseFilterFactory.class),
@TokenFilterDef(factory = NGramFilterFactory.class,
params = [
@Parameter(name = "minGramSize",value = "3"),
@Parameter(name = "maxGramSize",value = "3")
])
])
])
class Talk {
@Field(analyzer=@Analyzer(definition="ngram")) String title
@Field(analyzer=@Analyzer(definition="ngram")) String summary
}
由于该配置,当我搜索 'arti' 时,我得到标题或摘要包含 'arti' 是(艺术家、工匠等)的子词的演讲。不幸的是,在这些之后,我还得到了标题或摘要包含包含我的搜索词的子词(艺术、放屁等)的词的演讲。可能有一些 fine-tuning 可以消除这些,但至少我现在可以更快地得到结果,而且它们的顺序合理。
您可以在这里做很多事情。通过在索引时间进行适当的分析,可以做很多事情。
例如,您想要应用适合您的语言的词干提取器。对于英语,这通常是 Snowball stemmer.The 的想法是在索引期间所有单词都减少到它们的词干,testing 和 tested 到 _test例如。这会让您有所收获。
您可以研究的另一件事是 ngramm 索引。根据您的描述,您还想在不相关的词中找到匹配项。这里的想法是索引每个单词 "subwords",以便以后可以找到它们。
关于您想要查看 Hibernate Search 文档的 named analyzers 部分的分析器。这里的关键是 @AnalyzerDef
注释。
在查询端你也可以应用一些"tricks"。实际上,您可以使用通配符查询,但是,如果您使用的是 Hibernate Search 查询 DSL,则不能使用 keyword
查询,但需要使用 wildcard
查询。再次检查 Hibernate Search 文档。
在 Lucene 4.9 版中,我为此使用了 EnglishAnalyzer
。我认为这是 SnowballAnalyzer 的纯英文实现,但不是 100% 确定。我用它来创建和搜索索引。没有什么特别需要使用它。
Analyzer analyzer = new EnglishAnalyzer(Version.LUCENE_4_9);
IndexWriterConfig iwc = new IndexWriterConfig(Version.LUCENE_4_9, analyzer);
和
analyzer = new EnglishAnalyzer(Version.LUCENE_4_9);
parser = new StandardQueryParser(analyzer);
您可以在 Guided Code Search 上看到它的实际效果。这仅在 Lucene 上运行。
Lucene 可以集成到 Hibernate 搜索中,但我自己还没有尝试这样做。好像很厉害,不知道:见Apache Lucene™ Integration.
我还读到可以将 lucene 修补到 SQL 引擎中,但我也没有尝试过。示例:Indexing Databases with Lucene。
您应该使用 Ngram
或 EdgeNGram
过滤器作为索引输入,正如您在回答中正确指出的那样。但是您应该按照 lucene 文档中的建议对您的查询使用不同的分析器(参见 search_analyzer
):
https://www.elastic.co/guide/en/elasticsearch/guide/current/_index_time_search_as_you_type.html
这样您的搜索查询就不会被标记为 ngram,您的结果将更像是 SQL 中的 %text%
或 text%
。
不幸的是,由于未知原因,Hibernate Search 目前不支持 search_analyzer
字段规范。您只能将特定的分析器用于索引,这也将用于搜索查询分析。
我打算自己实现这个功能。
编辑:
您可以像这样指定搜索时间分析器 (search_analyzer
):
List<Talk> search(String text) {
FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(entityManager)
EntityContext entityContext = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(Talk);
entityContext.overridesForField("myField", "myNamedAnalyzerDef");
QueryBuilder queryBuilder = ec.get()
Query query = queryBuilder
.keyword()
.onFields("title", "summary")
.matching(text)
.createQuery()
FullTextQuery jpaQuery = fullTextEntityManager.createFullTextQuery(query, Talk)
return jpaQuery.getResultList()
}
我已经使用这种技术有效地模拟了 Lucene search_analyzer
属性.