elasticsearch-rails/erasticsearch-persistenceについて

Speeee Advent Calendar 8日目です。

![elasticsearch](https://www.elastic.co/static/img/logo-elastic.png)

ここ約3ヶ月ほど全く触ったことのないElasticsearchを使うプロジェクトにアサインされ、よくわからんことだらけだった中でネット上に日本語であまり見当たらなかったような気がした```elasticsearch-persistence```について軽くまとめられればなーと思っております。

## 使用してる環境
elasticsearch(1.7.2)
elasticsearch-persistence (0.1.8)
rails(4.2.3)
ruby(2.2.3)

## なぜpersistenceを使ったのか
アプリケーションにテーブルの存在するモデル(ActiveRecordを継承したモデル)にelasticsearchによる検索を適応する場合には、```Elasticsearch::Model```を利用することで、そのモデルにelasticsearchの機能が付与することができます。
ただ、今回私が携わってきたプロダクトではテーブルが存在しないモデル(ActiveRecordを継承しないモデル)でelasticsearchを使った検索をしたかったためpersistenceを使うことにしました。

## Elasticsearch::Persistenceの2つの実装方法
persistenceには
- [Repositoryタイプ](https://github.com/elastic/elasticsearch-rails/tree/master/elasticsearch-persistence#the-repository-pattern)
- [Active Recordタイプ](https://github.com/elastic/elasticsearch-rails/tree/master/elasticsearch-persistence#the-activerecord-pattern)
があります。

### Repositoryタイプ
この場合はrepositoryがelasticsearchへの検索、参照、保存、削除などを担っており、repositoryへsaveメソッドの引数として値の入ったモデルを入れることにより、保存してくれたりします。
[repositoryパターン](http://martinfowler.com/eaaCatalog/repository.html)を提供したものであるようです。

### ActiveRecordタイプ
こちらは文字通りActiveRecordのモデルと同じ感覚でElasticsearchの検索などを使えるようにするものです。
今回はこちらを採用しました。
理由はいつものrailsっぽく使えるから、という理由のみです。

## ModelとRepositoryの関係
ActiveRecordタイプで実装するものでも、内部的にはRepositoryを使っていて、その利用が隠蔽されているだけに過ぎないので、例えばElasticsearch::Persistence::ModelをincludeしたHogeモデルがあるとすると

```
Hoge.gateway
```
にElasticsearch::Persistence::Repositoryが格納されています。

## Elasticsearch::Persistence::Modelの使い方
```
include Elasticsearch::Persistence::Model
```
ってモデルに書けばOK
```Elasticsearch::Persistence::Model```自体は[virtus](https://github.com/solnic/virtus)とActiveModelが入っているのでこれ入れるだけで基本的なモデルがかけます。

``` hoge.rb
class Hoge
  include Elasticsearch::Persistence::Model

  attribute :fuga,  Integer
  validates :fuga,  presence: true
end
```

こいつに```.save```とかやれば保存してくれたり、```.search(#{query})```とかやれば検索してくれて検索結果を取得できます。
検索結果として戻ってくるのは```Elasticsearch::Persistence::Repository::Response::Results```ですが、eachなどで展開すると直接呼び出したモデルに格納された状態で操作できます。
直接呼び出したモデルなので、例えば親クラスでのみincludeしておいて、その小クラスで検索した場合には親クラスに格納された結果になるので少し注意が必要です。
また、aggregationを利用した場合にはこの限りではないので、直接値を掘り出して、モデルに格納するなどの操作が必要です。

## indexとdocumentの指定方法
```
index_name 'hoge'
document_type 'fuga'
```
と書けばOK
書かないとクラス名から推測されて勝手に作られます。

``` hoge.rb
module Elasticsearch
  class Hoge
    include Elasticsearch::Persistence::Model
    
    index_name 'hoge'        # add
    document_type 'fuga'     # add

    attribute :fuga,  Integer
    validates :fuga,  presence: true
  end
end
```

## indexの強制更新
よくelasticsearchを実際に使ったrspecなどのテストでelasticsearchにデータ入れた後すぐ反映したい場合、

```
{Elasticsearch::Model}.__elasticsearch__.create_index!
{Elasticsearch::Model}.__elasticsearch__.flush_index!
```
と書きますが、persistenceの場合は

```
{Elasticsearch::Persistence::Model}.gateway.create_index!
{Elasticsearch::Persistence::Model}.gateway.flush_index!
```
となります。
つまり、persistenceの場合には```__elasticsearch__```に対応するのが```gateway```になるので、```Elasticsearch::Model```を使っている記事の```__elasticsearch__```を```gateway```に読み替えれば結構いけること多いです。

## hostを環境毎に変更する
めっちゃ普通

```config/initializer/elasticsearch.rb
Elasticsearch::Persistence.client = Elasticsearch::Client.new(host: ENV['ELASTICSEARCH_HOST'], logs: Rails.env.development?, trace: Rails.env.development?)
```

これはほとんど```Elasticsearch::Model```の記事を参考にさせていただきました。
参考: [RSpecでElasticsearchを使ったテストを書く](http://blog.bitjourney.com/entry/2015/05/22/162250)

# おわりに
persistenceを使った場合の本当に基本的なところと、```Elasticsearch::Model```とちょっと違うところをまとめました。
当時は右も左も分からない状態で疑う箇所が多かったので、直接persistenceを使った記事少なくて困ったなーと思っていましたが、今になってみると結局要点を抑えれば```Elasticsearch::Model```を利用した記事を参考にすれば良いということです。