控制文本搜索
解析文档
Vastbase中提供了to_tsvector函数把文档处理成tsvector数据类型。
to_tsvector([ config regconfig, ] document text) returns tsvector
to_tsvector将文本文档解析为token,再将token简化到词素,并返回一个tsvector。其中tsvector中列出了词素及它们在文档中的位置。文档是根据指定的或默认的文本搜索分词器进行处理的。这里有一个简单的例子:
vastbase=# SELECT to_tsvector('english', 'a fat cat sat on a mat - it ate a fat rats');
to_tsvector
-----------------------------------------------------
'ate':9 'cat':3 'fat':2,11 'mat':7 'rat':12 'sat':4
通过以上例子可发现结果tsvector不包含词a、on或者it,rats变成rat,并且忽略标点符号-。
to_tsvector函数内部调用一个解析器,将文档的文本分解成token并给每个token指定一个类型。对于每个token,有一系列词典可供查询。词典系列因token类型的不同而不同。识别token的第一本词典将发出一个或多个标准词素来表示token。例如:
rats变成rat因为词典认为词rats是rat的复数形式。
有些词被作为停用词(请参考12.8.6.2停用词),这样它们就会被忽略,因为它们出现得太过频繁以致于搜索中没有用处。比如例子中的a、on和it。
如果没有词典识别token,那么它也被忽略。在这个例子中,符号”-“被忽略,因为词典没有给它分配token类型(空间符号),即空间记号永远不会被索引。
语法解析器、词典和要索引的token类型由选定的文本搜索分词器决定。可以在同一个数据库中有多种不同的分词器,以及提供各种语言的预定义分词器。在以上例子中,使用缺省分词器english。
函数setweight可以给tsvector的记录加权重,权重是字母A、B、C、D之一。这通常用于标记来自文档不同部分的记录,比如标题、正文。之后,这些信息可以用于排序搜索结果。
因为to_tsvector(NULL)会返回空,当字段可能是空的时候,建议使用coalesce。以下是推荐的为结构化文档创建tsvector的方法:
vastbase=# CREATE TABLE tsearch.tt (id int, title text, keyword text, abstract text, body text, ti tsvector);
vastbase=# INSERT INTO tsearch.tt(id, title, keyword, abstract, body) VALUES (1, 'China', 'Beijing', 'China','China, officially the People''s Republic of China (PRC), located in Asia, is the world''s most populous state.');
vastbase=# UPDATE tsearch.tt SET ti =
setweight(to_tsvector(coalesce(title,'')), 'A') ||
setweight(to_tsvector(coalesce(keyword,'')), 'B') ||
setweight(to_tsvector(coalesce(abstract,'')), 'C') ||
setweight(to_tsvector(coalesce(body,'')), 'D');
vastbase=# DROP TABLE tsearch.tt;
上例使用setweight标记已完成的tsvector中的每个词的来源,并且使用tsvector连接操作符||合并标记过的tsvector值,12.8.4.1处理tsvector一节详细介绍了这些操作。
解析查询
Vastbase提供了函数to_tsquery和plainto_tsquery将查询转换为tsquery数据类型,to_tsquery提供比plainto_tsquery更多的功能,但对其输入要求更严格。
to_tsquery([ config regconfig, ] querytext text) returns tsquery
to_tsquery从querytext中创建一个tsquery,querytext必须由布尔运算符& (AND),| (OR)和! (NOT)分割的单个token组成。这些运算符可以用圆括弧分组。换句话说,to_tsquery输入必须遵循tsquery输入的通用规则,具体请参见12.3.10文本搜索类型。不同的是基本tsquery以token表面值作为输入,而to_tsquery使用指定或默认分词器将每个token标准化成词素,并依据分词器丢弃属于停用词的token。例如:
vastbase=# SELECT to_tsquery('english', 'The & Fat & Rats');
to_tsquery
---------------
'fat' & 'rat'
(1 row)
像在基本tsquery中的输入一样,weight(s)可以附加到每个词素来限制它只匹配那些有相同weight(s)的tsvector词素。比如:
vastbase=# SELECT to_tsquery('english', 'Fat | Rats:AB');
to_tsquery
------------------
'fat' | 'rat':AB
(1 row)
同时,*也可以附加到词素来指定前缀匹配:
vastbase=# SELECT to_tsquery('supern:*A & star:A*B');
to_tsquery
--------------------------
'supern':*A & 'star':*AB
(1 row)
这样的词素将匹配tsquery中指定字符串和权重的项。
plainto_tsquery([ config regconfig, ] querytext text) returns tsquery
plainto_tsquery将未格式化的文本querytext变换为tsquery。类似于to_tsvector,文本被解析并且标准化,然后在存在的词之间插入&(AND)布尔算子。
比如:
vastbase=# SELECT plainto_tsquery('english', 'The Fat Rats');
plainto_tsquery
-----------------
'fat' & 'rat'
(1 row)
请注意,plainto_tsquery无法识别布尔运算符、权重标签,或在其输入中的前缀匹配标签:
vastbase=# SELECT plainto_tsquery('english', 'The Fat & Rats:C');
plainto_tsquery
---------------------
'fat' & 'rat' & 'c'
(1 row)
在这里,所有输入的标点符号作为空格符号丢弃。
排序查询结果
排序试图针对特定查询衡量文档的相关度,从而将众多的匹配文档中相关度最高的文档排在最前。Vastbase提供了两个预置的排序函数。函数考虑了词法,距离,和结构信息;也就是,他们考虑查询词在文档中出现的频率、紧密程度、以及他们出现的地方在文档中的重要性。然而,相关性的概念是模糊的,并且是跟应用强相关的。不同的应用程序可能需要额外的信息来排序,比如,文档的修改时间,内置的排序函数等。也可以开发自己的排序函数或者采用附加因素组合这些排序函数的结果来满足特定需求。
两个预置的排序函数:
ts_rank([ weights float4[], ] vector tsvector, query tsquery [, normalization integer ]) returns float4
基于词素匹配率对vector进行排序:
ts_rank_cd([ weights float4[], ] vector tsvector, query tsquery [, normalization integer ]) returns float4
该函数需要位置信息的输入。因此它不能在”剥离”tsvector值的情况下运行—它将总是返回零。
对于这两个函数,可选的weights参数提供给词加权重的能力,词的权重大小取决于所加的权值。权重阵列指定在排序时为每类词汇加多大的权重。
{D-weight, C-weight, B-weight, A-weight}
如果没有提供weights,则使用缺省值:{0.1, 0.2, 0.4, 1.0}。
通常的权重是用来标记文档特殊领域的词,如标题或最初的摘要,所以相对于文章主体中的词它们有着更高或更低的重要性。
由于较长的文档有更多的机会包含查询词,因此有必要考虑文档的大小。例如,包含有5个搜索词的一百字文档比包含有5个搜索词的一千字文档相关性更高。两个预置的排序函数都采用了一个整型的标准化选项来定义文档长度是否影响排序及如何影响。这个整型选项控制多个行为,所以它是一个屏蔽字:可以使用|指定一个或多个行为(例如,2|4)。
0(缺省)表示:跟长度大小没有关系
1 表示:排名(rank)除以(文档长度的对数+1)
2表示:排名除以文档的长度
4表示:排名除以两个扩展词间的调和平均距离。只能使用ts_rank_cd实现
8表示:排名除以文档中单独词的数量
16表示:排名除以单独词数量的对数+1
32表示:排名除以排名本身+1
当指定多个标志位时,会按照所列的顺序依次进行转换。
需要特别注意的是,排序函数不使用任何全局信息,所以不可能产生一个某些情况下需要的1%或100%的理想标准值。标准化选项32 (rank/(rank+1))可用于所有规模的从零到一之间的排序,当然,这只是一个表面变化;它不会影响搜索结果的排序。
下面是一个例子,仅选择排名前十的匹配:
vastbase=# SELECT id, title, ts_rank_cd(to_tsvector(body), query) AS rank
FROM tsearch.pgweb, to_tsquery('america') query
WHERE query @@ to_tsvector(body)
ORDER BY rank DESC
LIMIT 10;
id | title | rank
----+---------+------
11 | Brazil | .2
2 | America | .1
12 | Canada | .1
13 | Mexico | .1
(4 rows)
这是使用标准化排序的相同例子:
vastbase=# SELECT id, title, ts_rank_cd(to_tsvector(body), query, 32 /* rank/(rank+1) */ ) AS rank
FROM tsearch.pgweb, to_tsquery('america') query
WHERE query @@ to_tsvector(body)
ORDER BY rank DESC
LIMIT 10;
id | title | rank
----+---------+----------
11 | Brazil | .166667
2 | America | .0909091
12 | Canada | .0909091
13 | Mexico | .0909091
(4 rows)
下面是使用中文分词法排序查询的例子:
vastbase=# CREATE TABLE tsearch.ts_ngram(id int, body text);
vastbase=# INSERT INTO tsearch.ts_ngram VALUES(1, '中文');
vastbase=# INSERT INTO tsearch.ts_ngram VALUES(2, '中文检索');
vastbase=# INSERT INTO tsearch.ts_ngram VALUES(3, '检索中文');
--精确匹配
vastbase=# SELECT id, body, ts_rank_cd(to_tsvector('ngram',body), query) AS rank FROM tsearch.ts_ngram, to_tsquery('中文') query WHERE query @@ to_tsvector(body);
id | body | rank
----+------+------
1 | 中文 | .1
(1 row)
--模糊匹配
vastbase=# SELECT id, body, ts_rank_cd(to_tsvector('ngram',body), query) AS rank FROM tsearch.ts_ngram, to_tsquery('中文') query WHERE query @@ to_tsvector('ngram',body);
id | body | rank
----+----------+------
3 | 检索中文 | .1
1 | 中文 | .1
2 | 中文检索 | .1
(3 rows)
排序要遍历每个匹配的tsvector,因此资源消耗多,可能会因为I/O限制导致排序慢。可是这是很难避免的,因为实际查询中通常会有大量的匹配。
高亮搜索结果
搜索结果的理想显示是:列出每篇文档中与搜索相关的部分,并标识为什么与查询相关。搜索引擎能够显示标识了搜索词的文档片段。Vastbase提供了函数ts_headline支持这部分功能。
ts_headline([ config regconfig, ] document text, query tsquery [, options text ]) returns text
ts_headline的输入是带有查询条件的文档,其返回文档中的摘录,在摘录中查询词是高亮显示的。用来解析文档的分词器由config参数指定。如果省略config,则使用default_text_search_config的值所指定的分词器。
指定options字符串时,需由一个或多个option=value对组成,且必须用逗号分隔。options可以是下面的选项:
StartSel,StopSel:分隔文档中出现的查询词,以区别于其他摘录词。当包含有空格或逗号时,必须用双引号将字符串引起来。
MaxWords,MinWords:定义摘录的最长和最短值。
ShortWord:在摘录的开始和结束会丢弃此长度或更短的词。默认值3会消除常见的英语冠词。
HighlightAll:布尔标志。如果为真,整个文档将作为摘录。忽略前面三个参数的值。
MaxFragments:要显示的文本摘录或片段的最大数量。默认值0表示选择非片段的摘录生成方法。 大于0的值表示选择基于片段的摘录生成。此方法查找带有尽可能多查询词的文本片段,并显示查询词周围的上下文片段。因此,查询词临近每个片段的中间,且查询词两边都有词。每个片段至多有MaxWords,并且长度为ShortWord或更短的词在每一个片段开始和结束被丢弃。如果在文档中没有找到所有的查询词,则文档中开头将显示MinWords单片段。
FragmentDelimiter:当有一个以上的片段时,通过该字符串分隔这些片段。
不声明选项时,采用下面的缺省值:
StartSel=<b>, StopSel=</b>,
MaxWords=35, MinWords=15, ShortWord=3, HighlightAll=FALSE,
MaxFragments=0, FragmentDelimiter=" ... "
例如:
vastbase=# SELECT ts_headline('english',
'The most common type of search
is to find all documents containing given query terms
and return them in order of their similarity to the
query.',
to_tsquery('english', 'query & similarity'));
ts_headline
------------------------------------------------------------
containing given <b>query</b> terms
and return them in order of their <b>similarity</b> to the
<b>query</b>.
(1 row)
vastbase=# SELECT ts_headline('english',
'The most common type of search
is to find all documents containing given query terms
and return them in order of their similarity to the
query.',
to_tsquery('english', 'query & similarity'),
'StartSel = <, StopSel = >');
ts_headline
-------------------------------------------------------
containing given <query> terms
and return them in order of their <similarity> to the
<query>.
(1 row)
ts_headline使用原始文档,而不是tsvector摘录,因此使用起来会慢,应慎重使用。