我有一个支持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
,处理所有关于休眠的事情,不需要每次都定制代码。
3.这段代码已经可以使用了,但是只适用于withEditDistanceUpTo(2)
,这意味着QUERY_TO_MATCH
和数据库或索引中的匹配条目之间最多有2个"差异"。缺少的字符也算作差异。
4. withEditDistanceUpTo(2)
不接受大于2的值。
有人有什么想法来实现这一点吗?
2条答案
按热度按时间kx5bkwkv1#
我不知道有任何解决方案可以指定允许的更改的确切数量。
无论如何,这种方法有严重的缺点:将“foo”与最多3个更改匹配是什么意思?2只是匹配任何内容?3正如您所看到的,一个适用于不同术语长度的解决方案可能会更好。
一种解决方案是索引n-gram。我不是像你已经做的那样谈论边缘n-gram,而是从整个术语中提取的实际n-gram,而不仅仅是边缘。所以当索引
foooo
的2-gram时,你应该索引:fo
oo
(出现多次)并且在查询时,项
fouuu
将被变换为:fo
ou
uu
...并且它将与索引文档匹配,因为它们至少有一个共同的术语(
fo
)。显然,这也有一些缺点。如果使用2-gram,
fuuuu
就不会匹配foooo
,但barfooo
会匹配,因为它们都有一个2-gram。所以你会得到误报。gram越长,你得到误报的可能性就越小,但你的搜索就越模糊。您可以依靠评分和按得分排序将最佳匹配放在结果列表的第一位来消除这些误报。例如,您可以配置ngram过滤器以保留原始术语,以便将
fooo
转换为[fooo
,fo
,oo
],而不仅仅是[fo
,oo
]。因此,与包含barfooo
的文档相比,包含fooo
的文档的精确搜索fooo
的得分更高(因为有更多的匹配项)。您还可以设置多个单独的字段:一个不具有ngram,一个具有3-gram,一个具有2-gram,并构建每个字段具有一个should
子句布尔查询:匹配的子句越多,得分就越高,并且在命中中找到该文档的概率也越高。而且,我认为
fooo
和类似的词实际上是人为的例子,在真实世界的数据集中不太可能有这些词;您应该尝试您提出的任何解决方案来对真实的数据集进行测试,看看它是否足够好用。如果您想要模糊搜索,您必须接受 * 一些 * 误报:问题不在于它们是否存在,而在于它们是否足够稀少,以至于用户仍然可以很容易地找到他们要找的东西。要使用ngram,请使用
org.apache.lucene.analysis.ngram.NGramFilterFactory
应用n-gram过滤器。在索引和查询时都应用它。使用参数minGramSize
/maxGramSize
配置ngram的大小,使用参数keepShortTerm
(true
/false
)控制是否保留原始字词。您可以保留或不保留边图过滤器;看看它是否提高了结果的相关性?我怀疑如果你使用
keepShortTerm = true
,它可能会稍微提高相关性。在任何情况下,确保在ngram过滤器 * 之前 * 应用edge-ngram过滤器。ryoqjall2#
好的,我和我的朋友找到了一个解决方案。我们在lucene的changelog中发现了一个问题,它要求相同的特性,我们实现了一个solution:Lucene的沙盒版本中有一个
SlowFuzzyQuery
,它比较慢(很明显),但是支持大于2的editDistance。