Elasticsearch 7 を macOS 上に構築して全文検索をする

## 概要

- Homebrew で Elasticsearch 7 を macOS にインストールする
- Elasticsearch にデータを入れて全文検索する
- 今回の環境: macOS Catalina + Elasticsearch 7.8

## インストール

[Install Elasticsearch on macOS with Homebrew \| Elasticsearch Reference \[7\.8\] \| Elastic](https://www.elastic.co/guide/en/elasticsearch/reference/current/brew.html) に載っている通りに実施する。

```
$ brew tap elastic/tap
$ brew install elastic/tap/elasticsearch-full
```

## 設定ファイルの用意

今回は設定ファイルを置くディレクトリを任意の場所に用意する。

```
$ mkdir elasticsearch
$ cd elasticsearch
```

デフォルトの設定ファイルから最低限必要なものをコピーする。

- elasticsearch.yml: Elasticsearch の設定ファイル
- jvm.options: JVM の設定ファイル
- log4j2.properties: ログ出力の設定ファイル

```
$ cp /usr/local/etc/elasticsearch/elasticsearch.yml .
$ cp /usr/local/etc/elasticsearch/jvm.options .
$ cp /usr/local/etc/elasticsearch/log4j2.properties .
```

elasticsearch.yml の内容を以下のように書き換える。

```elasticsearch.yml
cluster:
  name: mycluster
node:
  name: mynode
network:
  host: localhost
path:
  data: /usr/local/var/lib/elasticsearch/
  logs: /usr/local/var/log/elasticsearch/
```

## Elasticsearch の起動

設定ファイルを置いたディレクトリを環境変数 ES_PATH_CONF に指定する。

```
$ export ES_PATH_CONF=/Users/foo/elasticsearch
```

elasticsearch コマンドで起動する。

```
$ elasticsearch
```

他のターミナルから curl で Elasticsearch にアクセスして起動していることを確認する。

```
$ curl http://localhost:9200/
{
  "name" : "mynode",
  "cluster_name" : "mycluster",
  "cluster_uuid" : "XXXXXXXXXXXXXXXXXXXXXX",
  "version" : {
    "number" : "7.8.0",
    "build_flavor" : "default",
    "build_type" : "tar",
    "build_hash" : "757314695644ea9a1dc2fecd26d1a43856725e65",
    "build_date" : "2020-06-14T19:35:50.234439Z",
    "build_snapshot" : false,
    "lucene_version" : "8.5.1",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}
```

## Elasticsearch の REST API

Elasticsearch には管理・操作するための REST API が用意されている。
今回は curl で REST API をコールすることでデータの追加や検索を行う。

[REST APIs \| Elasticsearch Reference \[7\.8\] \| Elastic](https://www.elastic.co/guide/en/elasticsearch/reference/current/rest-apis.html)

## インデックスの作成

データを入れる器となるインデックスを作成する。
インデックスはリレーショナルデータベースにおけるテーブルに近い概念となる。

HTTP PUT でインデックスを作成できる。

[Create index API \| Elasticsearch Reference \[7\.8\] \| Elastic](https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-create-index.html)

```
PUT /<index>
```

ここでは foo_index というインデックスを作成する。
pretty パラメータを付けることでレスポンスの JSON が見やすく整形される。

```
$ curl --include -XPUT "http://localhost:9200/foo_index?pretty"
HTTP/1.1 200 OK
content-type: application/json; charset=UTF-8
content-length: 85

{
  "acknowledged" : true,
  "shards_acknowledged" : true,
  "index" : "foo_index"
}
```

HTTP GET で作成したインデックスを確認できる。

[Get index API \| Elasticsearch Reference \[7\.8\] \| Elastic](https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-get-index.html)

```
GET /<index>
```

foo_index インデックスの情報を取得する。

```
$ curl --include -XGET "http://localhost:9200/foo_index?pretty"
HTTP/1.1 200 OK
content-type: application/json; charset=UTF-8
content-length: 379

{
  "foo_index" : {
    "aliases" : { },
    "mappings" : { },
    "settings" : {
      "index" : {
        "creation_date" : "1595771960816",
        "number_of_shards" : "1",
        "number_of_replicas" : "1",
        "uuid" : "D7t0dUBPSqKKyvw5Z7eujw",
        "version" : {
          "created" : "7080099"
        },
        "provided_name" : "foo_index"
      }
    }
  }
}
```

## ドキュメントの追加

ドキュメントはリレーショナルデータベースにおけるレコードに近い概念となる。
ドキュメントは JSON フォーマットで表される。

HTTP POST でドキュメントを追加できる。

[Index API \| Elasticsearch Reference \[7\.8\] \| Elastic](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-index_.html)

```
POST /<index>/_doc/
```

ここでは foo_user と foo_message というフィールドを持つドキュメントを登録する。
ドキュメントの ID は自動で生成される(ID を指定することも可能)。今回は「9xNyi3MBhHaU2qi75ayU」というIDが生成された。

```
$ curl --include -XPOST "http://localhost:9200/foo_index/_doc?pretty" \
-H 'Content-Type: application/json' \
-d '{
  "foo_user": "Alice",
  "foo_message": "The night was young, and so was he. But the night was sweet, and he was sour."
}'
HTTP/1.1 201 Created
Location: /foo_index/_doc/9xNyi3MBhHaU2qi75ayU
content-type: application/json; charset=UTF-8
content-length: 242

{
  "_index" : "foo_index",
  "_type" : "_doc",
  "_id" : "9xNyi3MBhHaU2qi75ayU",
  "_version" : 1,
  "result" : "created",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 2,
  "_primary_term" : 1
}
```

HTTP GET で追加したドキュメントを確認できる。

[Get API \| Elasticsearch Reference \[7\.8\] \| Elastic](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-get.html)

```
GET <index>/_doc/<_id>
```

ID を指定してドキュメントの情報を取得する。

```
$ curl --include -XGET "http://localhost:9200/foo_index/_doc/9xNyi3MBhHaU2qi75ayU?pretty"
HTTP/1.1 200 OK
content-type: application/json; charset=UTF-8
content-length: 306

{
  "_index" : "foo_index",
  "_type" : "_doc",
  "_id" : "9xNyi3MBhHaU2qi75ayU",
  "_version" : 1,
  "_seq_no" : 2,
  "_primary_term" : 1,
  "found" : true,
  "_source" : {
    "foo_user" : "Alice",
    "foo_message" : "The night was young, and so was he. But the night was sweet, and he was sour."
  }
}
```

同様に2つ目のドキュメントも追加しておく。

```
$ curl --include -XPOST "http://localhost:9200/foo_index/_doc?pretty" \
-H 'Content-Type: application/json' \
-d '{
  "foo_user": "ボブ",
  "foo_message": "夜は若く、彼も若かった。が、夜の空気は甘いのに、彼の気分は苦かった。"
}'
HTTP/1.1 201 Created
Location: /foo_index/_doc/-BNzi3MBhHaU2qi7EazP
content-type: application/json; charset=UTF-8
content-length: 242

{
  "_index" : "foo_index",
  "_type" : "_doc",
  "_id" : "-BNzi3MBhHaU2qi7EazP",
  "_version" : 1,
  "result" : "created",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 3,
  "_primary_term" : 1
}
```

## データの全文検索

Elasticsearch のクエリ DSL でドキュメントを検索することができる。

[Query DSL \| Elasticsearch Reference \[7\.8\] \| Elastic](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html)

クエリ DSL には JSON フォーマットで検索条件を指定する。

[Match query \| Elasticsearch Reference \[7\.8\] \| Elastic](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-match-query.html)

```
GET /_search
{
  "query": {
    "match": {
      "message": {
        "query": "this is a test"
      }
    }
  }
}
```

ここでは foo_message フィールドに「夜の空気」が含まれるドキュメントを検索するクエリを実行する。

```
$ curl --include -XGET "http://localhost:9200/foo_index/_search?pretty" \
-H 'Content-Type: application/json' \
-d '
{
  "query": {
    "match": {
      "foo_message": "夜の空気"
    }
  }
}'
HTTP/1.1 200 OK
content-type: application/json; charset=UTF-8
content-length: 611

{
  "took" : 16,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 3.4337347,
    "hits" : [
      {
        "_index" : "foo_index",
        "_type" : "_doc",
        "_id" : "-BNzi3MBhHaU2qi7EazP",
        "_score" : 3.4337347,
        "_source" : {
          "foo_user" : "ボブ",
          "foo_message" : "夜は若く、彼も若かった。が、夜の空気は甘いのに、彼の気分は苦かった。"
        }
      }
    ]
  }
}
```

## REST API によるインデックスの操作例

[Index APIs \| Elasticsearch Reference \[7\.8\] \| Elastic](https://www.elastic.co/guide/en/elasticsearch/reference/current/indices.html)

REST API を利用して curl でインデックスを操作する例を挙げる。

### インデックスの作成

[Create index API \| Elasticsearch Reference \[7\.8\] \| Elastic](https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-create-index.html)

```
$ curl -XPUT http://localhost:9200/foo_index
```

### インデックスの削除

[Delete index API \| Elasticsearch Reference \[7\.8\] \| Elastic](https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-delete-index.html)

```
$ curl -XDELETE http://localhost:9200/foo_index
```

### インデックス一覧の取得

[cat APIs \| Elasticsearch Reference \[7\.8\] \| Elastic](https://www.elastic.co/guide/en/elasticsearch/reference/current/cat.html)

```
$ curl -XGET "http://localhost:9200/_cat/indices?format=json"
```

## REST API によるドキュメントの操作例

[Document APIs \| Elasticsearch Reference \[7\.8\] \| Elastic](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs.html)

REST API を利用して curl でドキュメントを操作する例を挙げる。

### ドキュメントの追加

[Index API \| Elasticsearch Reference \[7\.8\] \| Elastic](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-index_.html)

```
$ curl -XPOST http://localhost:9200/foo_index/_doc \
-H 'Content-Type: application/json' \
-d '{"foo_user": "Carol", "foo_message": "Hello, world."}'
```

### ドキュメントの取得

[Get API \| Elasticsearch Reference \[7\.8\] \| Elastic](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-get.html)

```
$ curl -XGET http://localhost:9200/foo_index/_doc/XXXXXXXXXXXXXXXXXXXX
```

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

[Update API \| Elasticsearch Reference \[7\.8\] \| Elastic](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-update.html)

```
$ curl -XPUT http://localhost:9200/foo_index/_doc/XXXXXXXXXXXXXXXXXXXX \
-H 'Content-Type: application/json' \
-d '{"foo_user": "Carol", "foo_message": "Goodbye, world."}'
```

### ドキュメントの削除

[Delete API \| Elasticsearch Reference \[7\.8\] \| Elastic](https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-delete.html)

```
$ curl -XDELETE http://localhost:9200/foo_index/_doc/XXXXXXXXXXXXXXXXXXXX
```

### ドキュメント数の取得

[Count API \| Elasticsearch Reference \[7\.8\] \| Elastic](https://www.elastic.co/guide/en/elasticsearch/reference/current/search-count.html)

```
$ curl -XGET http://localhost:9200/foo_index/_count
```

### ドキュメントの検索

[Query DSL \| Elasticsearch Reference \[7\.8\] \| Elastic](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html)
[Match query \| Elasticsearch Reference \[7\.8\] \| Elastic](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-match-query.html)

```
$ curl -XGET http://localhost:9200/foo_index/_search \
-H 'Content-Type: application/json' \
-d '
{
  "query": {
    "match": {
      "foo_message": "夜"
    }
  }
}'
```

## 参考資料

### Homebrew による Elasticsearch のインストール

- [macOS向けに、オフィシャル版Elastic Homebrewタップをリリース \| Elastic Blog](https://www.elastic.co/jp/blog/official-elastic-homebrew-tap-package-manager-macos)
- [Install Elasticsearch on macOS with Homebrew \| Elasticsearch Reference \[7\.8\] \| Elastic](https://www.elastic.co/guide/en/elasticsearch/reference/current/brew.html)
- [Configuring Elasticsearch \| Elasticsearch Reference \[7\.8\] \| Elastic](https://www.elastic.co/guide/en/elasticsearch/reference/current/settings.html)
- [GitHub \- elastic/homebrew\-tap: Homebrew tap for the Elastic Stack](https://github.com/elastic/homebrew-tap)
- [elasticsearch — Homebrew Formulae](https://formulae.brew.sh/formula/elasticsearch)

### Elasticsearch の REST API

- [基本概念 \| Elasticsearchリファレンス \[5\.4\] \| Elastic](https://www.elastic.co/guide/jp/elasticsearch/reference/current/gs-basic-concepts.html)
- [タイプレスAPIに移行する：Elasticsearch 7\.0の変更点 \| Elastic Blog](https://www.elastic.co/jp/blog/moving-from-types-to-typeless-apis-in-elasticsearch-7-0)
- [REST APIs \| Elasticsearch Reference \[7\.8\] \| Elastic](https://www.elastic.co/guide/en/elasticsearch/reference/current/rest-apis.html)