PostgreSQLで高速に全文検索するための拡張PGroongaの開発をしている堀本です。
突然ですが、日本語には踊り字というものがあります。 久々の"々"とか、こゝろの"ゝ"とか、前の文字を繰り返す記号のことです。 現代の文書でも見かけますが、古い文書でも多用されています。
「久々」は、「久々」と検索しそうですが、「こゝろ」は「こころ」で検索したくなります。 このような表記ゆれを統一して検索したいというのは、よくある問題です。
最近よく聞くセマンティックサーチもこの問題を解決するための一つの手段ですが、正規化するというのも一つの解決手段です。
少し前にGroonga(PGroongaのバックエンドで動いている全文検索エンジン)に踊り字を正規化する機能を追加したので、PGroongaでそれを使う方法を紹介します。
前提知識
最初に正規化の説明をします。 正規化とは、ある一定のルールで文字列を変換することです。 今回の例だと、「こゝろ」のような踊り字が混じった文字列を「こころ」という踊り字なしの文字列に変換することを正規化といいます。
次に、踊り字の説明をします。 踊り字にはいくつかのパターンがあります。
- 「久々(久久)」、「こゝろ(こころ)」、「ぶゞ漬け(ぶぶ漬け)」などの、直前の一文字を繰り返すパターン。
- 部分々々(部分部分)のように直前の複数文字を繰り返すパターン。
- 古々々米(古古古米)のように直前の一文字を複数回繰り返すパターン。
今のPGroonga(Groonga)は1のみ対応していて、2,3は対応していません。
具体的には以下の踊り字に対応しています。
- 々 (U+3005)
- ゝ (U+309D)
- ヽ (U+30FD)
- ゞ (U+309E)
- ヾ (U+30FE)
- 〻 (U+303B)
したがって、例えば「こゝろ」を「こころ」でも「こゝろ」でも検索できますし、「バナヽ」を「バナナ」でも「バナヽ」でも検索できます。 古い文書のような踊り字がよく出てくる文書に対する検索で便利な機能です。
ただし、前述した通り2,3に該当する以下の2点は未対応です。
- 「〱 (U+3031)」と「〲 (U+3032)」(くの字点)
- 々と〻で「直前1文字の繰り返し」以外のパターン。(例えば、"部分々々" -> "部分部分"には対応していません。)
使い方
では、どのように使うかを見てみましょう。
以下のように使います。
ポイントはCREATE INDEXのNormalizerNFKCで指定しているunify_iteration_markをtrueにしているところです。
このオプションを有効にすると踊り字を正規化します。結果、以下の例のように「こころ」で「こゝろ」もヒットします。
もちろん「こころ」で「こころ」もヒットします。
CREATE EXTENSION IF NOT EXISTS pgroonga;
CREATE TABLE books (
title text,
author text
);
CREATE INDEX pgrn_title_index
ON books
USING pgroonga (title)
WITH (normalizers='NormalizerNFKC("unify_iteration_mark", true)');
INSERT INTO books VALUES ('こゝろ 上', '夏目漱石');
INSERT INTO books VALUES ('こころ 下', '夏目漱石');
INSERT INTO books VALUES ('学問のすゝめ', '福沢諭吉');
INSERT INTO books VALUES ('あゝ、荒野', '寺山修司');
SELECT *
FROM books
WHERE title &@~ 'こころ';
-- title | author
-- -----------+----------
-- こゝろ 上 | 夏目漱石
-- こころ 下 | 夏目漱石
-- (2 rows)
念の為、PGroongaのインデックスが使われているかも確認しておきましょう。 以下のように確認します。
EXPLAIN ANALYZE VERBOSE
SELECT *
FROM books
WHERE title &@~ 'こころ';
-- QUERY PLAN
-- ------------------------------------------------------------------------------------------------------------------------------------
-- Index Scan using pgrn_title_index on public.books (cost=0.00..46.60 rows=1 width=64) (actual time=0.919..0.923 rows=2.00 loops=1)
-- Output: title, author
-- Index Cond: (books.title &@~ 'こころ'::text)
-- Index Searches: 0
-- Buffers: shared hit=1
-- Planning:
-- Buffers: shared hit=35
-- Planning Time: 1.069 ms
-- Execution Time: 1.182 ms
-- (9 rows)
"Index Scan using pgrn_title_index on public.books"と表示されているので、PGroongaのインデックスが使用されていることも確認できました。
まとめ
踊り字の有無を無視して検索する方法を紹介しました。是非、使ってみてください。
お使いのPGroongaで困ったことがあれば、クリアコードはサポートサービスを提供しているので、そちらを使うことをご検討ください。
また、毎月のリリース内容を自慢するリリース自慢会もYouTubeで配信しているので、そちらも見てください。