Elasticsearch入門

# 内容

Elasticsearchの[リファレンス](https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html)より、[Getting Started](https://www.elastic.co/guide/en/elasticsearch/reference/current/getting-started.html)を簡単に眺める。
Elasticsearchの基本的な概念と簡単な操作がわかる。（と思う。）

# 基本的な概念

Elasticsearchの理解のために必要な基本的な概念。
正直文字だけ見てもよくわからない。

詳細は[こちら](https://www.elastic.co/guide/en/elasticsearch/reference/current/_basic_concepts.html)を参照。

## Near Realtime（準リアルタイム）

Elasticsearchでは準リアルタイムな検索処理が可能らしい。
通常は1分ほどかかる検索処理が、わずかなレイテンシで可能とのこと。

## Cluster（クラスタ）

1つ以上のノードの集合で、データ全体を包括している。
インデックスおよびノード横断的な検索機能を提供する。
クラススタは識別子を持ち、デフォルトでは`elasticsearch`となる。

## Node（ノード）

クラスタを構成するサーバのこと。ここにデータが格納される。
クラスタ同様に識別子を持ち、デフォルトではノードの起動時に生成されるUUIDが設定される。

ノードは、クラス名を指定することでそのクラスタに接続することができる。
デフォルトでは各ノードは`elasticsearch`クラスタに接続するよう設定されている。

クラスタにはノードをいくつ接続してもOK。

## Index（インデックス）

ドキュメントの集合のこと。
インデックスも識別子（すべて小文字でなければならないらしい）を持ち、
これはインデックス内のドキュメントに対してインデクシング、検索、更新、削除を行う際に参照される。

クラスタにはインデックスをいくつ定義してもOK。

## Type（タイプ）

タイプは、インデックスの論理的な区分を表し、その意味はユーザの定義に完全に依存する。
一般には、共通的なフィールドの集合を持つドキュメントを定義する。

インデックスは１つ以上のタイプを持つことができる。

## Document（ドキュメント）

情報の基本的な単位のこと。

# 動かしてみる

## Dockerで起動する

[offcialイメージは非推奨らしい](https://hub.docker.com/_/elasticsearch/)ので、Elastic社のリポジトリから`pull`する。

```
$ sudo docker pull docker.elastic.co/elasticsearch/elasticsearch:5.6.0
```

コンテナを起動する。

```
$ docker run -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:5.6.0
```

DockerイメージにはX-Packも入っており、↑だけだとAPI利用時にX-Packの認証が必要になる。
これを無効化したい場合は環境変数`xpack.security.enabled=false`を定義して起動する。

```
$ sudo docker run -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e "xpack.security.enabled=false" docker.elastic.co/elasticsearch/elasticsearch:5.6.0

```

コンテナが起動できたら、APIを色々触ってみる。

## クラスタ関連API

### ヘルスチェック

クラスタやノードの状態が知りたい場合には[_cat API](https://www.elastic.co/guide/en/elasticsearch/reference/5.6/cat.html)が利用できる。

`/_cat/health`（`GET`）はクラスタの状態が取得できる。

```
$ curl http://localhost:9200/_cat/health?v
epoch      timestamp cluster        status node.total node.data shards pri relo init unassign pending_tasks max_task_wait_time active_shards_percent
1505626666 05:37:46  docker-cluster yellow          1         1      5   5    0    0        5             0                  -                 50.0%
```

また、`/_cat/nodes`（`GET`）ではノードの状態が取得できる。

```
$ curl http://localhost:9200/_cat/nodes?v
ip         heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name
172.17.0.2           18          95   7    0.59    0.42     0.37 mdi       *      i3eIOck
```

### インデックスの一覧／作成／削除

`/_cat/indices`（`GET`）でインデックスの一覧が取得できる。

```
$ curl http://localhost:9200/_cat/indices?v
health status index                         uuid                   pri rep docs.count docs.deleted store.size pri.store.size
yellow open   .triggered_watches            r9uHh50bTpqMoAYnlqwPtw   1   1          0            0       162b           162b
yellow open   .monitoring-alerts-6          0kI23ZNOT8uIoVdsn3B3gA   1   1          1            0      6.2kb          6.2kb
yellow open   .watcher-history-6-2017.09.17 ZOdwm9Y3SEm2ODXSYMWiQA   1   1        220            0    315.6kb        315.6kb
yellow open   .monitoring-es-6-2017.09.17   DyEY0HBkSOi97rJ7iSpI4Q   1   1       2402           80      1.8mb          1.8mb
yellow open   .watches                      -0DQQke8QESzwt6SJqroVg   1   1          4            0       20kb           20kb
```

インデックスを作成したい場合は`/{インデックス名}`（`PUT`）だけでOK。
例えば、`customer`というインデックスを作成する場合は↓のようになる。

```
$ curl -X PUT http://localhost:9200/customer?pretty
{
  "acknowledged" : true,
  "shards_acknowledged" : true,
  "index" : "customer"
}
```

インデックスの一覧を見ると、`customer`インデックスが追加されているのがわかる。

```
$ curl http://localhost:9200/_cat/indices?v
health status index                         uuid                   pri rep docs.count docs.deleted store.size pri.store.size
yellow open   .triggered_watches            r9uHh50bTpqMoAYnlqwPtw   1   1          0            0      8.1kb          8.1kb
yellow open   customer                      Jq6-6AOYTNOHgGmGSWLCcw   5   1          0            0       810b           810b
yellow open   .monitoring-alerts-6          0kI23ZNOT8uIoVdsn3B3gA   1   1          1            0      6.2kb          6.2kb
yellow open   .watcher-history-6-2017.09.17 ZOdwm9Y3SEm2ODXSYMWiQA   1   1        290            0    297.9kb        297.9kb
yellow open   .monitoring-es-6-2017.09.17   DyEY0HBkSOi97rJ7iSpI4Q   1   1       3177           40      1.8mb          1.8mb
yellow open   .watches                      -0DQQke8QESzwt6SJqroVg   1   1          4            0     11.4kb         11.4kb
```

また、インデックスを削除した場合は`/{インデックス名}`（`DELETE`）とする。

```
$ curl -X DELETE http://localhost:9200/customer?pretty
{
  "acknowledged" : true
}
```

インデックス一覧から`customer`インデックスが削除されているのがわかる。

```
$ curl http://localhost:9200/_cat/indices?v
health status index                         uuid                   pri rep docs.count docs.deleted store.size pri.store.size
yellow open   .triggered_watches            r9uHh50bTpqMoAYnlqwPtw   1   1          0            0      8.1kb          8.1kb
yellow open   .monitoring-alerts-6          0kI23ZNOT8uIoVdsn3B3gA   1   1          1            0      6.2kb          6.2kb
yellow open   .watcher-history-6-2017.09.17 ZOdwm9Y3SEm2ODXSYMWiQA   1   1        310            0    293.5kb        293.5kb
yellow open   .monitoring-es-6-2017.09.17   DyEY0HBkSOi97rJ7iSpI4Q   1   1       3433           20        2mb            2mb
yellow open   .watches                      -0DQQke8QESzwt6SJqroVg   1   1          4            0     27.8kb         27.8kb
```

### ドキュメントの登録と検索と削除

上述の`customer`インデックスが存在する状態で操作してみる。

インデックスにドキュメントを登録するには`/{インデックス名}/{タイプ名}/{ID}`（`PUT`）に登録したいドキュメントを渡す。

```
$ curl -X PUT http://localhost:9200/customer/external/1?pretty -d '
> {
>     "name": "John Doe"
> }'
{
  "_index" : "customer",
  "_type" : "external",
  "_id" : "1",
  "_version" : 1,
  "result" : "created",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "created" : true
}
```

登録したドキュメントを取得したい場合は`/{インデックス名}/{タイプ名}/{ID}`（`GET`）とする。

```
$ curl http://localhost:9200/customer/external/1?pretty
{
  "_index" : "customer",
  "_type" : "external",
  "_id" : "1",
  "_version" : 1,
  "found" : true,
  "_source" : {
    "name" : "John Doe"
  }
}
```

また、登録済みのドキュメントを削除したい場合は`/{インデックス名}/{タイプ名}/{ID}`（`DELETE`）とする。

```
$ curl -X DELETE http://localhost:9200/customer/external/1?pretty
{
  "found" : true,
  "_index" : "customer",
  "_type" : "external",
  "_id" : "1",
  "_version" : 2,
  "result" : "deleted",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  }
}
```

ドキュメントを検索してみると、削除されているのが確認できる。

```
$ curl http://localhost:9200/customer/external/1?pretty
{
  "_index" : "customer",
  "_type" : "external",
  "_id" : "1",
  "found" : false
}
```

ちなみに、ドキュメント登録時に存在しないインデックスを指定しても、それは自動的に生成されるためエラーにはならない。

試しに、今はまだ存在しない`another-customer`インデックスへドキュメントを登録してみる。

```
$ curl -X PUT http://localhost:9200/another-customer/external/1?pretty -d '
> {
>     "name": "John Doe"
> }'
{
  "_index" : "another-customer",
  "_type" : "external",
  "_id" : "1",
  "_version" : 1,
  "result" : "created",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "created" : true
}
```

インデックスの一覧を見ると`another-customer`が作成されているのが確認できる。

```
$ curl http://localhost:9200/_cat/indices?v
health status index                         uuid                   pri rep docs.count docs.deleted store.size pri.store.size
yellow open   .monitoring-alerts-6          0kI23ZNOT8uIoVdsn3B3gA   1   1          1            0      6.2kb          6.2kb
yellow open   .monitoring-es-6-2017.09.17   DyEY0HBkSOi97rJ7iSpI4Q   1   1       6386           30      3.5mb          3.5mb
yellow open   .triggered_watches            r9uHh50bTpqMoAYnlqwPtw   1   1          0            0     16.1kb         16.1kb
yellow open   customer                      YbUPxi4PS6qSXXRoQAgkNA   5   1          1            0        4kb            4kb
yellow open   another-customer              GuYOZSfnSmK7izOIYoEI5w   5   1          1            0      3.9kb          3.9kb
yellow open   .watcher-history-6-2017.09.17 ZOdwm9Y3SEm2ODXSYMWiQA   1   1        555            0    470.2kb        470.2kb
yellow open   .watches                      -0DQQke8QESzwt6SJqroVg   1   1          4            0     27.8kb         27.8kb
```

もちろん、検索すればドキュメントが取得できる。

```
$ curl http://localhost:9200/another-customer/external/1?pretty
{
  "_index" : "another-customer",
  "_type" : "external",
  "_id" : "1",
  "_version" : 1,
  "found" : true,
  "_source" : {
    "name" : "John Doe"
  }
}
```

## ドキュメントの操作

ドキュメントの操作には[Document API](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs.html)が提供されている。

### ドキュメントの更新

ドキュメントの更新には[_update API](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-update.html)を使用する。

登録済みのドキュメントを更新するには、`/{インデックス名}/{タイプ名}/{ID}/_update`（`POST`）に更新後のドキュメントを渡せばOK。
（`"name"`を`"John"`から`"Jane"`へ変更する。）

```
$ curl -X POST http://localhost:9200/customer/external/1/_update?pretty -d '
> {
>     "doc": { "name": "Jane Doe" }
> }'
{
  "_index" : "customer",
  "_type" : "external",
  "_id" : "1",
  "_version" : 2,
  "result" : "updated",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  }
}
```

↓で更新されていることが確認できる。

```
$ curl http://localhost:9200/customer/external/1?pretty
{
  "_index" : "customer",
  "_type" : "external",
  "_id" : "1",
  "_version" : 2,
  "found" : true,
  "_source" : {
    "name" : "Jane Doe"
  }
}
```

フィールドを追加することもできる。
（"age"フィールドを追加している。）

```
$ curl -X POST http://localhost:9200/customer/external/1/_update?pretty -d '
{
    "doc": { "name": "Jane Doe", "age": 20 }
}'
{
  "_index" : "customer",
  "_type" : "external",
  "_id" : "1",
  "_version" : 3,
  "result" : "updated",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  }
}
```

↓でフィールドが追加されているのが確認できる。

```
$ curl http://localhost:9200/customer/external/1?pretty
{
  "_index" : "customer",
  "_type" : "external",
  "_id" : "1",
  "_version" : 3,
  "found" : true,
  "_source" : {
    "name" : "Jane Doe",
    "age" : 20
  }
}
```

更新時は、スクリプトを利用することもできる。
更新対象のドキュメントは`ctx._source`で参照可能。
（この機能は現時点では一度に1ドキュメントに対してのみ適用可能だが、将来的にはSQSの`update-where`文のように複数ドキュメントを対象に適用できるようになるらしい。）

↓では`"age"`フィールドの値を`+5`している。

```
$ curl -X POST http://localhost:9200/customer/external/1/_update?pretty -d '
{
    "script": "ctx._source.age += 5"  
}'
{
  "_index" : "customer",
  "_type" : "external",
  "_id" : "1",
  "_version" : 4,
  "result" : "updated",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  }
}
```

↓で数値が変わっているのが確認できる。

```
$ curl http://localhost:9200/customer/external/1?pretty
{
  "_index" : "customer",
  "_type" : "external",
  "_id" : "1",
  "_version" : 4,
  "found" : true,
  "_source" : {
    "name" : "Jane Doe",
    "age" : 25
  }
}
```

### バッチ処理

[_bulk API](https://www.elastic.co/guide/en/elasticsearch/reference/5.6/docs-bulk.html)を使用すれば[バッチ処理](https://www.elastic.co/guide/en/elasticsearch/reference/current/_batch_processing.html#_batch_processing)が可能となる。

## より複雑なドキュメントの操作

後で書く。

# 参考
* [Elasticsearchのリファレンス](https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html)
  * [基本的な概念](https://www.elastic.co/guide/en/elasticsearch/reference/current/_basic_concepts.html)
  * [Dockerで起動する方法](https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html)