我在基于 RoR 的网站上的搜索功能有点问题。我有很多带有一些代码的产品。此代码可以是任何字符串,例如“AB-123-lHdfj”。现在我使用ILIKE
运算符来查找产品:
Product.where("code ILIKE ?", "%" + params[:search] + "%")
它工作正常,但找不到带有“AB123-lHdfj”或“AB123lHdfj”等代码的产品。
我该怎么办?可能是 Postgres 有一些字符串规范化功能,或者其他一些可以帮助我的方法?
我在基于 RoR 的网站上的搜索功能有点问题。我有很多带有一些代码的产品。此代码可以是任何字符串,例如“AB-123-lHdfj”。现在我使用ILIKE
运算符来查找产品:
Product.where("code ILIKE ?", "%" + params[:search] + "%")
它工作正常,但找不到带有“AB123-lHdfj”或“AB123lHdfj”等代码的产品。
我该怎么办?可能是 Postgres 有一些字符串规范化功能,或者其他一些可以帮助我的方法?
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 个结果,其中代码值与输入 'AB123-lHdfj' 之间的编辑距离小于 3。您将返回代码值在 3 个字符以内的所有行AB123-lHdfj'...
注意:如果您收到如下错误:
function levenshtein(character varying, unknown) does not exist
使用以下命令安装fuzzystrmatch
扩展:
test=# CREATE EXTENSION fuzzystrmatch;
保罗告诉过你levenshtein()
。这是一个非常有用的工具,但是对于大表来说它也很慢。它必须计算每一行与搜索词的Levenshtein 距离。这很昂贵并且不能使用索引。“加速”变体levenshtein_less_equal()
对于长字符串更快,但在没有索引支持的情况下仍然很慢。
如果您的要求与示例所建议的一样简单,您仍然可以使用LIKE
. 只需将-
搜索词%
中的任何内容替换为WHERE
子句中的内容即可。所以而不是:
WHERE code ILIKE '%AB-123-lHdfj%'
采用:
WHERE code ILIKE '%AB%123%lHdfj%'
或者,动态地:
WHERE code ILIKE '%' || replace('AB-123-lHdfj', '-', '%') || '%'
%
in LIKE
patterns 代表 0-n 个字符。或_
仅用于一个字符。或者使用正则表达式进行更智能的匹配:
WHERE code ~* 'AB.?123.?lHdfj'
.?
... 0 或 1 个字符
或者:
WHERE code ~* 'AB\-?123\-?lHdfj'
\-?
... 0 或 1 个破折号
您可能希望转义LIKE
或正则表达式模式中的特殊字符。看:
如果您的实际问题更复杂并且您需要更快的解决方案,那么有多种选择,具体取决于您的要求:
当然,还有全文搜索。但这在您的情况下可能是矫枉过正。
更可能的候选者是与附加模块pg_trgm 进行三元组匹配。看:
可以与LIKE
, ILIKE
,~
或~*
PostgreSQL 9.1 之后的组合使用。
在这种情况下也很有趣:该模块的similarity()
函数或%
运算符。
最后但并非最不重要的一点是,您可以实现一个手工编织的解决方案,该解决方案具有标准化要搜索的字符串的功能。例如,您可以转换AB1-23-lHdfj
--> ab123lhdfj
,将其保存在附加列中,并使用以相同方式转换的术语进行搜索。
或者在表达式上使用索引而不是冗余列。(涉及的功能必须是IMMUTABLE
。)可能与pg_tgrm
上面的结合。
模式匹配技术概述: