使用模糊搜索查找近似匹配项

本页介绍了如何在全文搜索中使用模糊搜索。

除了使用 SEARCHSEARCH_SUBSTRING 函数执行精确令牌搜索之外,Spanner 还支持近似(或模糊)搜索。模糊搜索会找到与查询存在细微差异的匹配文档。

Spanner 支持以下类型的模糊搜索:

  • 基于 N 元语法的近似搜索
  • 使用 Soundex 进行拼音搜索

基于 N 元语元的模糊搜索依赖于与子字符串搜索所需的相同子字符串分词。分词器的配置非常重要,因为它会影响搜索质量和性能。以下示例展示了如何使用拼写错误或拼写方式不同的字词创建查询,以便在搜索索引中查找近似匹配项。

架构

CREATE TABLE Albums (
  AlbumId STRING(MAX) NOT NULL,
  AlbumTitle STRING(MAX),
  AlbumTitle_Tokens TOKENLIST AS (
    TOKENIZE_SUBSTRING(AlbumTitle, ngram_size_min=>2, ngram_size_max=>3,
                       relative_search_types=>["word_prefix", "word_suffix"])) HIDDEN
) PRIMARY KEY(AlbumId);

CREATE SEARCH INDEX AlbumsIndex
ON Albums(AlbumTitle_Tokens)
STORING (AlbumTitle);

查询

以下查询会查找与“Hatel Kaliphorn”最接近的专辑名称,例如“Hotel California”。

SELECT AlbumId
FROM Albums
WHERE SEARCH_NGRAMS(AlbumTitle_Tokens, "Hatel Kaliphorn")
ORDER BY SCORE_NGRAMS(AlbumTitle_Tokens, "Hatel Kaliphorn") DESC
LIMIT 10

优化基于 n 元语元的近似搜索的性能和召回率

上一部分中的示例查询会分两个阶段进行搜索,并使用两个不同的函数:

  1. SEARCH_NGRAMS 会查找与搜索查询共享 n 元语素的所有候选影集。例如,“California”的三字母 n 元组包括 [cal, ali, lif, ifo, for, orn, rni, nia],而“Kaliphorn”的三字母 n 元组包括 [kal, ali, lip, iph, pho, hor, orn]。这些数据集中的共享 n 元语素为 [ali, orn]。默认情况下,SEARCH_NGRAMS 会与至少有两个共享 n 元语的所有文档匹配,因此“Kaliphorn”与“California”匹配。
  2. SCORE_NGRAMS 会按相似度对匹配项进行排名。两个字符串的相似度定义为不重复的共享 n 元语法与不重复的非共享 n 元语法的比率:
$$ \frac{shared\_ngrams}{total\_ngrams_{index} + total\_ngrams_{query} - shared\_ngrams} $$

通常,SEARCH_NGRAMSSCORE_NGRAMS 函数中的 search_query 是相同的。建议的方法是将该参数与查询参数(而非字符串字面量)搭配使用,并在 SEARCH_NGRAMSSCORE_NGRAMS 函数中指定相同的查询参数。

Spanner 有三个可与 SEARCH_NGRAMS 搭配使用的配置参数:

  1. TOKENIZE_SUBSTRINGTOKENIZE_NGRAMS 中指定的 n 元语的最小和最大大小。我们不建议使用单个字符 n 元语,因为它们会与大量文档匹配。另一方面,长 n 元语法会导致 SEARCH_NGRAMS 漏掉拼写错误的短字词。
  2. SEARCH_NGRAMS 必须匹配的 n 元语词数下限(使用 SEARCH_NGRAMS 中的 min_ngramsmin_ngrams_percent 参数设置)。数值越高,查询速度通常越快,但召回率越低。

为了在性能和召回率之间取得良好的平衡,您可以配置这些参数以适应特定的查询和工作负载。

我们还建议添加内部 LIMIT,以避免在遇到热门 n 元语组组合时创建非常昂贵的查询:

SELECT AlbumId
FROM (
  SELECT AlbumId,
         SCORE_NGRAMS(AlbumTitle_Tokens, @p) AS score
  FROM Albums
  WHERE SEARCH_NGRAMS(AlbumTitle_Tokens, @p)
  LIMIT 10000  # inner limit
)
ORDER BY score DESC
LIMIT 10  # outer limit

基于 N 元语元的模糊搜索与增强型查询模式

除了基于 n 元语元的模糊搜索之外,增强型查询模式还可以处理一些拼写有误的字词。因此,这两项功能之间存在一些重叠。下表总结了这些差异:

基于 n-gram 的模糊搜索 增强型查询模式
费用 需要基于 n 元语素进行更昂贵的子字符串分词 需要更经济的全文令牌化
搜索查询类型 适用于包含少量字词的简短文档,例如包含人名、城市名称或商品名称的文档 适用于任何大小的文档和任何大小的搜索查询
部分字词搜索 执行子字符串搜索,允许出现拼写错误 仅支持搜索完整字词(SEARCH_SUBSTRING 不支持 enhance_query 参数)
拼写有误的字词 支持索引或查询中的错拼字 仅支持查询中的错拼字
更正 查找所有拼写有误的匹配项,即使匹配项不是真实字词也包括在内 更正常见的知名字词的拼写错误

使用 Soundex 进行音素搜索

Spanner 提供了 SOUNDEX 函数,用于查找拼写不同但发音相同的字词。例如,SOUNDEX("steven")SOUNDEX("stephen")SOUNDEX("stefan") 均为“s315”,而 SOUNDEX("stella") 为“s340”。SOUNDEX 区分大小写,并且仅适用于基于拉丁字母的字母表。

您可以使用生成的列和搜索索引实现使用 SOUNDEX 进行音素搜索,如以下示例所示:

CREATE TABLE Singers (
  SingerId INT64,
  AlbumTitle STRING(MAX),
  AlbumTitle_Tokens TOKENLIST AS (TOKENIZE_FULLTEXT(AlbumTitle)) HIDDEN,
  Name STRING(MAX),
  NameSoundex STRING(MAX) AS (LOWER(SOUNDEX(Name))),
  NameSoundex_Tokens TOKENLIST AS (TOKEN(NameSoundex)) HIDDEN
) PRIMARY KEY(SingerId);

CREATE SEARCH INDEX SingersPhoneticIndex ON Singers(AlbumTitle_Tokens, NameSoundex_Tokens);

以下查询会将“stefan”与 SOUNDEX 上的“Steven”进行匹配,同时 AlbumTitle 包含“cat”:

SELECT SingerId
FROM Singers
WHERE NameSoundex = LOWER(SOUNDEX("stefan")) AND SEARCH(AlbumTitle_Tokens, "cat")

后续步骤