Elasticsearch + Kibanaでとりあえず検索できるようになるまで編

#はじめに#
こんにちは！GxPの[神原](https://qiita.com/ykanbara-gxp)です。
こちらは[グロースエクスパートナーズ アドベントカレンダー](https://qiita.com/advent-calendar/2020/gxp)の20日目の記事になります。
今回は業務の関係でElasticsearchを触ることになったので、実際の手順や詰まったところなどをお話ししていければなと思います。
範囲としてはElasticsearch自体の簡単な紹介と導入方法、実際の検索ができるぐらいまでを想定しています。
僕自身何も知らないところからのスタートでしたので、かなり初歩的な内容になってしまいますがご容赦ください。
今回は検索メインになるので、Elasticsearch+Kibanaの構成でやっていこうと思います。

#Elasticsearchとは#
* Elastic社が開発している全文検索エンジン
* ログ解析や検索、リアルタイムのデータ解析などが行える
* 他ツールを併用することで分析結果のグラフ表示やログの集約がしやすくなる
* RDBで言うところのデータベースを横断しての検索が可能

このあたりは[公式のページ](https://www.elastic.co/jp/what-is/elasticsearch)にいろいろ書いてあるので一旦割愛します。

#Kibanaとは#
* Elasticsearch内のデータを様々な形式で可視化できるツール
* GUIベースでの操作が可能


簡単にまとめると「データのグラフ化や分析に強いツール」という感じになります。

#RDBとの用語の違い#
ElasticsearchとRDBでは同じ単語でも意味が違ったり、別の単語に置き換わっていたりするので最初はちょっと違和感があるかもしれません。

| 用語| 説明 |
|---|---|
| Index（インデックス）  | データの保管場所。RDBMSにおけるデータベース|
| Document（ドキュメント）  | データ本体。RDBMSにおけるレコード。  |
|  Field（フィールド） | データフィールド。RDBMSにおけるカラム。  |
| Mapping（マッピング）  | indexでdocumentをどのようなfiled/データ型で登録しておくのかを定義する。RDBにおけるテーブル定義  |

#手順#
##1. 起動方法##
今回はDockerを使用して起動します。

###1. docker-compose.yamlを作成###


```言語:docker-compose.yaml
version: '2.2'
services:
  es01:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.9.3
    container_name: es01
    environment:
      - node.name=es01
      - cluster.name=es-docker-cluster
      - discovery.type=single-node
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - data01:/usr/share/elasticsearch/data
    ports:
      - 9200:9200
    networks:
      - elastic

  kib01:
    image: docker.elastic.co/kibana/kibana:7.9.3
    container_name: kib01
    ports:
      - 5601:5601
    environment:
      ELASTICSEARCH_URL: http://es01:9200
      ELASTICSEARCH_HOSTS: http://es01:9200
    networks:
      - elastic

volumes:
  data01:
    driver: local

networks:
  elastic:
    driver: bridge
```



###2.作成したyamlファイルのディレクトリに移動して起動###

```
$ docker-compose up
```

###3.下記URLにアクセスしてKibanaの画面が表示されれば成功！###

http://localhost:5601/
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/952273/29ac3350-2448-bf50-dde8-ce92e10e3d74.png)


##2.テストデータ投入##

ここからは実際にデータを投入していきます。

###1.下記のzipファイルをダウンロードし適当なところで展開する###
[accounts.zip](https://download.elastic.co/demos/kibana/gettingstarted/accounts.zip)
中に入っているJsonデータの構成はこちら

```accounts.json
{
    "account_number": INT,
    "balance": INT,
    "firstname": "String",
    "lastname": "String",
    "age": INT,
    "gender": "M or F",
    "address": "String",
    "employer": "String",
    "email": "String",
    "city": "String",
    "state": "String"
}
```

###2.accounts.jsonがあるディレクトリに移動し下記を実行###
今回は複数データを投入するのでBulkApiを使用します。

```
curl -H "Content-Type: application/x-ndjson" -XPOST http://localhost:9200/accounts/_bulk --data-binary "@accounts.json"
```

実行すると、投入されたデータがどうなったか1件ずつ返ってきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/952273/23c9da5d-90a5-3f28-1ad3-735c87c2c0b9.png)

これで検索対象のデータ投入完了になります！

##3.Kibanaから実際に検索してみる##
ここからはKibanaのコンソール上から操作をしていきます。
###1.KibanaのTOP画面からConsole画面へ移動###
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/952273/792972e2-8f1b-b266-694b-db9f5bedfac4.png)

###2.全件検索してみる###
では実際に先ほど投入したデータに対して検索してみます。
検索する場合はGETで行います。

```言語:全検索コマンド
GET accounts/_search
```

```言語:実行結果
{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1000,
      "relation" : "eq"
    },
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "accounts",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 1.0,
        "_source" : {
          "account_number" : 1,
          "balance" : 39225,
          "firstname" : "Amber",
          "lastname" : "Duke",
          "age" : 32,
          "gender" : "M",
          "address" : "880 Holmes Lane",
          "employer" : "Pyrami",
          "email" : "amberduke@pyrami.com",
          "city" : "Brogan",
          "state" : "IL"
        }
      },

 # 省略

　　]
  }
}

```

###3.検索条件を指定して検索###
今度はいくつか検索条件を指定した状態でやってみたいと思います。

####条件を１つ指定####
```言語:firstnameが"Aurelia"のデータを検索
GET accounts/_search
{
  "query": {
    "match": {
      "firstname": "Aurelia"
    }
  }
}
```

```言語:実行結果
{
  "took" : 3,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 6.5032897,
    "hits" : [
      {
        "_index" : "accounts",
        "_type" : "_doc",
        "_id" : "44",
        "_score" : 6.5032897,
        "_source" : {
          "account_number" : 44,
          "balance" : 34487,
          "firstname" : "Aurelia",
          "lastname" : "Harding",
          "age" : 37,
          "gender" : "M",
          "address" : "502 Baycliff Terrace",
          "employer" : "Orbalix",
          "email" : "aureliaharding@orbalix.com",
          "city" : "Yardville",
          "state" : "DE"
        }
      }
    ]
  }
}

```
####And検索####
Genderが"M"、ageが"31"、cityが"Vincent"のデータを検索します。
デフォルトだとその単語を含んでいるデータがすべて出てくる（部分一致検索）のですが、field名の後ろに```.keyword```を付けると完全一致検索にすることができます。

```
GET accounts/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "gender": "M"
          }
        },
        {
          "match": {
            "age": "31"
          }
        },
        {
          "match": {
            "city.keyword": "Vincent"
          }
        }
      ]
    }
  }
}
```
```言語:実行結果
{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 8.185206,
    "hits" : [
      {
        "_index" : "accounts",
        "_type" : "_doc",
        "_id" : "277",
        "_score" : 8.185206,
        "_source" : {
          "account_number" : 277,
          "balance" : 29564,
          "firstname" : "Romero",
          "lastname" : "Lott",
          "age" : 31,
          "gender" : "M",
          "address" : "456 Danforth Street",
          "employer" : "Plasto",
          "email" : "romerolott@plasto.com",
          "city" : "Vincent",
          "state" : "VT"
        }
      }
    ]
  }
}

```


#つまったところ#
一番詰まったのはRDBとElasticsearchでは単語の意味が全然違う部分でした。
なんとなく検索がたくさんできるRDBみたいなものかな？というノリで触り始めてしまったので、Indexと言われたときに全く別のものを想像してしまったりして理解するのに少し時間がかかってしまいました。

あとはベタな部分で、ElasticsearchとKibanaのバージョンがズレていて動かなかったりがありました。
この辺はちゃんと気を付けてれば回避できそうだったので今後は気を付けようと思います・・・。

#おわりに#
何も知らない状態からの作業だったので「結構時間かかっちゃうかな・・・」と思っていたんですが、意外と検索まではあまり手間がかからずにたどり着けたのでびっくりしました。
今回はMappingやIndexについてなどいろいろ端折って検索まで進めてしまったので、また何か書く機会があればそのあたりについても触れたいなと思います。