🔍

Supabase で 日本語 類似文書検索

2023/06/02に公開

Supabaseでは拡張機能としてPGroonga(ぴーじーるんが)を利用することができます。
このPGroongaが提供する&@*演算子を用いることで類似した文章を検索することができます。

PGroonga を有効にする

Database > Extensions よりpgroongaの拡張機能を有効化します。

今後はextensionsスキーマでも利用出来るようになるそうです。
https://twitter.com/ktou/status/1648225647951769602?s=20
(PGroonga 作者の須藤さんのツイートより引用)

対象テーブルにインデックスを貼る

SQL Editorよりインデックスを作成します。

ここで使用しているテーブルスキーマ
CREATE TABLE books (
  id integer not null,
  title character varying not null,
  content text not null
);

PGroongaで演算子を利用するには正しい演算子クラスでインデックスを張る必要があります。

演算子クラス

この演算子を使うには次のどれかの演算子クラスを指定する必要があります。

pgroonga_text_full_text_search_ops_v2text型のデフォルト
pgroonga_text_array_full_text_search_ops_v2text[]型のデフォルト
pgroonga_varchar_full_text_search_ops_v2varchar

今回のテーブル設計ではcontentカラムはtext型なのでデフォルトで指定されている演算子クラスで問題なく、以下のようなsqlでインデックスを張ることができます。

CREATE INDEX pgroonga_varchar_content_index ON books 
  USING pgroonga (content)
  With (tokenizer = 'TokenMecab');

ですが、titleカラムはvarcharなので、こちらで指定してインデックスを張る必要があります。

CREATE INDEX pgroonga_varchar_content_index ON books 
  USING pgroonga (title pgroonga_varchar_full_text_search_ops_v2)
  With (tokenizer = 'TokenMecab');

(title pgroonga_varchar_full_text_search_ops_v2)のようにtitleカラムに適切な演算子クラスを指定しています。

ここで、tokenizer = 'TokenMecab'としていますが、日本語の文書を類似文書検索する場合はデフォルトのTokenBigramではなくTokenMecabを使う方がよいためです。

TokenMecabは対象の文書を(ほぼ)単語にトークナイズします。これにより類似文書検索の精度が上がります。

データ取得

サンプルデータ
INSERT INTO books VALUES (1, 'PostgreSQL', 'PostgreSQLはリレーショナル・データベース管理システムです。');
INSERT INTO books VALUES (2, 'Groonga', 'Groongaは日本語対応の高速な全文検索エンジンです。');
INSERT INTO books VALUES (3, 'PGroonga', 'PGroongaはインデックスとしてGroongaを使うためのPostgreSQLの拡張機能です。');
INSERT INTO books VALUES (4, 'groonga', 'groongaコマンドがあります。');
SELECT * FROM books WHERE content &@* 'MroongaはGroongaを使うMySQLの拡張機能です。';

無事類似文書検索ができるようになりました🎉

エラーが出る際は正しくインデックスが貼れているか、拡張機能が有効化されているかなどを確認してみてください。

Jsから取得

おそらく現状supabaseのjsクライアントライブラリから上記のようなクエリを投げることはできません。そのため、ストアドファンクションを定義して、それをjsからRPCで呼び出す必要があります。

以下のようなファンクションを定義し、

CREATE OR REPLACE FUNCTION get_similar_books(text varchar)
RETURNS RECORD AS
$$
DECLARE
    rec books;
BEGIN
    SELECT INTO rec * FROM books WHERE content &@* text;
    RETURN rec;
END;
$$
LANGUAGE 'plpgsql';

jsからは以下の様にrpcで呼び出しましょう

const { data, error } = await createClient<Database>(url, key).rpc(
    'get_similar_books',
    {
      text: 'MroongaはGroongaを使うMySQLの拡張機能です。',
    }
  )

間違いありましたらコメントにてお願いいたしますm(__)m

参考

https://pgroonga.github.io/ja/reference/operators/similar-search-v2.html
https://supabase.com/docs/guides/database/extensions/pgroonga
https://tech-lab.sios.jp/archives/12862
https://zenn.dev/hmatsu47/articles/supabase_pgroonga_flutter
https://zenn.dev/chimame

Discussion