【问题标题】:How to create simple fuzzy search with PostgreSQL only?如何仅使用 PostgreSQL 创建简单的模糊搜索?
【发布时间】:2011-12-05 12:19:06
【问题描述】:

我在基于 RoR 的网站上的搜索功能有点问题。我有很多带有一些代码的产品。此代码可以是任何字符串,例如“AB-123-lHdfj”。现在我使用ILIKE 运算符来查找产品:

Product.where("code ILIKE ?", "%" + params[:search] + "%")

它工作正常,但找不到带有“AB123-lHdfj”或“AB123lHdfj”之类代码的产品。

我该怎么做?可能是 Postgres 有一些字符串规范化功能,或者其他一些帮助我的方法?

【问题讨论】:

    标签: sql ruby-on-rails postgresql levenshtein-distance fuzzy-search


    【解决方案1】:

    Postgres 提供了一个具有多个字符串比较功能的模块,例如 soundex 和 metaphone。但是你会想要使用levenshtein 编辑距离功能。

    Example:
    
    test=# SELECT levenshtein('GUMBO', 'GAMBOL');
     levenshtein
    -------------
               2
    (1 row)
    

    2 是两个词之间的编辑距离。当您将其应用于多个单词并按编辑距离结果排序时,您将获得您正在寻找的模糊匹配类型。

    试试这个查询示例:(当然是你自己的对象名称和数据)

    SELECT * 
    FROM some_table
    WHERE levenshtein(code, 'AB123-lHdfj') <= 3
    ORDER BY levenshtein(code, 'AB123-lHdfj')
    LIMIT 10
    

    这个查询说:

    给我 some_table 中所有数据的前 10 个结果,其中 code 值与输入 'AB123-lHdfj' 之间的编辑距离小于 3。您将返回 code 值在 3 个字符内的所有行与“AB123-lHdfj”的区别......

    注意:如果您收到如下错误:

    function levenshtein(character varying, unknown) does not exist
    

    使用以下命令安装fuzzystrmatch 扩展:

    test=# CREATE EXTENSION fuzzystrmatch;
    

    【讨论】:

    • 你能给我看一些这个函数使用的简单例子吗?我不知道它对我有什么帮助:)
    • 我添加了一个简单的硬编码查询示例,应该足以让您入门。不要忘记确保您可以使用fuzzystrmatch 模块:postgresonline.com/journal/categories/57-fuzzystrmatch
    • Levenshtein 是一个昂贵的功能,这是每次调用运行 Levenshtein 两次吗?
    • 感谢提示需要安装 fuzzystrmatch 以在 postgresql 中使用 levenshtein。它帮助我摆脱了HINT: No function matches the given name and argument types. You might need to add explicit type casts
    • 注意! 还有一个levenshtein_less_equal()!它是 Levenshtein 函数的加速版本,仅在 small distances 感兴趣时使用。如果实际距离小于等于 max_d,则 levenshtein_less_equal 返回正确的距离;否则它返回一些大于 max_d 的值。如果 max_d 为负,则行为与 levenshtein 相同。 test=# SELECT levenshtein_less_equal('extensive', 'exhaustive',2);
    【解决方案2】:

    Paul 告诉过你levenshtein()。这是一个非常有用的工具,但是对于大表来说它也很慢。它必须从每一行的搜索词中计算出Levenshtein distance。这很昂贵并且不能使用索引。 “加速”变体 levenshtein_less_equal() 对于长字符串更快,但在没有索引支持的情况下仍然很慢。

    如果您的要求如示例所示的那样简单,您仍然可以使用LIKE。只需将搜索词中的任何- 替换为WHERE 子句中的%。所以而不是:

    WHERE code ILIKE '%AB-123-lHdfj%'

    用途:

    WHERE code ILIKE '%AB%123%lHdfj%'
    

    或者,动态:

    WHERE code ILIKE '%' || replace('AB-123-lHdfj', '-', '%') || '%'
    

    LIKE 模式中的% 代表 0-n 个字符。或者使用_ 只代表一个字符。或者使用正则表达式进行更智能的匹配:

    WHERE code ~* 'AB.?123.?lHdfj'
    

    .? ... 0 或 1 个字符

    或者:

    WHERE code ~* 'AB\-?123\-?lHdfj'
    

    \-? ... 0 或 1 个破折号

    您可能希望转义 LIKE 或正则表达式模式中的特殊字符。见:


    如果您的实际问题更复杂并且您需要更快的解决方案,那么有多种选择,具体取决于您的要求:

    模式匹配技术概述:

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-09-20
      • 2020-01-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多