Hibernate搜索模糊2多
Hibernate search fuzzy more than 2
我有一个 Java 后端,带有 hibernate、lucene 和 hibernate-search。现在我想做一个模糊查询,但不是 0、1 或 2,我想在查询和预期结果之间允许更多 "differences"(例如补偿长单词中的拼写错误)。有什么办法可以做到这一点?稍后将根据查询的长度计算允许差异的最大值。
我想要的是一个自动完成搜索,可以更正错误的字母。这个自动完成应该只搜索给定查询后面的缺失字符,而不是前面的。如果查询前的字符与条目相比丢失,则应将其计为差异。
例子:
此示例中允许的最大不同字符为 2。
fooo
应该匹配
fooo (no difference)
fooobar (only characters added -> autocomplete)
fouubar (characters added and misspelled -> autocomplete and spelling correction)
fooo
不应匹配
barfooo (we only allow additional characters behind the query, but this example is less important)
fuuu (more than 2 differences)
这是我当前的 SQL 查询代码:
FullTextEntityManager fullTextEntityManager = this.sqlService.getFullTextEntityManager();
QueryBuilder queryBuilder = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(MY_CLASS.class).overridesForField("name", "foo").get();
Query query = queryBuilder.keyword().fuzzy().withEditDistanceUpTo(2).onField("name").matching("QUERY_TO_MATCH").createQuery();
FullTextQuery fullTextQuery = fullTextEntityManager.createFullTextQuery(query, MY_CLASS.class);
List<MY_CLASS> results = fullTextQuery.getResultList();
备注:
1. 我用org.apache.lucene.analysis.ngram.EdgeNGramFilterFactory
做索引,但应该不会有任何变化。
2.这是使用自定义框架,不是开源的。您可以忽略 sqlService
,它只提供 FullTextEntityManager
并处理与休眠有关的所有事情,每次都不需要自定义代码。
3. 此代码确实已经有效,但仅适用于 withEditDistanceUpTo(2)
,这意味着 QUERY_TO_MATCH
与数据库或索引中的匹配条目之间最多有 2 "differences"。缺少字符也算作差异。
4. withEditDistanceUpTo(2)
不接受大于 2 的值。
有人有什么想法可以实现吗?
我不知道有任何解决方案可以指定允许的更改的确切数量。
无论如何,这种方法有严重的缺点:将 "foo" 与最多 3 个更改匹配是什么意思?只是匹配任何东西?如您所见,适用于不同期限长度的解决方案可能会更好。
一种解决方案是索引 n-gram。我不是在谈论边缘 ngram,就像你已经做的那样,而是从整个术语中提取的实际 ngram,而不仅仅是边缘。因此,当索引 foooo
的 2 克时,您将索引:
fo
oo
(出现多次)
并且在查询的时候,fouuu
这个词会被转化为:
fo
ou
uu
... 它将与索引文档匹配,因为它们至少有一个共同术语 (fo
)。
显然有一些缺点。对于 2-gram,术语 fuuuu
不会匹配 foooo
,但术语 barfooo
会匹配,因为它们有一个共同的 2-gram。所以你会得到误报。克越长,您得到误报的可能性就越小,但您的搜索就越不模糊。
您可以通过评分和按分数排序将最佳匹配放在结果列表中的第一位来消除这些误报。例如,您可以配置 ngram 过滤器以保留原始术语,以便 fooo
将转换为 [fooo
、fo
、oo
] 而不仅仅是 [fo
、oo
],因此对 fooo
的精确搜索对于包含 fooo
的文档比对包含 barfooo
的文档具有更好的分数(因为有是更多的比赛)。您还可以设置多个单独的字段:一个没有 ngrams,一个有 3-grams,一个有 2-grams,并为每个字段构建一个带有 on should
子句的布尔查询:匹配的子句越多,越高得分越高,您将在点击中找到该文档。
此外,我认为 fooo
和类似的例子实际上是人为的例子,你不太可能在现实世界的数据集中使用这些术语;你应该尝试针对真实数据集提出的任何解决方案,看看它是否足够好。如果你想要模糊搜索,你将不得不接受 一些 误报:问题不在于它们是否存在,而是它们是否足够罕见以至于用户仍然可以轻松找到他们正在寻找的东西为.
要使用 ngram,请使用 org.apache.lucene.analysis.ngram.NGramFilterFactory
应用 n-gram 过滤器。在索引和查询时都应用它。使用参数minGramSize
/maxGramSize
配置ngrams的大小,使用keepShortTerm
(true
/false
)控制是否保留原始term .
你可以保留或不保留edge-ngram过滤器;看看它是否提高了结果的相关性?我怀疑如果您使用 keepShortTerm = true
,它可能会略微提高相关性。在任何情况下,确保在 ngram 过滤器之前应用 edge-ngram 过滤器 。
好的,我和我的朋友找到了解决办法。
我们找到了 question in the changelog of lucene which asks for the same feature, and we implemented a solution:
在 lucene 的沙盒版本中有一个 SlowFuzzyQuery
。它较慢(显然)但支持大于 2 的 editDistance。
我有一个 Java 后端,带有 hibernate、lucene 和 hibernate-search。现在我想做一个模糊查询,但不是 0、1 或 2,我想在查询和预期结果之间允许更多 "differences"(例如补偿长单词中的拼写错误)。有什么办法可以做到这一点?稍后将根据查询的长度计算允许差异的最大值。
我想要的是一个自动完成搜索,可以更正错误的字母。这个自动完成应该只搜索给定查询后面的缺失字符,而不是前面的。如果查询前的字符与条目相比丢失,则应将其计为差异。
例子:
此示例中允许的最大不同字符为 2。
fooo
应该匹配
fooo (no difference)
fooobar (only characters added -> autocomplete)
fouubar (characters added and misspelled -> autocomplete and spelling correction)
fooo
不应匹配
barfooo (we only allow additional characters behind the query, but this example is less important)
fuuu (more than 2 differences)
这是我当前的 SQL 查询代码:
FullTextEntityManager fullTextEntityManager = this.sqlService.getFullTextEntityManager();
QueryBuilder queryBuilder = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(MY_CLASS.class).overridesForField("name", "foo").get();
Query query = queryBuilder.keyword().fuzzy().withEditDistanceUpTo(2).onField("name").matching("QUERY_TO_MATCH").createQuery();
FullTextQuery fullTextQuery = fullTextEntityManager.createFullTextQuery(query, MY_CLASS.class);
List<MY_CLASS> results = fullTextQuery.getResultList();
备注:
1. 我用org.apache.lucene.analysis.ngram.EdgeNGramFilterFactory
做索引,但应该不会有任何变化。
2.这是使用自定义框架,不是开源的。您可以忽略 sqlService
,它只提供 FullTextEntityManager
并处理与休眠有关的所有事情,每次都不需要自定义代码。
3. 此代码确实已经有效,但仅适用于 withEditDistanceUpTo(2)
,这意味着 QUERY_TO_MATCH
与数据库或索引中的匹配条目之间最多有 2 "differences"。缺少字符也算作差异。
4. withEditDistanceUpTo(2)
不接受大于 2 的值。
有人有什么想法可以实现吗?
我不知道有任何解决方案可以指定允许的更改的确切数量。
无论如何,这种方法有严重的缺点:将 "foo" 与最多 3 个更改匹配是什么意思?只是匹配任何东西?如您所见,适用于不同期限长度的解决方案可能会更好。
一种解决方案是索引 n-gram。我不是在谈论边缘 ngram,就像你已经做的那样,而是从整个术语中提取的实际 ngram,而不仅仅是边缘。因此,当索引 foooo
的 2 克时,您将索引:
fo
oo
(出现多次)
并且在查询的时候,fouuu
这个词会被转化为:
fo
ou
uu
... 它将与索引文档匹配,因为它们至少有一个共同术语 (fo
)。
显然有一些缺点。对于 2-gram,术语 fuuuu
不会匹配 foooo
,但术语 barfooo
会匹配,因为它们有一个共同的 2-gram。所以你会得到误报。克越长,您得到误报的可能性就越小,但您的搜索就越不模糊。
您可以通过评分和按分数排序将最佳匹配放在结果列表中的第一位来消除这些误报。例如,您可以配置 ngram 过滤器以保留原始术语,以便 fooo
将转换为 [fooo
、fo
、oo
] 而不仅仅是 [fo
、oo
],因此对 fooo
的精确搜索对于包含 fooo
的文档比对包含 barfooo
的文档具有更好的分数(因为有是更多的比赛)。您还可以设置多个单独的字段:一个没有 ngrams,一个有 3-grams,一个有 2-grams,并为每个字段构建一个带有 on should
子句的布尔查询:匹配的子句越多,越高得分越高,您将在点击中找到该文档。
此外,我认为 fooo
和类似的例子实际上是人为的例子,你不太可能在现实世界的数据集中使用这些术语;你应该尝试针对真实数据集提出的任何解决方案,看看它是否足够好。如果你想要模糊搜索,你将不得不接受 一些 误报:问题不在于它们是否存在,而是它们是否足够罕见以至于用户仍然可以轻松找到他们正在寻找的东西为.
要使用 ngram,请使用 org.apache.lucene.analysis.ngram.NGramFilterFactory
应用 n-gram 过滤器。在索引和查询时都应用它。使用参数minGramSize
/maxGramSize
配置ngrams的大小,使用keepShortTerm
(true
/false
)控制是否保留原始term .
你可以保留或不保留edge-ngram过滤器;看看它是否提高了结果的相关性?我怀疑如果您使用 keepShortTerm = true
,它可能会略微提高相关性。在任何情况下,确保在 ngram 过滤器之前应用 edge-ngram 过滤器 。
好的,我和我的朋友找到了解决办法。
我们找到了 question in the changelog of lucene which asks for the same feature, and we implemented a solution:
在 lucene 的沙盒版本中有一个 SlowFuzzyQuery
。它较慢(显然)但支持大于 2 的 editDistance。