12.1. Introduction

全文搜索(或仅文本搜索)提供了识别满足* query 的自然语言 documents 的功能,并可以根据查询的相关性对它们进行排序。最常见的搜索类型是查找包含给定查询词*的所有文档,并按与查询类似的 Sequences 返回它们。 querysimilarity的概念非常灵活,并取决于特定的应用程序。最简单的搜索将query视为一组单词,并将similarity视为文档中查询单词的频率。

文本搜索运算符已存在于数据库中多年。 PostgreSQL 具有用于文本数据类型的~~*LIKEILIKE运算符,但是它们缺少现代信息系统所需的许多基本属性:

  • 即使是英语,也没有语言支持。正则表达式是不够的,因为它们无法轻松处理派生词,例如satisfiessatisfy。您可能会错过包含satisfies的文档,尽管您可能希望在搜索satisfy时找到它们。可以使用OR搜索多种派生形式,但这很繁琐且容易出错(某些单词可能具有数千个派生形式)。

  • 它们不提供搜索结果的排序(排名),当找到成千上万个匹配的文档时,它们将无效。

  • 由于没有索引支持,它们往往很慢,因此它们必须为每次搜索处理所有文档。

全文索引允许对文档进行预处理,并保存索引以供以后快速搜索。预处理包括:

  • 将文档解析为令牌**。识别各种类别的令牌是有用的,例如数字,单词,复杂单词,电子邮件地址,以便可以对它们进行不同的处理。原则上,令牌类取决于特定的应用程序,但是对于大多数目的而言,使用 sched 义的类集就足够了。 PostgreSQL 使用* parser *执行此步骤。提供了一个标准解析器,可以为特定需求创建自定义解析器。

  • 将标记转换为词法**。一个词素是一个字符串,就像一个记号一样,但是它已经被“规范化”了,因此相同单词的不同形式也变得相似。例如,标准化几乎总是包括将大写字母折叠为小写字母,并且经常涉及删除后缀(例如英语中的ses)。这样一来,搜索就可以找到同一单词的变体形式,而不必 Importing 所有可能的变体。同样,此步骤通常会消除停用词,这些词太普遍了以至于它们对于搜索没有用。 (简而言之,令牌是文档文本的原始片段,而词素是被认为对索引和搜索有用的单词.)PostgreSQL 使用* dictionaries *来执行此步骤。提供了各种标准词典,可以针对特定需求创建自定义词典。

  • 存储针对搜索而优化的预处理文档。例如,每个文档可以表示为归一化词素的排序数组。与词素一起,通常需要存储位置信息以用于“邻近排名”,以便为包含更多“密集”查询词区域的文档分配比具有分散查询词的文档更高的排名。

字典允许对令牌的规范化进行细粒度控制。使用适当的词典,您可以:

  • 定义不应索引的停用词。

  • 使用 Ispell 将同义词 Map 到单个单词。

  • 使用词库将短语 Map 到单个单词。

  • 使用 Ispell 词典将单词的不同变体 Map 为规范形式。

  • 使用 Snowball stemmer 规则将单词的不同变体 Map 为规范形式。

提供了用于存储预处理文档的数据类型tsvector以及用于表示已处理查询(Section 8.11)的类型tsquery。这些数据类型(Section 9.13)有很多可用的函数和运算符,其中最重要的是匹配运算符@@,我们在Section 12.1.2中进行了介绍。可以使用索引(Section 12.9)加快全文搜索。

12 .1.1. 什么是文件?

文档是全文搜索系统中的搜索单位;例如,杂志文章或电子邮件。文本搜索引擎必须能够解析文档并存储词素(关键字)与其父文档的关联。以后,这些关联用于搜索包含查询词的文档。

对于在 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;

Note

实际上,在这些示例查询中,应使用coalesce来防止单个NULL属性对整个文档产生NULL结果。

另一种可能性是将文档作为简单文本文件存储在文件系统中。在这种情况下,数据库可用于存储全文索引并执行搜索,并且某些唯一标识符可用于从文件系统中检索文档。但是,从数据库外部检索文件需要超级用户权限或特殊功能支持,因此通常不如将所有数据保留在 PostgreSQL 中方便。另外,将所有内容保留在数据库中可以轻松访问文档元数据,以帮助进行索引和显示。

为了进行文本搜索,必须将每个文档缩小为预处理的tsvector格式。搜索和排序完全在文档的tsvector表示上执行-仅当选择了文档显示给用户时才需要检索原始文本。因此,我们经常将tsvector称为文档,但是当然,它只是完整文档的紧凑表示。

12 .1.2. 基本 Literals 匹配

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不仅仅是原始文本,而不仅仅是tsvectortsquery包含搜索词,这些词必须已经标准化了词素,并且可以使用 AND,OR,NOT 和 FOLLOWED BY 运算符组合多个词。 (有关语法的详细信息,请参见Section 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的元素是词素,假定已标准化,因此ratsrat不匹配。

@@运算符还支持textImporting,从而允许在简单情况下跳过文本字符串到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运算符的帮助下搜索短语,该运算符仅在其参数具有相邻且按给定 Sequences 的匹配项时才匹配。例如:

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,当某些单词是停用词时,该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如果不是紧接着x则匹配y;在文档中其他地方出现x不会阻止匹配。另一个示例是x & y通常只要求xy都出现在文档中的某个位置,但是(x & y) <-> z要求xy在同一位置匹配,紧靠在z之前。因此,此查询的行为与x <-> z & y <-> z不同,后者将匹配包含两个单独的序列x zy z的文档。 (由于xy不能在同一位置匹配,因此该特定查询在编写时是无用的;但是对于更复杂的情况(例如前缀匹配模式),这种形式的查询可能会有用。)

12.1.3. Configurations

以上都是简单的文本搜索示例。如前所述,全文搜索功能包括执行更多操作的能力:跳过对某些单词(停用词)构建索引,处理同义词以及使用复杂的语法分析(例如,基于多个空格进行语法分析)的功能。此功能由文本搜索配置控制。 PostgreSQL 带有针对多种语言的 sched 义配置,您可以轻松创建自己的配置。 (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/区域中有附加的解析器和模板的示例.)由于字典和配置仅将某些基础的解析器和模板参数化并将它们连接在一起,因此无需特殊特权即可创建新的字典或配置。创建自定义词典和配置的示例将在本章后面介绍。