Mattermost(v2.1.0)の日本語メッセージ全文検索をMySQL 5.7の標準ngramパーサを使い暫定対応

  • 24
    Like
  • 4
    Comment
More than 1 year has passed since last update.

Mattermost 2.1.0が2016/03/16にリリースされました。

@yuya-ocによるelectron製クライアントが公式採用されたり、公式ブログにどっかで見たような画面が出てきたり、"If anyone is interested in working on a Japanese translation, or other language translations, please join in the discussion." と絶賛日本語ローカライズしてくれる人大募集中らしくていろいろアツい感じです。

で、日本語検索が動かなかったのが今更気になったので軽く追ってみたところ、インデックス貼るだけで対応できそうだったので展開させていただきます。
皆様の世知辛いMattermost生活の一助になれば幸いです。

追伸: 2016/03/28
既出でした。。。 Cannot search CJK contents · Issue #2033 · mattermost/platform

概要

こちらでわかりやすくまとめられておりますが、簡単に検索に関する部分だけ。
Mattermost の検索機能 - Qiita

検索SQL組み立て

全文検索インデックス作成

どちらも各RDBMSの標準的な全文検索を使っているだけのようです。
特にMySQL側は、5.7から標準で対応しているngramパーサでインデックスを貼り直せば簡単にそのまま動きそうです。

参考: MySQL ngram全文検索パーサ

対応方針

  • RDBMSをMySQL 5.7.xに変更
  • FULLTEXT Indexの貼り直し

RDBMSをMySQL 5.7.xに変更

Vagrantの動作サンプルをMySQL 5.7.xを使うように修正しました。(Postgresqlでも引き続き使えます)

Mattermost Sample for Vagrant (VirtualBox + CentOS 7.2.x)

my.cnf設定

Mattermostの文字コードがutf8mb4なので、ラージインデックス設定を入れてあります。
あと、DB接続文字列を公式サンプルそのままにするためにvalidate_passwordプラグインを殺しておきました。
本運用する場合はその他のパラメータ含め、適宜設定してください。

my.cnf
[mysql]
default-character-set = utf8mb4

[mysqld]
# setting for utf8mb4
character-set-server = utf8mb4
innodb_large_prefix = ON
innodb_file_format = Barracuda
innodb_file_format_max = Barracuda

# for test-env setting (DO NOT USE PRODUCTION!!)
validate_password=OFF

インデックス貼り直し

スキーマ確認

$  mysqldump -d mattermost -u mmuser -pmmuser_password > ddl.sql

$  grep "FULLTEXT" ddl.sql
  FULLTEXT KEY `idx_posts_message_txt` (`Message`),
  FULLTEXT KEY `idx_posts_hashtags_txt` (`Hashtags`)
ddl.sql
CREATE TABLE `Posts` (
  `Id` varchar(26) NOT NULL,
  `CreateAt` bigint(20) DEFAULT NULL,
  `UpdateAt` bigint(20) DEFAULT NULL,
  `DeleteAt` bigint(20) DEFAULT NULL,
  `UserId` varchar(26) DEFAULT NULL,
  `ChannelId` varchar(26) DEFAULT NULL,
  `RootId` varchar(26) DEFAULT NULL,
  `ParentId` varchar(26) DEFAULT NULL,
  `OriginalId` varchar(255) DEFAULT NULL,
  `Message` text,
  `Type` varchar(26) DEFAULT NULL,
  `Props` text,
  `Hashtags` text,
  `Filenames` text,
  PRIMARY KEY (`Id`),
  KEY `idx_posts_update_at` (`UpdateAt`),
  KEY `idx_posts_create_at` (`CreateAt`),
  KEY `idx_posts_channel_id` (`ChannelId`),
  KEY `idx_posts_root_id` (`RootId`),
  FULLTEXT KEY `idx_posts_message_txt` (`Message`),
  FULLTEXT KEY `idx_posts_hashtags_txt` (`Hashtags`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

FULLTEXTってPostsのメッセージとハッシュタグ(twitterみたいに #タグ を付与できる機能)しかないんですね。
ハッシュタグの方は完全一致のほうがいいと思うので、そのままにしておきます。

ngramインデックスで貼り直し

既存のインデックスを落として、新しくngramを指定して貼り直します。

$  mysql mattermost -u mmuser -pmmuser_password

mysql> ALTER TABLE Posts DROP INDEX idx_posts_message_txt;
Query OK, 0 rows affected (0.01 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> ALTER TABLE Posts ADD FULLTEXT INDEX idx_posts_message_txt (`Message`) WITH PARSER ngram COMMENT 'ngram index sample';
Query OK, 0 rows affected (0.22 sec)
Records: 0  Duplicates: 0  Warnings: 0


mysql> show index from Posts;
+-------+------------+------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+--------------------+
| Table | Non_unique | Key_name               | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment      |
+-------+------------+------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+--------------------+
| Posts |          0 | PRIMARY                |            1 | Id          | A         |           5 |     NULL | NULL   |      | BTREE      |         |                    |
| Posts |          1 | idx_posts_update_at    |            1 | UpdateAt    | A         |           5 |     NULL | NULL   | YES  | BTREE      |         |                    |
| Posts |          1 | idx_posts_create_at    |            1 | CreateAt    | A         |           5 |     NULL | NULL   | YES  | BTREE      |         |                    |
| Posts |          1 | idx_posts_channel_id   |            1 | ChannelId   | A         |           1 |     NULL | NULL   | YES  | BTREE      |         |                    |
| Posts |          1 | idx_posts_root_id      |            1 | RootId      | A         |           1 |     NULL | NULL   | YES  | BTREE      |         |                    |
| Posts |          1 | idx_posts_hashtags_txt |            1 | Hashtags    | NULL      |           5 |     NULL | NULL   | YES  | FULLTEXT   |         |                    |
| Posts |          1 | idx_posts_message_txt  |            1 | Message     | NULL      |           5 |     NULL | NULL   | YES  | FULLTEXT   |         | ngram index sample |
+-------+------------+------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+--------------------+
7 rows in set (0.00 sec)

コメント欄以外で区別が付かないのですが、これでngramパーサ指定のインデックスで貼り直されたはずです。確認してみます。

動作確認

こんな感じで検索結果に出るようになりました。N-gramなんで途中文字列でも当たります。

デフォルトは2-gramみたいですが、my.cnfの設定で変えられるみたいなので、そのへんはお好みで。MeCabパーサを使ってみるのもいいかもですね。

ハイライトはクライアント側?なので対応してないのですが、jump機能が使えるのであんまり気にならないかもしれません。
とりあえずやっつけ対応にしては上々じゃないでしょうか。

副作用として英単語も部分一致になったりします。

インデックス方式を変えただけなので問題は出ないだろう…と思ってはいたのですが、以下のように関係ない英語文字列がHitしたりしていたので、使っていくといろいろ怪しいかもしれません。(もともとwithはストップワードなのかな…って気もしますが)

あと上記のテーブル形式とN-gramインデックスだと、きつめのパフォーマンス低下とかあるかもしれません。
インデックス貼っただけなので何かあっても比較的すぐ元に戻せると思いますが、自己責任でよろしくお願い致します。

皆様の世知辛いMattermostライフのお役にたてば幸いです。

その他

  • postgresの場合はts_query("english") で実装されているので、インデックス貼り直すだけではなく手を入れないとダメっぽいのと、posgresいじるのがだんだんめんどくさくなってきて途中で投げました…
    • fulltext-ja + mecabでクエリ投げて動作確認まではできたんですが、RDBMS側で頑張るよりelasticsearchとかの全文検索エンジンと連携させたほうがいいかなって…
      • Mattermostの実装自体はかなりわかりやすい(と思う)のでどなたか…
  • MySQL 5.7の標準組み込みパーサによる全文検索はすごく楽に使えて素晴らしかったです。
    • 他にも全文検索対応してないやつでもこんな感じでやっつけ対応できそうで便利だなって思いました。
    • でも、それ以上に5.7の罠が多すぎて正直辛かったです…

以上です。