クエリの概要

このページでは、Spanner テーブルに対する全文検索クエリの実行に使用される SEARCH 関数と拡張クエリモードについて説明します。

検索インデックスをクエリする

Spanner には、検索インデックス クエリに使用する SEARCH 関数があります。ユースケースの例としては、ユーザーが検索ボックスにテキストを入力し、アプリケーションがユーザーによる入力を SEARCH 関数に直接送信するアプリなどがあります。SEARCH 関数は検索インデックスを使用してそのテキストを検索します。

SEARCH 関数には次の 2 つの引数が必要です。

  • 検索インデックス名
  • 未加工の検索クエリ

SEARCH 関数は、検索インデックスが定義されている場合にのみ機能します。SEARCH 関数は、フィルタ、集計、結合など、任意の SQL 構文と組み合わせることができます。

SEARCH 関数は、トランザクション クエリでは使用できません。

次のクエリでは、SEARCH 関数を使用して、タイトルに friday または monday が含まれているすべてのアルバムを返します。

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

未加工の検索クエリと rquery 言語

SEARCH 関数の 2 番目の引数は、未加工の検索クエリです。Spanner は、rquery というドメイン固有の言語(DSL)を使用します。

rquery 言語は、入力文字列を個別の語句に分割するときに、プレーンテキスト トークナイザーと同じルールに従います。これには、アジアの言語のセグメンテーションも含まれます。

rquery の使用方法については、rquery の構文をご覧ください。

拡張クエリモード

Spanner には、基本的なトークンベースの検索と、enhance_query という高度なモードの 2 つの全文検索モードがあります。有効にすると、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 クエリがいくつかの条件を満たしている必要があります。これらの条件が満たされない場合、クエリは代替のクエリプランを使用するか、代替プランが存在しない場合は失敗します。

クエリは次の条件を満たしている必要があります。

  • SEARCH 関数と SEARCH_SUBSTRING 関数には検索インデックスが必要です。Spanner では、ベーステーブルまたはセカンダリ インデックスに対するクエリでこれらの関数はサポートされていません。

  • パーティション分割インデックスでは、すべてのパーティション列がクエリの WHERE 句の等式の条件でバインドされている必要があります。

    たとえば、検索インデックスが PARTITION BY x, y として定義されている場合、クエリの x = <parameter or constant> AND y = <parameter or constant>WHERE 句で連結詞が必要です。このような条件がない場合、クエリ オプティマイザーは検索インデックスを考慮しません。

  • SEARCH 演算子と SEARCH_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 列が含まれていない場合、条件は高速化されず、残存条件のままになります。

クエリ パラメータ

rquery と OFFSET などの他のパラメータは、リテラルまたはクエリ パラメータとして指定されます。全文検索には、文字列リテラルではなくクエリ パラメータを使用することをおすすめします。

インデックスの選択

通常、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 タグを削除します。

たとえば、以下では SNIPPET を使用して AlbumTitle からテキストを取得します。

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

次のステップ