Help us understand the problem. What is going on with this article?

Mroongaで全文検索以外でGroongaのノーマライザーを使う方法

More than 3 years have passed since last update.

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_flagsALLOW_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のクエリー構文で記載することにより、複数のインデックスでかなり高速に絞込ができて嬉しいという使い方もあります。

参考:MroongaによりMySQLで複数インデックスを有効に全文検索する方法

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした