Groonga Advent Calendar 2015の18日目の記事です。
こちらの記事では全文検索以外の一致検索ではGroongaのカスタムノーマライザー使えなくて困りましたねということを記載しました。
今回はその解決方法の例を紹介します。
mroonga_normalize
mroonga_normalizeというUDFを作成してマージしてもらいました。たぶん、Mroonga5.11から利用できるとおもいます。
これは文字列をGroongaのノーマライザーを使って正規化する関数です。
DROP TABLE tags;
CREATE TABLE IF NOT EXISTS tags (
tag varchar(255) NOT NULL,
text varchar(1000) NOT NULL,
FULLTEXT KEY text (text) COMMENT 'normalizer "NormalizerAuto"'
) ENGINE=Mroonga DEFAULT CHARSET=utf8;
INSERT INTO tags VALUES("Hoge", "㍑");
mysql> SELECT * FROM tags WHERE mroonga_normalize(text) = mroonga_normalize("リットル");
+------+------+
| tag | text |
+------+------+
| Hoge | ㍑ |
+------+------+
1 row in set (0.01 sec)
mysql> SELECT * FROM tags WHERE mroonga_normalize(text) = mroonga_normalize("㍑");
+------+------+
| tag | text |
+------+------+
| Hoge | ㍑ |
+------+------+
1 row in set (0.01 sec)
このように出力させるカラムと検索クエリの両方にmroonga_normalize()を使えばGroongaのノーラマライザーを使って一致させることができます。
なお、mroonga_normalize()は、自動的にカラムのノーマライザーを調べて使ってくれるということをしてくれません(たぶんUDFの世界ではMySQLのフィールドにアクセスできず判断できない)。
そのため、NormalizerAuto以外のノーマライザーを使う場合は以下のように明示的にノーマライザー名を指定する必要があります。
以下はNormalizerMySQLUnicodeCIExceptKanaCIKanaWithVoicedSoundMark
を使う例です。
DROP TABLE tags;
CREATE TABLE IF NOT EXISTS tags (
tag varchar(255) NOT NULL,
text varchar(1000) NOT NULL,
FULLTEXT KEY text (text) COMMENT 'normalizer "NormalizerMySQLUnicodeCIExceptKanaCIKanaWithVoicedSoundMark"'
) ENGINE=Mroonga DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
INSERT INTO tags VALUES("Hoge", "ふらつく");
INSERT INTO tags VALUES("Hoge2", "ブラック");
mysql> SELECT * FROM tags WHERE text = "ブラック";
+-------+--------------+
| tag | text |
+-------+--------------+
| Hoge | ふらつく |
| Hoge2 | ブラック |
+-------+--------------+
mysql> SELECT * FROM tags WHERE mroonga_normalize(text, "NormalizerMySQLUnicodeCIExceptKanaCIKanaWithVoicedSoundMark") = mroonga_normalize("ブラック", "NormalizerMySQLUnicodeCIExceptKanaCIKanaWithVoicedSoundMark");
+-------+--------------+
| tag | text |
+-------+--------------+
| Hoge2 | ブラック |
+-------+--------------+
1 row in set (0.01 sec)
NormalizerMySQLUnicodeCIExceptKanaCIKanaWithVoicedSoundMark
はutf8_unicode_ciのようにひらがなとカタカナは同一視させつつ、ちっちゃい文字(っとか)、濁音有無の同一視を除外させたノーマライザーです。ふらつくとブラックが一致しないようにしたりできます。
他のストレージエンジンでも利用可
mroonga_normalizeはテーブルのストレージエンジンに依存せず、InnoDBやMyISAMのテーブルでも単なるSELECTだけでも利用できます。
カジュアルにMySQLサイドだけで以下のような文字種変換をしたいときにも使えるかもしれません。
- アルファベットを半角小文字アルファベットに統一したい
- NormalizerAuto
- 全角半角は区別して小文字アルファベットを大文字アルファベットに統一したい
- NormalizerMySQLGeneralCI
- アルファベットを半角大文字アルファベットに統一したい
- NormalizerMySQLUnicodeCI
- カタカナをひらがなに統一したい
- NormalizerMySQLUnicodeCIExceptKanaCIKanaWithVoicedSoundMark
mysql> SELECT mroonga_normalize("AbCdあイ㍑", "NormalizerAuto");
+----------------------------------------------------------+
| mroonga_normalize("AbCdあイ㍑", "NormalizerAuto") |
+----------------------------------------------------------+
| abcdあイリットル |
+----------------------------------------------------------+
1 row in set (0.00 sec)
mysql> SELECT mroonga_normalize("AbCdあイ㍑", "NormalizerMySQLGeneralCI");
+--------------------------------------------------------------------+
| mroonga_normalize("AbCdあイ㍑", "NormalizerMySQLGeneralCI") |
+--------------------------------------------------------------------+
| ABCDあイ㍑ |
+--------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> SELECT mroonga_normalize("AbCdあイ㍑", "NormalizerMySQLUnicodeCI");
+--------------------------------------------------------------------+
| mroonga_normalize("AbCdあイ㍑", "NormalizerMySQLUnicodeCI") |
+--------------------------------------------------------------------+
| ABCDあい㍑ |
+--------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> SELECT mroonga_normalize("ブラック", "NormalizerMySQLUnicodeCI");
+---------------------------------------------------------------+
| mroonga_normalize("ブラック", "NormalizerMySQLUnicodeCI") |
+---------------------------------------------------------------+
| ふらつく |
+---------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> SELECT mroonga_normalize("ブラック", "NormalizerMySQLUnicodeCIExceptKanaCIKanaWithVoicedSoundMark");
+--------------------------------------------------------------------------------------------------+
| mroonga_normalize("ブラック", "NormalizerMySQLUnicodeCIExceptKanaCIKanaWithVoicedSoundMark") |
+--------------------------------------------------------------------------------------------------+
| ぶらっく |
+--------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
インデックスを使ってGroongaのノーマライザーで高速に一致検索したい場合
なお、mroonga_normalizeでは、インデックスを使った検索を一致させることはできません。
どうしてもインデックスを使いたい場合、以下のようにシステム変数mroonga_boolean_mode_syntax_flags
にALLOW_COLUMN
を設定し、AGAINST句にGroongaのクエリー構文を使えば実現できます。
DROP TABLE tags;
CREATE TABLE IF NOT EXISTS tags (
tag varchar(255) NOT NULL,
text varchar(1000) NOT NULL,
text2 varchar(1000) NOT NULL,
KEY text (text) COMMENT 'normalizer "NormalizerAuto"',
FULLTEXT KEY text2 (text2) COMMENT 'normalizer "NormalizerAuto"'
) ENGINE=Mroonga DEFAULT CHARSET=utf8;
INSERT INTO tags VALUES("Hoge", "㍑", "");
SET mroonga_boolean_mode_syntax_flags = "SYNTAX_QUERY,ALLOW_LEADING_NOT,ALLOW_COLUMN";
SELECT * FROM tags WHERE MATCH(text2) AGAINST("text:リットル" IN BOOLEAN MODE);
ALLOW_COLUMN
は[カラム名]:[検索クエリ]
という構文を許すというフラグです。これを使うとMATCH句に指定したカラム以外でもGroongaのクエリー構文をフルで利用することができます。
ちなみにMySQLでは1つのクエリーにつき1つのインデックスしか使えませんが、Groongaのクエリー構文で記載することにより、複数のインデックスでかなり高速に絞込ができて嬉しいという使い方もあります。