ElasticSearchの検索クエリに関して（基礎編）

#ElasticSearchの検索クエリに関して
## 目的
ElasticSearchの基本的な検索のやり方の説明と記述の仕方を説明する。


## 使用環境
- Amazon Elasticsearch Service
 - Elasticsearch6.4
 - Kibana6.4


##用語説明
###Index 
索引。
このindexに検索対称のドキュメントを格納。Indexは複数もつことが可能。
例：先頭にuser_price_index_がつく**Index**が1日ごとに作られる

- user_price_index_20190501
- user_price_index_20190502
- user_price_index_20190503
- user_price_index_20190504

###Type  

リレーショナルデータベースでいえばテーブルに相当。
バージョン6以降は廃止される。


###Document 
格納されたドキュメント。
リレーショナルデータベースでいえば1レコードに相当。




##サンプルログ



ユーザーがとあるお店で、商品を購入したと想定
東スーパー、西コンビニ、南商店、北デパートと4つのお店が存在し、以下の商品を購入

| 商品名       |       単価 |
|:-----------------|:------------------|
|キノコの山| 120円|
|コアラのマーチ | 92円|
|杉のこ村| 111円|
|たけのこの里|140円|
|ハーゲンダッツ|1000円|



ログはjson形式で登録されており以下のような構造になっている。

### ユーザーがお店に行ったら作られるログ

```json
{"user_id": 52, "kind_name": "アクセス", "place": "北デパート", "@timestamp": "2019-06-13T19:55:23+09:00:00"}
```

### ユーザーが購入したら作られるログ

```json
{"user_id": 43, "kind_name": "購入", "price": 140, "place": "南商店", "total_price": 280, "total_price_with_tax": 302.4, "product": "たけのこの里", "p_value": 2, "@timestamp": "2019-06-29T05:42:17+09:00:00"}
```



ログは1日ごとに作成されるuser_price_index_yyymmddという名前の**Index**に**document**として記録される。[^sanko1]
[^sanko1]: yyymmddには、ログを記録した日付が入る。例えば、2019-06-01に作成されたログなら20190601

##検索の基本

ElasticSeachは　レコードの処理（検索、作成、更新、削除処理）が可能なREST APIが用意されているのでそれを利用する。

Kibana のDevToolを利用する場合は以下のように記述して、実行すれば検索可能。
検索パラメータははjson形式で記述する。

```
GET user_price_index_*/_search
{
  "size":3,
  "query": {
           ....
  }
}
```
**size**で取得件数を指定し、**query**内に検索条件を記述する


##検索クエリ

自分がよく使う検索クエリとそのサンプルを紹介。

###term

指定したフィールドを完全一致検索する。

```json:term
"term":{
    "フィールド":検索値
}
```

文字列のフィールドを完全一致検索する場合はフィールド名に*.keyword*をつける。

```json:term(テキスト検索の場合)
"term":{
    "フィールド.keyword":"検索値"
}
```



　
例：productが「杉のこ村」の**document**を検索する場合の検索クエリ

```json:リクエスト(productが「杉のこ村」の**document**を検索する場合の検索クエリ)
GET user_price_index_*/_search
{
  "size":3,
  "query": {
    "term":{
      "product.keyword":"杉のこ村"
    }
  }
}
```


<details><summary>検索結果</summary><div>

```json:検索結果(productが「杉のこ村」の**document**を検索する場合の検索クエリ)
{
  "took": 60,
  "timed_out": false,
  "_shards": {
    "total": 145,
    "successful": 145,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 938,
    "max_score": 2.9618306,
    "hits": [
      {
        "_index": "user_price_index_20190608",
        "_type": "_doc",
        "_id": "BBmGxGoBzDMFYAVDLKj9",
        "_score": 2.9618306,
        "_source": {
          "user_id": 60,
          "kind_name": "購入",
          "price": 111,
          "place": "西コンビニ",
          "total_price": 444,
          "total_price_with_tax": 479.52,
          "product": "杉のこ村",
          "p_value": 4,
          "@timestamp": "2019-06-08T06:45:22+09:00:00"
        }
      },
      {
        "_index": "user_price_index_20190602",
        "_type": "_doc",
        "_id": "qxmIxGoBzDMFYAVDcLq0",
        "_score": 2.7725887,
        "_source": {
          "user_id": 1,
          "kind_name": "購入",
          "price": 111,
          "place": "西コンビニ",
          "total_price": 444,
          "total_price_with_tax": 479.52,
          "product": "杉のこ村",
          "p_value": 4,
          "@timestamp": "2019-06-02T07:30:48+09:00:00"
        }
      },
      {
        "_index": "user_price_index_20190604",
        "_type": "_doc",
        "_id": "ShmHxGoBzDMFYAVDqrUM",
        "_score": 2.7725887,
        "_source": {
          "user_id": 56,
          "kind_name": "購入",
          "price": 111,
          "place": "南商店",
          "total_price": 555,
          "total_price_with_tax": 599.4,
          "product": "杉のこ村",
          "p_value": 5,
          "@timestamp": "2019-06-04T20:21:35+09:00:00"
        }
      }
    ]
  }
}
```
</div></details>
###terms

**term**の複数条件版。

```json:terms
"terms":{
    "フィールド":[検索値1, 検索値2,...]
}
```

文字列のフィールドを完全一致検索する場合はフィールド名に*.keyword*をつける。

```json:terms(テキスト検索の場合)
"terms":{
    "フィールド.keyword":["検索値1","検索値2",...]
}
```


例：productが「杉のこ村」と「きのこの山」のdocumentを検索する場合の検索クエリ

```json:リクエスト(productが「杉のこ村」と「きのこの山」の**document**を検索する場合の検索クエリ)
GET user_price_index_*/_search
{
  "size":3,
  "query": {
    "terms":{
      "product.keyword":["杉のこ村","キノコの山"]
    }
  }
}
```



<details><summary>検索結果</summary><div>

```json:検索結果(productが「杉のこ村」と「きのこの山」のdocumentを検索する場合の検索クエリ)
{
  "took": 18,
  "timed_out": false,
  "_shards": {
    "total": 145,
    "successful": 145,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 1935,
    "max_score": 1,
    "hits": [
      {
        "_index": "user_price_index_20190601",
        "_type": "_doc",
        "_id": "0BmGxGoBzDMFYAVDLKX5",
        "_score": 1,
        "_source": {
          "user_id": 23,
          "kind_name": "購入",
          "price": 111,
          "place": "東スーパー",
          "total_price": 222,
          "total_price_with_tax": 239.76,
          "product": "杉のこ村",
          "p_value": 2,
          "@timestamp": "2019-06-01T19:19:35+09:00:00"
        }
      },
      {
        "_index": "user_price_index_20190601",
        "_type": "_doc",
        "_id": "_BmGxGoBzDMFYAVDLKj9",
        "_score": 1,
        "_source": {
          "user_id": 57,
          "kind_name": "購入",
          "price": 111,
          "place": "南商店",
          "total_price": 333,
          "total_price_with_tax": 359.64,
          "product": "杉のこ村",
          "p_value": 3,
          "@timestamp": "2019-06-01T14:38:21+09:00:00"
        }
      },
      {
        "_index": "user_price_index_20190601",
        "_type": "_doc",
        "_id": "KxmHxGoBzDMFYAVDqq0K",
        "_score": 1,
        "_source": {
          "user_id": 34,
          "kind_name": "購入",
          "price": 120,
          "place": "南商店",
          "total_price": 120,
          "total_price_with_tax": 129.6,
          "product": "キノコの山",
          "p_value": 1,
          "@timestamp": "2019-06-01T21:02:55+09:00:00"
        }
      }
    ]
  }
}
```
</div></details>

###range
検索条件を範囲指定できるクエリ。

```json:range
"range":{
  "フィールド名":{
    "gt": 範囲開始(境界含まない),
    "gte": 範囲開始(境界含む),
    "lt": 範囲終了(境界含まない),
    "lte": 範囲終了(境界含む),

}
```

検索条件が時間の場合

```json:range(検索条件が時間の場合)
"range":{
  "フィールド名":{
    "time_zone": "タイムゾーン",
    "gt": 範囲開始(境界含まない),
    "gte": 範囲開始(境界含む),
    "lt": 範囲終了(境界含まない),
    "lte": 範囲終了(境界含む),
}
```




例：total_priceが500以上、1000未満の**document**を検索する場合の検索クエリ

```json:リクエスト(total_priceが500以上、1000未満の**document**を検索する場合の検索クエリ)

GET user_price_index_*/_search
{
  "size":3,
  "query": {
    "range":{
      "total_price":{
        "gte": 500,
        "lt": 1000
      }
    }
  }
}
```


<details><summary>検索結果</summary><div>

```json:検索結果(total_priceが500以上、1000未満の**document**を検索する場合の検索クエリ)
{
  "took": 79,
  "timed_out": false,
  "_shards": {
    "total": 145,
    "successful": 145,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": 817,
    "max_score": 1,
    "hits": [
      {
        "_index": "user_price_index_20190601",
        "_type": "_doc",
        "_id": "GRmHxGoBzDMFYAVDqrAL",
        "_score": 1,
        "_source": {
          "user_id": 81,
          "kind_name": "購入",
          "price": 140,
          "place": "南商店",
          "total_price": 700,
          "total_price_with_tax": 756,
          "product": "たけのこの里",
          "p_value": 5,
          "@timestamp": "2019-06-01T06:34:09+09:00:00"
        }
      },
      {
        "_index": "user_price_index_20190601",
        "_type": "_doc",
        "_id": "oRmHxGoBzDMFYAVDqrUM",
        "_score": 1,
        "_source": {
          "user_id": 59,
          "kind_name": "購入",
          "price": 140,
          "place": "東スーパー",
          "total_price": 700,
          "total_price_with_tax": 756,
          "product": "たけのこの里",
          "p_value": 5,
          "@timestamp": "2019-06-01T22:19:17+09:00:00"
        }
      },
      {
        "_index": "user_price_index_20190601",
        "_type": "_doc",
        "_id": "yBmIxGoBzDMFYAVDcLez",
        "_score": 1,
        "_source": {
          "user_id": 57,
          "kind_name": "購入",
          "price": 140,
          "place": "南商店",
          "total_price": 560,
          "total_price_with_tax": 604.8,
          "product": "たけのこの里",
          "p_value": 4,
          "@timestamp": "2019-06-01T23:42:33+09:00:00"
        }
      }
    ]
  }
}
```
</div></details>


例：2019-06-28に記録されたログを検索する場合のクエリ

```json:リクエスト(2019-06-28に記録されたログを検索する場合のクエリ)

GET user_price_index_*/_search
{
  "size":3,
  "query": {
    "range":{
      "@timestamp":{
        "time_zone": "Asia/Tokyo",
        "gte": "2019-06-28T00:00:00",
        "lt": "2019-06-29T00:00:00"
      }
    }
  }
}
```

<details><summary>検索結果</summary><div>

```json:検索結果(2019-06-28に記録されたログを検索する場合のクエリ)
{
  "took": 5,
  "timed_out": false,
  "_shards": {
    "total": 145,
    "successful": 145,
    "skipped": 140,
    "failed": 0
  },
  "hits": {
    "total": 217,
    "max_score": 1,
    "hits": [
      {
        "_index": "user_price_index_20190628",
        "_type": "_doc",
        "_id": "mhmGxGoBzDMFYAVDLKLc",
        "_score": 1,
        "_source": {
          "user_id": 83,
          "kind_name": "アクセス",
          "place": "北デパート",
          "@timestamp": "2019-06-28T06:41:56+09:00:00"
        }
      },
      {
        "_index": "user_price_index_20190628",
        "_type": "_doc",
        "_id": "JBmGxGoBzDMFYAVDLKPc",
        "_score": 1,
        "_source": {
          "user_id": 1,
          "kind_name": "アクセス",
          "place": "西コンビニ",
          "@timestamp": "2019-06-28T04:16:37+09:00:00"
        }
      },
      {
        "_index": "user_price_index_20190628",
        "_type": "_doc",
        "_id": "pBmGxGoBzDMFYAVDLKPc",
        "_score": 1,
        "_source": {
          "user_id": 57,
          "kind_name": "購入",
          "price": 120,
          "place": "西コンビニ",
          "total_price": 360,
          "total_price_with_tax": 388.8,
          "product": "キノコの山",
          "p_value": 3,
          "@timestamp": "2019-06-28T09:32:34+09:00:00"
        }
      }
    ]
  }
}

```
</div></details>


#ElasticSearchシリーズ
- ElasticSearchの検索クエリに関して（基礎編）
- [ElaticSearchの検索クエリに関して（応用編）] (https://qiita.com/horankey_jet_city/private/67bb73b42014cfb99f27)
- [ElasticSearchの集約クエリに関して（基礎編）] (https://qiita.com/horankey_jet_city/private/94eda5046a8fe1febe45)
- [ElasticSearchの集約クエリに関して（応用？編）](https://qiita.com/horankey_jet_city/private/c193c7785465f73a17a0)