title | emoji | type | topics | published | |||
---|---|---|---|---|---|---|---|
Supabase の PGroonga 全文検索で同義語検索してみる |
📖 |
tech |
|
true |
こちらの記事の続きです。
https://zenn.dev/hmatsu47/articles/supabase_pgroonga_flutter
PGroonga による全文検索で同義語検索できるようにしてみます。
:::message Flutter アプリケーション側に変更はありません。 :::
PGroonga のドキュメントを参考に実装していきます。
https://pgroonga.github.io/ja/how-to/synonym-expansion.html
:::message
これを書いている 2023/1/28 現在、当該ページに Typo があるようです(Pull Request を送っておきました) 修正されました。
pgroonga/pgroonga.github.io#97
:::
まずは同義語テーブルを作成します。
CREATE TABLE synonyms (
term text PRIMARY KEY,
synonyms text[]
);
そして、そのテーブルにインデックスを作成します。
CREATE INDEX synonyms_search ON synonyms USING pgroonga (term pgroonga_text_term_search_ops_v2);
テーブルとインデックスの作成ができたら、同義語をテーブルに追加します。
INSERT INTO synonyms (term, synonyms) VALUES ('美術館', ARRAY['美術館', 'ミュージアム']);
INSERT INTO synonyms (term, synonyms) VALUES ('博物館', ARRAY['博物館', 'ミュージアム']);
INSERT INTO synonyms (term, synonyms) VALUES ('ミュージアム', ARRAY['ミュージアム', '美術館', '博物館']);
- 「美術館」で検索したときに「ミュージアム」を含む文章も対象とする
- 「博物館」で検索したときに「ミュージアム」を含む文章も対象とする
- 「ミュージアム」で検索したときに「美術館」「博物館」を含む文章も対象とする
という指定です。
:::message このアプリケーションでは、検索対象の「文章」はスポットの「タイトル」「説明文」「都道府県・市区町村」で構成されています。 :::
:::message
pgroonga_query_expand
関数のドキュメントの「使い方」に記されているとおり、同義語グループを使う方法もあります。
先の例では「美術館」と「博物館」を同義語として扱わないので単語を個別に指定していますが、すべての単語を相互に同義語として扱う場合は同義語グループのほうが便利です。
:::
前回の記事で、WHERE
句の条件に
ft_text &@~ keywords
と指定したところを、
ft_text &@~ pgroonga_query_expand('synonyms', 'term', 'synonyms', keywords)
に書き換えます。
https://pgroonga.github.io/ja/reference/functions/pgroonga-query-expand.html
:::message
同義語がOR
条件で列挙(展開)されるイメージです。
:::
CREATE OR REPLACE
FUNCTION get_spots(point_latitude double precision, point_longitude double precision, dist_limit int, category_id_number int, keywords text)
RETURNS TABLE (
distance double precision,
category_name text,
title text,
describe text,
latitude double precision,
longitude double precision,
prefecture text,
municipality text
) AS $$
BEGIN
RETURN QUERY
SELECT ((ST_POINT(point_longitude, point_latitude)::geography <-> spot_opendata.location::geography) / 1000) AS distance,
category.category_name,
spot_opendata.title,
spot_opendata.describe,
ST_Y(spot_opendata.location),
ST_X(spot_opendata.location),
spot_opendata.prefecture,
spot_opendata.municipality
FROM spot_opendata
INNER JOIN category ON spot_opendata.category_id = category.id
WHERE
(CASE WHEN dist_limit = -1 AND keywords = '' THEN false ELSE true END)
AND
(CASE WHEN dist_limit = -1 THEN true
ELSE (ST_POINT(point_longitude, point_latitude)::geography <-> spot_opendata.location::geography) <= dist_limit END)
AND
(CASE WHEN category_id_number = -1 THEN true
ELSE category.id = category_id_number END)
AND
(CASE WHEN keywords = '' THEN true
ELSE ft_text &@~ pgroonga_query_expand('synonyms', 'term', 'synonyms', keywords) END)
ORDER BY distance;
END;
$$ LANGUAGE plpgsql;
以上で全文検索が同義語対応になりました。
SQL Editor で「ミュージアム」を検索してみると、
「美術館」「博物館」もヒットしていることがわかります。
:::message
- サンプルデータ
- このサンプルデータは、以下の著作物を改変して利用しています。
- 愛知県文化財マップ(ナビ愛知)、愛知県、クリエイティブ・コモンズ・ライセンス 表示2.1日本
- https://www.pref.aichi.jp/soshiki/joho/0000069385.html
- このサンプルデータは、以下の著作物を改変して利用しています。
:::
:::message 前述のとおり、Flutter アプリケーション側は変更不要です。 :::
2023/2/1 追記:
続きの記事を書きました。
https://zenn.dev/hmatsu47/articles/supabase_pgroonga_stopword_wa