PostgreSQL 在许多列上进行全文搜索

Den*_*nov 8 postgresql full-text-search pattern-matching

我需要一个基于指定字符串搜索记录的建议。

搜索字符串可以包含来自这些列的值。此字符串中的值不必以正确的顺序严格相同,并且此字符串中某些列的值可能会丢失。

搜索字符串示例:

22 Karntner Wien
Run Code Online (Sandbox Code Playgroud)

例如,我得到了前 5 条类似记录的结果。

我想我应该使用全文搜索,但我没有使用它的经验。你能告诉我如何进行吗?

Erw*_*ter 10

我建议使用这个表达式来查询和索引:

SELECT * FROM tbl
WHERE  to_tsvector('simple', f_concat_ws(' ', country, city, street, house_nr, postcode))
    @@ plainto_tsquery('simple', '22 Kärntner Wien');
Run Code Online (Sandbox Code Playgroud)

注意f_concat_ws()上面的自定义函数。那是因为concat_ws()不仅STABLE不是IMMUTABLE。您需要创建它第一

CREATE OR REPLACE FUNCTION f_concat_ws(text, VARIADIC text[])
  RETURNS text LANGUAGE sql IMMUTABLE AS 'SELECT array_to_string($2, $1)';
Run Code Online (Sandbox Code Playgroud)

它可以用作 的替代品concat_ws(),除了它只接受实际的文本数据作为输入(这允许我们IMMUTABLE有效地制作它而不会作弊)。详细说明(阅读!):

关于VARIADIC

对于许多列,这更短、更快。你可以不用它,但语法会变得相当冗长(参见joanolo 的回答)。

与此匹配的索引:

CREATE INDEX tbl_adr_fts_idx ON tbl USING GIN (
       to_tsvector('simple', f_concat_ws(' ', country, city, street, house_nr, postcode)));
Run Code Online (Sandbox Code Playgroud)

你是在处理国际地址数据,所以千万不能使用english文本搜索配置。词干对名称毫无意义, 而且您的大多数示例数据一开始甚至都不是英文。请改用simple配置。您需要带有两个参数的表单 - 见下文。

连接字符串并调用更昂贵的函数to_tsvector() 一次。用于concat_ws()优雅地处理可能的 NULL 值。整体更便宜,也更短。

就像我评论的那样,全文搜索对模糊匹配的支持有限,但前缀匹配有一个经常被忽视的特性:

因此,如果您不确定它是'Kärntner'还是'Kärnten',以及它是'Straße''strasse'还是'Strabe'(就像在您的越野车示例数据中一样),但您知道第二个词在第一个之后,你可以:

... @@ to_tsquery('simple', '22 & Kärnt:* <-> Stra:* & Wien')
Run Code Online (Sandbox Code Playgroud)

<->是短语搜索运算符,需要 Postgres 9.6

如果您也想忽略变音符号('ä' <> 'a'),请添加unaccent()到组合中。您可以将其用作单独的功能,也可以将其作为字典添加到您的文本搜索配置中。您需要先安装扩展...

典型 Postgres 安装中模式匹配选项的概述:

Joanolo 已经提供了一些关于 FTS 的基本信息和更多的手册链接。

处理你的评论

我正在尝试添加这个索引,但给了我一个错误:

ERROR: functions in index expression must be marked IMMUTABLE
Run Code Online (Sandbox Code Playgroud)

该函数有两种变体to_tsvector()- 请参阅“函数重载”。第一个只取text,第二个取regconfigtext。你自己看:

SELECT proname, provolatile, proargtypes[0]::regtype, proargtypes[1]::regtype
FROM   pg_proc
WHERE  proname = 'to_tsvector';
Run Code Online (Sandbox Code Playgroud)

只有第二个是IMMUTABLE并且可以直接在索引表达式中使用。上例中的“简单”是文本搜索配置 ( regconfig)。

更重要的是,我的疏忽:(concat_ws()我在第一个版本中就有)只是STABLE,不是IMMUTABLE。我在上面添加了必要的步骤。

有关的: