elasticsearch5.0.0でfieldsを指定したクエリを使うときは store:true をつけておくのがよさげ

# 概要

elasticsaerchで検索クエリで、取得したいfieldsを指定して投げるときの注意点という名の個人用備忘録。

# 結論

**2016/07/07 修正**
そもそも、クエリでfields指定するよりは、**_sourceからフィルタリングして**取れないかを検討する。

どうしても、クエリでfieldsを使いたいんや、という場合は・・・
(2.3だと、store:trueとしていないstringのフィールドでもfieldsでとれていた。）

- elasticsearch5.0以降で、クエリでfieldsを指定したいときは、mapping定義でstore:trueを入れておく
- stored_fieldsだとUnknown key for a START_ARRAY in [stored_fields].というエラーになった・・・



# 試した環境

|製品|バージョン|
|:--|----:|
|elasticsearch|5.0.0-alpha4|
|kibana|5.0.0-alpha4|


elasticsearchはalpineをベースにしたDockerイメージで動かす。


# ドキュメントの確認

## Search and Query DSL changes

[Search and Query DSL changes](https://www.elastic.co/guide/en/elasticsearch/reference/master/breaking_50_search_changes.html#_literal_fields_literal_parameter)によると、fields parameterのところには、こんな風に書いてある

> The fields parameter has been replaced by stored_fields. The stored_fields parameter will only return stored fields — it will no longer extract values from the _source.

たぶん、こういうことだと理解した。

- fieldsパラメータは、stored_fieldsに置き換えられた（ので、そっちを使うと良いよ）
- stored_fieldsパラメータは、storeされたフィールドしか返さないよ
- stored_fieldsパラメータは、_sourceフィールドから（結果を）抽出するわけじゃないよ

## stored_fieldsの確認

[Fields](https://www.elastic.co/guide/en/elasticsearch/reference/master/search-request-fields.html)を確認しますと、また気になる記述があります。

> The stored_fields parameter is about fields that are explicitly marked as stored in the mapping, which is off by default and generally not recommended. Use source filtering instead to select subsets of the original source document to be returned.

mappingでstoredと設定されたものだけが対象だよ。デフォルトだとオフになっているから、source filteringを使うと良いかもねかもねそうかもね。


## source filterringの確認

[Source Filtering](https://www.elastic.co/guide/en/elasticsearch/reference/master/search-request-source-filtering.html)で、sourceのフィルタリング方法を確認しておきます。



# いざ、実践

kibanaにあるConsoleを使っていろいろテストしてみます。

## テストデータの生成

```
PUT my_index
{
  "mappings": {
    "my_type": {
      "properties": {
        "title": { 
          "type": "text"
        },
        "first_name": {
          "type": "text",
          "store": true
        },
        "last_name": {
          "type": "text",
          "store": true
        }
      }
    }
  }
}

PUT my_index/my_type/1
{
  "title": "Mr",
  "first_name": "Barry",
  "last_name": "White"
}
```


## インデックスの確認

```
GET my_index/_search
{
  "query" : {
    "match_all": {}
  }
}
```
検索結果

```json
{
  "took": 2,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 1,
    "max_score": 1,
    "hits": [
      {
        "_index": "my_index",
        "_type": "my_type",
        "_id": "1",
        "_score": 1,
        "_source": {
          "title": "Mr",
          "first_name": "Barry",
          "last_name": "White"
        }
      }
    ]
  }
}
```

## fieldsを指定する

### storeを指定していないフィールドの場合

store: trueとしなかった、titleフィールドを指定してみると、こんな結果になります。

クエリ

```
GET my_index/_search
{
  "query" : {
    "match_all": {}
  },
  "fields": ["title"]
}
```

結果がこちら。titleがどこにもありませんが、1件ヒットしていることが確認できます。

```json
{
  "took": 1,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 1,
    "max_score": 1,
    "hits": [
      {
        "_index": "my_index",
        "_type": "my_type",
        "_id": "1",
        "_score": 1
      }
    ]
  }
}
```

### storeを指定したフィールドの場合

クエリ

```
GET my_index/_search
{
  "query" : {
    "match_all": {}
  },
  "fields": ["first_name", "last_name"]
}
```

結果がこちら。先ほどと異なり、first_nameとlast_nameが結果に含まれていることが確認できました。

```json
{
  "took": 1,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 1,
    "max_score": 1,
    "hits": [
      {
        "_index": "my_index",
        "_type": "my_type",
        "_id": "1",
        "_score": 1,
        "fields": {
          "last_name": [
            "White"
          ],
          "first_name": [
            "Barry"
          ]
        }
      }
    ]
  }
}
```

## stored_fieldsを指定する

うまく結果が得られたfieldsでのクエリを少し編集して、fieldsをstored_fieldsに変えました。

```
GET my_index/_search
{
  "query" : {
    "match_all": {}
  },
  "stored_fields": ["first_name", "last_name"]
}
```

結果がこちら。悲しいパースエラー。

```json
{
  "error": {
    "root_cause": [
      {
        "type": "parsing_exception",
        "reason": "Unknown key for a START_ARRAY in [stored_fields].",
        "line": 5,
        "col": 20
      }
    ],
    "type": "parsing_exception",
    "reason": "Unknown key for a START_ARRAY in [stored_fields].",
    "line": 5,
    "col": 20
  },
  "status": 400
}
```

## sourceのfilterringを使う

store:trueを指定していなかったtitleと指定したlast_nameの２つを指定してみます。

```
GET my_index/_search
{
  "query" : {
    "match_all": {}
  },
  "_source": ["title", "last_name"]
}
```

結果がこちら。_sourceの中で指定したフィールドに絞られて結果が得られていることが確認できました。

```json
{
  "took": 1,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 1,
    "max_score": 1,
    "hits": [
      {
        "_index": "my_index",
        "_type": "my_type",
        "_id": "1",
        "_score": 1,
        "_source": {
          "last_name": "White",
          "title": "Mr"
        }
      }
    ]
  }
}
```


# おまけ

2.3系でfieldsを試したときのsense

```
PUT my_index
{
  "mappings": {
    "my_type": {
      "properties": {
        "title": { 
          "type": "string"
        },
        "first_name": {
          "type": "string",
          "store": true
        },
        "last_name": {
          "type": "string",
          "store": true
        }
      }
    }
  }
}

PUT my_index/my_type/1
{
  "title": "Mr",
  "first_name": "Barry",
  "last_name": "White"
}

GET my_index/_search
{
  "query" : {
    "match_all": {}
  },
  "fields": ["title", "last_name"]
}

```

これで、結果はtitleとlast_nameと両方入る形。

```json
{
  "took": 3,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 1,
    "max_score": 1,
    "hits": [
      {
        "_index": "my_index",
        "_type": "my_type",
        "_id": "1",
        "_score": 1,
        "fields": {
          "title": [
            "Mr"
          ],
          "last_name": [
            "White"
          ]
        }
      }
    ]
  }
}
```