これはPGroonga 2.X and 3.X用のドキュメントです。古いPGroongaを使っているならPGroonga 1.xのドキュメントを見てください。
pgroonga_query_expand
関数1.2.2で追加。
pgroonga_query_expand
関数はクエリー構文を使ったクエリー内にある登録済みの同義語を展開します。クエリー構文は&@~
演算子や&@~|
演算子で使われています。
pgroonga_query_expand
関数はクエリー展開機能を実現するときに便利です。Groongaのクエリー展開機能のドキュメントも参照してください。
この関数の構文は次の通りです。
text pgroonga_query_expand(table_name,
term_column_name,
synonyms_column_name,
query)
table_name
はtext
型の値です。同義語を格納している既存のテーブルの名前を指定します。
term_column_name
はtext
型の値です。table_name
テーブル内の展開対象の単語を格納しているカラムの名前を指定します。このカラムはtext
型かtext[]
型のカラムです。
synonyms_column_name
はtext
型の値です。term
カラムの同義語を格納しているカラム名を指定します。このカラムはtext[]
型のカラムです。
query
はtext
型の値です。クエリー構文を使っているクエリーです。
pgroonga_query_expand
はtext
型の値を返します。query
中にある登録済みの同義語がすべて展開されています。
次のようにpgroonga_text_term_search_ops_v2
演算子クラス指定のPGroongaで${table_name}.${term_column_name}
のインデックスを作成することをオススメします。これは高速にクエリー展開できるようにするためです。
CREATE TABLE synonyms (
term text,
synonyms text[]
);
CREATE INDEX synonyms_term
ON synonyms
USING pgroonga (term pgroonga_text_term_search_ops_v2);
pgroonga_query_escape
関数はインデックスなしでも動きますが、インデックスがあるとより高速に動きます。
btree
のようにtext
型の=
に対応しているインデックスアクセスメソッドであればどのインデックスアクセスメソッドでも使えます。しかし、PGroongaを使うことをオススメします。なぜなら、PGroongaはtext
の値を正規化した=
(大文字小文字を無視した比較を含む)に対応しているからです。クエリー展開時は値を正規化した=
が便利です。
次のスタイルを使えます。
1単語を複数の同義語にマッピング
同義語グループ
サンプルスキーマとデータは次の通りです。
CREATE TABLE synonyms (
term text,
synonyms text[]
);
CREATE INDEX synonyms_term
ON synonyms
USING pgroonga (term pgroonga_text_term_search_ops_v2);
INSERT INTO synonyms VALUES ('PGroonga', ARRAY['PGroonga', 'Groonga PostgreSQL']);
このサンプルではPGroongaインデックスを使っているのでクエリー中の"PGroonga"
も"pgroonga"
もすべて展開されます。
SELECT pgroonga_query_expand('synonyms', 'term', 'synonyms',
'PGroonga OR Mroonga') AS query_expand;
-- query_expand
-- -------------------------------------------------
-- ((PGroonga) OR (Groonga PostgreSQL)) OR Mroonga
-- (1 row)
SELECT pgroonga_query_expand('synonyms', 'term', 'synonyms',
'pgroonga OR mroonga') AS query_expand;
-- query_expand
-- -------------------------------------------------
-- ((PGroonga) OR (Groonga PostgreSQL)) OR mroonga
-- (1 row)
2.2.1で追加。
サンプルスキーマとデータは次の通りです。
CREATE TABLE synonym_groups (
synonyms text[]
);
CREATE INDEX synonym_groups_synonyms
ON synonym_groups
USING pgroonga (synonyms pgroonga_text_array_term_search_ops_v2);
INSERT INTO synonym_groups
VALUES (ARRAY['PGroonga', 'Groonga']);
このサンプルではPGroongaインデックスを使っているのでクエリー中の"PGroonga"
も"pgroonga"
もすべて展開されます。
SELECT pgroonga_query_expand('synonym_groups', 'synonyms', 'synonyms',
'PGroonga OR Mroonga') AS query_expand;
-- query_expand
-- --------------------------------------
-- ((PGroonga) OR (Groonga)) OR Mroonga
-- (1 row)
SELECT pgroonga_query_expand('synonym_groups', 'synonyms', 'synonyms',
'pgroonga OR mroonga') AS query_expand;
-- query_expand
-- --------------------------------------
-- ((PGroonga) OR (Groonga)) OR mroonga
-- (1 row)
人名テーブルの中から同じ読み方で漢字の異なる人を探すサンプルです。(例:斉藤, 齊藤, 斎藤, 齋藤)
サンプルとなるスキーマとデータは次の通りです。
CREATE TABLE names (
name varchar(255)
);
CREATE INDEX pgroonga_names_index
ON names
USING pgroonga (name pgroonga_varchar_full_text_search_ops_v2);
INSERT INTO names
(name)
VALUES ('斉藤'),('齊藤'),('斎藤'),('鈴木'),('田中'),('佐藤');
CREATE TABLE synonym_groups (
synonyms text[]
);
CREATE INDEX synonym_groups_synonyms
ON synonym_groups
USING pgroonga (synonyms pgroonga_text_array_term_search_ops_v2);
INSERT INTO synonym_groups
VALUES (ARRAY['斉藤', '齊藤', '斎藤', '齋藤']);
このサンプルでは「斉藤」で検索することでnamesテーブル内データの"斉藤"
も"齊藤"
も"斎藤"
もすべて以下のように展開されます。
SELECT pgroonga_query_expand('synonym_groups', 'synonyms', 'synonyms',
'斉藤') AS query_expand;
-- query_expand
-- --------------------------------------
-- ((斉藤) OR (齊藤) OR (斎藤) OR (齋藤))
-- (1 row)
この例では、人名テーブルの検索をするので以下のように検索します。
注意: 下記の例では、検索対象のカラムがvarchar
型なので、pgroonga_query_expand(...)::varchar
としてpgroonga_query_expand
の結果を明示的にキャストする必要があります。(pgroonga_query_expand()
の戻り値の型はtext
型なので、検索対象のカラムがtext
型の場合はキャストは不要です。)
このようにキャストしないと、検索対象のカラムとpgroonga_query_expand()
の型が異なりシーケンシャルサーチになるため、パフォーマンスが出ません。
SELECT name AS synonym_names from names where name &@~ pgroonga_query_expand(
'synonym_groups', 'synonyms', 'synonyms','斉藤')::varchar;
-- synonym_names
-- -----------------
-- 斉藤
-- 齊藤
-- 斎藤
--(3 rows)
-- varcharでのキャストが無い時のEXPLAIN ANALYZE結果(シーケンシャルサーチが使われる):
EXPLAIN ANALYZE VERBOSE SELECT name AS synonym_names from names where name &@~ pgroonga_query_expand(
'synonym_groups', 'synonyms', 'synonyms','斉藤');
-- QUERY PLAN
-- ---------------------------------------------------------------------------------------------------------------------------------------
-- Seq Scan on public.names (cost=0.00..124.38 rows=1 width=516) (actual time=66.684..67.619 rows=3 loops=1)
-- Output: name
-- Filter: ((names.name)::text &@~ pgroonga_query_expand('synonym_groups'::cstring, 'synonyms'::text, 'synonyms'::text, '斉藤'::text))
-- Rows Removed by Filter: 3
-- Planning Time: 0.420 ms
-- Execution Time: 67.750 ms
-- (6 rows)
-- varcharでキャスト時のEXPLAIN ANALYZE結果(インデックス利用に注目):
EXPLAIN ANALYZE VERBOSE SELECT name AS synonym_names from names where name &@~ pgroonga_query_expand(
'synonym_groups', 'synonyms', 'synonyms','斉藤')::varchar;
-- QUERY PLAN
-- --------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Bitmap Heap Scan on public.names (cost=0.00..29.06 rows=1 width=516) (actual time=2.212..2.214 rows=3 loops=1)
-- Output: name
-- Recheck Cond: (names.name &@~ (pgroonga_query_expand('synonym_groups'::cstring, 'synonyms'::text, 'synonyms'::text, '斉藤'::text))::character varying)
-- Heap Blocks: exact=1
-- -> Bitmap Index Scan on pgroonga_names_index (cost=0.00..0.00 rows=25 width=0) (actual time=2.197..2.198 rows=3 loops=1)
-- Index Cond: (names.name &@~ (pgroonga_query_expand('synonym_groups'::cstring, 'synonyms'::text, 'synonyms'::text, '斉藤'::text))::character varying)
-- Planning Time: 3.855 ms
-- Execution Time: 2.447 ms
-- (8 rows)