さくらVPS上のWEBアプリ(PHP)でMroongaを使って全文検索したい その2

前回の続き。

表記ゆれに対応させていく

読みがなで検索できるようにする

トークナイザーとして採用している「TokenMecab」のオプション「use_reading」を利用すると、検索用インデックスに読みがなも格納するようになるらしい。これにより漢字/かな表記のゆれに対応する。

これはトークナイザー指定時に併せて記述しておくもので、SELECT時などにどうこうするものではない。当たり前のことだと思うが僕はよくわかっていないので改めて書いておく。

例えばこういうふうにする。

CREATE TABLE questions4 (
 id INT PRIMARY KEY AUTO_INCREMENT ,
 content VARCHAR(255),
 FULLTEXT INDEX (content) COMMENT 'tokenizer "TokenMecab(\'use_reading\',true)",
 normalizer "NormalizerAuto"'
)Engine=Mroonga DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

で、

INSERT INTO questions4 (content) VALUES ("焼き肉");
INSERT INTO questions4 (content) VALUES ("ヤキニク");
INSERT INTO questions4 (content) VALUES ("焼き肉");
INSERT INTO questions4 (content) VALUES ("焼肉");

として実際に検索してみると……

SELECT * FROM questions4 WHERE MATCH(content) AGAINST("+焼き肉" IN BOOLEAN MODE)
+----+--------------+
| id | content      |
+----+--------------+
|  5 | 焼き肉       |
|  8 | 焼肉         |
|  7 | 焼き肉       |
|  6 | ヤキニク     |
+----+--------------+

ばっちり。

類義語を指定してやる

「極道」とか「暴力団」で検索しても「ヤクザ」がヒットしてほしいという話。Mroongaでは、テーブルに類義語を登録して検索結果に反映させることができる。例えば、

CREATE TABLE synonyms (
    ->   id int PRIMARY KEY auto_increment,
    ->   term varchar(255) NOT NULL,
    ->   synonym varchar(255) NOT NULL,
    ->   INDEX term (term) USING BTREE COMMENT 'normalizer "NormalizerAuto"'
    -> ) ENGINE=Mroonga DEFAULT CHARSET=utf8mb4;

こうして

INSERT INTO synonyms (term, synonym) VALUES ('ジャパンカップ','ジャパンカップ'),
    -> ('ジャパンカップ','ジャパンC'),
    -> ('ジャパンカップ','JC'),
    -> ('ジャパンカップ','東京優駿');

こうしてあげておいて、SELECT時に「mroonga_query_expand」であれしてやると、

SELECT * FROM questions4 WHERE MATCH(content) 
    -> AGAINST(mroonga_query_expand(
    -> 'synonyms',
    -> 'term',
    -> 'synonym',
    -> 'ジャパンカップ') 
    -> IN BOOLEAN MODE);

以下のようになってくれると。

+----+-----------------------+
| id | content               |
+----+-----------------------+
|  9 | ジャパンカップ        |
| 12 | 東京優駿              |
| 11 | JC                    |
| 10 | ジャパンC             |
+----+-----------------------+

指定したいキーワードが多いと大変だが、とりあえずこの案件ではこれでなんとかなるだろう。

SQLの使い方をもうちょっと考えたほうがいいかも

今回取り組んでいるサイトは一種のネット掲示板なのだが、トピックにつき

  1. 「投稿者」によるメインの記事

  2. 「回答者」によるメイン記事に対する回答(複数)

  3. 「一般ユーザー」からのコメント(複数)

が、それぞれ別のテーブルに格納されている。
2と3は、紐付けられたメイン記事のID(PostID)を格納するカラムを持っている。

前回までの内容でメイン記事のタイトルと本文を対象にあいまい検索を実装することはできそうだが、可能ならば「回答」と「コメント」も検索対象としたい。そこで問題になってくるのが僕のSQLに対する造詣の浅さだ。

これを今の僕の知識レベルで実現しようと思うと、例えば

1と2と3のテーブルをOUTER JOIN してヒットしたレコードのPostIDを配列か何かで取得し、重複を省く。これをWHEREで指定してメイン記事を絞り込む。ただしこれはMroongaの機能を十全に発揮できない気がする。

もしくは、

力技だけど、メイン記事のテーブルに検索用のカラムを追加。
で、「回答」「コメント」の投稿があった際にそれらのコピーをすべてこのカラムに文字列としてブチ込んでしまう。これを検索に使用すれば、希望通りの動きはすると思う。が、パフォーマンスがどうか。あとすでに投稿済みの内容については適用できない。

……という感じのアイディアしか出てこない。多分だけどもっとスマートな方法があるのではないか。以下のページが関連していると思うが、理解が追いつかない。

もう少し調べよう。

娘にプリキュアの光るパジャマを買ってあげたいのでサポートお願いします!