查询概览

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

查询搜索索引

Spanner 提供了 SEARCH 函数,可用于搜索索引查询。一个示例应用场景是:在某个应用中,用户在搜索框中输入文本,然后应用将用户输入的内容直接发送到 SEARCH 函数中。SEARCH 函数随后会使用搜索索引来查找该文本。

SEARCH 函数需要两个参数:

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

SEARCH 函数仅在定义了搜索索引时有效。SEARCH 函数可以与任何任意 SQL 构造(例如过滤条件、聚合或联接)结合使用。

SEARCH 函数不能与事务查询搭配使用。

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

GoogleSQL

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

PostgreSQL

此示例使用 spanner.search

SELECT albumid
FROM albums
WHERE spanner.search(albumtitle_tokens, 'friday OR monday')

搜索查询

搜索查询默认使用原始搜索查询语法。您可以使用 SEARCH dialect 参数指定替代语法。

rquery 方言

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

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

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

words 方言

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

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

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

words_phrase 方言

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

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

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

增强型查询模式

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

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

GoogleSQL

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

PostgreSQL

SELECT albumid
FROM albums
WHERE spanner.search(albumtitle_tokens, 'hotl cal', enhance_query=>true)

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

Google 在不断改进查询增强算法。因此,包含 enhance_query == true 的查询可能会随着时间推移生成略有不同的结果。

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

例如,以下查询使用 3 秒的超时时间,在 enhance_query 不可用时会失败:

GoogleSQL

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

PostgreSQL

/*@require_enhance_query=true, enhance_query_timeout_ms=3000*/
SELECT albumid
FROM albums
WHERE spanner.search(albumtitle_tokens, 'fast car', enhance_query=>true)

SQL 查询要求

SQL 查询必须满足多种条件才能使用搜索索引。如果不满足这些条件,查询会使用替代查询计划,在没有替代计划时会失败。

查询必须满足以下条件:

  • SEARCH 函数SEARCH_SUBSTRING 函数需要搜索索引。Spanner 不支持在针对基表或二级索引的查询中使用这些函数。

  • 分区索引必须在查询的 WHERE 子句中通过相等性条件绑定所有分区列。

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

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

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

    GoogleSQL

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

    PostgreSQL

    CREATE TABLE albums (
        albumid character varying NOT NULL,
        albumtitle character varying,
        albumstudio character varying,
        albumtitle_tokens spanner.tokenlist GENERATED ALWAYS AS (spanner.tokenize_fulltext(albumtitle)) VIRTUAL HIDDEN,
        albumstudio_tokens spanner.tokenlist GENERATED ALWAYS AS (spanner.tokenize_fulltext(albumstudio)) VIRTUAL HIDDEN,
    PRIMARY KEY(albumid));
    
    CREATE SEARCH INDEX albumstitleindex ON albums(albumtitle_tokens);
    CREATE SEARCH INDEX albumsstudioindex ON albums(albumstudio_tokens);
    

    以下查询会失败,因为不存在同时将 AlbumTitle_TokensAlbumStudio_Tokens 编入索引的单个搜索索引:

    GoogleSQL

    SELECT AlbumId
    FROM Albums
    WHERE SEARCH(AlbumTitle_Tokens, @p1)
        AND SEARCH(AlbumStudio_Tokens, @p2)
    

    PostgreSQL

    此示例使用查询参数 $1$2,它们分别绑定到“fast car”和“blue note”。

    SELECT albumid
    FROM albums
    WHERE spanner.search(albumtitle_tokens, $1)
        AND spanner.search(albumstudio_tokens, $2)
    
  • 如果排列顺序列可为 null,则架构和查询都必须排除排列顺序列为 NULL 的行。如需了解详情,请参阅搜索索引排列顺序

  • 如果搜索索引会过滤掉 NULL,查询必须包含索引中使用的同一 NULL 过滤表达式。如需了解详情,请参阅过滤掉 NULL 值的搜索索引

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

  • 搜索索引搜索函数通常用于只读事务。如果应用要求允许存在过时结果,您或许可以通过运行过时时长为 10 秒或更长的搜索查询来缩短延迟时间。如需了解详情,请参阅读取过时数据。这对于扇出到许多索引分块的搜索查询特别有用。

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

满足索引资格条件后,查询优化器会尝试加速非文本查询条件(例如 Rating > 4)。如果搜索索引不包含相应的 TOKENLIST 列,则该条件不会加速,仍为残余条件

查询参数

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

索引选择

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

GoogleSQL

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

PostgreSQL

SELECT albumid
FROM albums/*@force_index=albumsindex*/
WHERE spanner.search(albumtitle_tokens, 'fifth symphony')

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

搜索结果中的摘要

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

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

摘要列表

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

  1. 便利性:您无需实现逻辑来根据搜索查询生成摘要。
  2. 效率:摘要可减小服务器的输出大小。

SNIPPET 函数可创建摘要。它会返回原始字符串值的相关部分以及要突出显示的字符的位置。客户端随后可以选择如何向最终用户显示摘要(例如,使用突出显示文本或粗体文本)。

例如,以下内容使用 SNIPPETAlbumTitle 中检索文本:

GoogleSQL

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

PostgreSQL

此示例使用 spanner.snippet

SELECT albumid, spanner.snippet(albumtitle, 'Fast Car')
FROM albums
WHERE spanner.search(albumtitle_tokens, 'Fast Car')

后续步骤