Chinaunix首页 | 论坛 | 博客
  • 博客访问: 90878023
  • 博文数量: 19283
  • 博客积分: 9968
  • 博客等级: 上将
  • 技术积分: 196062
  • 用 户 组: 普通用户
  • 注册时间: 2007-02-07 14:28
文章分类

全部博文(19283)

文章存档

2011年(1)

2009年(125)

2008年(19094)

2007年(63)

分类:

2008-05-21 15:10:05

From PgsqlWiki

Jump to: ,
 

 控制文本搜索

要实现文本搜索,我们必须有一个函数从文档中创建一个 tsvector,和一个函数从用户的查询中创建 tsquery。还有,我们需要以有用的方式返回结果,因此我们需要一个函数对比文档和查询之间的相关性。把结果显示得比较漂亮也很重要。PostgreSQL 提供了所有这些函数的支持。

分析文档(分词)

PostgreSQL 提供了函数 to_tsvector 用于把文档转换成 tsvector 数据类型。

   to_tsvector([ config regconfig, ] document text) returns tsvector

to_tsvector 把一个文本文档分解成记号,把记号分解成语意(lexeme),然后返回一个 tsvector ,这个类型把所有语意和它们在文档中的位置都串联在一起。文档是使用指定的或者缺省的文本搜索配置进行处理的。下面是一个简单的例子:

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 函数在内部调用一个分析器,把文档文本分解成记号。然后,对每个记号都进行一系列的词典审核(),这个词典的列表可能和记号的类型密切相关。第一个识别了该记号的词典发出一个或多个规范化的语意(lexeme)以代表该记号。比如,rats 变成了 rat,因为其中一个词典认出单词 rats 是 rat 的复数形式。有些词会别当作屏蔽词看待(),这样它们就会被忽略,因为它们出现得太过频繁,对搜索没什么用处。在我们的例子里,屏蔽词是 a,on,和it。如果列表中的词典都没有识别该记号,那么它也会被忽略。在我们的例子里,标点符号就是这么处理的 - 因为实际上没有什么词典给它们标识记号类型(间隔符号),意味着空间记号永远不会被索引。分析器(分词器),词典和需要索引的记号的选择是由选定的文本搜索配置()决定的。我们可以在同一个数据库里有多个不同的配置,预定义的那些配置可以用于多种语言。在我们的例子里,我们用缺省的配置 english 处理英文。

我们可以使用函数 setweight 来给 tsvector 的项不同的重量,这里的“重量”是A,B,C或者D四个字母之一。这些东西通常用于标记项的是来自于文档的不同部分,比如标题、正文等部分。然后,这些信息可以用于对搜索结构排序。

因为 to_tsvector(NULL)会返回 NULL,我们建议在那些可能是空的字段上用 coalesce。下面是一个从结构华的文档中创建 tsvector 的推荐的方法:

UPDATE 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');

这里我们用 setweight 在完成后的 tsvector 里面标记每个语意(lexeme)的源,然后使用 tsvector 的连接操作符 || 把标记后的 tsvector 值融合起来。(给出了有关这些操作的细节。)

 分析查询

PostgreSQL 提供了函数 to_tsquery 和 plainto_tsquery 用于把查询转换成 tsquery 数据类型。to_tsquery 提供了获取比 plainto_tsquery 更多特性的机制,但是对输入要求更严。

   to_tsquery([ config regconfig, ] querytext text) returns tsquery

to_tsquery 从一个查询文本创建一个 tsquery 值,它们必须由使用布尔操作符 & (与),|(或)和 ! (非)分隔的单个记号。这些操作符可以用圆括弧分组。换句话说,给 to_tsquery 的输入必须已经遵守了 tsquery 输入的通用规则,如所示。区别是基本的 tsquery 输入以字面值接受输入的记号,而 to_tsquery 使用指定的或者缺省的配置,把每个记号都规范化成一个语意(lexeme),并且抛弃任何配置里规定的屏蔽词。比如:

SELECT to_tsquery('english', 'The & Fat & Rats');
  to_tsquery   
---------------
 'fat' & 'rat'

和基本的 tsquery 输入一样,我们可以给每个语意(lexeme)附加附加权重以限制它之匹配那些权重的 tsvector 语意。比如:

SELECT to_tsquery('english', 'Fat | Rats:AB');
    to_tsquery    
------------------
 'fat' | 'rat':AB

to_tsquery 还接受单引号包围的短语。这个功能在配置包含(thesaurus)知识词典、可能触发这类短语的时候很有效。在下面的例子里,一个包含规则 supernovae stars : sn 的知识词典:

SELECT to_tsquery('''supernovae stars'' & !crab');
  to_tsquery
---------------
 'sn' & !'crab'

如果没有引号,to_tsquery 会生成一个语法错误,因为记号没有被“与”或者“或”操作符分隔。

   plainto_tsquery([ config regconfig, ] querytext text) returns tsquery

plainto_tsquery 把未格式化的文本 querytext 转换成 tsquery。文本会像在 to_tsvector 里那样分析和规范化,然后用布尔操作符 & (与)插入到余下的单词中。

例子:

 SELECT plainto_tsquery('english', 'The Fat Rats');
 plainto_tsquery 
-----------------
 'fat' & 'rat'

请注意 plainto_tsquery 不能在其输入中识别布尔操作符或者权重标签:

SELECT plainto_tsquery('english', 'The Fat & Rats:C');
   plainto_tsquery   
---------------------
 'fat' & 'rat' & 'c'

这里,所有输入的标点都被当做间隔符号抛弃。

计算搜索结果相关性

相关性是企图评判文档与特定的查询之间是多相关的一个衡量标准,这样,如果有很多匹配的项,那么最相关的就会显示在前头。PostgreSQL提供了两个预定义的相关性函数,它们考虑了词法,相似性和结构信息;也就是说,它们会考虑查询词在文档中出现的频率,这些词在文档中的位置有多靠近,以及它们在文档中的位置有多重要。不过,相关性的概念是很模糊的,并且和应用密切相关。不同的应用可能需要相关性的额外信息,比如,文档的修改时间。内置的相关性函数只是例子。你可以书写你自己的相关性函数和/或把它们的结果跟额外的因素组合起来满足自己特定的需求。

目前可用的两个相关性函数是:

       ts_rank([ weights float4[], ] vector tsvector, query tsquery [, normalization integer ]) returns float4
标准相关性函数。
       ts_rank_cd([ weights float4[], ] vector tsvector, query tsquery [, normalization integer ]) returns float4
这个函数对给出的向量和查询进行覆盖密度相关性计算,这个算法在 Clarke,Cormack 和 Tudhope 的 "Relevance Ranking for One to Three Term Queries" 一文里有描述,该文发表于 1999 年的 "Information Processing and Management" (信息处理和管理)学报上。
这个函数要求在其输入内有位置信息。因此它不能在“剥离”后的 tsvector 值上进行运算 -- 那样会总是返回零。

对于这两个函数,我们都可选的权重参数提供根据词自身的(权重)标签来加重或者减轻单词的相关性分量。权重数组声明每个级别的单词有多重,按照下面顺序:

{D-weight, C-weight, B-weight, A-weight}

如果没有提供权重,那么使用下面的缺省:

{0.1, 0.2, 0.4, 1.0}

通常来说,权重用于把单词标记为来自于文档的哪个趋于,比如标题和开头的摘要,这样就可以以比文档体更重要或更不重要的方式对待它们。

因为更长的文档有更大的机会包含搜索的词,所以把文档尺寸考虑在内也是很合理的,也就是说,假如一个一百个词的文档里,出现了五个搜索词的实例,那么其相关性要比一千个词的文档出现五个实例要更相关。两个相关性函数都接受一个整数的规范化选项,声明一个文档的长度是否影响相关性,以及如何影响。这个整数选项控制好几个行为,所以它是一个位掩码:你可以用 | 声明一个或多个行为(比如,2|4)。

  • 0 (缺省)忽略文档长度
  • 1 对相关性除上 1 + 文档长度的对数
  • 2 对相关性除上文档长度
  • 4 对相关性除上宽度之间的平均谐距(只有 ts_rank_cd 实现了这个功能)
  • 8 对相关性除上文档中的唯一词的数目
  • 16 对相关性除上 1+ 文档中的唯一词数的对数
  • 32 对相关性除上自身 + 1

如果提供了多于一个的标志位,那么变换以列出的顺序施加。

我们要注意的是相关性函数并不使用任何全局信息,所以我们不可能生成一个公平的 1% 或者 100% 这样的规范化的东西,像某些场合期望的那样。规范化选项 32(rank/(rank+1))可以用于把所有相关性缩放成零到一之间的东西,但很显然这也只是一个装饰性的修改;它实际上不会影响搜索结果的排序。

下面是一个例子,选取了头十条最相关的匹配:

SELECT title, ts_rank_cd(textsearch, query) AS rank
FROM apod, to_tsquery('neutrino|(dark & matter)') query
WHERE query @@ textsearch
ORDER BY rank DESC LIMIT 10;
                     title                     |   rank
-----------------------------------------------+----------
 Neutrinos in the Sun                          |      3.1
 The Sudbury Neutrino Detector                 |      2.4
 A MACHO View of Galactic Dark Matter          |  2.01317
 Hot Gas and Dark Matter                       |  1.91171
 The Virgo Cluster: Hot Plasma and Dark Matter |  1.90953
 Rafting for Solar Neutrinos                   |      1.9
 NGC 4650A: Strange Galaxy and Dark Matter     |  1.85774
 Hot Gas and Dark Matter                       |   1.6123
 Ice Fishing for Cosmic Neutrinos              |      1.6
 Weak Lensing Distorts the Universe            | 0.818218

下面是使用规范化的相关性:

SELECT title, ts_rank_cd(textsearch, query, 32 /* rank/(rank+1) */ ) AS rank
FROM apod, to_tsquery('neutrino|(dark & matter)') query
WHERE  query @@ textsearch
ORDER BY rank DESC LIMIT 10;
                     title                     |        rank
-----------------------------------------------+-------------------
 Neutrinos in the Sun                          | 0.756097569485493
 The Sudbury Neutrino Detector                 | 0.705882361190954
 A MACHO View of Galactic Dark Matter          | 0.668123210574724
 Hot Gas and Dark Matter                       |  0.65655958650282
 The Virgo Cluster: Hot Plasma and Dark Matter | 0.656301290640973
 Rafting for Solar Neutrinos                   | 0.655172410958162
 NGC 4650A: Strange Galaxy and Dark Matter     | 0.650072921219637
 Hot Gas and Dark Matter                       | 0.617195790024749
 Ice Fishing for Cosmic Neutrinos              | 0.615384618911517
 Weak Lensing Distorts the Universe            | 0.450010798361481

相关性计算的开销可能会很大,因为它要求对每个匹配的文档的 tsvector 都进行计算,这个很可能是I/O密集型的操作,因此也会比较慢。不幸的是,我们几乎不可能避免这个事情,因为实际的查询总是会有大量的匹配。

 结果高亮

为了呈现搜索结果,最好是显示每个文档的一部分以及它和查询之间是如何关联的。通常,搜索引擎显示带有标识出来的搜索词的文档片段。PostgreSQL 提供一个函数 ts_headline 实现了这个功能。

   ts_headline([ config regconfig, ] document text, query tsquery [, options text ]) returns text

ts_headline 接受一个文档以及对应的查询,然后返回一个文档的摘要,在摘要里面,查询是高亮的。用于分析文档的配置可以在 config 里面声明;如果忽略 config,那么使用 default_text_search_config 配置。

如果声明了一个可选的字串,那么它必须由以一个逗号分隔的一个或多个“选项=值”对组成。可用的选项有:

  • StartSel, StopSel:用来把文档中出现的查询词和其它摘要词分隔开的分隔符。
  • MaxWords, MinWords:这些数值决定输出的摘要的长短。
  • ShortWord:小于或等于这个长度的词将被从摘要的开头和结尾抛弃。在英文文档里缺省值是三。
  • HighlightAll:布尔标志;如果为真,那么整个文档都会被高亮。

任何未声明的选项都会得到下面的缺省值:

  StartSel=, StopSel=, MaxWords=35, MinWords=15, ShortWord=3, HighlightAll=FALSE

比如:

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('query & similarity'));
                        ts_headline                         
------------------------------------------------------------
 given query terms
 and return them in order of their similarity to the
 query.

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('query & similarity'), 
  'StartSel = <, StopSel = >');
                      ts_headline                      
-------------------------------------------------------
 given  terms
 and return them in order of their  to the
 .

ts_headline 使用原始的文档,而不是 tsvector 摘要,所以它可能比较慢,因此要小心使用。一个常见的错误是对每个匹配的文档都调用 ts_headline,而实际上只需要显示十个文档。这个时候 SQL 的子查询可以帮忙;下面是一个例子:

SELECT id, ts_headline(body, q), rank
FROM (SELECT id, body, q, ts_rank_cd(ti, q) AS rank
      FROM apod, to_tsquery('stars') q
      WHERE ti @@ q
      ORDER BY rank DESC LIMIT 10) AS foo;
阅读(2175) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~