开发者社区> 李博 bluemind> 正文

PostgreSQL 10.1 手册_部分 II. SQL 语言_第 12 章 全文搜索_12.1. 介绍

简介: 12.1. 介绍 12.1.1. 什么是一个文档? 12.1.2. 基本文本匹配 12.1.3. 配置 全文搜索(或者文本搜索)提供了确定满足一个查询的自然语言文档的能力,并可以选择将它们按照与查询的相关度排序。
+关注继续查看

12.1. 介绍

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

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

  • 即使对英语也缺乏语言的支持。正则表达式是不够的,因为它们不能很容易地处理派生词,例如satisfiessatisfy。你可能会错过包含satisfies的文档,尽管你可能想要在对于satisfy的搜索中找到它们。可以使用OR来搜索多个派生形式,但是这样做太罗嗦也容易出错(有些词可能有数千种派生)。

  • 它们不提供对搜索结果的排序(排名),这使它们面对数以千计被找到的文档时变得无效。

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

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

  • 将文档解析成记号。标识出多种类型的记号是有所帮助的,例如数字、词、复杂的词、电子邮件地址,这样它们可以被以不同的方式处理。原则上记号分类取决于相关的应用,但是对于大部分目的都可以使用一套预定义的分类。PostgreSQL使用一个解析器来执行这个步骤。其中提供了一个标准的解析器,并且为特定的需要也可以创建定制的解析器。

  • 将记号转换成词位。和一个记号一样,一个词位是一个字符串,但是它已经被正规化,这样同一个词的不同形式被变成一样。例如,正规化几乎总是包括将大写字母转换成小写形式,并且经常涉及移除后缀(例如英语中的ses)。这允许搜索找到同一个词的变体形式,而不需要冗长地输入所有可能的变体。此外,这个步骤通常会消除停用词,它们是那些太普通的词,它们对于搜索是无用的(简而言之,记号是文档文本的原始片段,而词位是那些被认为对索引和搜索有用的词)。PostgreSQL使用词典来执行这个步骤。已经提供了多种标准词典,并且为特定的需要也可以创建定制的词典。

  • 为搜索优化存储预处理好的文档。例如,每一个文档可以被表示为正规化的词位的一个有序数组。与词位一起,通常还想要存储用于近似排名的位置信息,这样一个包含查询词更密集区域的文档要比那些包含分散的查询词的文档有更高的排名。

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

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

  • 使用Ispell把同义词映射到一个单一词。

  • 使用一个分类词典把短语映射到一个单一词。

  • 使用一个Ispell词典把一个词的不同变体映射到一种规范的形式。

  • 使用Snowball词干分析器规则将一个词的不同变体映射到一种规范的形式。

我们提供了一种数据类型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 mid = did AND 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_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输出,它允许在简单情况下跳过从文本字符串到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的文档。但是,如果不是紧跟在x之后, 那么!x <-> y匹配y;文档中其他地方出现的x并不妨碍匹配。 另一个例子是x & y通常只要求xy都出现在文档的某处, 但是(x & y) <-> z要求xy匹配在z 之前的相同位置。因此,这个查询的行为不同于x <-> z & y <-> z, 它将与包含两个单独的序列x zy 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/区域中有一些附加的解析器和模板的例子)。由于词典和配置只是对底层解析器和模板的参数化和连接,不需要特殊的权限来创建一个新词典或配置。创建定制词典和配置的例子将在本章稍后的部分给出。

本文转自PostgreSQL中文社区,原文链接:12.1. 介绍

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
为什么同样的Sql语句在SqlServer RDS 查询得到的和自己本机SqlServer 查询的不一样呢?
为什么同样的Sql语句在SqlServer RDS 查询得到的和自己本机SqlServer 查询的不一样呢?
42 0
通过RDS MySQL SQL洞察和审计排查如何丢失数据?
最近遇到多次业务方,反馈数据写入成功,但是需要查询使用时,数据确找不到了,所以需要确认数据什么不见了?
101 0
本地执行 RDS SQL 语句| 学习笔记
快速学习本地执行 RDS SQL 语句
56 0
RDS SQL Server通过配置镜像为高性能模式提高写入性能
RDS SQL Server通过配置镜像为高性能模式提高写入性能
150 0
PostgreSQL 执行计划,成本公式解说,代价因子校准,自动跟踪SQL执行计划(三)|学习笔记
快速学习PostgreSQL 执行计划,成本公式解说,代价因子校准,自动跟踪SQL执行计划(三)
188 0
PostgreSQL psql的使用,SQL语法,数据类型,递归SQL用法(四)|学习笔记
快速学习3 PostgreSQL psql的使用,SQL语法,数据类型,递归SQL用法(四)
138 0
3 PostgreSQL psql的使用,SQL语法,数据类型,递归SQL用法(三)|学习笔记
快速学习3 PostgreSQL psql的使用,SQL语法,数据类型,递归SQL用法(三)
89 0
知识分享之PostgreSQL——OIDS的特性与新版本去除SQL
之前一直使用的PostgreSQL 9.6系列版本,由于官方不再维护了,就准备换成最新稳定版本的,查看了一下官方版本说明,发现13系列版本是目前稳定性较好的版本,于是兴冲冲的更换了过来,但随之而来的就是一些新特性,其中就比如表中的OID字段,这个字段是对象标识符,之前能用于行标记,现在发现只有表才具有这个隐藏字段,行数据没有这个支持了,于是就需要将老版本的表进行关闭掉这个字段。下面我们就开始关闭之旅。
37 0
PostgreSQL集群篇——常用的运维SQL
本文主要是我日常使用的一些运维SQL和整理于互联网上的SQL,为了方便日常的使用,特把其汇总起来,遇到常用的时将会进行补充该文,欢迎大家在评论区进行提出一些常用的SQL。
139 0
RDS SQL Server 自带证书开启TDE的解决方案
RDS SQL Server 自带证书开启TDE的解决方案
26184 0
+关注
李博 bluemind
云栖社区Java、Redis、MongoDB运营小编,有意合作请联系钉钉:15810436147
文章
问答
文章排行榜
最热
最新
相关电子书
更多
云数据库RDS MySQL从入门到高阶
立即下载
PolarDB for PostgreSQL 源码与应用实战
立即下载
PolarDB for PostgreSQL 开源必读手册
立即下载