支持的版本:当前 (17) / 16 / 15 / 14 / 13
开发版本:devel
不支持的版本:12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3

12.3. 控制文本搜索 #

要实现全文搜索,必须有一个函数从文档创建 tsvector,以及一个函数从用户查询创建 tsquery。此外,我们需要以有用的顺序返回结果,因此我们需要一个函数来比较文档与查询的相关性。能够很好地显示结果也很重要。PostgreSQL 为所有这些功能提供支持。

12.3.1. 解析文档 #

PostgreSQL 提供了函数 to_tsvector,用于将文档转换为 tsvector 数据类型。

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

to_tsvector 将文本文件解析为标记,将标记简化为词素,并返回一个 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 不包含单词 aonit,单词 rats 变成了 rat,标点符号 - 被忽略了。

to_tsvector 函数在内部调用一个解析器,该解析器将文档文本分解为标记并为每个标记分配一个类型。对于每个标记,都会查询一个字典列表(第 12.6 节),其中列表可以根据标记类型而变化。第一个识别标记的字典会发出一个或多个规范化的词素来表示该标记。例如,rats 变成了 rat,因为其中一个字典识别出单词 ratsrat 的复数形式。某些单词被识别为停用词第 12.6.1 节),由于它们出现过于频繁而无法在搜索中发挥作用,因此它们会被忽略。在我们的示例中,这些是 aonit。如果列表中的任何字典都无法识别该标记,则也会忽略该标记。在此示例中,标点符号 - 发生这种情况,因为实际上没有为它的标记类型(空格符号)分配字典,这意味着空格标记将永远不会被索引。解析器、字典以及要索引的标记类型的选择由选定的文本搜索配置确定(第 12.7 节)。可以在同一个数据库中拥有许多不同的配置,并且预定义的配置可用于各种语言。在我们的示例中,我们使用了英语的默认配置 english

函数 setweight 可用于使用给定的权重标记 tsvector 的条目,其中权重是字母 ABCD 之一。这通常用于标记来自文档不同部分的条目,例如标题与正文。稍后,此信息可用于对搜索结果进行排名。

由于 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 中每个词素的来源,然后使用 tsvector 连接运算符 || 合并了标记的 tsvector 值。(第 12.4.1 节 提供了有关这些操作的详细信息。)

12.3.2. 解析查询 #

PostgreSQL 提供了函数 to_tsqueryplainto_tsqueryphraseto_tsquerywebsearch_to_tsquery,用于将查询转换为 tsquery 数据类型。to_tsquery 提供的功能比 plainto_tsqueryphraseto_tsquery 更多,但它对其输入的容错性较差。websearch_to_tsqueryto_tsquery 的简化版本,使用替代语法,类似于 Web 搜索引擎使用的语法。

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

to_tsqueryquerytext 创建一个 tsquery 值,它必须由用 tsquery 运算符 &(AND)、|(OR)、!(NOT)和 <->(FOLLOWED BY)分隔的单个标记组成,可以使用括号进行分组。换句话说,to_tsquery 的输入必须已经遵循 tsquery 输入的一般规则,如 第 8.11.2 节中所述。不同之处在于,当基本的 tsquery 输入按字面意思接受标记时,to_tsquery 会使用指定的或默认的配置将每个标记规范化为词素,并丢弃根据配置作为停用词的任何标记。例如

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

与基本的 tsquery 输入一样,可以将权重附加到每个词素,以将其限制为仅匹配这些权重的 tsvector 词素。例如

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

此外,可以将 * 附加到词素以指定前缀匹配

SELECT to_tsquery('supern:*A & star:A*B');
        to_tsquery
--------------------------
 'supern':*A & 'star':*AB

此类词素将匹配 tsvector 中以给定字符串开头的任何单词。

to_tsquery 还可以接受单引号短语。当配置包含可能触发此类短语的词库字典时,这尤其有用。在下面的示例中,词库包含规则 supernovae stars : sn

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

如果没有引号,to_tsquery 将为未由 AND、OR 或 FOLLOWED BY 运算符分隔的标记生成语法错误。

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

plainto_tsquery 将未格式化的文本 querytext 转换为 tsquery 值。文本被解析并规范化,就像 to_tsvector 一样,然后在剩余的单词之间插入 &(AND)tsquery 运算符。

示例

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

请注意,plainto_tsquery 将无法识别其输入中的 tsquery 运算符、权重标签或前缀匹配标签

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

这里,所有输入标点符号都被丢弃了。

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

phraseto_tsquery 的行为很像 plainto_tsquery,不同之处在于它在剩余的单词之间插入 <->(FOLLOWED BY)运算符,而不是 &(AND)运算符。此外,停用词不仅仅被丢弃,而是通过插入 <N> 运算符而不是 <-> 运算符来考虑。当搜索确切的词素序列时,此函数非常有用,因为 FOLLOWED BY 运算符会检查词素顺序,而不仅仅是所有词素的存在。

示例

SELECT phraseto_tsquery('english', 'The Fat Rats');
 phraseto_tsquery
------------------
 'fat' <-> 'rat'

plainto_tsquery 一样,phraseto_tsquery 函数将无法识别其输入中的 tsquery 运算符、权重标签或前缀匹配标签

SELECT phraseto_tsquery('english', 'The Fat & Rats:C');
      phraseto_tsquery
-----------------------------
 'fat' <-> 'rat' <-> 'c'
websearch_to_tsquery([ config regconfig, ] querytext text) returns tsquery

websearch_to_tsquery 使用替代语法从 querytext 创建 tsquery 值,其中简单的未格式化文本是有效的查询。与 plainto_tsqueryphraseto_tsquery 不同,它还识别某些运算符。此外,此函数永远不会引发语法错误,这使得可以使用原始用户提供的输入进行搜索。支持以下语法

  • 未加引号的文本:引号外的文本将转换为由 & 运算符分隔的术语,就像由 plainto_tsquery 处理一样。

  • "带引号的文本":引号内的文本将被转换为由 <-> 运算符分隔的词项,如同通过 phraseto_tsquery 处理一样。

  • OR:单词 or 将被转换为 | 运算符。

  • -:短横线将被转换为 ! 运算符。

其他标点符号将被忽略。因此,与 plainto_tsqueryphraseto_tsquery 类似,websearch_to_tsquery 函数不会识别其输入中的 tsquery 运算符、权重标签或前缀匹配标签。

示例

SELECT websearch_to_tsquery('english', 'The fat rats');
 websearch_to_tsquery
----------------------
 'fat' & 'rat'
(1 row)

SELECT websearch_to_tsquery('english', '"supernovae stars" -crab');
       websearch_to_tsquery
----------------------------------
 'supernova' <-> 'star' & !'crab'
(1 row)

SELECT websearch_to_tsquery('english', '"sad cat" or "fat rat"');
       websearch_to_tsquery
-----------------------------------
 'sad' <-> 'cat' | 'fat' <-> 'rat'
(1 row)

SELECT websearch_to_tsquery('english', 'signal -"segmentation fault"');
         websearch_to_tsquery
---------------------------------------
 'signal' & !( 'segment' <-> 'fault' )
(1 row)

SELECT websearch_to_tsquery('english', '""" )( dummy \\ query <->');
 websearch_to_tsquery
----------------------
 'dummi' & 'queri'
(1 row)

12.3.3. 搜索结果排序 #

排序尝试衡量文档与特定查询的相关性,以便在存在多个匹配项时,可以优先显示最相关的匹配项。PostgreSQL 提供了两个预定义的排序函数,它们会考虑词汇、邻近度和结构信息;也就是说,它们会考虑查询词项在文档中出现的频率、词项在文档中的接近程度,以及词项出现位置在文档中的重要性。但是,相关性的概念是模糊的,并且与具体应用高度相关。不同的应用程序可能需要额外的排序信息,例如文档修改时间。内置的排序函数仅仅是示例。您可以编写自己的排序函数,和/或将它们的结果与其他因素结合起来,以满足您的特定需求。

当前可用的两个排序函数是

ts_rank([ weights float4[], ] vector tsvector, query tsquery [, normalization integer ]) 返回 float4

基于匹配词位的频率对向量进行排序。

ts_rank_cd([ weights float4[], ] vector tsvector, query tsquery [, normalization integer ]) 返回 float4

此函数计算给定文档向量和查询的覆盖密度排名,如 Clarke、Cormack 和 Tudhope 在 1999 年《信息处理与管理》期刊上发表的“一到三个词项查询的相关性排名”中所述。覆盖密度与 ts_rank 排名相似,不同之处在于它考虑了匹配词位之间的邻近性。

此函数需要词位位置信息来执行其计算。因此,它会忽略 tsvector 中的任何 stripped 词位。如果输入中没有未剥离的词位,则结果将为零。(有关 strip 函数和 tsvector 中位置信息的更多信息,请参见 第 12.4.1 节。)

对于这两个函数,可选的 weights 参数允许根据词的标签对词实例进行或多或少的加权。权重数组指定如何对每个词的类别进行加权,顺序为

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

如果未提供 weights,则使用以下默认值

{0.1, 0.2, 0.4, 1.0}

通常,权重用于标记来自文档特殊区域的词,例如标题或初始摘要,以便它们可以比文档正文中的词更重要或更不重要地处理。

由于较长的文档包含查询词项的可能性更大,因此考虑文档大小是合理的,例如,一个包含五个搜索词实例的 100 字文档可能比一个包含五个实例的 1000 字文档更相关。这两个排名函数都采用一个整数 normalization 选项,该选项指定文档的长度是否以及如何影响其排名。整数选项控制多种行为,因此它是一个位掩码:您可以使用 | 指定一个或多个行为(例如,2|4)。

  • 0(默认值)忽略文档长度

  • 1 将排名除以 1 + 文档长度的对数

  • 2 将排名除以文档长度

  • 4 将排名除以范围之间的平均谐波距离(这仅由 ts_rank_cd 实现)

  • 8 将排名除以文档中唯一词的数量

  • 16 将排名除以 1 + 文档中唯一词数量的对数

  • 32 将排名除以自身 + 1

如果指定了多个标志位,则按照列出的顺序应用转换。

需要注意的是,排名函数不使用任何全局信息,因此不可能产生期望的 1% 或 100% 的公平归一化。归一化选项 32 (rank/(rank+1)) 可以应用于将所有排名缩放到 0 到 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 密集型的,因此速度很慢。不幸的是,这几乎是不可避免的,因为实际查询通常会导致大量匹配。

12.3.4. 突出显示结果 #

为了呈现搜索结果,理想的情况是显示每个文档的一部分及其与查询的关系。通常,搜索引擎会显示带有标记搜索词的文档片段。PostgreSQL 提供了一个 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 对组成。可用的选项是

  • MaxWordsMinWords(整数):这些数字决定了要输出的最长和最短标题。默认值分别为 35 和 15。

  • ShortWord(整数):此长度或更短的词将在标题的开头和结尾被删除,除非它们是查询词项。默认值 3 将删除常见的英语冠词。

  • HighlightAll(布尔值):如果为 true,则整个文档将用作标题,忽略前面的三个参数。默认值为 false

  • MaxFragments(整数):要显示的最大文本片段数。默认值 0 选择非基于片段的标题生成方法。大于 0 的值选择基于片段的标题生成(见下文)。

  • StartSelStopSel(字符串):用于分隔文档中出现的查询词的字符串,以将其与摘录的其他词区分开来。默认值是 <b></b>,它们可能适用于 HTML 输出。

  • FragmentDelimiter(字符串):当显示多个片段时,这些片段将由该字符串分隔。默认值为 ...

这些选项名称不区分大小写。如果字符串值包含空格或逗号,则必须使用双引号。

在非基于片段的标题生成中,ts_headline 定位给定 query 的匹配项,并选择一个要显示的匹配项,优先选择在允许的标题长度内具有更多查询词的匹配项。在基于片段的标题生成中,ts_headline 定位查询匹配项,并将每个匹配项分割成不超过 MaxWords 个词的 片段,优先选择具有更多查询词的片段,并且在可能的情况下 扩展 片段以包含周围的词。因此,当查询匹配项跨越文档的较大区域时,或者当希望显示多个匹配项时,基于片段的模式更有用。在任一模式下,如果无法识别任何查询匹配项,则将显示文档中前 MinWords 个词的单个片段。

例如

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>.

SELECT ts_headline('english',
  'Search terms may occur
many times in a document,
requiring ranking of the search matches to decide which
occurrences to display in the result.',
  to_tsquery('english', 'search & term'),
  'MaxFragments=10, MaxWords=7, MinWords=3, StartSel=<<, StopSel=>>');
                        ts_headline
------------------------------------------------------------
 <<Search>> <<terms>> may occur                            +
 many times ... ranking of the <<search>> matches to decide

ts_headline 使用原始文档,而不是 tsvector 摘要,因此速度可能很慢,应谨慎使用。

提交更正

如果您发现文档中的任何内容不正确,与您在特定功能上的经验不符或需要进一步澄清,请使用此表格报告文档问题。