9.3 9.4 9.5 9.6 10 11 12 13 14 Current(15)
阿里云PostgreSQL 问题报告 纠错本页面

12.1. 介绍

12.1.1. 什么是一个文档?
12.1.2. 基本文本匹配
12.1.3. 配置

全文搜索(或者文本搜索)提供了确定满足一个查询的自然语言文档的能力,并可以选择将它们按照与查询的相关度排序。最常用的搜索类型是找到所有包含给定查询词的文档并按照它们与查询的相似性顺序返回它们。查询相似性的概念非常灵活并且依赖于特定的应用。最简单的搜索认为查询是一组词而相似性是查询词在文档中的频度。

文本搜索操作符已经在数据库中存在很多年了。PostgreSQL对文本数据类型提供了~~*LIKEILIKE操作符,但是它们缺少现代信息系统所要求的很多基本属性:

全文索引允许文档被预处理并且保存一个索引用于以后快速的搜索。预处理包括:

词典允许对记号如何被正规化进行细粒度的控制。使用合适的词典,你可以:

我们提供了一种数据类型tsvector来存储预处理后的文档,还提供了一种类型tsquery来表示处理过的查询(第 8.11 节)。有很多函数和操作符可以用于这些数据类型(第 9.13 节),其中最重要的是匹配操作符@@,它在第 12.1.2 节中介绍。全文搜索可以使用索引来加速(第 12.9 节)。

12.1.1. 什么是一个文档?

一个document是在一个全文搜索系统中进行搜索的单元,例如,一篇杂志文章或电子邮件消息。文本搜索引擎必须能够解析文档并存储词位(关键词)与它们的父文档之间的关联。随后,这些关联会被用来搜索包含查询词的文档。

对于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说成是文档,但是当然它只是完整文档的一种紧凑表示。

12.1.2. 基本文本匹配

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_tsqueryplainto_tsqueryphraseto_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输出,它允许在简单情况下跳过从文本字符串到tsvectortsquery的显式转换。可用的变体是:

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中匹配的准确位置是有意义的。例如,通常!x仅匹配在任何地方都不包含x的文档。但如果y不是紧接在一个x后面,!x <-> y就会匹配那个y,在文档中其他位置出现的x不会阻止匹配。另一个例子是,x & y通常仅要求xy均出现在文档中的某处,但是(x & y) <-> z要求xy在紧挨着z之前的同一个位置匹配。因此这个查询的行为会不同于x <-> z & y <-> z,它将匹配一个含有两个单独序列x z以及y z的文档(这个特定的查询一点用都没有,因为xy不可能在同一个位置匹配,但是对于前缀匹配模式之类的更复杂的情况,这种形式的查询就会有用武之地)。

12.1.3. 配置

前述的都是简单的文本搜索例子。正如前面所提到的,全文搜索功能包括做更多事情的能力:跳过索引特定词(停用词)、处理同义词并使用更高级的解析,例如基于空白之外的解析。这个功能由文本搜索配置控制。PostgreSQL中有多种语言的预定义配置,并且你可以很容易地创建你自己的配置(psql\dF命令显示所有可用的配置)。

在安装期间一个合适的配置将被选择并且default_text_search_config也被相应地设置在postgresql.conf中。如果你正在对整个集簇使用相同的文本搜索配置,你可以使用在postgresql.conf中使用该值。要在集簇中使用不同的配置但是在任何一个数据库内部使用同一种配置,使用ALTER DATABASE ... SET。否则,你可以在每个会话中设置default_text_search_config

依赖一个配置的每一个文本搜索函数都有一个可选的regconfig参数,因此要使用的配置可以被显式指定。只有当这个参数被忽略时,default_text_search_config才被使用。

为了让建立自定义文本搜索配置更容易,一个配置可以从更简单的数据库对象来建立。PostgreSQL的文本搜索功能提供了四类配置相关的数据库对象:

  • 文本搜索解析器将文档拆分成记号并分类每个记号(例如,作为词或者数字)。

  • 文本搜索词典将记号转变成正规化的形式并拒绝停用词。

  • 文本搜索模板提供位于词典底层的函数(一个词典简单地指定一个模板和一组用于模板的参数)。

  • 文本搜索配置选择一个解析器和一组用于将解析器产生的记号正规化的词典。

文本搜索解析器和模板是从低层 C 函数构建而来,因此它要求 C 编程能力来开发新的解析器和模板,并且还需要超级用户权限来把它们安装到一个数据库中(在PostgreSQL发布的contrib/区域中有一些附加的解析器和模板的例子)。由于词典和配置只是对底层解析器和模板的参数化和连接,不需要特殊的权限来创建一个新词典或配置。创建定制词典和配置的例子将在本章稍后的部分给出。