QiitaのElasticsearchを7系にアップグレードしました

こんにちは。普段はQiita Teamを開発しているWakameSunと言います。
QiitaとQiita TeamではElasticsearchを記事検索のために利用しているのですが、[10月10日のメンテナンス](https://blog.qiita.com/2020-10-10-maintenance/)で5.5から7.7にアップグレードした時の話や行ったことを軽くまとめようと思います。
社内で書いたまとめ記事がバカでかくなりかなり端折っているため、ここはどうした？みたいなものがあればコメントをいただけると幸いです。（全ては返答できないかもしれませんが）可能な限りお答えします。

- ちなみに7.7なのはメンテナンスを行った時点でAESの最新バージョンが7.7だったからです(7.9に上げたい)・・・
 - 対応バージョンは[こちら](https://docs.aws.amazon.com/ja_jp/elasticsearch-service/latest/developerguide/what-is-amazon-elasticsearch-service.html#aes-choosing-version)で確認できます。


## 前提として
Qiita・Qiita TeamはバックエンドがRailsで動いており、Elasticsearchも[elasticsearch-rails](https://github.com/elastic/elasticsearch-rails)を使っています。

## updateを行うために行ったこと

- 公式がBreaking Changeをまとめてくれているのでそれを一通り読みました。
  - https://www.elastic.co/guide/en/elasticsearch/reference/6.8/breaking-changes-6.0.html
  - https://www.elastic.co/guide/en/elasticsearch/reference/currennt/breaking-changes-7.0.html
- 英語が苦手な人でも[優秀な翻訳サイト](https://www.deepl.com/ja/translator)があるのでそれを駆使してがんばりましょう。
- 一応日本語でアップグレードを行った記事も調べればたくさんあるので、そちらも一通り目を通しておくと大体のハマりどころは網羅されてるので楽になると思います。


## Elasticsearch 5.5 -> 6.8 (elasticsearch-rails 5.1.0 -> 6.1.1)
Qiitaは5.5に上げた時に大半の6.x移行で使えなくなるものは先人が対応してくれており、本当にありがたかったのですが一つ大きいものがありました。
それは多くのes5系から6系へ上げた人のうちそこそこの割合の方が対応したであろうタイプレス移行です。

まずQiitaでは主にesの用途として

- 記事検索
- Organization検索

の２つがあり、こちらは記事とOrganizationは当然インデックスを分けて運用していたし、typeも特にもともと利用していなかったため問題ありませんでした。

Qiita Teamでは

- チーム内記事検索
- チーム内プロジェクト記事検索 (通常プロジェクトとだけ読んでますが、Qiita Teamを利用されてない人向けに敢えて記事と付けています。)
- チーム内の通常記事とプロジェクト記事両方を検索

の３つの用途があります。
しかし最後の両方検索するときに容易だからか、データベースで見ても別テーブルである通常記事とプロジェクト記事をtypeを使って一つのインデックスに分けて入れている・・・という状況でした。
アップグレードでそのような運用はできなくなったので結論としてはtypeをつかわなくても良いように、プロジェクト記事と通常記事は別インデックスに入れるようにしています。
どっちの検索結果もほしい！という場合は今回の場合インデックス名を

```
・qiita-article(qiitaの記事)
・qiita-organization(organization)
・qiita-team-article(Qiita Teamの通常記事)
・qiita-team-project(Qiita Teamのプロジェクト記事)
```
のように管理している（厳密にはもう少し違うが）ので、検索時には`(esのurl)/qiita-team-*/_search`といった風にワイルドカードを使って少々強引に解決をしています。
ただ、今回は２つだけなのでいっそインデックスを複数指定するのがベストエフォートかもしれません。そこは今後の課題ですね。
また、6.xではタイプが消えたわけではなく、一つのindexに一つのみという制約なのですが、もうtypeはなしでも運用はできるので、元々typeを扱っていたところには`type: '_doc'`と入れてました。
`_doc`はtypeの何も代入しないときに入る値なのですが、変な値が勝手に入ると怖いので明示的に至るところに入れていました。これは一時的な対応で、7に上げたときにほぼすべて消えることになります。
一つ注意点としては、２つのインデックスのmappingを同じ構成にしないと検索の条件次第で結果が該当する記事はあるはずなのに０件となることがあります。DSLが変わった影響なのか、元々なのかはわかりませんが、片方のmappingに存在しないfieldを検索条件にすると起こるようです。
今回は同じmapping構成にして使わないカラムには整合性を持たせるための値を入れましたが、そうしなくてもできる方法があるかは次触るときに調べてみようと思います。


## Elasticsearch 6.8 -> 7.7 (elasticsearch-rails 6.1.1 -> 7.1.1)
6に上げたので次は7への移行、なのですが大半のことは[クラウドワークスさんのアップデート記事](https://engineer.crowdworks.jp/entry/2020/06/04/132334)に沿っていくだけで完了しました。
ただ、タイプレス移行に関しては6の段階でほぼできていたので至るところに入れていた`type: '_doc'`を消し去るだけで良かったのは幸いでした、
また、上記の記事ではFaradayのアップデートがネックになりelasticsearch-rubyを7.5で止めていますが、Qiitaの場合Faradayを上げても動作に問題はなかったのでelasticsearch-rubyを7.8(着手時の最新)まで上げてしまっています。

他にはindexのmax_ngramとmin_ngramの差が１より大きいフィールドが存在し、そこにmax_ngram_diffオプションを付けたくらいでした。([こちらのQAを参考にしました](https://stackoverflow.com/questions/60417477/elasticsearch-max-ngram-diff-working-for-edge-ngram-but-not-for-ngram-token))

## 上げてみて
アップグレードして見ると気をつける点はさほど多くなく、タイプレス移行さえなんとかなればあとは割と簡単な気がします。
しかし、この記事は触れていませんが結構[他の細々とした(特にQiita Teamの)改善](https://teams.qiita.com/news/search-improvement/)（チームができるごとに作成されてたインデックスを一つに統合したりハイライトのアルゴリズムを改良したり）を同時に並行して進めていたため、かなり時間をかけてしまいました・・・そろそろ来るであろう8.0では破壊的変更が軽微であることを祈ります・・・
話が変わって今回アップデートや改善に取り組んで良かったのは、ほぼ触ったことがなかった状態からElasticsearchに関してのかなりの期間Elasticsearchに触れたことで、他の社員さんから質問が飛んできたときにスッと答えられるようになったことです。苦しんだ分、他のメンバーが検索部分で何かしら触るときに力になれるのは嬉しいですね。
それとelasticsearchはeolが通常一年半で早いので今回みたいに5.5からエイヤエイヤで上げるのではなく、次はマメにローリングアップデートなどを駆使して最新を保つようにしたいですね・・・。
ということで、Qiitaのelasticsearchを上げたときに対応したことを大まかにまとめた記事でした。古いバージョンで止まっているプロダクトがある方は、最新まで上げてみるのを検討してみてはいかがでしょう？