Elasticsearch入門


# Elasticsearchとは？

[Elasticsearch](https://www.elastic.co/products/elasticsearch)は全文検索エンジンサービスです。

ざっくり言うと、全文検索エンジンの[Apache Lucene](https://lucene.apache.org/core/)に、検索・データ投入用のRESTfulAPIや、クラスタリング機能を追加したものです。

Javaで出来ています。
実行には`Oracle JDK version 1.8.0_25`を推奨しています。

その他の入門資料です。
[ElasticSearch入門 // Speaker Deck](https://speakerdeck.com/johtani/elasticsearchru-men)


# Macへのインストール
今回はtarからのインストールを前提にします。

## tar
最新版(2.0.0)を使う場合は、この方法でインストールします。

```
curl -L -O https://download.elastic.co/elasticsearch/release/org/elasticsearch/distribution/tar/elasticsearch/2.0.0/elasticsearch-2.0.0.tar.gz
tar -xvf elasticsearch-2.0.0.tar.gz
```


最新情報は[Installation](https://www.elastic.co/guide/en/elasticsearch/reference/current/_installation.html)を参照してください。

## Homebrew
パスの設定が簡単な方法です。

```
brew install elasticsearch
```
1.7.3がインストールされます。

```
~ brew install elasticsearch
==> Downloading https://download.elastic.co/elasticsearch/elasticsearch/elasticsearch-1.7.3.tar.gz
######################################################################## 100.0%
==> Caveats
Data:    /usr/local/var/elasticsearch/elasticsearch_shigerunakajima/
Logs:    /usr/local/var/log/elasticsearch/elasticsearch_shigerunakajima.log
Plugins: /usr/local/var/lib/elasticsearch/plugins/
Config:  /usr/local/etc/elasticsearch/

To have launchd start elasticsearch at login:
  ln -sfv /usr/local/opt/elasticsearch/*.plist ~/Library/LaunchAgents
Then to load elasticsearch now:
  launchctl load ~/Library/LaunchAgents/homebrew.mxcl.elasticsearch.plist
Or, if you don't want/need launchctl, you can just run:
  elasticsearch --config=/usr/local/opt/elasticsearch/config/elasticsearch.yml
==> Summary
🍺  /usr/local/Cellar/elasticsearch/1.7.3: 34 files, 30M, built in 19 seconds
```

# 起動
```
cd elasticsearch-2.0.0/bin
./elasticsearch
```

```
[2015-11-16 16:13:31,357][INFO ][node                     ] [Awesome Android] version[2.0.0], pid[44579], build[de54438/2015-10-22T08:09:48Z]
[2015-11-16 16:13:31,358][INFO ][node                     ] [Awesome Android] initializing ...
[2015-11-16 16:13:31,511][INFO ][plugins                  ] [Awesome Android] loaded [], sites []
[2015-11-16 16:13:31,611][INFO ][env                      ] [Awesome Android] using [1] data paths, mounts [[/ (/dev/disk1)]], net usable_space [40.9gb], net total_space [232.6gb], spins? [unknown], types [hfs]
[2015-11-16 16:13:33,258][INFO ][node                     ] [Awesome Android] initialized
[2015-11-16 16:13:33,259][INFO ][node                     ] [Awesome Android] starting ...
[2015-11-16 16:13:33,350][INFO ][transport                ] [Awesome Android] publish_address {127.0.0.1:9300}, bound_addresses {127.0.0.1:9300}, {[fe80::1]:9300}, {[::1]:9300}
[2015-11-16 16:13:33,359][INFO ][discovery                ] [Awesome Android] elasticsearch/wKCH_i4zTh-wi-ibI-ab9Q
[2015-11-16 16:13:36,399][INFO ][cluster.service          ] [Awesome Android] new_master {Awesome Android}{wKCH_i4zTh-wi-ibI-ab9Q}{127.0.0.1}{127.0.0.1:9300}, reason: zen-disco-join(elected_as_master, [0] joins received)
[2015-11-16 16:13:36,421][INFO ][http                     ] [Awesome Android] publish_address {127.0.0.1:9200}, bound_addresses {127.0.0.1:9200}, {[fe80::1]:9200}, {[::1]:9200}
[2015-11-16 16:13:36,421][INFO ][node                     ] [Awesome Android] started
[2015-11-16 16:13:36,449][INFO ][gateway                  ] [Awesome Android] recovered [0] indices into cluster_state
```
デフォルトのノード名（この例ではAwesome Android）は、マーブルコミックのキャラクター名からランダムに決まるそうです。

## ノード名を指定して起動

クラスタ名やノード名を指定して起動することもできます。

```
~ ./elasticsearch --cluster.name my_cluster_name --node.name my_node_name
[2015-11-16 16:31:31,783][INFO ][node                     ] [my_node_name] version[2.0.0], pid[44673], build[de54438/2015-10-22T08:09:48Z]
[2015-11-16 16:31:31,784][INFO ][node                     ] [my_node_name] initializing ...
[2015-11-16 16:31:31,898][INFO ][plugins                  ] [my_node_name] loaded [], sites []
[2015-11-16 16:31:31,993][INFO ][env                      ] [my_node_name] using [1] data paths, mounts [[/ (/dev/disk1)]], net usable_space [40.8gb], net total_space [232.6gb], spins? [unknown], types [hfs]
[2015-11-16 16:31:33,640][INFO ][node                     ] [my_node_name] initialized
[2015-11-16 16:31:33,640][INFO ][node                     ] [my_node_name] starting ...
[2015-11-16 16:31:33,730][INFO ][transport                ] [my_node_name] publish_address {127.0.0.1:9300}, bound_addresses {127.0.0.1:9300}, {[fe80::1]:9300}, {[::1]:9300}
[2015-11-16 16:31:33,740][INFO ][discovery                ] [my_node_name] my_cluster_name/9GWiJ5aARim9wlvo9BJ_sA
[2015-11-16 16:31:36,778][INFO ][cluster.service          ] [my_node_name] new_master {my_node_name}{9GWiJ5aARim9wlvo9BJ_sA}{127.0.0.1}{127.0.0.1:9300}, reason: zen-disco-join(elected_as_master, [0] joins received)
[2015-11-16 16:31:36,799][INFO ][http                     ] [my_node_name] publish_address {127.0.0.1:9200}, bound_addresses {127.0.0.1:9200}, {[fe80::1]:9200}, {[::1]:9200}
[2015-11-16 16:31:36,800][INFO ][node                     ] [my_node_name] started
[2015-11-16 16:31:36,813][INFO ][gateway                  ] [my_node_name] recovered [0] indices into cluster_state
```

クラスター名とノード名が何の役に立つかは、知りません。

# データ構造
データ構造は三階層です。

## 1. index
全文検索エンジンなのでインデックスです。
検索単位です。

## 2. type
よく知りません。
ドキュメントの解釈方法の単位のようです。

## 3. document
検索対象のドキュメントです。

# RESTful API
## 豆知識（pretty）
クエリストリングにprettyをつけるとレスポンスが読みやすくなります。

prettyなし

```
~ curl -XPUT 'localhost:9200/customer'
{"acknowledged":true}
```

prettyあり

```
~ curl -XPUT 'localhost:9200/customer?pretty'
{
  "acknowledged" : true
}
```

## インデックス編集 API
### インデックス作成

```
curl -XPUT 'localhost:9200/customer'
```

### インデックス参照

作ったインデックスを確認します

```
curl 'localhost:9200/_cat/indices?v'                                                                                                                                              ~
health status index    pri rep docs.count docs.deleted store.size pri.store.size
yellow open   customer   5   1          0            0       127b           127b
```

各項目の意味は...

- helth:yellow
  デフォルトのレプリカ1で作られているけど、レプリカの割り当てがないのでyellow。レプリカが割り当てられるとgreenになります。
- index:customer
  インデックス名
- pri:5
  プライマリーシャードの数。意味は知りません
- rep:1
  レプリカの数
- docs.count:0
  ドキュメント数
  
詳細は[Create an Index](https://www.elastic.co/guide/en/elasticsearch/reference/current/_create_an_index.html)


### インデックス削除

```
curl -XDELETE 'localhost:9200/customer?pretty'
```

詳細は[Delete an Index](https://www.elastic.co/guide/en/elasticsearch/reference/current/_delete_an_index.html)
  

## ドキュメント操作用RESTful API

主に次の三つです。

```
curl -XPUT 'localhost:9200/customer/external/1' -d '
{
  "name": "John Doe"
}'
```
```
curl 'localhost:9200/customer/external/1'
```
```
curl -XDELETE 'localhost:9200/customer/external/2'
```

詳細は[Index and Query a Document](https://www.elastic.co/guide/en/elasticsearch/reference/current/_index_and_query_a_document.html)

### ドキュメント追加
PUTメソッドでドキュメントをPUTします。

```
curl -XPUT 'localhost:9200/customer/external/1?pretty' -d '
{
  "name": "John Doe"
}'
```

- type: external
- ID: 1

を指定します。

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

IDを指定しない時は、POSTメソッドを使います。

```
curl -XPOST 'localhost:9200/customer/external?pretty' -d '
{
  "name": "Jane Doe"
}'
```

IDはランダム生成されます。

```
~ curl -XPOST 'localhost:9200/customer/external?pretty' -d '
  {
    "name": "Jane Doe"
  }'
{
  "_index" : "customer",
  "_type" : "external",
  "_id" : "AVETZfwQQYD2LWGYXctZ",
  "_version" : 1,
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "created" : true
}
```

ドキュメント追加時にindexがなければ自動的に作ります。

```
~ curl -XDELETE 'localhost:9200/customer?pretty'
{
  "acknowledged" : true
}
~ curl -XPUT 'localhost:9200/customer/external/1?pretty' -d '
  {
    "name": "John Doe"
  }'
{
  "_index" : "customer",
  "_type" : "external",
  "_id" : "1",
  "_version" : 1,
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "created" : true
}
```

詳細は[Modifying Your Data](https://www.elastic.co/guide/en/elasticsearch/reference/current/_modifying_your_data.html)

### ドキュメント取得
GETメソッドでドキュメントをGETします。

```
curl -XGET 'localhost:9200/customer/external/1?pretty'
```

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

### ドキュメント更新
更新もPUTメソッドで行います。

```
curl -XPUT 'localhost:9200/customer/external/1?pretty' -d '
  {
    "name": "John Doe"
  }'
{
  "_index" : "customer",
  "_type" : "external",
  "_id" : "1",
  "_version" : 1,
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "created" : true
}
~ curl -XPUT 'localhost:9200/customer/external/1?pretty' -d '
  {
    "name": "John Doe1"
  }'
{
  "_index" : "customer",
  "_type" : "external",
  "_id" : "1",
  "_version" : 2,
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "created" : false
}
```
2回PUTするとversionが上がります。
もちろん中身も更新します。

### ドキュメント削除

DELETEメソッドです。

```
curl -XDELETE 'localhost:9200/customer/external/2?pretty'
```

詳細は[Deleting Documents](https://www.elastic.co/guide/en/elasticsearch/reference/current/_deleting_documents.html)

# 感想
RESTful APIが美しい。