对搜索结果进行排名

本页介绍了如何对 Spanner 中的全文搜索的搜索结果进行排名。

Spanner 支持计算主题相关性得分,这为创建复杂的排名函数提供了构建块。这些得分会根据查询字词频率和其他可自定义选项来计算结果与查询的相关性。

以下示例展示了如何使用 SCORE 函数执行排名搜索:

GoogleSQL

SELECT AlbumId
FROM Albums
WHERE SEARCH(AlbumTitle_Tokens, "fifth symphony")
ORDER BY SCORE(AlbumTitle_Tokens, "fifth symphony") DESC

PostgreSQL

此示例将 spanner.searchspanner.score 搭配使用。

SELECT albumid
FROM albums
WHERE spanner.search(albumtitle_tokens, 'fifth symphony')
ORDER BY spanner.score(albumtitle_tokens, 'fifth symphony') DESC

使用 SCORE 函数为查询字词评分

SCORE 函数会为每个查询字词计算得分,然后合并这些得分。每个字词的分数大致基于词频 - 逆向文档频率 (TF/IDF) 计算得出。该得分是记录最终排序的组成部分之一。该查询会将其与其他信号(例如用于调节主题性得分的最新性)相结合。

在当前实现中,只有在使用 enhance_query=>true 时,TF/IDF 的 IDF 部分才可用。它根据 Google 搜索使用的完整网页语料库(而非特定搜索索引)计算字词的相对频率。如果未启用 rquery 增强功能,则评分仅使用字词频率 (TF) 组件(即 IDF 字词设置为 1)。

SCORE 函数会返回值,这些值用作相关性得分,Spanner 会使用这些得分来确定排序顺序。它们没有独立含义。得分越高,与查询的匹配度就越高。

通常,SEARCHSCORE 函数中的 queryenhance_query 等参数相同,以确保检索和排名的一致性。

建议的方法是将这些参数与查询参数(而不是字符串字面量)搭配使用,并在 SEARCHSCORE 函数中指定相同的查询参数。

为多列评分

Spanner 使用 SCORE 函数分别为每个字段评分。然后,查询会将这些单独的得分合并在一起。常见的方法是将各个得分相加,然后根据用户提供的字段权重(使用 SQL 查询参数提供)对其进行提升。

例如,以下查询会组合两个 SCORE 函数的输出:

GoogleSQL

SELECT AlbumId
FROM Albums
WHERE SEARCH(Title_Tokens, @p1) AND SEARCH(Studio_Tokens, @p2)
ORDER BY SCORE(Title_Tokens, @p1) * @titleweight + SCORE(Studio_Tokens, @p2) * @studioweight
LIMIT 25

PostgreSQL

此示例使用了查询参数 $1$2,分别绑定到“第五交响曲”和“蓝色音符”。

SELECT albumid
FROM albums
WHERE spanner.search(title_tokens, $1) AND spanner.search(studio_tokens, $2)
ORDER BY spanner.score(title_tokens, $1) * $titleweight
        + spanner.score(studio_tokens, $2) * $studioweight
LIMIT 25

以下示例添加了两个提升参数:

  • 新鲜度 (FreshnessBoost) 会按 (1 + @freshnessweight * GREATEST(0, 30 - DaysOld) / 30) 提高得分
  • “热门程度”(PopularityBoost) 会将得分乘以系数 (1 + IF(HasGrammy, @grammyweight, 0) 来提高得分。

为方便阅读,该查询使用了 WITH 运算符。

GoogleSQL

SELECT AlbumId
FROM Albums
WHERE SEARCH(Title_Tokens, @p1) AND SEARCH(Studio_Tokens, @p2)
ORDER BY WITH(
  TitleScore AS SCORE(Title_Tokens, @p1) * @titleweight,
  StudioScore AS SCORE(Studio_Tokens, @p2) * @studioweight,
  DaysOld AS (UNIX_MICROS(CURRENT_TIMESTAMP()) - ReleaseTimestamp) / 8.64e+10,
  FreshnessBoost AS (1 + @freshnessweight * GREATEST(0, 30 - DaysOld) / 30),
  PopularityBoost AS (1 + IF(HasGrammy, @grammyweight, 0)),
  (TitleScore + StudioScore) * FreshnessBoost * PopularityBoost)
LIMIT 25

PostgreSQL

此示例使用了查询参数 $1$2$3$4$5$6,这些参数分别绑定到为 titlequerystudioquerytitleweightstudioweightgrammyweightfreshnessweight 指定的值。

SELECT albumid
FROM
  (
    SELECT
      albumid,
      spanner.score(title_tokens, $1) * $3 AS titlescore,
      spanner.score(studio_tokens, $2) * $4 AS studioscore,
      (extract(epoch FROM current_timestamp) * 10e+6 - releasetimestamp) / 8.64e+10 AS daysold,
      (1 + CASE WHEN hasgrammy THEN $5 ELSE 0 END) AS popularityboost
    FROM albums
    WHERE spanner.search(title_tokens, $1) AND spanner.search(studio_tokens, $2)
  ) AS subquery
ORDER BY (subquery.TitleScore + subquery.studioscore)
  * (1 + $6 * greatest(0, 30 - subquery.daysold) / 30) * subquery.popularityboost
LIMIT 25

TOKENLIST_CONCAT 还可用于搜索和评分,以便在适当情况下简化查询。

GoogleSQL

SELECT AlbumId
FROM Albums
WHERE SEARCH(TOKENLIST_CONCAT([Title_Tokens, Studio_Tokens]), @p)
ORDER BY SCORE(TOKENLIST_CONCAT([Title_Tokens, Studio_Tokens]), @p)
LIMIT 25

PostgreSQL

本示例使用 spanner.tokenlist_concat。查询参数 $1 绑定到“蓝色记事”。

SELECT albumid
FROM albums
WHERE spanner.search(spanner.tokenlist_concat(ARRAY[title_tokens, studio_tokens]), $1)
ORDER BY spanner.score(spanner.tokenlist_concat(ARRAY[title_tokens, studio_tokens]), $1)
LIMIT 25

提升查询顺序匹配分值

对于包含查询字词(按查询中显示的顺序)的值,Spanner 会对 SCORE 函数的输出应用乘法提升。此提升效果有两个版本:部分匹配和完全匹配。在以下情况下,系统会应用部分匹配加权:

  1. TOKENLIST 包含查询中的所有原始字词。
  2. 令牌彼此相邻,并且与在查询中显示的顺序相同。

连接词、否定词和短语有一些特殊规则:

  • 包含否定词的查询无法获得部分匹配加权。
  • 如果析取运算的一部分出现在适当的位置,则包含析取运算的查询会获得加权。
  • 如果包含某个词组的查询中的词组出现在 TOKENLIST 中,并且查询中该词组左侧的字词也出现在 TOKENLIST 中该词组左侧,并且该词组右侧的字词也出现在 TOKENLIST 中该词组右侧,则该查询会获得权重提升。

如果前面的所有规则均为 true,并且查询中的第一个和最后一个令牌是文档中的第一个和最后一个令牌,Spanner 会应用完全匹配加权。

文档示例:Bridge Over Troubled Water

查询 已应用的提升效果
Bridge Troubled 无提升
桥梁 - 其他水域 无提升
桥梁(跨越或在湍急水域) 无提升
Bridge Over 部分提升
桥梁(水流湍急或水面) 部分提升
Bridge Over Troubled Water 完全提升
横跨“动荡”水域的桥 完全增强
桥(“Over Troubled”或 missingterm)水 完全增强

限制检索深度

搜索索引通常包含数百万个文档。对于谓词选择性较低的查询,对所有结果进行排名并不实用。评分查询通常有两个限制:

  1. 检索深度限制:要为其评分的最大行数。
  2. 结果集大小限制:查询应返回的行数上限(通常是页面大小)。

查询可以使用 SQL 子查询限制检索深度:

GoogleSQL

SELECT *
FROM (
  SELECT AlbumId, Title_Tokens
  FROM Albums
  WHERE SEARCH(Title_Tokens, @p1)
  ORDER BY ReleaseTimestamp DESC
  LIMIT @retrieval_limit
)
ORDER BY SCORE(Title_Tokens, @p1)
LIMIT @page_size

PostgreSQL

此示例使用查询参数 $1$2$3,这些参数分别绑定到为 title_queryretrieval_limitpage_size 指定的值。

SELECT *
FROM (
  SELECT albumid, title_tokens
  FROM albums
  WHERE spanner.search(title_tokens, $1)
  ORDER BY releasetimestamp DESC
  LIMIT $2
) AS subquery
ORDER BY spanner.score(subquery.title_tokens, $1)
LIMIT $3

如果 Spanner 使用最重要的排名信号对索引进行排序,这种方法尤为有效。

后续步骤