【问题标题】:Hibernate search fuzzy more than 2Hibernate搜索模糊超过2
【发布时间】:2020-07-25 13:15:23
【问题描述】:

我有一个带有 hibernate、lucene 和 hibernate-search 的 Java 后端。现在我想做一个模糊查询,但不是 0、1 或 2,我想允许查询和预期结果之间有更多的“差异”(以补偿例如长词中的拼写错误)。有没有办法实现这一目标?稍后将根据查询的长度计算允许的最大差异。
我想要的是自动完成搜索并纠正错误的字母。此自动完成应该只搜索给定查询后面的缺失字符,而不是前面的。如果查询前面的字符与条目相比缺失,则应计为差异。

示例: 此示例中允许的最大不同字符数为 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并处理hibernate周围的所有事情,每次都不需要自定义代码。
3. 此代码确实有效,但仅适用于withEditDistanceUpTo(2),这意味着QUERY_TO_MATCH 与数据库或索引中的匹配条目之间最多存在2 个“差异”。缺少的字符也算作差异。
4.withEditDistanceUpTo(2)不接受大于2的值。

有没有人有任何想法来实现这一目标?

【问题讨论】:

    标签: java hibernate lucene hibernate-search


    【解决方案1】:

    好的,我和我的朋友找到了解决方案。 我们在 lucene 的变更日志中找到了一个question,它要求相同的功能,我们实现了一个solution: 在沙盒版本的 lucene 中有一个SlowFuzzyQuery。它速度较慢(显然),但支持大于 2 的 editDistance。

    【讨论】:

      【解决方案2】:

      我不知道有任何解决方案可以指定允许的确切更改数量。

      无论如何,这种方法有严重的缺点:将“foo”与最多 3 个更改匹配是什么意思?随便什么都配?如您所见,适用于不同期限长度的解决方案可能会更好。

      一种解决方案是索引 n-gram。我不是在谈论边缘 ngram,就像你已经做过的那样,而是从整个术语中提取的实际 ngram,而不仅仅是边缘。因此,当索引 2 克 foooo 时,您将索引:

      • fo
      • oo(出现多次)

      并且在查询时,术语fouuu 将被转换为:

      • fo
      • ou
      • uu

      ... 它会匹配被索引的文档,因为它们至少有一个共同的术语 (fo)。

      显然有一些缺点。对于 2-gram,术语 fuuuu 不会匹配 foooo,但术语 barfooo 会匹配,因为它们有一个 2-gram 相同。所以你会得到误报。克数越长,误报的可能性就越小,但搜索的模糊性也就越小。

      您可以依靠评分和按分数排序将最佳匹配项放在结果列表的首位,从而消除这些误报。例如,您可以配置 ngram 过滤器以保留原始术语,以便 fooo 将转换为 [fooo, fo, oo] 而不仅仅是 [fo, oo],因此,精确搜索fooo 对于包含fooo 的文档将比包含barfooo 的文档获得更好的分数(因为匹配项更多)。您还可以设置多个单独的字段:一个不带 ngram,一个带 3-gram,一个带 2-gram,并为每个字段构建一个带有 on should 子句的布尔查询:匹配的子句越多,得分越高是,并且您会在点击中找到文档。

      另外,我认为fooo 和类似的确实是人为的例子,你不太可能在现实世界的数据集中拥有这些术语;您应该尝试针对真实数据集提出的任何解决方案,看看它是否足够好。如果您想要模糊搜索,您将不得不接受一些误报:问题不在于它们是否存在,而在于它们是否足够稀有以至于用户仍然可以轻松找到他们正在寻找的东西。

      要使用 ngram,请使用 org.apache.lucene.analysis.ngram.NGramFilterFactory 应用 n-gram 过滤器。在索引和查询时都应用它。使用参数minGramSize/maxGramSize配置ngrams的大小,keepShortTerm(true/false)控制是否保留原词。

      您可以保留或不保留 edge-ngram 过滤器;看看它是否提高了结果的相关性?我怀疑如果您使用keepShortTerm = true,它可能会稍微提高相关性。在任何情况下,请确保在 ngram 过滤器之前应用 edge-ngram 过滤器。

      【讨论】:

      • 是的,在我的最终代码中,我将动态计算允许的“差异”数量,同时考虑到搜索词的长度,如前所述。具有 3 个字母的搜索词的 3 个差异将毫无用处。我在上面的示例中将允许的差异数设置为 2,因此我的示例比具有更多差异的示例更容易。我认为您可能错过了示例中的一行:barfoo 不应与 foo 匹配,如果最大差异量为 2。我想获得以 foo 或其他接近的词开头的词。
      猜你喜欢
      • 1970-01-01
      • 2020-03-15
      • 2014-08-20
      • 2011-10-28
      • 2016-06-12
      • 2017-06-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多