 
形態素解析を使用した、組み込み型の日本語全文検索です。
日本語テキストの全文検索を行います。 PostgreSQL 8.3 で追加された組み込みテキスト検索を拡張するため、 英語文書の検索と同様の方法で、日本語文書を検索することができます。 検索は形態素解析を利用した単語単位で行われます。 形態素解析には MeCab を使用しています。
利点として、GIN または GiST インデックスをベースにしているため、全文検索用のインデックスがリカバリ可能であることが挙げられます。 また、既に tsearch2 に対応している欧米言語を対象とした製品を大きく改変することなく、日本語対応できる強みがあります。
非自立語を除外してキーワードを抽出するため、インデックスのサイズを小さく押さえられます。 以下の例では「すもも」と「もも」のみ単語として扱っています。 単語ごとではなく文字ごとの検索が必要な場合には、代わりに textsearch_senna を使用してください。
=# SELECT to_tsvector('japanese', 'すもももももももものうち');
      to_tsvector
-----------------------
 'すもも':1 'もも':2,3
(1 row)Windows 版のインストーラを使う場合、辞書の文字コードの選択 で UTF-8 を選んでください。 mecab-dict-index で辞書を再編成することもできます。 詳しくは MeCab のマニュアルを参照してください。
インストールすると、pg_catalog.japanese という名前のテキスト検索設定が登録されます。 以下の形式で使用します。
入力テキストは、まず、後述の ja_normalize() 関数を使用して正規化されます。 正規化後の文字列に対して、形態素解析で単語を分割してインデックスを張ります。
サーバエンコーディングと、mecab のエンコーディングを一致させる必要があります。 プラットホームによっては、mecab 辞書の再コンパイルが必要になるかもしれません。
textsearch では、関数インデックスを使用して直接検索する方式と、 追加した補助カラムをキーとして検索する方式があります。 詳しくはドキュメント Full Text Search/Creating Indexes を参照してください。 補助カラムの自動更新は、tsvector_update_trigger に任せることもできます。
=# CREATE TABLE test (id serial, t text);
=# COPY test(t) FROM '...';
=# CREATE INDEX idx ON test USING gin(to_tsvector('japanese', t));
=# ANALYZE;
=# EXPLAIN SELECT * FROM test WHERE to_tsvector('japanese', t) @@ to_tsquery('japanese', 'リレーショナルデータベース');
                                                      QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------
 Index Scan using idx on test  (cost=0.00..8.27 rows=1 width=248)
   Index Cond: (to_tsvector('japanese'::regconfig, t) @@ '''リレーショナル'' & ''データベース'''::tsquery)
(2 rows)ts_headline() に対応しており、一致した箇所を切り出して強調表示できます。
=# \d document
                         Table "public.document"
 Column |   Type   |                      Modifiers
--------+----------+-------------------------------------------------------
 id     | integer  | not null default nextval('document_id_seq'::regclass)
 body   | text     |
 tsv    | tsvector |
Indexes:
    "document_pkey" PRIMARY KEY, btree (id)
    "document_tsv_index" gin (tsv)
Triggers:
    tsv_trigger BEFORE INSERT OR UPDATE ON document FOR EACH ROW EXECUTE PROCEDURE
      tsvector_update_trigger('tsv', 'pg_catalog.japanese', 'body')
=# SELECT id, ts_rank_cd(tsv, query) AS rank, ts_headline('japanese', body, query)
     FROM document, to_tsquery('japanese', 'リーフページ') AS query
    WHERE tsv @@ query ORDER BY rank DESC;
 id |   rank   |                                                                 ts_headline
----+----------+----------------------------------------------------------------------------------------------------------------------------------------------
  3 | 0.108333 | <b>ページ</b>中で隣接しているキーである。これは非ユニークキーに対しては動作しない ( たとえば、いくつかの<b>リーフ</b><b>ページ</b>にまたがっ
  2 |     0.01 | <b>ページ</b>を指しているはずである。同じ方法で、つまりヒープタプルへのリンクを調査することで、<b>リーフ</b>レベルにある複数の item
(2 rows)=# SELECT * FROM ts_debug('japanese', '日本語とEnglishがmixedな文も解析OKです。');
   alias   |    description    |  token  |  dictionaries  |  dictionary  |  lexemes
-----------+-------------------+---------+----------------+--------------+-----------
 word      | Word, all letters | 日本語  | {simple}       | simple       | {日本語}
 blank     | Space symbols     | と      | {}             |              |
 asciiword | Word, all ASCII   | English | {english_stem} | english_stem | {english}
 blank     | Space symbols     | が      | {}             |              |
 asciiword | Word, all ASCII   | mixed   | {english_stem} | english_stem | {mix}
 blank     | Space symbols     | な      | {}             |              |
 word      | Word, all letters | 文      | {simple}       | simple       | {文}
 blank     | Space symbols     | も      | {}             |              |
 word      | Word, all letters | 解析    | {simple}       | simple       | {解析}
 asciiword | Word, all ASCII   | OK      | {english_stem} | english_stem | {ok}
 blank     | Space symbols     | です    | {}             |              |
 blank     | Space symbols     | 。      | {}             |              |
(12 rows)
類義語辞書を使用できます。
<インストール先>/share/tsearch_data/japanese.syn というテキストファイルを用意します。
文字エンコーディングは UTF-8 でなければなりません。
バキューム vacuum ポスグレ pgsql類義語辞書は、以下のSQLを使って登録します。
-- Install japanese synonym dictionary
CREATE TEXT SEARCH DICTIONARY japanese_syn (
    TEMPLATE = synonym,
    SYNONYMS = japanese
);
ALTER TEXT SEARCH CONFIGURATION japanese
    ALTER MAPPING FOR word, hword_part, hword WITH japanese_syn, simple;類義語は変換して処理されるため、表記に揺れがあっても検索や強調ができます。
=# SELECT ts_headline('japanese', 'autovacuumを使えばバキュームも手間要らず',  to_tsquery('japanese', 'VACUUM'));
                   ts_headline
-------------------------------------------------
 autovacuumを使えば<b>バキューム</b>も手間要らず
(1 row)
同様に、$PGDATA/share/tsearch_data/english.syn を用意すれば、
英語の類義語辞書も同時に使用できます。
このとき、英語と日本語で必ずしも *.syn を分ける必要はありませんが、
英数字(半角)には必ず英語辞書が使われ、
日本語文字(全角)には必ず日本語辞書が使われるため、
分割したほうが若干変換速度が向上します。
-- Install english synonym dictionary
CREATE TEXT SEARCH DICTIONARY english_syn (
    TEMPLATE = synonym,
    SYNONYMS = english
);
ALTER TEXT SEARCH CONFIGURATION japanese
    ALTER MAPPING FOR asciiword, hword_asciipart, asciihword WITH english_syn, english_stem;現在、以下の品詞はストップワード(blank)とみなし、インデックス化していません。 不必要なインデックス・エントリを減らせるため、単純なスペース区切りによる分かち書きよりも効率が高まります。
実際には、類義語辞書と同じ方式で、textsearch 機能のストップワード辞書機能を使うことも可能です。 ただし、既に MeCab により単語区分が分かっているため、辞書よりも先に判定を行っています。 将来のバージョンでは、動作を選択可能にすることを考えています。
textsearch は AND, OR, NOT 検索に &, |, ! を使用しています。 ウェブ検索で一般的に使用される制御演算子とは異なるため、検索フォームへの入力をそのまま条件に使用するには不便です。 関数 web_query() は、AND, OR, NOT 演算子として、スペース, OR (大文字), - を使用します。
=# SELECT to_tsquery('english', 'Relational Database');
ERROR:  syntax error in tsquery: "Relational Database"
=# SELECT to_tsquery('english', web_query('Relational Database'));
     to_tsquery
---------------------
 'relat' & 'databas'
(1 row)注意: 現在、OR 演算子よりも AND 演算子のほうが結合順序が高いままです。 一般的なウェブ検索では、OR 演算子のほうが高いものが多いようです。
日本語の文字列を扱う関数と、MeCab を直接利用する関数が登録されます。
=# SELECT ja_normalize('カタカナは全角に、ABC123は半角に。ダクテンにも対応。');
                     ja_normalize
------------------------------------------------------
 カタカナは全角に、ABC123は半角に。ダクテンにも対応。
(1 row)=# SELECT ja_wakachi('分かち書きも使用できます。スペースで区切られます。');
                          ja_wakachi
---------------------------------------------------------------
 分かち書き も 使用 でき ます 。 スペース で 区切ら れ ます 。
(1 row)=# SELECT * FROM ja_analyze('mecabを利用して形態素解析をします。');
  word  |  type  | subtype1 | subtype2 | subtype3 |  conjtype  | conjugation | basic  |    ruby    | pronounce
--------+--------+----------+----------+----------+------------+-------------+--------+------------+------------
 mecab  | 名詞   | 一般     |          |          |            |             | mecab  | mecab      | mecab
 を     | 助詞   | 格助詞   | 一般     |          |            |             | を     | ヲ         | ヲ
 利用   | 名詞   | サ変接続 |          |          |            |             | 利用   | リヨウ     | リヨー
 し     | 動詞   | 自立     |          |          | サ変・スル | 連用形      | する   | シ         | シ
 て     | 助詞   | 接続助詞 |          |          |            |             | て     | テ         | テ
 形態素 | 名詞   | 一般     |          |          |            |             | 形態素 | ケイタイソ | ケイタイソ
 解析   | 名詞   | サ変接続 |          |          |            |             | 解析   | カイセキ   | カイセキ
 を     | 助詞   | 格助詞   | 一般     |          |            |             | を     | ヲ         | ヲ
 し     | 動詞   | 自立     |          |          | サ変・スル | 連用形      | する   | シ         | シ
 ます   | 助動詞 |          |          |          | 特殊・マス | 基本形      | ます   | マス       | マス
 。     | 記号   | 句点     |          |          |            |             | 。     | 。         | 。
(11 rows)MeCab を利用し、ふりがな(発音)をカタカナで取得します。
=# SELECT furigana('ふりがな(発音)を取得します。');
                furigana
----------------------------------------
 フリガナ(ハツオン)ヲシュトクシマス。
(1 row)半角/全角カタカナをひらがなへ変換します。
=# SELECT hiragana('ダクテンとゼンカクをひらがなに変換');
              hiragana
------------------------------------
 だくてんとぜんかくをひらがなに変換
(1 row)ひらがなを全角カタカナへ変換します。
=# SELECT katakana('ひらがなを全角カタカナに変換');
           katakana
------------------------------
 ヒラガナヲ全角カタカナニ変換
(1 row)