12.3. 控制 Literals 搜索

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

12 .3.1. 解析文件

PostgreSQL 提供了将文件转换为tsvector数据类型的功能to_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函数在内部调用一个解析器,该解析器将文档文本分成令牌,并为每个令牌分配类型。对于每个令牌,将查询字典列表(Section 12.6),该列表可能会根据令牌类型而有所不同。 “识别”令牌的第一个词典会发出一个或多个规范化的“词法”来表示令牌。例如,rats变为rat,因为其中一个词典认识到单词ratsrat的复数形式。有些单词被识别为停用词(Section 12.6.1),由于它们出现的频率太高而无法在搜索中使用,因此它们被忽略。在我们的示例中,这些是aonit。如果列表中没有字典可识别令牌,则也将忽略它。在此示例中,标点符号-发生了,因为实际上没有为其令牌类型(Space symbols)分配字典,这意味着永远不会索引空间令牌。解析器,字典的选择以及要索引的标记的类型由所选的文本搜索配置(Section 12.7)确定。同一数据库中可能有许多不同的配置,并且 sched 义的配置可用于各种语言。在我们的示例中,我们为英语使用了默认配置english

函数setweight可用于以给定* weight *标记tsvector的条目,其中 weight 是字母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值。 (Section 12.4.1提供了有关这些操作的详细信息。)

12 .3.2. 解析查询

PostgreSQL 提供了函数to_tsqueryplainto_tsqueryphraseto_tsquerywebsearch_to_tsquery,用于将查询转换为tsquery数据类型。 to_tsquery可以提供比plainto_tsqueryphraseto_tsquery更多的功能,但是对它的 Importing 的需求却较小。 websearch_to_tsqueryto_tsquery的简化版本,具有替代语法,类似于网络搜索引擎使用的语法。

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

to_tsquery从* querytext *创建一个tsquery值,该值必须由单个标记组成,这些标记由tsquery运算符&(AND),|(OR),!(NOT)和<->(FOLLOWED BY)分隔,并且可能使用括号分组。换句话说,to_tsquery的 Importing 必须已经遵循tsqueryImporting 的一般规则,如Section 8.11.2中所述。区别在于,虽然基本tsqueryImporting 以面值获取令牌,但to_tsquery使用指定的或默认配置将每个令牌归一化为词素,并根据该配置丢弃作为停用词的所有令牌。例如:

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

就像在基本tsqueryImporting 中一样,可以将权重附加到每个词素上,以限制其仅匹配那些权重的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运算符。

Example:

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

请注意,plainto_tsquery不会在其 Importing 中识别tsquery个运算符,权重标签或前缀匹配标签:

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

在此,所有 Importing 的标点符号都作为空格符号被丢弃。

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

phraseto_tsquery的行为与plainto_tsquery相似,不同之处在于它在幸存单词之间插入<->(FOLLOWED BY)运算符,而不是&(AND)运算符。同样,停用词不只是被丢弃,而是通过插入<N>运算符而不是<->运算符来解决。当搜索精确的词素序列时,此功能很有用,因为 FOLLOWED BY 运算符不仅检查所有词素的存在,还检查词素 Sequences。

Example:

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

plainto_tsquery一样,phraseto_tsquery函数在其 Importing 中将无法识别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不同,它还识别某些运算符。此外,该函数绝不会引发语法错误,从而可以使用用户提供的原始 Importing 进行搜索。支持以下语法:

  • unquoted text:不在引号内的文本将转换为由&运算符分隔的术语,就像由plainto_tsquery处理一样。

  • "quoted text":引号内的文本将转换为由<->运算符分隔的术语,就像由phraseto_tsquery处理一样。

  • OR:逻辑或将转换为|运算符。

  • -:逻辑非运算符,转换为!运算符。

Examples:

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 提供了两个 sched 义的排名功能,这些功能考虑了词法,邻近度和结构信息。也就是说,他们考虑查询词在文档中出现的频率,术语在文档中的紧密程度以及出现它们的文档部分的重要性。但是,相关性的概念含糊不清且非常针对特定应用。不同的应用程序可能需要其他信息来进行排名,例如文档修改时间。内置的排名功能仅是示例。您可以编写自己的排名函数和/或将其结果与其他因素结合起来以满足您的特定需求。

当前可用的两个排名功能是:

  • 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 在“信息处理与 Management”杂志(1999 年)中 Tudhope 的“一到三个查询的相关性等级”中所述。覆盖密度除了考虑匹配的词素彼此之间的接近度之外,与ts_rank排名相似。

此功能需要词素位置信息来执行其计算。因此,它忽略tsvector中的任何“剥离”词素。如果 Importing 中没有解串的词素,则结果将为零。 (有关strip函数和tsvector s 中的位置信息的更多信息,请参见Section 12.4.1。)

对于这两个功能,可选的* weights *参数提供了根据实例的标记来或多或少地加权单词实例的功能。权重数组按以下 Sequences 指定对每个单词类别加权的权重:

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

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

{0.1, 0.2, 0.4, 1.0}

通常,权重用于标记文档特殊区域(如标题或初始摘要)中的单词,因此与文档正文中的单词相比,权重的重要性更高或更低。

由于较长的文档包含查询词的机会更大,因此考虑文档大小是合理的,例如,带有五个搜索词实例的一百字文档比带有五个实例的千字文档更相关。这两个排序函数均采用整数* normalization *选项,该选项指定是否以及如何影响文档的等级。整数选项控制几种行为,因此它是一个位掩码:您可以使用|(例如2|4)指定一个或多个行为。

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

  • 1 将等级除以文档长度的对数

  • 2 将等级除以文档长度

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

  • 8 将等级除以文档中唯一词的数量

  • 16 将等级除以文档中唯一字数的对数

  • 32 自行将等级除以 1

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

重要的是要注意,排名函数不使用任何全局信息,因此,有时无法将公平归一化到 1%或 100%。可以使用规范化选项 32(rank/(rank+1))将所有等级缩放到零到一的范围,但这当然只是表面上的变化;它不会影响搜索结果的 Sequences。

这是仅选择十个最高匹配项的示例:

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(整数):要显示的最大文本片段数。默认值为零,选择一种不基于片段的标题生成方法。大于零的值将选择基于片段的标题生成(请参见下文)。

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

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

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

在基于非片段的标题生成中,ts_headline查找给定* query *的匹配项,然后选择一个要显示的匹配项,而是选择在允许的标题长度内具有更多查询词的匹配项。在基于片段的标题生成中,ts_headline定位查询匹配项,并将每个匹配项分成每个不超过MaxWords个单词的“片段”,更喜欢包含更多查询词的片段,并在可能的情况下“拉伸”片段以包含周围的单词。因此,当查询匹配跨文档的大部分内容时,或者需要显示多个匹配项时,基于片段的模式将更加有用。在任何一种模式下,如果都找不到查询匹配项,则将显示文档中前MinWords个单词的单个片段。

For example:

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的摘要,因此它可能会很慢,应谨慎使用。