附加特性
[编辑] 操作文档
8.3 12.3 控制文本搜索一节显示了裸文本文档如何转换成 tsvector 值。Postgresql 还提供了可以用于操作已经是 tsvector 形式的文档的函数。
tsvector || tsvector
- tsvector 连接操作符返回一个向量,这个向量组合了两个给出的向量参数的语意(lexeme)和位置信息。在连接的过程中,位置和权重信息被保留。出现在操作符右手边的向量的位置会加上一个左手边向量中提到过的最大的位置,因此这样做的结果是近乎等效于在两个原始文档之上进行连接然后再执行 to_tsvector 的效果。(这个等效不是完全一样的,因为任何从左手边参数删除的屏蔽词都不会影响结果,而如果先进行文本连接的话,那么它们将会影响右手边参数的语意的位置。)
- 以向量的形式连接比在 to_tsvector 之前直接连接文本的一个优势是,你可以用不同的配置分析(分词)文档的不同位置。还有,因为 setweight 函数给同一个给出的向量里的所有语意(lexeme)做同样的标识,所以,如果你想给文档的不同部分加上不同的权重,那么我们有必要先分词,然后在连接之前 setweight。
setweight(vector tsvector,weight "char") returns tsvector
- 这个函数返回一个输入向量的拷贝,这个拷贝里头每个位置都标记成给出的权重,可能是A,B,C或者D之一。(D是新向量的缺省,因此不会在输出里显示。)在向量连接的时候,这些标签会保留,这样就允许来自不同部分的单词在相关性函数里头有不同的权重。
- 请注意权重标签适用于位置,而不是语意(lexeme)。如果输入的向量抽除了位置,那么 setweight 不会干任何事情。
length(vector tsvector) returns integer
- 返回存储在向量里面的语意(lexeme)的数目。
strip(vector tsvector) returns tsvector
- 返回一个带有一列与给出向量相同语意(lexeme)的向量,但是它去掉了所有的位置和权重信息。虽然返回的向量比未抽除向量在相关性计算方面用处少很多,但是它通常更小。
[编辑] 操作查询
8.3 12.2 表和索引节显示了裸文本查询如何转换成 tsquery 值。Postgresql 还提供了可以用于操作已经是 tsquery 形式的查询的函数和操作符。
tsquery && tsquery
- 返回两个给出查询的与组合。
tsquery || tsquery
- 返回两个给出查询的或组合。
!! tsquery
- 返回给出查询的非。
numnode(query tsquery) returns integer
SELECT numnode(plainto_tsquery('the any')); NOTICE: query contains only stopword(s) or doesn't contain lexeme(s),ignored numnode --------- 0 SELECT numnode('foo & bar'::tsquery); numnode --------- 3
querytree(query tsquery) returns text
SELECT querytree(to_tsquery('!defined')); querytree -----------
[编辑] 查询改写
ts_rewrite 族函数搜索一个特定的 tsquery 以获取一个目标子查询是否出现,然后把每个出现都替换成其它的子查询。实际上这个操作是一个tsquery 版本的子字串替换。目标和替换的组合可以认为是一个重写规则。一组这样的重写规则可以成为搜索的强力助手。比如,你可以适用同义词扩展搜索(比如,new york,big apple,nyc,goham)或者把搜索限制起来,把用户导向一些热点主体。这个函数和知识词典(第12.6.4节的功能之间有一些重叠。不过,你可以运行时修改一个重写规则集而不用重建索引,而更新知识词典需要重建索引才能生效。
ts_rewrite (query tsquery,target tsquery,substitute tsquery) returns tsquery
- 这个形式的 ts_rewrite 只是接受单个重写规则:在任何地方出现的目标都被替换者替换。比如:
SELECT ts_rewrite('a & b'::tsquery,'a'::tsquery,'c'::tsquery); ts_rewrite ------------ 'b' & 'c'
ts_rewrite (query tsquery,select text) returns tsquery
- 这个形式的 ts_rewrite 接受一个开始查询和一个 sql select 命令,后者以文本字串的形式给出。这个 select 必须生成两个字段,类型都必须是 tsquery。对于当前查询值而言,select 结果的每一行里出现的第一个字段的值(目标)都会被第二个字段的值(替换者)替换。比如:
CREATE TABLE aliases (t tsquery PRIMARY KEY,s tsquery); INSERT INTO aliases VALUES('a','c'); SELECT ts_rewrite('a & b'::tsquery,'SELECT t,s FROM aliases'); ts_rewrite ------------ 'b' & 'c'
- 请注意,如果有需要施加的多个重写规则,那么这个施加的顺序可能就回很重要;所以实际上你可能需要对源查询采用一些排序键字上的 ORDER BY。
让我们看看一个真实的宇航搜索的例子。我们会对查询 supernovae 使用表驱动的重写规则进行扩展:
CREATE TABLE aliases (t tsquery primary key,s tsquery); INSERT INTO aliases VALUES(to_tsquery('supernovae'),to_tsquery('supernovae|sn')); SELECT ts_rewrite(to_tsquery('supernovae & crab'),'SELECT * FROM aliases'); ts_rewrite --------------------------------- 'crab' & ( 'supernova' | 'sn' )
我们可以通过更新表来修改重写规则:
UPDATE aliases SET s = to_tsquery('supernovae|sn &!nebulae') WHERE t = to_tsquery('supernovae'); SELECT ts_rewrite(to_tsquery('supernovae & crab'),'SELECT * FROM aliases'); ts_rewrite --------------------------------------------- 'crab' & ( 'supernova' | 'sn' &!'nebula' )
如果有很多重写规则的话,重写可能会很慢,因为它需要检查每一个可能会命中的规则。要过滤掉明显不是候选者的规则,我们可以使用tsquery 类型的包含操作符。在下面的例子力,我们只选取那些可能匹配原始查询的规则:
SELECT ts_rewrite('a & b'::tsquery,s FROM aliases WHERE ''a & b''::tsquery @> t'); ts_rewrite ------------ 'b' & 'c'
[编辑] 用于自动更新的触发器
在使用独立的字段存储文档的 tsvector 表现形式的时候,我们必须创建一个触发器,用来在文档字段内容改变的时候更新 tsvector 字段。有两个内置的触发器函数可以干这个事情,当然,你也可以写自己的。
tsvector_update_trigger(tsvector_column_name,config_name,text_column_name [,... ]) tsvector_update_trigger_column(tsvector_column_name,config_column_name,... ])
这些触发器函数自动从一个或多个文本字段里计算一个 tsvector 字段,同时在通过 CREATE TRIGGER 声明的参数的控制下。下面是一个使用它们的例子:
CREATE TABLE messages ( title text,body text,tsv tsvector ); CREATE TRIGGER tsvectorupdate BEFORE INSERT OR UPDATE ON messages FOR EACH ROW EXECUTE PROCEDURE tsvector_update_trigger(tsv,'pg_catalog.english',title,body); INSERT INTO messages VALUES('title here','the body text is here'); SELECT * FROM messages; title | body | tsv ------------+-----------------------+---------------------------- title here | the body text is here | 'bodi':4 'text':5 'titl':1 SELECT title,body FROM messages WHERE tsv @@ to_tsquery('title & body'); title | body ------------+----------------------- title here | the body text is here
在创建触发器之后,任何在 title 或者 body 内的改变都会自动对应到 tsv 里面,而不用应用操心这些事情。
第一个触发器参数必须是要更新的 tsvector 字段。第二个参数声明执行转换的时候需要用的文本搜索配置。对于 tsvector_update_trigger,配置名就以第二个触发器参数的方式给出。它必须是上面所示那样的有模式修饰的,这个触发器的行为就不会因为 search_path 的改变而改变。对于 tsvector_update_trigger_column,第二个触发器参数是另外一个表字段的名字,这个字段类型必须是 regconfig。这样就允许我们可以针对不同行使用不同的配置。剩下的参数是文本字段的名称(类型是 text,varchar 或者 char)。这些将被以给出的顺序包含在文档中。NULL 值会被忽略(但是其它字段仍将被索引)。
内置的触发器的一个限制是它们把所有输入字段都看成是一样的。要想用不同方式处理字段 -- 比如,给 body 赋予不同的权重 -- 我们就需要书写一个客户化的触发器。下面是一个使用 PL/pgsql 做为触发器语言的例子:
CREATE FUNCTION messages_trigger() RETURNS trigger AS $$ begin new.tsv:= setweight(to_tsvector('pg_catalog.english',coalesce(new.title,'')),'A') || setweight(to_tsvector('pg_catalog.english',coalesce(new.body,'D'); return new; end $$ LANGUAGE plpgsql; CREATE TRIGGER tsvectorupdate BEFORE INSERT OR UPDATE ON messages FOR EACH ROW EXECUTE PROCEDURE messages_trigger();
要注意,在触发器里创建 tsvector 数值的时候,明确地声明配置名称是非常重要的,这样字段的内容就不会被 default_text_search_config 的改变所影响。没能这么做可能会导致诸如转储和恢复之后搜索结果改变的问题。
[编辑] 收集文档的统计信息
ts_stat(sqlquery text,[ weights text,] OUT word text,OUT ndoc integer,OUT nentry integer) returns setof record
sqlquery 是一个文本值,包含一个 sql 查询,它必须返回单个 tsvector 字段。ts_stat 执行这个查询并返回包含在这个 tsvector 数据里有关每个独立的语意(lexeme)(单词)的统计,返回的字段有
- word text — 语意(lexeme)的值
- ndoc integer — 该词发生的文档(tsvector)的数目
- nentry integer — 该词发生的总次数
如果提供了权重,那么只计算包含该权重的词。
比如,在文档里找出十个最常见的词:
SELECT * FROM ts_stat('SELECT vector FROM apod') ORDER BY nentry DESC,ndoc DESC,word LIMIT 10;
同样的东西,但是只计算权重 A 和 B 的词:
SELECT * FROM ts_stat('SELECT vector FROM apod','ab') ORDER BY nentry DESC,word LIMIT 10;