赞
踩
文本搜索对于数据库系统来说是一个十分重要的功能,它可以在数据库文本文档(包括表中的text字段等)中搜索一个模式,可以是一个或多个单词、短语、短句,并且可以按照相关度排序。最常见的文本搜索是找到所有包含给定查询项的文本文档,然后按照与查询项的相似度排序并返回。查询项和相似度的定义都非常灵活,取决于具体的应用。这个最简单的搜索将查询看作一组单词,将相似度看作查询项在文本文档中的频率。
文本搜索操作符在数据库中已经存在多年。PostgreSQL对文本数据类型有~
、~*
、LIKE
和ILIKE
运算符,但它们缺乏现代信息系统所需的许多基本特征:
OR
搜索多个派生形式,但这既繁琐又容易出错(有些词可能有数千个派生词)。全文索引可以对文档进行预处理并保存索引,以供之后快速搜索。预处理包括以下内容:
词典允许细粒度地控制token的规范化方式。使用适当的词典可以:
数据类型tsvector
用于存储预处理文档,类型tsquery
用于表示已处理的查询,这些数据类型有许多函数和运算符,其中最重要的是匹配运算符@@
,全文搜索可以使用索引来加速。
文档是全文检索系统中的检索单位,例如一篇杂志文章或一封电子邮件。文本搜索引擎必须能够解析文档并存储词位(关键词)与它们的父文档的关联。之后,这些关联被用来搜索包含查询词的文档。
对于PostgreSQL中的搜索,一个文档通常是数据库表中的一行文本字段,或者可能是这种字段的组合(连接),可能存储在几个表中或动态地获得。换句话说,一个文档可以由不同的部分构成索引,它可能不会作为一个整体存储在任何地方。比如:
-- 并操作( || )是用来防止一个NULL的属性导致整个结果都为NULL
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;
另一种可能性是将文件作为简单的文本文件存储在文件系统中。在这种情况下,数据库可以用来存储全文索引和执行搜索,而一些唯一的标识符可以用来从文件系统中检索文件。然而,从数据库外检索文件需要超级用户权限或特殊功能支持,所以这通常不如将所有数据保存在PostgreSQL内部方便。另外,把所有的东西都保存在数据库里面,可以很容易地访问文件元数据以便索引和显示。
出于文本搜索的目的,每个文档必须被简化成预处理后的tsvector
格式。搜索和排名完全是在文档的tsvecto
表示上进行的,只有当文档被选中显示给用户时,才需要检索原始文本。因此,我们经常把tsvector
说成是文档,但当然它只是完整文档的一个紧凑的表示。
PostgreSQL中的全文搜索基于匹配运算符@@
,如果一个tsvector
(文档)与一个tsquery
(查询)匹配,则返回true
,哪个数据类型先被写入并不重要:
SELECT 'a fat cat sat on a mat and ate a fat rat'::tsvector @@ 'cat & rat'::tsquery;
-- tsvector匹配了tsquery中的cat和rat,返回true
?column?
----------
t
SELECT 'fat & cow'::tsquery @@ 'a fat cat sat on a mat and ate a fat rat'::tsvector;
-- tsvector匹配了tsquery中的fat但是没有匹配cow,返回false
?column?
----------
f
正如上面的例子所表明的,和tsvector
一样,一个tsquery
也不是原始文本。一个tsquery包含搜索词,这些搜索词必须是已经规范化的词组,并且可以使用AND
、OR
、NOT
和FOLLOWED BY
操作符组合多个词。有一些函数to_tsquery
, plainto_tsquery
, phraseto_tsquery
可以把用户输入的文本转换为适当的tsquery
,主要是通过规范化文本中出现的词。类似地,to_tsvector
被用来解析和规范化一个文档字符串。所以在实践中,一个文本搜索匹配看起来更像这样:
SELECT to_tsvector('fat cats ate fat rats') @@ to_tsquery('fat & rat');
-- tsvector匹配了tsquery中的fat和rat
?column?
----------
t
如果写成以下形式,这个匹配就不会成功:
SELECT 'fat cats ate fat rats'::tsvector @@ to_tsquery('fat & rat');
-- 由于这里是直接输入的tsvector而不是文档,相当于处理后的结果,没有包含tsquery中的rat,所以匹配失败
?column?
----------
f
因为在这里不会发生对rats这个词的规范化处理。tsvector
的元素是假定已经规范化了的lexemes,所以rats与rat不匹配。
@@
操作符也支持文本输入,允许在简单情况下跳过文本字符串到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的文档:
在tsquery
的<->(FOLLOWED BY)
操作符帮助下可以搜索短语,该操作符只在其参数相邻且符合给定顺序时才匹配。例如:
SELECT to_tsvector('fatal error') @@ to_tsquery('fatal <-> error');
?column?
----------
t
SELECT to_tsvector('error is not fatal') @@ to_tsquery('fatal <-> error');
-- tsquery中的fatal和error在tsvector中不相邻且顺序错误,返回false
?column?
----------
f
FOLLOWED BY
操作符有一个更通用的版本,其形式为<N>
,其中N是一个整数,代表匹配lexemes的位置之差。<1>
与<->
相同,而<2>
允许在匹配词组之间正好出现一个其他词组,以此类推。phraseto_tsquery
函数利用这个操作符来构造一个tsquery
,当一些词是停顿词时,它可以匹配一个多字短语。例如:
SELECT phraseto_tsquery('cats ate rats');
-- cat、ate、rat都相邻
phraseto_tsquery
-------------------------------
'cat' <-> 'ate' <-> 'rat'
SELECT phraseto_tsquery('the cats ate the rats');
-- cat和ate相邻,但ate和rat的距离为2,中间有一个其他词
phraseto_tsquery
-------------------------------
'cat' <-> 'ate' <2> 'rat'
一个有时很有用的特殊情况是,<0>
可以用来要求两个模式匹配同一个词。
圆括号可以用来控制tsquery
操作符的嵌套。如果没有括号,优先级从低到高依次为:~
,<->
,&
,|
。
值得注意的是,当AND/OR/NOT
操作符在FOLLOWED BY
操作符的参数范围内时,它们的含义有细微的不同,因为在FOLLOWED BY
中匹配的确切位置是重要的。例如,通常情况下!x
只匹配不包含x的文档。但是!x <-> 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/
目录有一些附加解析器和模板的例子)。由于词典和配置只是将一些底层的分析器和模板参数化并连接在一起,所以不需要特殊权限来创建一个新的词典或配置。创建自定义词典和配置的例子在本章后面出现。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。