groonga/mroongaの文字列正規化機能 (normalizer) の挙動を追ってみる
groonga/mroongaが標準で備える文字列正規化機能(ノーマライザ・normalizer)として、
いわゆるgroonga独自のnormalizer(NormalizerAuto)と呼ばれるもがあります。
これは、大文字・小文字だけでなく、全角・半角を同一視できるという
UnicodeのNFKCを用いた機能です。
とても便利な正規化機能なのですが、この文字列の正規化方法は
MySQLで利用されているCOLLATION(照合順序)とは異なる動作です。
利用シーンによっては、MySQLでの作法に合わせた方が都合が良い場合があります。
そこで、groonga-normalizer-mysql
というパッケージの出番です。
本記事では、それら正規化機能の挙動の違いを追いかけてみたいと思います。
mroongaで使えるnormalizer
いまのところ4つのnormalizerが提供されています。
なお、動作サンプルに関しては後半に記述しております。
NormalizerAuto
大文字・小文字や、全角・半角の区別なく検索する事が出来ます。
特殊文字への対応が厚く、例えば「㌖」は「キロメートル」に正規化されます。
NormalizerMySQLGeneralCI
MySQLでいうutf8mb4_general_ci
を模したノーマライザーです。
NormalizerMySQLUnicodeCI
MySQLでいうutf8mb4_unicode_ci
を模したノーマライザーです。
NormalizerMySQLUnicodeCIExceptKanaCIKanaWithVoicedSoundMark
濁音・半濁音・「ぁ」「ぃ」「ぅ」「ぇ」「ぉ」「っ」などは区別しつつ、
カタカナとひらがなは同一視することができるノーマライザーです。
「ブラック」と「ふらつく」、「バルス」と「パルス」を区別できます。
normalizerの指定方法
以下のように、FULLTEXT INDEXのCOMMENTに指定します。
CREATE TABLE test ( id int(11) NOT NULL AUTO_INCREMENT, content varchar(255) NOT NULL, PRIMARY KEY (id), FULLTEXT INDEX (content) COMMENT 'normalizer "NormalizerMySQLUnicodeCIExceptKanaCIKanaWithVoicedSoundMark"' ) ENGINE=mroonga DEFAULT CHARSET=utf8;
parserとの併用時の記述方法などは、以下の記事をご参照ください。
http://y-ken.hatenablog.com/entry/mroonga-use-parser-and-normalizer-together
無指定の際に利用するノーマライザ
groonga-normalizer-mysql
をインストールしている環境では、
テーブルの照合順序(collation)に応じたノーマライザが自動選択されます。
NormalizerMySQLGeneralCI
utf8mb4_general_ci
またはutf8_general_ci
のテーブルで自動選択されるノーマライザ
NormalizerMySQLUnicodeCI
utf8mb4_unicode_ci
またはutf8_unicode_ci
のテーブルで自動選択されるノーマライザ
NormalizerAuto
その他の文字コードのテーブルで自動選択されるノーマライザ
groonga-normalizer-mysql
がインストールされていない環境では、NormalizerAutoがフォールバック先として暗黙的に利用されます。
動作サンプルに利用するコマンド
mysql-mroonga-3.02
より対応となったUDF、mroonga_command
を用います。
これを用いてgroongaに直接クエリを投げ、どのように正規化されるか追ってみましょう。
MySQLクエリから、UDFを呼び出す場合
SELECT mroonga_command('normalize 正規化機能名 文字列');
※ mysql-mroonga-3.03の場合には、必ずどこかしらのデータベースをuse foo;
といったクエリで選択してから叩いてください。3.04以降ではデータベース選択は不要です。
groongaコマンドを使う場合
参考情報までに、直接groongaコマンドを利用する場合の手順も併記します。
$ groonga > register normalizers/mysql > normalize 正規化機能名 文字列
動作サンプル
「ブラックふらつくバルスパルスaABCaABC」という文字列を渡し、どのように正規化されるかの動作サンプルを以下に載せました。
NormalizerAuto
mysql> select mroonga_command('normalize NormalizerAuto ブラックふらつくバルスパルスaABCaABC') as result\G *************************** 1. row *************************** result: {"normalized":"ブラックふらつくバルスパルスaabcaabc","types":[]} 1 row in set (0.01 sec)
NormalizerMySQLGeneralCI
mysql> select mroonga_command('normalize NormalizerMySQLGeneralCI ブラックふらつくバルスパルスaABCaABC') as result\G *************************** 1. row *************************** result: {"normalized":"ブラックふらつくバルスパルスAABCAABC","types":[]} 1 row in set (0.00 sec)
NormalizerMySQLUnicodeCI
mysql> select mroonga_command('normalize NormalizerMySQLUnicodeCI ブラックふらつくバルスパルスaABCaABC') as result\G *************************** 1. row *************************** result: {"normalized":"ふらつくふらつくはるすはるすAABCAABC","types":[]} 1 row in set (0.01 sec)
NormalizerMySQLUnicodeCIExceptKanaCIKanaWithVoicedSoundMark
mysql> select mroonga_command('normalize NormalizerMySQLUnicodeCIExceptKanaCIKanaWithVoicedSoundMark ブラックふらつくバルスパルスaABCaABC') as result\G *************************** 1. row *************************** result: {"normalized":"ぶらっくふらつくばるすぱるすAABCAABC","types":[]} 1 row in set (0.00 sec)
まとめ
mroongaに使うnormalizerの選択に迷ったら、こうしましょう。便利です!
「半角・全角」「ひらがな・カタカナ」「英字の大文字・小文字」を同一視したい場合
NormalizerAuto
「半角・全角」「ひらがな・カタカナ」を同一視したい場合
NormalizerMySQLUnicodeCIExceptKanaCIKanaWithVoicedSoundMark
補足資料(用語説明)
NormalizerAuto(groonga独自のnormalizer)とは
以下の処理が行われる正規化機能です。
UTF-8でエンコードされたテキストにはUnicodeのNFKC(Normalization Form Compatibility Composition)を使います。
他のエンコーディング用にはエンコーディング毎に独自の正規化をします。
これらの独自の正規化の結果はNFKCでの結果と似たものになります。
例えば、半角カタカナ(例えば「カ」: U+FF76 HALFWIDTH KATAKANA LETTER KA) + 半角カタカナの濁点(「゙」: U+FF9E HALFWIDTH KATAKANA VOICED SOUND MARK)は濁点付きの全角カタカナ(「ガ」: U+30AC KATAKANA LETTER GA)に正規化されます。
前者は2文字ですが、後者は1文字です。
http://groonga.org/ja/docs/reference/normalizers.html#built-in-normalizers
MySQLでのutf8とutf8mb4の違い
MySQL 5.1のUTF-8では、3バイトで収まるもの、すなわち基本多言語面しか対応していなかった。現在では、UTF-8で有効な文字には4バイトのものがあり、それらは追加面と呼ばれているのだが、MySQL 5.1のUTF-8ではそれを扱うことが出来なかった。(どうしても取り扱い対場合にはVARCHARなどの文字列型の使用をやめ、BINARY型のカラムを使うという回避方法があったが、至極面倒であったと思う。)MySQL 5.5では新たに4バイトUTF-8に対応した! http://nippondanji.blogspot.jp/2010/12/mysql-55.html
MySQLでのutf8_general_ciとutf8_unicode_ciの違い
そもそもの挙動の違いとしては、以下の通りです。
- utf8_general_ci
- MySQLのデフォルト
- 大文字-小文字を同一視する
- utf8_unicode_ci
- 大文字-小文字を同一視する
- 半角-全角も同一視する。
- 数字の場合、丸数字も同一視
- 「け」の場合「~ヶ所」のような小書きも同一視
- 半角の濁音や半濁音も同一視
より掘り下げた内容については、以下ページをご参照下さい。
- mysqlのcollateを使って大文字-小文字や全角-半角を無視した検索
http://d.hatena.ne.jp/end0tknr/20100613/1276427626 - MySQLにおける、utf8_general_ci と utf8_unicode_ci の違いとは何か。
http://www.php-seed.net/blog/archives/129 - MySQL 5.5 の unicode collation で同一視される文字 - @tmtms のメモ
http://tmtms.hatenablog.com/entry/20110416/mysql_unicode_collation