查询概览

本页介绍了 SEARCH 函数和增强型查询模式,它们用于对 Spanner 表执行全文搜索查询。

查询搜索索引

Spanner 提供了 SEARCH 函数,供搜索索引查询使用。示例用例是,用户在搜索框中输入文本,应用将用户输入的内容直接发送到 SEARCH 函数。然后,“SEARCH”函数会使用搜索索引查找该文本。

SEARCH 函数需要两个参数:

  • 搜索索引名称
  • 搜索查询

SEARCH 函数仅在定义搜索索引后才会起作用。SEARCH 函数可与任何任意 SQL 结构(例如过滤器、汇总或联接)结合使用。

SEARCH 函数不能与事务查询一起使用。

以下查询使用 SEARCH 函数返回标题中包含 fridaymonday 的所有专辑:

SELECT AlbumId
FROM Albums
WHERE SEARCH(AlbumTitle_Tokens, 'friday OR monday')

搜索查询

搜索查询默认使用原始搜索查询语法。您可以使用 dialect 实参指定其他语法。

rquery 方言

默认方言为原始搜索查询。Spanner 使用一种称为 rquery 的特定领域语言 (DSL)。

在将输入搜索查询拆分为不同的字词时,rquery 语言遵循与纯文本分词器相同的规则。这包括亚洲语言细分。

如需了解如何使用 rquery,请参阅 rquery 语法

字词方言

词语方言类似于 rquery,但更简单。它不使用任何特殊运算符。例如,系统会将 OR 视为搜索字词,而不是析取运算符。系统会将双引号视为标点符号,而不是字词搜索,并将其忽略。

对于词方言,AND 会隐式应用于所有字词,并且在匹配期间是必需的。在将输入的搜索查询拆分为字词时,它遵循与纯文本分词器相同的规则。

如需了解如何使用 words 方言,请参阅 words 语法

words_phrase 方言

words_phrase 方言不使用任何特殊运算符,并且所有字词都被视为词组,这意味着字词必须相邻且按指定顺序排列。

与 rquery 一样,在将输入搜索查询拆分为字词时,words_phrase 方言遵循与纯文本分词器相同的规则。

如需了解如何使用 words_phrase 方言,请参阅词组语法

增强型查询模式

Spanner 提供两种全文搜索模式:一种是基于令牌的基本搜索,另一种是名为 enhance_query 的更高级别的搜索。启用后,enhance_query 会扩展搜索查询,以包含相关字词和同义词,从而提高找到相关结果的可能性。

如需启用此选项,请在 SEARCH 函数中设置可选参数 enhance_query=>true。例如,搜索查询 hotl cal 与影集 Hotel California 匹配。

SELECT AlbumId
FROM Albums
WHERE SEARCH(AlbumTitle_Tokens, 'hotl cal', enhance_query=>true)

enhance_query 模式是一种查询时选项。这不会影响令牌化。无论是否使用 enhance_query,您都可以使用相同的搜索索引。

Google 会不断改进查询优化算法。因此,使用 enhance_query == true 的查询可能会随着时间的推移而产生略有不同的结果。

启用 enhance_query 模式后,SEARCH 函数要查找的字词数量可能会增加,这可能会略微增加延迟时间。

例如,以下查询使用 3 秒的超时设置,如果 enhance_query 不可用,则会失败:

@{require_enhance_query=true, enhance_query_timeout_ms=3000}
SELECT AlbumId
FROM Albums
WHERE SEARCH(AlbumTitle_Tokens, 'fast car', enhance_query=>true)

如需详细了解如何使用 enhance_query 选项,请参阅 SEARCH 函数

SQL 查询要求

SQL 查询必须满足多项条件才能使用搜索索引。如果不满足这些条件,则查询会使用备选查询计划,如果没有备选计划,则会失败。

查询必须满足以下条件:

  • SEARCHSEARCH_SUBSTRING 函数需要搜索索引。Spanner 不支持对基表或二级索引进行查询时使用这些函数。

  • 分区索引中的所有分区列都必须由查询的 WHERE 子句中的等式条件所约束。

    例如,如果搜索索引定义为 PARTITION BY x, y,则查询必须在 x = <parameter or constant> AND y = <parameter or constant>WHERE 子句中包含析取运算。如果缺少此类条件,查询优化器将不会考虑该搜索索引。

  • SEARCHSEARCH_SUBSTRING 运算符引用的所有 TOKENLIST 列都必须在同一搜索索引中编入索引。

    例如,请考虑以下表和索引定义:

    CREATE TABLE Albums (
        AlbumId STRING(MAX) NOT NULL,
        AlbumTitle STRING(MAX),
        AlbumStudio STRING(MAX),
        AlbumTitle_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(AlbumTitle)) HIDDEN,
        AlbumStudio_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(AlbumStudio)) HIDDEN
      ) PRIMARY KEY(AlbumId);
    
      CREATE SEARCH INDEX AlbumsTitleIndex ON Albums(AlbumTitle_Tokens);
      CREATE SEARCH INDEX AlbumsStudioIndex ON Albums(AlbumStudio_Tokens);
    

    以下查询会失败,因为没有单个搜索索引同时对 AlbumTitle_TokensAlbumStudio_Tokens 进行编制索引:

    SELECT AlbumId
    FROM Albums
    WHERE SEARCH(AlbumTitle_Tokens, @p1) AND SEARCH(AlbumStudio_Tokens, @p2)
    
  • 如果排序顺序列可为 null,则架构和查询都必须排除排序顺序列为 null 的行。如需了解详情,请参阅搜索索引排序顺序

  • 如果搜索索引采用了 NULL 过滤,则查询必须包含与索引中使用的 NULL 过滤表达式相同的表达式。如需了解详情,请参阅已过滤 null 的搜索索引

  • DML、分区 DML 或分区查询不支持搜索索引搜索函数

  • 搜索索引搜索函数通常用于只读事务。如果应用要求允许使用过时结果,我们建议运行搜索查询时,过时时长应至少为 10 秒。如需了解详情,请参阅读取过时数据。这对于分散到多个 Paxos 组的大型搜索查询特别有用。

    不建议在读写事务中使用搜索索引搜索函数。在执行期间,搜索查询会锁定整个索引分区;因此,读写事务中的搜索查询速率过高可能会导致锁争用,进而导致延迟时间激增。默认情况下,系统不会在读写事务中自动选择搜索索引。如果在读写事务中强制使用搜索索引,查询默认会失败。如果查询包含任何搜索函数,也会失败。可以使用 @{ALLOW_SEARCH_INDEXES_IN_TRANSACTION=TRUE} 语句级提示来覆盖此行为(但查询仍容易发生锁争用)。

满足索引使用条件后,查询优化器会尝试加速非文本查询条件(例如 Rating > 4)。如果搜索索引不包含适当的 TOKENLIST 列,则该条件不会加速,并保持为残留条件

查询参数

搜索查询参数可以指定为字面量或查询参数。当参数允许使用查询参数值时,我们建议您使用查询参数进行全文搜索,而不是使用字符串字面量。

索引选择

Spanner 通常会使用基于成本的建模方法为查询选择最有效的索引。不过,FORCE_INDEX 提示会明确指示 Spanner 使用特定的搜索索引。例如,以下代码展示了如何强制 Spanner 使用 AlbumsIndex

SELECT AlbumId
FROM Albums @{FORCE_INDEX=AlbumsIndex}
WHERE SEARCH(AlbumTitle_Tokens, "fifth symphony")

如果指定的搜索索引不符合条件,则查询会失败,即使有其他符合条件的搜索索引也是如此。

搜索结果中的摘要

摘要是从给定字符串中提取的一段文本,可让用户大致了解搜索结果的内容以及该结果与其查询相关的原因。

例如,Gmail 会使用摘要来指明电子邮件中与搜索查询匹配的部分:

代码段列表

让数据库生成摘要有以下几个好处:

  1. 便捷:您无需实现逻辑即可根据搜索查询生成摘要。
  2. 效率:代码段可缩减服务器的输出大小。

SNIPPET 函数会创建该代码段。它会返回原始字符串值的相关部分,以及要突出显示的字符的位置。然后,客户端可以选择如何向最终用户显示摘要(例如,使用突出显示或粗体文本)。SNIPPET 函数会从原始字符串中移除所有 HTML 标记。

例如,以下代码使用 SNIPPETAlbumTitle 检索文本:

SELECT AlbumId, SNIPPET(AlbumTitle, "Fast Car")
FROM Albums
WHERE SEARCH(AlbumTitle_Tokens, "Fast Car")

后续步骤