全文搜索(或简称文本搜索)提供了识别满足查询的自然语言文档的功能,并且可以选择按与查询的相关性对它们进行排序。最常见的搜索类型是查找包含给定查询词的所有文档,并按照它们与查询的相似度的顺序返回它们。查询
和相似度
的概念非常灵活,取决于具体的应用程序。最简单的搜索将查询
视为一组单词,将相似度
视为查询单词在文档中出现的频率。
文本搜索运算符在数据库中已经存在多年。PostgreSQL 具有用于文本数据类型的~
、~*
、LIKE
和ILIKE
运算符,但它们缺少现代信息系统所需的许多基本属性
没有语言支持,即使是英语也是如此。正则表达式是不够的,因为它们无法轻松处理派生词,例如satisfies
和satisfy
。您可能会错过包含satisfies
的文档,尽管您在搜索satisfy
时可能希望找到它们。可以使用OR
搜索多个派生形式,但这既繁琐又容易出错(某些单词可能有数千个派生词)。
它们不提供搜索结果的排序(排名),这使得当找到数千个匹配文档时它们效率低下。
它们往往速度较慢,因为没有索引支持,因此它们必须为每次搜索处理所有文档。
全文索引允许预处理文档,并保存索引以供以后快速搜索。预处理包括
将文档解析为词元。识别各种词元类别很有用,例如数字、单词、复合词、电子邮件地址,以便可以对它们进行不同的处理。原则上,词元类别取决于特定的应用程序,但对于大多数目的来说,使用预定义的类别集就足够了。PostgreSQL 使用解析器来执行此步骤。提供了标准解析器,并且可以为特定需求创建自定义解析器。
将词元转换为词位。词位是一个字符串,就像词元一样,但它已被规范化,因此同一单词的不同形式变得相似。例如,规范化几乎总是包括将大写字母折叠为小写字母,并且通常包括删除后缀(例如英语中的s
或es
)。这允许搜索找到同一单词的变体形式,而无需繁琐地输入所有可能的变体。此外,此步骤通常会消除停用词,这些词非常常见,以至于对于搜索毫无用处。(简而言之,词元是文档文本的原始片段,而词位是被认为对索引和搜索有用的单词。)PostgreSQL 使用字典来执行此步骤。提供了各种标准字典,并且可以为特定需求创建自定义字典。
存储为搜索优化的预处理文档。例如,每个文档可以表示为规范化的词位的排序数组。除了词位之外,通常还希望存储位置信息以用于邻近排名,以便将包含更“密集”的查询词区域的文档的排名高于具有分散查询词的文档。
字典允许对如何规范化词元进行细粒度的控制。使用适当的字典,您可以
定义不应索引的停用词。
使用Ispell将同义词映射到单个单词。
使用词库将短语映射到单个单词。
使用Ispell字典将单词的不同变体映射到规范形式。
使用Snowball词干规则将单词的不同变体映射到规范形式。
提供了一种数据类型tsvector
用于存储预处理的文档,以及一种类型tsquery
用于表示处理过的查询(第 8.11 节)。这些数据类型有许多可用的函数和运算符(第 9.13 节),其中最重要的是匹配运算符@@
,我们在第 12.1.2 节中介绍它。可以使用索引加速全文搜索(第 12.9 节)。
文档是全文搜索系统中搜索的单位;例如,杂志文章或电子邮件消息。文本搜索引擎必须能够解析文档并将词位(关键字)与其父文档的关联存储起来。稍后,这些关联用于搜索包含查询词的文档。
对于在PostgreSQL中的搜索,文档通常是数据库表中一行的文本字段,或者可能是此类字段的组合(连接),可能存储在多个表中或动态获取。换句话说,文档可以从不同的部分构建以进行索引,并且可能不会作为整体存储在任何地方。例如
SELECT title || ' ' || author || ' ' || abstract || ' ' || body AS document FROM messages WHERE mid = 12; SELECT m.title || ' ' || m.author || ' ' || m.abstract || ' ' || d.body AS document FROM messages m, docs d WHERE m.mid = d.did AND m.mid = 12;
实际上,在这些示例查询中,应该使用coalesce
来防止单个NULL
属性导致整个文档的NULL
结果。
另一种可能性是将文档作为简单的文本文件存储在文件系统中。在这种情况下,可以使用数据库来存储全文索引并执行搜索,并且可以使用一些唯一的标识符从文件系统中检索文档。但是,从数据库外部检索文件需要超级用户权限或特殊函数支持,因此这通常不如将所有数据保留在PostgreSQL中方便。此外,将所有内容保留在数据库中可以轻松访问文档元数据,以帮助进行索引和显示。
对于文本搜索目的,每个文档都必须简化为预处理的tsvector
格式。搜索和排名完全在文档的tsvector
表示上执行 — 原始文本只需在文档被选择向用户显示时才检索。因此,我们经常将tsvector
称为文档,但这当然只是完整文档的紧凑表示。
PostgreSQL中的全文搜索基于匹配运算符@@
,如果tsvector
(文档)与tsquery
(查询)匹配,则该运算符返回true
。哪个数据类型写在前面并不重要
SELECT 'a fat cat sat on a mat and ate a fat rat'::tsvector @@ 'cat & rat'::tsquery; ?column? ---------- t SELECT 'fat & cow'::tsquery @@ 'a fat cat sat on a mat and ate a fat rat'::tsvector; ?column? ---------- f
如上面的示例所示,tsquery
不仅仅是原始文本,就像tsvector
一样。tsquery
包含搜索词,这些搜索词必须是已经规范化的词位,并且可以使用 AND、OR、NOT 和 FOLLOWED BY 运算符组合多个词。(有关语法详细信息,请参阅第 8.11.2 节。)有函数to_tsquery
、plainto_tsquery
和phraseto_tsquery
有助于将用户编写的文本转换为正确的tsquery
,主要是通过规范化文本中出现的单词。类似地,to_tsvector
用于解析和规范化文档字符串。因此,在实践中,文本搜索匹配看起来更像是这样
SELECT to_tsvector('fat cats ate fat rats') @@ to_tsquery('fat & rat'); ?column? ---------- t
请注意,如果写成以下形式,则此匹配不会成功
SELECT 'fat cats ate fat rats'::tsvector @@ to_tsquery('fat & rat'); ?column? ---------- f
因为这里不会发生单词rats
的规范化。tsvector
的元素是词位,假设它们已经规范化,因此rats
不匹配rat
。
@@
运算符还支持text
输入,允许在简单情况下跳过将文本字符串显式转换为tsvector
或tsquery
。可用的变体包括
tsvector @@ tsquery tsquery @@ tsvector text @@ tsquery text @@ text
我们已经见过这两种形式中的前两种。text
@@
tsquery
的形式等价于 to_tsvector(x) @@ y
。text
@@
text
的形式等价于 to_tsvector(x) @@ plainto_tsquery(y)
。
在 tsquery
中,&
(AND)操作符指定其两个参数都必须出现在文档中才能匹配。 类似地,|
(OR)操作符指定其至少一个参数必须出现,而 !
(NOT)操作符指定其参数必须不出现才能匹配。例如,查询 fat & ! rat
匹配包含 fat
但不包含 rat
的文档。
通过 <->
(FOLLOWED BY) tsquery
操作符可以搜索短语,仅当其参数的匹配项相邻且按给定顺序排列时才匹配。例如
SELECT to_tsvector('fatal error') @@ to_tsquery('fatal <-> error'); ?column? ---------- t SELECT to_tsvector('error is not fatal') @@ to_tsquery('fatal <-> error'); ?column? ---------- f
FOLLOWED BY 操作符有一个更通用的版本,其形式为 <
,其中 N
>N
是一个整数,表示匹配词位的位置之间的差异。<1>
与 <->
相同,而 <2>
允许在匹配之间正好出现一个其他词位,依此类推。phraseto_tsquery
函数利用此操作符构建一个 tsquery
,当某些词是停用词时,它可以匹配多词短语。例如
SELECT phraseto_tsquery('cats ate rats'); phraseto_tsquery ------------------------------- 'cat' <-> 'ate' <-> 'rat' SELECT phraseto_tsquery('the cats ate the rats'); phraseto_tsquery ------------------------------- 'cat' <-> 'ate' <2> 'rat'
有时有用的一个特殊情况是,<0>
可以用来要求两个模式匹配同一个词。
可以使用括号来控制 tsquery
操作符的嵌套。 如果没有括号,|
的绑定最松散,然后是 &
,然后是 <->
,而 !
的绑定最紧密。
值得注意的是,当 AND/OR/NOT 操作符在 FOLLOWED BY 操作符的参数中时,它们的意思与它们不在 FOLLOWED BY 操作符的参数中时略有不同,因为在 FOLLOWED BY 中,匹配的确切位置非常重要。例如,通常 !x
仅匹配不包含任何 x
的文档。但是,!x <-> y
如果 y
不是紧跟在 x
之后,则匹配 y
;在文档中其他位置出现 x
不会阻止匹配。另一个例子是,x & y
通常只要求 x
和 y
都出现在文档中的某个位置,但 (x & y) <-> z
要求 x
和 y
在同一位置匹配,紧接着一个 z
。因此,此查询的行为与 x <-> z & y <-> z
不同,后者将匹配包含两个单独的序列 x z
和 y z
的文档。(这个特定的查询按编写方式是无用的,因为 x
和 y
不能在同一位置匹配;但是对于更复杂的情况,例如前缀匹配模式,这种形式的查询可能很有用。)
以上都是简单的文本搜索示例。如前所述,全文搜索功能包括执行更多操作的能力:跳过索引某些词(停用词)、处理同义词以及使用复杂的解析,例如,基于除空格之外的更多内容进行解析。此功能由文本搜索配置控制。PostgreSQL 附带了许多语言的预定义配置,您可以轻松创建自己的配置。(psql 的 \dF
命令显示所有可用的配置。)
在安装期间,会选择合适的配置,并在 postgresql.conf
中相应地设置 default_text_search_config。如果您为整个集群使用相同的文本搜索配置,则可以使用 postgresql.conf
中的值。要在整个集群中使用不同的配置,但在任何一个数据库中使用相同的配置,请使用 ALTER DATABASE ... SET
。否则,您可以在每个会话中设置 default_text_search_config
。
每个依赖于配置的文本搜索函数都有一个可选的 regconfig
参数,因此可以显式指定要使用的配置。仅当省略此参数时才使用 default_text_search_config
。
为了更容易构建自定义文本搜索配置,配置是从更简单的数据库对象构建的。PostgreSQL 的文本搜索工具提供了四种类型的与配置相关的数据库对象
文本搜索解析器将文档分解为标记并对每个标记进行分类(例如,作为单词或数字)。
文本搜索词典将标记转换为规范化形式并拒绝停用词。
文本搜索模板提供词典的基础函数。(词典仅指定模板和模板的一组参数。)
文本搜索配置选择一个解析器和一组词典,用于规范化解析器生成的标记。
文本搜索解析器和模板是从低级 C 函数构建的;因此,开发新的解析器和模板需要 C 编程能力,并且需要超级用户权限才能将其安装到数据库中。(在 PostgreSQL 发行版的 contrib/
区域中有附加解析器和模板的示例。)由于词典和配置只是参数化并将一些底层解析器和模板连接在一起,因此创建新词典或配置不需要特殊权限。本章后面将出现创建自定义词典和配置的示例。
如果您发现文档中的任何内容不正确,与您使用特定功能的经验不符或需要进一步澄清,请使用 此表单报告文档问题。