【问题标题】:Fuzzy String matching of Strings in JavaJava中字符串的模糊字符串匹配
【发布时间】:2017-08-03 06:32:48
【问题描述】:

我有一个非常大的字符串列表,存储在 NoSQL 数据库中。传入的查询是一个字符串,我想检查这个字符串是否在列表中。在完全匹配的情况下,这非常简单。该 NoSQL DB 可能将字符串作为主键,我将检查是否有任何记录以该字符串作为主键。但我还需要检查模糊匹配。

有一种方法可以遍历该列表中的每个字符串并检查输入字符串与列表中的字符串的 Levenshtein 距离,但是这种方法将导致 O(n) 复杂度并且列表的大小非常大(1000 万)甚至可能增加。这种方法会导致我的解决方案的延迟更高。

有没有更好的方法来解决这个问题?

【问题讨论】:

  • 搜索模糊字符串总是很复杂。它会导致高度复杂性,我认为没有真正好的解决方案可以避免这种情况。搜索前是否可以纠正模糊字符串?但是您使用哪个非sql数据库。其中一些提供了模糊字符串的搜索功能。或者你应该尝试使用像 ElasticSearch 这样的搜索引擎
  • 为什么不用Soundex或Metaphone之类的语音算法。你可以试试看。
  • Apache commons-text 库提供了一些例程来执行此操作,例如余弦距离,但听起来您至少希望为此使用嵌入式 Lucene。即使使用 Lucene,Levenshtein 距离搜索的成本也很高,尽管 Lucene 对此进行了改进。
  • 标准方法是使用 n-gram。有关详细信息,请参阅下面的答案。

标签: java string fuzzy-search


【解决方案1】:

由于您发现的原因,模糊匹配很复杂。出于性能原因,计算搜索词与数据库词的每个组合的距离度量是不切实际的。

解决方案通常是使用 n-gram 索引。这既可以单独使用来给出结果,也可以作为过滤器来减少可能结果的大小,从而减少要计算的距离分数。

所以基本上,如果你有一个单词“stack”,你可以将它分解成 n-grams(通常是 trigrams),例如“s”、“st”、“sta”、“ack”、“ck”、“k” .您根据数据库行索引数据库中的那些。然后,您对输入执行相同操作,并查找具有相同匹配 n-gram 的数据库行。

这一切都很复杂,您最好的选择是使用现有的实现,例如 Lucene/Solr,它将为您完成 n-gram 的工作。在使用专有解决方案时,我自己没有使用过它,但是有一个可能相关的 stackoverflow 问题:

Return only results that match enough NGrams with Solr

一些数据库似乎实现了 n-gram 匹配。这是一个 Sybase 页面的链接,其中提供了一些讨论:

Sybase n-gram text index

不幸的是,关于 n-gram 的讨论会是一篇很长的文章,我没有时间。可能在 stackoverflow 和其他网站上的其他地方讨论过。我建议谷歌搜索这个术语并阅读它。

【讨论】:

    【解决方案2】:

    首先,如果您正在做的是搜索,那么您应该使用搜索引擎(ElasticSearch 几乎是默认设置)。他们擅长这一点,而您无需重新发明轮子。

    其次,您正在寻找的技术称为stemming。与原始字符串一起,在您的数据库中保存一个规范化的字符串。使用相同的机制规范化搜索查询。这样,您将获得更好的搜索结果。显然,这是搜索引擎在后台使用的技术之一。

    【讨论】:

    • 他想做一个 Levenshtein 距离,所以词干提取在那里没有帮助。那更复杂。
    • @rghome 我读到他尝试过这种方法,但不是要求
    • 感谢您的建议。我最初的方法是使用 Levenshtein 距离,但我也愿意使用任何其他方法,如果它更好的话。对于原始方法,我需要解析完整列表。我在想这是否可以优化,因为列表非常大,我不想为每个查询解析它。
    • Stemming 无助于返回一个字母错误的结果,例如 water/woter
    • @mishadoff 是的,但这比他现在拥有的要好。随意添加您自己更完整的答案
    【解决方案3】:

    使用 Solr(或 Lucene)可能是适合您的解决方案吗?

    Lucene 支持基于 Levenshtein 距离或编辑距离算法的模糊搜索。要进行模糊搜索,请在单个单词 Term 的末尾使用波浪号“~”符号。例如,要搜索拼写与“漫游”相似的术语,请使用模糊搜索:

    roam~
    

    此搜索将找到诸如泡沫和漫游之类的术语。

    从 Lucene 1.9 开始,一个附加(可选)参数可以指定所需的相似性。该值介于 0 和 1 之间,接近 1 的值只会匹配相似度较高的词条。例如:

    roam~0.8 
    

    https://lucene.apache.org/core/2_9_4/queryparsersyntax.html

    【讨论】:

    • 小提示:ElasticSearch 和 Solr 都在使用 Lucene。 @恶魔
    • 感谢您的建议!我从 cmets 中看到 Lucene 提供了 Exact 和 Fuzzy 匹配,并且 Solr 和 Elastic Search 都是基于 Lucene 的。 Solr 或 Elastic Search 中是否也有超时功能,它会在固定时间后删除记录?另外,我希望延迟都不是问题
    • @Devil AFAIK 没有任何“在固定时间后删除记录”功能,但您可以轻松地将 creation_timestamp 字段添加到您的文档中,并在指定日期时间之前过滤结果或/和删除定期所有早于...的文档
    猜你喜欢
    • 2012-02-14
    • 2014-11-02
    • 2018-03-23
    • 1970-01-01
    • 1970-01-01
    • 2018-05-31
    • 2021-04-19
    • 1970-01-01
    • 2015-03-26
    相关资源
    最近更新 更多