Version 9.12 (MariaDB 10.4.12)
— y2sunlight 2020-11-06
関連記事
リンク
本編では、Mroonga公式サイトのチュートリアルを参考にしてMroongaの全文検索の構成について、トークナイザー(Tokenizer)、ノーマラーザー(Normalizer)、トークンフィルタ―(Token Filter) について説明したいと思います。
全文検索とは、複数の文書から特定の文字列を検索することを意味し、ここでは特に「インデックス(索引)型」の全文検索を指すものとします。インデックス型の全文検索は大きく分けて次の2つの部分から構成されています。
Mroongaにおける文書検索については本編の「検索チュートリアル」を参照して下さい。本章では、文書登録について説明します。全文検索における文書登録はおおよそ次の流れで行われます:
Mroongaでは、これらの構成のほとんどをSQLのcreate文によって行います。以下ではノーマライズ、トーカナイズ、トークンフィルターについて説明します。
ノーマライズ(Normalize)とは、日本語の場合、大文字と小文字、全角と半角、カタカナとひらがななどを統一して正規化する処理を指します。ノーマライズはトーカナイズの前処理として行われます。以下に正規化の例を挙げておきます。
前 | 後 | 正規化 |
---|---|---|
Apple | APPLE | 小文字 => 大文字 |
ブラック | ブラック | 半角 => 全角 |
リンゴ | りんご | カタカナ => ひらがな |
結論から先に言うと、ノーマライザーとしては NormalizerAuto を使用すべきです。以下、チュートリアルに従ってその理由を説明します。
日本語の文字セットは UTF8 を使用することを前提にします。UTF8 の文字コードは次の2種類があります。
utf8
— 文字ごとに最大3バイトを使用しBMP(基本多言語面)のみを含みます。utf8mb4
— 文字ごとに最大4バイトを使用しBMP(基本多言語面)に加え補助文字をサポートしています。
utf8は、utf8mb3
と呼ばれることもあります。utf8mb4の補助文字としては絵文字が有名で、au、docomo、SoftBankなどそれぞれの機種によって絵文字がサポートされています。
UTF8 の代表的な照合順序には、以下のものがあります。
_bin
はbinary
、_ci
はcase insensitive
(大文字・小文字を区別しない) の略として照合順序の名称に使用される接尾語です。
Mroongaは、テーブルの照合順序に応じたノーマライザーを使用します。
照合順序 | ノーマライザー |
---|---|
utf8_bin | 指定無し |
utf8_general_ci | NormalizerMySQLGeneralCI |
utf8mb4_general_ci | |
utf8_unicode_ci | NormalizerMySQLUnicodeCI |
utf8mb4_unicode_ci |
以下の例では、utf8_unicode_ci の照合順序を指定することによって、デフォルトのノーマライザー(NormalizerMySQLUnicodeCI)を使用しています。
例
CREATE TABLE diaries_ci ( id INT PRIMARY KEY AUTO_INCREMENT, content VARCHAR(255), FULLTEXT INDEX (content) ) Engine=Mroonga DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; INSERT INTO diaries_ci (content) VALUES ("びょういんの朝食はブラックコーヒーとパンです。");
どのようにノーマライズされるかは、Groonga の normalize
コマンドで確認できます。
SELECT mroonga_command('normalize NormalizerMySQLUnicodeCI "びょういんの朝食はブラックコーヒーとパンです。"');
結果は以下のようになります:
{"normalized":"ひよういんの朝食はふらつくこーひーとはんてす。","types":[],"checks":[]}
拗音、促音、濁音、半濁音が全て清音に変換されています。従って以下の検索は全てヒットします。
SELECT * FROM diaries_ci WHERE MATCH (content) AGAINST ("+びよういん" IN BOOLEAN MODE); SELECT * FROM diaries_ci WHERE MATCH (content) AGAINST ("+ふらつく" IN BOOLEAN MODE); SELECT * FROM diaries_ci WHERE MATCH (content) AGAINST ("+はん" IN BOOLEAN MODE);
また、utf8_unicode_ci では半角・全角を区別しないので以下の検索もヒットします。
SELECT * FROM diaries_ci WHERE MATCH (content) AGAINST ("+ブラック" IN BOOLEAN MODE);
半角・全角の区別は良いとしても、「病院」と「美容院」、「ふらつく」と「ブラック」など拗音、促音、濁音、半濁音が清音と同じになるのは、言語的に「音」も「意味」も違うので問題が残る結果となっています。
CREATE TABLE 文の中でノーマライザーを指定するには、COMMENT
を使用した以下の構文に従います:
FULLTEXT [INDEX] [インデックス名] (カラム1,...) COMMENT 'normalizer "ノーマライザー名"'
Mrrongaに組み込まれているノーマライザーについては、以下のGroongaのドキュメントを参照して下さい:
以下は、ノーマラーザーとして NormalizerAuto
を使用した例です:
CREATE TABLE diaries_auto ( id INT PRIMARY KEY AUTO_INCREMENT, content VARCHAR(255), FULLTEXT INDEX (content) COMMENT 'normalizer "NormalizerAuto"' ) Engine=Mroonga DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; INSERT INTO diaries_auto (content) VALUES ("びょういんの朝食はブラックコーヒーとパンです。");
どのようにノーマライズされるかは、Groonga の normalize
コマンドで確認できます。
SELECT mroonga_command('normalize NormalizerAuto "びょういんの朝食はブラックコーヒーとパンです。"');
結果は以下のようになります:
{"normalized":"びょういんの朝食はブラックコーヒーとパンです。","types":[],"checks":[]}
拗音、促音、濁音、半濁音はそのまま保存さえれて清音のままです。従って、NormalizerMySQLUnicodeCI
とは異なり以下の検索は全てヒットしません。
SELECT * FROM diaries_auto WHERE MATCH (content) AGAINST ("+びよういん" IN BOOLEAN MODE); SELECT * FROM diaries_auto WHERE MATCH (content) AGAINST ("+ふらつく" IN BOOLEAN MODE); SELECT * FROM diaries_auto WHERE MATCH (content) AGAINST ("+はん" IN BOOLEAN MODE);
但し、NormalizerAuto
は半角・全角を区別しないので以下の検索はヒットします。
SELECT * FROM diaries_auto WHERE MATCH (content) AGAINST ("+ブラック" IN BOOLEAN MODE);
このように、NormalizerAuto
では、半角・全角の区別をしないだけで、拗音、促音、濁音、半濁音と清音は区別されており、NormalizerMySQLUnicodeCI
よりは自然な検索結果になっているのが分かります。
NormalizerAuto
についての詳細は以下の Groonga のドキュメントをご覧ください:
CREATE TABLE 文の中でトークナイザーを指定するには、COMMENT を使用した以下の構文に従います:
FULLTEXT [INDEX] [インデックス名] (カラム1,...) COMMENT 'tokenizer "トークナイザー名"'
トークナイザーは、MySQL(mariaDB) ではパーサと呼ばれています。MySQLでは、パーサの指定に別の構文( WITH PARSER
)がありますが、Mroongaでは、利便性のために上記の構文が採用されています。
トークナイザーに指定できる主なものを以下に示します。全てのリストは、Mroongaのドキュメントを参照して下さい。
トークナイザー | 説明 |
---|---|
none | トークナイズしません。 |
TokenDelimit | 空白区切りでトークナイズします。 |
TokenRegexp | 正規表現検索をサポートするトークナイザーです。 |
TokenUnigram | ユニグラム(1-Gram)でトークナイズします。 |
TokenBigram | バイグラム(2-Gram)でトークナイズします。 |
TokenTrigram | トリグラム(3-Gram)でトークナイズします。 |
TokenMecab | MeCabを用いてトークナイズします。 |
デフォルトのパーサは以下で確認できます:
SHOW VARIABLES LIKE 'mroonga_default_tokenizer';
デフォルトのトークナイザーはビルド時や my.cnf
( Windowsではmy.ini
)で指定できます。このオプションを指定しないときは TokenBigram
になります。my.cnf では以下のようにして指定します。
my.cnf:
[mysqld] mroonga_default_tokenizer=TokenMecab
本編で使用している MariaDB with Mroonga (Windows版バイナリ) ではパーサとして形態素解析エンジンの Mecab がバンドルされていて直ぐに利用することができます。Mecab の本体は以下に配置されています:
{XAMPP-Folder}/mysql/bin/mecab.exe
Mecabの設定ファイル mecabrc の配置場所と内容を以下の示します:
{XAMPP-Folder}/mysql/etc/mecabrc
; Configuration file of MeCab dicdir = $(rcpath)\..\share\mecab\dic\naist-jdic
mecabrc では形態素解析で使用する辞書が設定されています。初期設定の辞書としては、Mroongaにバンドルされている naist-jdic が指定されています。他の辞書に変えるときは、上の dicdir
を変更するだけです。
以下は「検索チュートリアル」で示した例を、Mecabのパーサに替えたものです。
CREATE TABLE diaries_mecab ( id INT PRIMARY KEY AUTO_INCREMENT, content VARCHAR(255), FULLTEXT INDEX (content) COMMENT 'tokenizer "TokenMecab"' ) ENGINE = Mroonga COLLATE utf8_unicode_ci; INSERT INTO diaries_mecab (content) VALUES ("今日の天気は晴れでしょう。"); INSERT INTO diaries_mecab (content) VALUES ("今日の天気は雨でしょう。"); INSERT INTO diaries_mecab (content) VALUES ("明日の東京都の天気は晴れでしょう。"); INSERT INTO diaries_mecab (content) VALUES ("明日の京都の天気は雨でしょう。");
以下の検索を実行します:
SELECT * FROM diaries_mecab WHERE MATCH(content) AGAINST('明日 京都 天気');
結果は以下のように出力されます:
明日の京都の天気は雨でしょう。
検索チュートリアルの例では、トークナイザーがバイグラム (TokenBigram) だったので、「明日の東京都の天気は晴れでしょう。」にもヒットしてしまいましたが、Mecabの場合は形態素解析により「東京都」と「京都」を区別するのでこのような現象は起こりません。
「明日の東京都の天気は晴れでしょう。」がバイグラムとMecabとでどのようにトークン化をされるかを確認するには、以下のようにSELECT文からGroongaコマンドを使います。
バイグラムの場合:
SELECT mroonga_command('tokenize TokenBigram "明日の東京都の天気は晴れでしょう。"');
[ {"value":"明日","position":0,"force_prefix":false,"force_prefix_search":false}, {"value":"日の","position":1,"force_prefix":false,"force_prefix_search":false}, {"value":"の東","position":2,"force_prefix":false,"force_prefix_search":false}, {"value":"東京","position":3,"force_prefix":false,"force_prefix_search":false}, {"value":"京都","position":4,"force_prefix":false,"force_prefix_search":false}, {"value":"都の","position":5,"force_prefix":false,"force_prefix_search":false}, {"value":"の天","position":6,"force_prefix":false,"force_prefix_search":false}, {"value":"天気","position":7,"force_prefix":false,"force_prefix_search":false}, {"value":"気は","position":8,"force_prefix":false,"force_prefix_search":false}, {"value":"は晴","position":9,"force_prefix":false,"force_prefix_search":false}, {"value":"晴れ","position":10,"force_prefix":false,"force_prefix_search":false}, {"value":"れで","position":11,"force_prefix":false,"force_prefix_search":false}, {"value":"でし","position":12,"force_prefix":false,"force_prefix_search":false}, {"value":"しょ","position":13,"force_prefix":false,"force_prefix_search":false}, {"value":"ょう","position":14,"force_prefix":false,"force_prefix_search":false}, {"value":"う。","position":15,"force_prefix":false,"force_prefix_search":false}, {"value":"。","position":16,"force_prefix":false,"force_prefix_search":false} ]
Mecabの場合:
SELECT mroonga_command('tokenize TokenMecab "明日の東京都の天気は晴れでしょう。"');
[ {"value":"明日","position":0,"force_prefix":false,"force_prefix_search":false}, {"value":"の","position":1,"force_prefix":false,"force_prefix_search":false}, {"value":"東京","position":2,"force_prefix":false,"force_prefix_search":false}, {"value":"都","position":3,"force_prefix":false,"force_prefix_search":false}, {"value":"の","position":4,"force_prefix":false,"force_prefix_search":false}, {"value":"天気","position":5,"force_prefix":false,"force_prefix_search":false}, {"value":"は","position":6,"force_prefix":false,"force_prefix_search":false}, {"value":"晴れ","position":7,"force_prefix":false,"force_prefix_search":false}, {"value":"でしょ","position":8,"force_prefix":false,"force_prefix_search":false}, {"value":"う","position":9,"force_prefix":false,"force_prefix_search":false}, {"value":"。","position":10,"force_prefix":false,"force_prefix_search":false}] ]
Mroongaではトークン化の後に、トークンに所定の処理を行うトークンフィルターを指定することができます。トークンフィルターを CREATE TABLE 文の中で指定するには、COMMENT
を使用した以下の構文を使用します:
FULLTEXT [INDEX] [インデックス名] (カラム1,...) COMMENT 'token_filters "フィルター名"'
以下の3つの組込みフィルターを使用できます。詳しくはGroongaのドキュメントを参照して下さい。