AlloyDB AI で PostgreSQL の多言語全文検索における制限を解消する方法
Charlie Wang
Cloud Solutions Architect
Paul Ramsey
Product Manager, AlloyDB AI
※この投稿は米国時間 2026 年 7 月 16 日に、Google Cloud blog に投稿されたものの抄訳です。
AlloyDB は、いくつかの世界最大規模の組織において、エンタープライズ グレードの検索を強化し、テキスト検索、ベクトル検索、キーワード検索をシンプルなランク付けされた SQL クエリに組み合わせる堅牢なハイブリッド検索機能を提供しています。また、最近リリースされた RUM インデックスのサポートにより、AlloyDB のお客様はさらに強力な全文検索機能をすぐに利用できるようになりました。
しかし、データベース開発者は、中国語、日本語、韓国語などの表語文字言語の連続するテキストをインデックス化する際に、従来の空白ベースのトークン化は機能しないという制限に直面しています。Gemini の多言語機能を使用すると、これらの言語のテキストをインテリジェントに解析して、高度な単語分割とストップワードの削除を実装できますが、大規模なデータセットで行単位の API 呼び出しをオーケストレートすると、処理が遅く、脆弱なものになります。AlloyDB AI 関数を使用すれば、Gemini モデルが持つ広範な知識をデータベースにネイティブに統合できるため、複雑な ETL パイプラインの管理オーバーヘッドなしで、表語文字言語の全文検索を高い精度で実行できるようになります。
連続したテキストと表語文字言語
このネイティブ統合が大きな進歩であることを理解するには、まずテキスト検索の基本的な仕組みと、連続したテキストの処理には従来のインデックス手法が機能しない理由を確認する必要があります。
PostgreSQL で効果的な全文検索インデックスを構築するには、エンジンが to_tsvector 関数を使用してテキストを解析し、検索トークン(語彙素)に分割する必要があります。simple や english などの標準的なテキスト検索構成では、デフォルトで単語が空白で区切られていると想定されます。データベース エンジンは、これらの空白位置で入力文字列を分割し、検索語句を抽出します。
しかし、中国語などの表語文字言語では、単語の間に空白は使用されません。単語は連続して記述され、境界を示すのは句読点のみです。そのため、標準の PostgreSQL パーサーでは個々のキーワードを抽出できません。代わりに、文全体や長い句を 1 つの連続した語彙素として扱います。
たとえば、入力文字列が「你们研究所有十个图书馆」(研究所には図書館が 10 館あります)であるとします。
この文字列を to_tsvector('simple', ...) に渡すと、空白がないため、「你们研究所有十个图书馆」という 1 つの長い語彙素が生成されます。
そのため、「研究所」または「图书馆」(図書館)を検索しても、一致は返されません。キーワードが長い文字列の中に埋め込まれているため、長い文字列を正確に指定して検索しなければ、結果は得られません。
従来の方法とその限界
従来のツールやパイプラインを使用してこの問題を解決しようとすると、どちらを使用した場合でも、大きな運用上の摩擦や精度の限界が生じます。
-
サードパーティのデータベース拡張機能:
zhparserやpg_jiebaなどの拡張機能は、PostgreSQL に中国語トークン化機能を追加します。ただし、これらの機能は、フルマネージド データベース環境ではサポートされていないことがほとんどです。また、定義済みの固定の辞書に依存しているため、最新の専門用語、ブランド名、文脈に依存する用語を正しく解析できないことがよくあります。 -
外部の前処理パイプライン: テキストを外部アプリケーション(
jiebaやspaCyを実行する Python マイクロサービスなど)にエクスポートし、スペースを挿入してデータベースに保存します。このパターンでは、データベース層外にデータを移動するため、ETL が大幅に複雑化し、ネットワーク レイテンシが発生し、データ漏洩のリスクが高まります。 -
ルールベースおよび辞書を利用した分割では不十分: 従来のトークナイザーは、固定の辞書と手動でコーディングされた構文ルールに依存してテキストを分割しますが、セマンティックな曖昧さにより、まったく同じ文字シーケンスでも、コンテキストに応じて分割方法を変えることが必要となるため、それを解消するのは困難です。正確な分割を行うには、Gemini のような大規模言語モデルが有する世界中の知識とコンテキスト インテリジェンスが必要です。
Gemini を使用したデータベース内での前処理
AlloyDB AI では、ai.generate() などのネイティブなデータベース内 AI 関数が導入されているため、これらの回避策は必要はなく、その限界に制限されることもありません。SQL から直接 Gemini を呼び出すことができるため、データとインテリジェンスは 1 箇所に保持されます。
このアプローチには、主に 3 つの利点があります。
-
データの移動なし: テキストの前処理と分割はすべて、データベース エンジン内で直接行われます。これにより、ネットワーク レイテンシが最小限に抑えられ、データはデータベースの境界内で保護されます。
-
データベース内インテリジェンス: 外部マイクロサービスやオーケストレーション フレームワークを構築、デプロイ、保守する必要はありません。モデルの呼び出しは、データベース エンジンによりネイティブに調整されます。
-
ストアド プロシージャ ベースのバッチ処理: 配列集計が組み込まれた PL/pgSQL ストアド プロシージャを使用すると、行を並列バッチで処理し、
GENERATE_SERIESを使用して結果を安全に展開して、各バッチを即座に commit できます。これにより、大規模なテーブルを処理する場合でも、データベースのメモリ枯渇や行ロックの競合が回避され、安定した高パフォーマンスの実行を実現できます。
セマンティック単語分割の実装
このソリューションを実装するために、元のコンテンツ、分割されたテキスト、検索ベクトル、ベクトル エンベディングをまとめて保存するデータベース テーブルを作成します。
search_vector と embedding を生成された列として定義することにより、content_segmented 列が更新されるたびに、AlloyDB で全文検索インデックスとベクトル エンベディングの両方が自動的に更新されます。これにより、アプリケーション側のロジックが 1 つの UPDATE ステートメントに削減されます。
ステップ 1: ドキュメントのバッチ分割
AlloyDB AI のドキュメントでは、大規模なデータセット(10,000 行から数百万行)を扱う場合、メモリのボトルネックを回避するために、カーソルベースの処理を使用することが推奨されていますが、
ドキュメントのカーソル例は、INSERT を使用して新しい空のテーブルにテキストをストリーミングする、追加専用の演算に重点を置いています。このブログ投稿のユースケースでは、ライブの documents テーブルでインプレース UPDATE を行う必要があります。標準的な匿名ブロック(DO $$)で未加工のカーソルループを使用してこれを実装すると、本番環境で発生する可能性のある重大なリスクがあります。ライブ アプリケーションをフリーズさせる過剰な行ロック、ネットワークの障害が発生した場合のロールバック リスク、並列カーソルが同期しなくなるアライメント リスクの 3 つです。
これらのリスクを軽減し、パフォーマンスを向上させるために、高スループットの配列ベースのバッチ処理を実行するよう構成されたストアド プロシージャを使用します。
テーブル全体に対してこの前処理パイプラインを実行するには、ストアド プロシージャを呼び出します。
このストアド プロシージャを使用するアプローチには、本番環境での課題を直接解決する次の 3 つの利点があります。
-
配列の並列集計(シーケンスのボトルネックを解消): バッチを配列に集計し、配列を指定して
ai.generate(prompts => ...)を呼び出すことで、AlloyDB がモデルのリクエストをバッチ処理し、並列実行します。これは、行を 1 つずつ順に処理するよりも高速です。 -
インデックス ベースの安全な展開(カーソルの同期ずれを解決):
GENERATE_SERIESを使用して配列インデックスを展開すると、モデル出力が正しいドキュメント ID にマッピングされ、並列ストリームの同期ずれが発生するリスクが軽減されます。 -
即時 commit とロック解除(ブロッキングとロールバックのリスクを解消):
COMMIT;を使用して各ループ イテレーションの最後にトランザクションを commit することは、重要な最適化です。進行状況がすぐにディスクに保存され、行ロックが解除されるため、トランザクションの長時間実行によりライブ アプリケーションがフリーズするのを防ぎ、ネットワーク障害によって数時間分の作業がロールバックされないようにします。
このパイプラインを実行すると、上記の例の文は content_segmented に「你们 研究所 有 十个 图书馆」として保存されます。
ステップ 2: simple と english の選択
生成された tsvector 列を定義する際は、適切な PostgreSQL テキスト検索構成を選択する必要があります。この選択はデータセットによって異なります。
-
simple を使用する場合: データベースに中国語のテキストのみが含まれている場合は、
simple構成が最適です。この構成ではテキストが小文字に変換されますが、ステミングやデフォルトのストップワードの削除は実行されません。モデルのプロンプトでセマンティック分割とカスタム ストップワード フィルタリングが処理されるため、simple構成は、それ以上の変更を加えることなく、モデルの最適化された出力に直接マッピングされます。 -
english を使用する場合: 最新のエンタープライズ アプリケーションでは、中国語のドキュメントやユーザーのクエリに、英単語(商品コード、ブランド名、技術用語など)が含まれていることがよくあります。これらのバイリンガル シナリオでは、
english構成の方が優れています。英語の Porter ステマーは、非 ASCII の中国語文字は語彙素としてそのまま残しながら、英単語を自動的に正規化(例: 「running」を「run」にステミング)し、一般的な英語のストップワード(「the」、「and」)をフィルタリングします。これにより、個別の列や複雑なルーティング ロジックを必要とせずに、バイリンガル検索機能を統合できます。
ステップ 3: クエリ時の前処理
ドキュメント コンテンツの分割は、処理の半分にすぎません。インデックス化されたデータと一致させるには、入力された検索クエリを、同じ Gemini ベースのセグメンテーション ロジックを使用して前処理する必要があります。
検索の精度を向上させるため、これをさらに一歩進めます。モデルをインテリジェントなストップワード フィルタとして機能させ、検索結果に不要な要素を追加する価値の低い文法的なノイズを取り除くよう指示できます。
-
文法助詞(例: 的、了)
-
代名詞(例: 你、我们)
-
比較語(例: 比、最)
-
疑問詞(例: 怎么、为什么)
たとえば、SQL クエリを使用して、ユーザーのクエリをその場で処理できます。
モデルはこのクエリを処理し、キーワード文字列「研究所 图书馆」を返します。
ステップ 4: RUM で検索を実行する
ドキュメントを分割してインデックス化したら、生成された search_vector 列に RUM インデックスを作成できます。RUM インデックスは、語彙素の位置を直接保存するインデックス タイプです。これにより、AlloyDB は検索の関連性と単語の距離をインデックス内で直接計算できるようになり、従来の GIN インデックスで必要だった低速の再スキャン操作を回避できます。
検索を実行するには、前処理されたクエリ文字列(「研究所 图书馆」)を plainto_tsquery を使用して検索クエリに変換し、RUM インデックスに対して実行します。RUM 距離演算子(<=>)を使用すると、結果を関連度順に並べ替えることができます。
RUM インデックスは距離スコアを直接計算するため、このクエリは効率的に実行され、関連する一致が数ミリ秒で返されます。
ハイブリッド検索への拡張
キーワード ベースのテキスト検索は、完全一致を見つけるには優れていますが、異なる用語を使用している関連ドキュメントを見逃す可能性があります。これを解決するには、多言語対応の gemini-embedding-001 モデルを使用して、分割された全文の検索とセマンティック ベクトル検索を組み合わせます。ハイブリッド検索と呼ばれるこのパターンでは、語彙的にも意味的にも関連する結果が取得されます。
AlloyDB を使用すると、ハイブリッド検索を簡単に実行できます。embedding 列に ScaNN インデックス(Google のベクトル インデックス技術)を作成し、RUM インデックスと組み合わせることができます。
ScaNN インデックスの作成
ベクトル検索を高速化するために、embedding 列に ScaNN インデックスを作成します。
ハイブリッド検索クエリの実行
ベクトル検索の結果とテキスト検索の結果を組み合わせるには、Reciprocal Rank Fusion(RRF)を実装する SQL クエリを使用できます。RRF は、ランクベースのアルゴリズムの一種で、複数の検索結果リストを 1 つの統合リストにマージするために、個々のリストにおけるドキュメントのランクに基づいて、各ドキュメントにスコアを割り当てます。
次のクエリは、共通テーブル式(CTE)を使用して両方の検索を並行実行し、FULL OUTER JOIN を使用して結果を結合して、最終的な RRF スコアを計算します。
クエリの説明:
-
vector_searchCTE は、gemini-embedding-001モデルを使用し、ScaNN インデックスを利用してクエリに意味的に最も近い上位 10 件のドキュメントを検索します。 -
text_searchCTE は、RUM インデックスを使用して、分割されたキーワードに一致する上位 10 件のドキュメントを検索します。ランキングには RUM 距離演算子が使用されます。 -
最後の SELECT ステートメントでは、これらのリストを結合し、標準定数 60 を使用して RRF スコアを計算します。キーワードの完全一致とセマンティックの一致を高い精度で組み合わせた、上位 5 件の結果が返されます。
検索に AlloyDB AI が最適な理由
AlloyDB AI を使用して多言語検索とハイブリッド検索の課題を解決することで、エンタープライズ AI アプリケーションの堅牢でスケーラブルかつ費用対効果の高い基盤を構築できます。
-
ネイティブのデータベース内インテリジェンス: AlloyDB AI 内で直接モデル処理を実行することにより、トランザクション データを外部の AI サービスに移動する場合の費用、レイテンシ、データ漏洩のリスクを回避できます。
-
エンタープライズ グレードの検索パフォーマンス: ScaNN ベクトル検索(Google の検索テクノロジーをベースに構築)と RUM 全文検索を単一のリレーショナル データベースに統合することで、複雑な検索エンジンを個別に維持することなく、高速かつ正確な検索結果が得られます。
-
高いコンテキスト アウェア精度: 最新の用語が含まれていないルールベースの固定の辞書とは異なり、Gemini の世界中の知識を活用して深く意味を理解した単語分割が行われるため、高い検索精度を実現します。
-
運用の簡素化: 標準 SQL を使用して、堅牢なバイリンガル検索機能とハイブリッド検索機能を利用できます。つまり、新しい API を習得したり、複雑な外部パイプラインを管理したりすることなく、すでに持っているデータベースのスキルを使用して AI アプリケーションを構築し、スケールできます。
次のステップ
アプリケーションがトランザクション データと安全かつ効率的にやり取りできるようにすることで、断片化されたデータサイロから、AI が企業の実体に確実にアクセスできるアーキテクチャへと移行できます。
構築を始める準備ができたら、30 日間の無料トライアルで AlloyDB をお試しください。また、AlloyDB でハイブリッド検索を使ってみる Codelab を活用して、アプリケーションでインテリジェントな検索エクスペリエンスの構築を今すぐ始めましょう。
- クラウド ソリューション アーキテクト、Charlie Wang
- AlloyDB AI プロダクト マネージャー、Paul Ramsey

