Hibernate 搜索 Lucene。建议但几乎像 SQL "LIKE"
Hibernate Search Lucene. Suggestion but almost like SQL "LIKE"
这是我第一次接触优化的搜索功能,我的部分熟练程度是android开发的前端,但我愿意冒险尝试hibernate-search。我确实了解 SQL "LIKE" 查询的功能,它的作用及其局限性,这就是我直接跳到 hibernate-search (lucene) 的原因,我的目标是基于自动建议关于输入(输入查询)。这是我目前得到的
@Indexed
@Table (name = "shop_table")
@Entity
@AnalyzerDef(name = "myanalyzer",
tokenizer = @TokenizerDef(factory = KeywordTokenizerFactory.class), //
filters = { //
@TokenFilterDef(factory = LowerCaseFilterFactory.class),
@TokenFilterDef(factory = WordDelimiterFilterFactory.class),
@TokenFilterDef(factory = EdgeNGramFilterFactory.class, params =
{ @Parameter(name = "maxGramSize", value = "1024") }),})
@Analyzer(definition = "myanalyzer")
public class Shop implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
enter code here
@Field(index = Index.YES, store = Store.YES, analyze = Analyze.YES)
@Column(name = "name")
private String name;
... other methods
我的查询
Query lucenQuery = qb.keyword().onField("name").matching(searchTerm).createQuery();
这只是一个基本查询,我只关注分析器配置以获得我想要的结果,这真的很令人困惑我应该关注哪一部分来实现我想要的,Tokenizing?过滤?还是查询本身?
无论如何,我已经将这 2 个短语编入索引。
"Apache Lychee Department"
"Apache Strawberry Club Large"
当我 process/query "Straw" 它给了我 Apache Strawberry Club Large
但是当我 process/query “Lychee” 或 “Apache Lychee” 时,查询给了我两个?我只期待 Apache 荔枝系
我理解所有配置的方式is/are
EdgeNGramFilterFactory (1024) 会给我一系列 1,024 索引的 EdgeNGrams
LowerCaseFilterFactory 会给我所有小写索引
WordDelimiterFilterFactory 将查询作为一个词进行过滤,并给我匹配的数据。
并且每个 entry/data 将被 KeywordTokenizerFactory 标记为关键字,并将被 EdgeNGram
索引为 1,024
我尝试查询一个短语,但仍然得到相同的输出
Query luceneQuery = qb.phrase().onField("name").sentence(searchTerm).createQuery();
我的目标是获得自动建议.. 或者至少从模仿 sql 的 "LIKE" 开始..
您应该考虑两件事:
- 默认情况下,当查询中有多个词时,结果将包括匹配 any 个词的文档,而不是 all 的条款。
- 默认情况下,您的查询将使用您在编制索引时使用的相同分析器进行分析。
这特别意味着您的查询 "Lychee" 将被分析为类似 "L Ly Lyc Lych Lyche Lychee" 的内容(由于边缘 ngram 过滤器)。之前分析了字符串 "Apache Strawberry Club Large",由于边缘 ngram 过滤器,术语 "Large" 被扩展为 "L La Lar Larg Large"。所以查询 "Lychee" 将匹配 "Apache Strawberry Club Large",因为它们都包含一个以 L...
开头的单词
这显然是不受欢迎的行为。
第一步是更改查询的分析方式,这样您就不会匹配完全不相关的文档。
基本上你需要定义另一个几乎相同的分析器,但没有 "edge ngram" 过滤器。然后您需要告诉 Hibernate Search 使用该分析器来分析您的查询。
有关详细说明,请参阅 。
作为第二步,如果 所有 包含的词出现在文档中,您需要使查询匹配。为此,最简单的解决方案是使用 simple query string query 而不是关键字查询。
替换为:
Query lucenQuery = qb.keyword().onField("name").matching(searchTerm).createQuery();
有了这个:
Query lucenQuery = qb.simpleQueryString().onField("name").withAndAsDefaultOperator().matching(searchTerm).createQuery();
关键是调用 .withAndAsDefaultOperator()
。
此更改还会产生其他一些影响,例如在输入字符串中启用特殊语法,因此我建议您阅读参考文档以了解 simpleQueryString
的确切含义。
感谢@yrodiere
@Indexed
@Table (name = "shop_table")
@Entity
@AnalyzerDef(name = "edgeNgram",
tokenizer = @TokenizerDef(factory = WhitespaceTokenizerFactory.class),
filters = {
@TokenFilterDef(factory = LowerCaseFilterFactory.class),
@TokenFilterDef(factory = EdgeNGramFilterFactory.class, params =
{ @Parameter(name = "maxGramSize", value = "1024") }),
})
@AnalyzerDef(name = "search_query_analyzer",
tokenizer = @TokenizerDef(factory = WhitespaceTokenizerFactory.class),
filters = {
@TokenFilterDef(factory = ASCIIFoldingFilterFactory.class),
@TokenFilterDef(factory = LowerCaseFilterFactory.class)
})
public class Shop implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Field(store = Store.YES, analyze = Analyze.YES)
@Column(name = "name")
@Analyzer(definition = "edgeNgram")
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
和我的查询
QueryBuilder qb = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(Shop.class)
.overridesForField("name", "search_query_analyzer").get();
Query lucenQuery = qb.simpleQueryString().onField("name").withAndAsDefaultOperator().matching(shopSearchTerm).createQuery();
但我不确定我是否以正确的方式实施它..
这是我第一次接触优化的搜索功能,我的部分熟练程度是android开发的前端,但我愿意冒险尝试hibernate-search。我确实了解 SQL "LIKE" 查询的功能,它的作用及其局限性,这就是我直接跳到 hibernate-search (lucene) 的原因,我的目标是基于自动建议关于输入(输入查询)。这是我目前得到的
@Indexed
@Table (name = "shop_table")
@Entity
@AnalyzerDef(name = "myanalyzer",
tokenizer = @TokenizerDef(factory = KeywordTokenizerFactory.class), //
filters = { //
@TokenFilterDef(factory = LowerCaseFilterFactory.class),
@TokenFilterDef(factory = WordDelimiterFilterFactory.class),
@TokenFilterDef(factory = EdgeNGramFilterFactory.class, params =
{ @Parameter(name = "maxGramSize", value = "1024") }),})
@Analyzer(definition = "myanalyzer")
public class Shop implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
enter code here
@Field(index = Index.YES, store = Store.YES, analyze = Analyze.YES)
@Column(name = "name")
private String name;
... other methods
我的查询
Query lucenQuery = qb.keyword().onField("name").matching(searchTerm).createQuery();
这只是一个基本查询,我只关注分析器配置以获得我想要的结果,这真的很令人困惑我应该关注哪一部分来实现我想要的,Tokenizing?过滤?还是查询本身? 无论如何,我已经将这 2 个短语编入索引。
"Apache Lychee Department"
"Apache Strawberry Club Large"
当我 process/query "Straw" 它给了我 Apache Strawberry Club Large 但是当我 process/query “Lychee” 或 “Apache Lychee” 时,查询给了我两个?我只期待 Apache 荔枝系
我理解所有配置的方式is/are
EdgeNGramFilterFactory (1024) 会给我一系列 1,024 索引的 EdgeNGrams
LowerCaseFilterFactory 会给我所有小写索引
WordDelimiterFilterFactory 将查询作为一个词进行过滤,并给我匹配的数据。
并且每个 entry/data 将被 KeywordTokenizerFactory 标记为关键字,并将被 EdgeNGram
索引为 1,024我尝试查询一个短语,但仍然得到相同的输出
Query luceneQuery = qb.phrase().onField("name").sentence(searchTerm).createQuery();
我的目标是获得自动建议.. 或者至少从模仿 sql 的 "LIKE" 开始..
您应该考虑两件事:
- 默认情况下,当查询中有多个词时,结果将包括匹配 any 个词的文档,而不是 all 的条款。
- 默认情况下,您的查询将使用您在编制索引时使用的相同分析器进行分析。
这特别意味着您的查询 "Lychee" 将被分析为类似 "L Ly Lyc Lych Lyche Lychee" 的内容(由于边缘 ngram 过滤器)。之前分析了字符串 "Apache Strawberry Club Large",由于边缘 ngram 过滤器,术语 "Large" 被扩展为 "L La Lar Larg Large"。所以查询 "Lychee" 将匹配 "Apache Strawberry Club Large",因为它们都包含一个以 L...
开头的单词这显然是不受欢迎的行为。
第一步是更改查询的分析方式,这样您就不会匹配完全不相关的文档。 基本上你需要定义另一个几乎相同的分析器,但没有 "edge ngram" 过滤器。然后您需要告诉 Hibernate Search 使用该分析器来分析您的查询。
有关详细说明,请参阅
作为第二步,如果 所有 包含的词出现在文档中,您需要使查询匹配。为此,最简单的解决方案是使用 simple query string query 而不是关键字查询。
替换为:
Query lucenQuery = qb.keyword().onField("name").matching(searchTerm).createQuery();
有了这个:
Query lucenQuery = qb.simpleQueryString().onField("name").withAndAsDefaultOperator().matching(searchTerm).createQuery();
关键是调用 .withAndAsDefaultOperator()
。
此更改还会产生其他一些影响,例如在输入字符串中启用特殊语法,因此我建议您阅读参考文档以了解 simpleQueryString
的确切含义。
感谢@yrodiere
@Indexed
@Table (name = "shop_table")
@Entity
@AnalyzerDef(name = "edgeNgram",
tokenizer = @TokenizerDef(factory = WhitespaceTokenizerFactory.class),
filters = {
@TokenFilterDef(factory = LowerCaseFilterFactory.class),
@TokenFilterDef(factory = EdgeNGramFilterFactory.class, params =
{ @Parameter(name = "maxGramSize", value = "1024") }),
})
@AnalyzerDef(name = "search_query_analyzer",
tokenizer = @TokenizerDef(factory = WhitespaceTokenizerFactory.class),
filters = {
@TokenFilterDef(factory = ASCIIFoldingFilterFactory.class),
@TokenFilterDef(factory = LowerCaseFilterFactory.class)
})
public class Shop implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Field(store = Store.YES, analyze = Analyze.YES)
@Column(name = "name")
@Analyzer(definition = "edgeNgram")
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
和我的查询
QueryBuilder qb = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(Shop.class)
.overridesForField("name", "search_query_analyzer").get();
Query lucenQuery = qb.simpleQueryString().onField("name").withAndAsDefaultOperator().matching(shopSearchTerm).createQuery();
但我不确定我是否以正确的方式实施它..