生活に終われ、疲弊しきった不肖高橋です。海風薫る千葉の田舎町から大都会東京のコンクリートジャングルへと一人ゲルマン民族大移動を毎日繰り返した結果、加齢臭が強くなってきました。
昔は総武線でビール飲んでいるオジサンを見るたびに汚いものを見るような視線を投げていたのですが、いまその視線がブーメランのように私を射抜いていきます。
[429] [429] Client error: `POST https://webservices.amazon.co.jp/paapi5/getitems` resulted in a `429 Too Many Requests` response: {"__type":"com.amazon.paapi5#TooManyRequestsException","Errors":[{"Code":"TooManyRequests","Message":"The request was de (truncated...)
さて、「電車でビール飲んでる」と無意味な嘘をついたところで、おもむろに全文検索について。
以前WordPressで大規模データを扱う場合のTipsという記事でも触れたのですが、WordPressの検索はLIKE検索なので、記事が数千件を超えたあたりから少しずつ遅くなってきます。今回はその解決策として日本語全文検索をやってみます。
アプローチ:FULLTEXTインデックスだけでなんとかする
全文検索には幾つかの手法がありますが、MySQLに元々そなわっているFULLTEXTインデックスと全文検索関数を利用します。5.6からはinnodbでもOKになったはず。
アプローチの仕方は次の通りです。
- FULLTEXTインデックスを有効にするためには対象となるカラムの文字列が分かち書きされている(半角スペースとかカンマで区切られている)必要がある
- 日本語オワタ\(^o^)/
- じゃあ、分かち書きしてどっか保存しておくか
- 分かち書きってどうやって作るの? ← イマココ
という具合に「いかにして分かち書きするか」という壁が立ちはだかってきました。
分かち書きをMecabで作る
対象となるのはブログ記事本文なので、それなりに長い文章を用意します。今回は青空文庫の室生犀星『みずうみ』を利用してみました。
サーバになにかをインストールするのは面倒なので、とりあえずYahooテキスト解析APIを使用してみます。
すると、Request Entity Too Large
つまり、「送ってくる文章長過ぎ」というエラーが返ってきました。そりゃそうだよね。
となると自分のサーバに分かち書きができるソフトをインストールする必要が出てきます。今回はMecabとそのPHPバインディングであるphp-mecabを利用します。
インストールはここら辺を参考にしてください。
インストールの注意点は次の通りです。
- mecabはUTF-8でインストールし、IPA辞書もUTF-8で入れましょう。
- わかんなくなったらmecabのインストールディレクトリにmecabrcがあるので、それを見てみましょう。
- php-mecabのインストールでこける人がいるようですが、pearじゃなくてpeclコマンドかもしれません。
- php-mecabはマニュアルがないのですが、Githubにあるmecab.cを読むと関数の戻り値などが記載されてます。
- php-mecabの利用サンプルは同じくGithubのexamplesフォルダにあります。
で、mecabをPHPから使えるようになったらこんな処理を行います。
- WordPressの投稿テーブルは
post_content_filtered
というカラムが余っているので、ここFULLTEXTインデックスを貼る - 投稿保存時に投稿本文とタイトル、抜粋を全部合体して、タグとショートコードを除去したのち、Mecabで分かち書きにした上で
post_content_filtered
に保存 - WordPressの検索クエリはLIKEを使うので、ここを
MATCH(post_content_filtered) AGAINST('ほにゃらら')
構文に変更
詳細を書くのが面倒なので、上記の処理をクラスにまとめたものをGistに上げておきました。確認してみてください。
あと、書き忘れていましたが、MySQLの全文検索は初期設定だと4文字未満を無視するという鬼畜仕様なので、これを変える必要があります。my.cnfに記載してください。
# mysqldのところに書く ft_min_word_len=1
この設定は一番最初にやっておく必要があります。懸命な読者諸氏は「じゃあ最初に書いておけよ」と思われたことでしょう。すみません。
お手数ですが、MySQLを再起動してインデックスを消去してください。上記クラスはお利口なので、インデックスを勝手に貼ってくれます。
なんのベンチマークもとっておりませんが、10万件のレコードで10秒近くかかっていた検索があっという間に完了するようになったのではないかと思われます。
これまで投稿したデータについては、Cron処理などで地道に書き換えていくのがよいでしょう。
残る課題がたくさん
さて、こうしてMySQLの全文検索機能がめでたく使えるようになったのですが、実用的なレベルになるには次の課題があります。
ストップワードどうする?
Mecabでなにも考えずに分かち書きをすると、「の」などの助詞なども入ってきます。これはストップワードとして除外すべき単語ですが、MySQLのデフォルトストップワードリストには入っていません。なので、このメンテナンス方法を考えなければいけません。
設定でどうにかできるみたいではあるのですが、まったく試していません。
Mecabの辞書どうする?
Mecabは分かち書きを行うときに辞書を使うのですが、辞書型のアプローチはどうも未知語に弱いようです。固有名詞や流行語など、追加した方が利便性が高いでしょう。
この辞書をメンテナンスする方法を考えないと、全文検索を導入が完了したとは言えないですね。基本的には決まった形式でCSVを作ってコンパイルすればよいようなのですが、毎回手作業ではやってらんねーというのもあるでしょうし、Webサービスの場合はなんとかして自動化したいところだと思われます。
クエリの調整
全文検索で使われるMATCH(post_content_filtered) AGAINST('ほにゃらら')
構文には色んなオプションがあります。今回はとりあえず検索クエリを半角スペースでくっつけるだけという簡単な道を選びましたが、文章を解析したりとか、色んなアプローチがあるはずです。ここを工夫すると、「なにこの検索便利!」という結果になるんじゃないでしょうか。
書き始めた当初は「mroongaをインストールするより簡単だろう」という考えから、「WordPressでお手軽全文検索」と名付けるところでしたが、Mecabをインストールしている時点でさしてお手軽でもないと考え直しました。
そのうちmroongaを導入したり、Mecabの他の使い方を書いてみようと思います。では、最後に関連リンクを挙げて終わります。
- MySQLで全文検索 – FULLTEXTインデックスの基礎知識
- MySQL-5.6.4からの新機能「InnoDB FullText Search」を用いた全文検索エンジンのベンチマークLTをしました。#mysqlcasual
- MySQL Fulltext Index × Ngram × 日本語検索は遅い
[429] [429] Client error: `POST https://webservices.amazon.co.jp/paapi5/getitems` resulted in a `429 Too Many Requests` response: {"__type":"com.amazon.paapi5#TooManyRequestsException","Errors":[{"Code":"TooManyRequests","Message":"The request was de (truncated...)