Y-Ken Studio

新しもの好きのデータエンジニアが四方山話をお届けします。

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
    • 大文字-小文字を同一視する
    • 半角-全角も同一視する。
      • 数字の場合、丸数字も同一視
      • 「け」の場合「~ヶ所」のような小書きも同一視
      • 半角の濁音や半濁音も同一視

より掘り下げた内容については、以下ページをご参照下さい。