ElasticsearchでNested Objectを試す


Elasticsearchのドキュメント内にあった、Nested Objectsを実際に、senseで実行します。

# indexの作成

```json
PUT /my_index
{
  "mappings": {
    "blogpost": {
      "properties": {
        "comments": {
          "type": "nested", 
          "properties": {
            "name":    { "type": "string"  },
            "comment": { "type": "string"  },
            "age":     { "type": "short"   },
            "stars":   { "type": "short"   },
            "date":    { "type": "date"    }
          }
        }
      }
    }
  }
}
```

# データ挿入

```json
PUT /my_index/blogpost/1
{
  "title": "Nest eggs",
  "body":  "Making your money work...",
  "tags":  [ "cash", "shares" ],
  "comments": [ 
    {
      "name":    "John Smith",
      "comment": "Great article",
      "age":     28,
      "stars":   4,
      "date":    "2014-09-01"
    },
    {
      "name":    "Alice White",
      "comment": "More like this please",
      "age":     31,
      "stars":   5,
      "date":    "2014-10-22"
    }
  ]
}

PUT /my_index/blogpost/2
{
  "title": "Investment secrets",
  "body":  "What they don't tell you ...",
  "tags":  [ "shares", "equities" ],
  "comments": [
    {
      "name":    "Mary Brown",
      "comment": "Lies, lies, lies",
      "age":     42,
      "stars":   1,
      "date":    "2014-10-18"
    },
    {
      "name":    "John Smith",
      "comment": "You're making it up!",
      "age":     28,
      "stars":   2,
      "date":    "2014-10-16"
    }
  ]
}
```

# 検索

ブログのタイトルがeggsが含まれており、johnという名前の含まれた28歳がコメントしたものを検索します。Array Objectと違って、どちらも満たしていないと検索結果に現れません。

```json
# クエリ
GET /my_index/blogpost/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "title": "eggs" 
          }
        },
        {
          "nested": {
            "path": "comments", 
            "query": {
              "bool": {
                "must": [ 
                  {
                    "match": {
                      "comments.name": "john"
                    }
                  },
                  {
                    "match": {
                      "comments.age": 28
                    }
                  }
                ]
              }
            }
          }
        }
      ]
}}}

# 結果
{
  "took": 1,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 1,
    "max_score": 1.8257527,
    "hits": [
      {
        "_index": "my_index",
        "_type": "blogpost",
        "_id": "1",
        "_score": 1.8257527,
        "_source": {
          "title": "Nest eggs",
          "body": "Making your money work...",
          "tags": [
            "cash",
            "shares"
          ],
          "comments": [
            {
              "name": "John Smith",
              "comment": "Great article",
              "age": 28,
              "stars": 4,
              "date": "2014-09-01"
            },
            {
              "name": "Alice White",
              "comment": "More like this please",
              "age": 31,
              "stars": 5,
              "date": "2014-10-22"
            }
          ]
        }
      }
    ]
  }
}
```

ソートも試しています。

```json
GET /my_index/blogpost/_search
{
  "query": {
    "nested": { 
      "path": "comments",
      "filter": {
        "range": {
          "comments.date": {
            "gte": "2014-10-01",
            "lt":  "2014-11-01"
          }
        }
      }
    }
  },
  "sort": {
    "comments.stars": { 
      "order": "asc",   
      "mode":  "min",   
      "nested_filter": { 
        "range": {
          "comments.date": {
            "gte": "2014-10-01",
            "lt":  "2014-11-01"
          }
        }
      }
    }
  }
}
```

# Aggregation

コメントの平均スター数を月別に取得します。

```json
# クエリ
GET /my_index/blogpost/_search
{
  "size" : 0,
  "aggs": {
    "comments": { 
      "nested": {
        "path": "comments"
      },
      "aggs": {
        "by_month": {
          "date_histogram": { 
            "field":    "comments.date",
            "interval": "month",
            "format":   "yyyy-MM"
          },
          "aggs": {
            "avg_stars": {
              "avg": { 
                "field": "comments.stars"
              }
            }
          }
        }
      }
    }
  }
}

# 結果
{
  "took": 20,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 2,
    "max_score": 0,
    "hits": []
  },
  "aggregations": {
    "comments": {
      "doc_count": 4,
      "by_month": {
        "buckets": [
          {
            "key_as_string": "2014-09",
            "key": 1409529600000,
            "doc_count": 1,
            "avg_stars": {
              "value": 4
            }
          },
          {
            "key_as_string": "2014-10",
            "key": 1412121600000,
            "doc_count": 3,
            "avg_stars": {
              "value": 2.6666666666666665
            }
          }
        ]
      }
    }
  }
}
```

# reverse_nested Aggregation

ネスト検索された結果に対して、ネストされているフィールドの外側（今回だと、tags)の集計を取得できる。


```json
# クエリ
GET /my_index/blogpost/_search
{
  "size" : 0,
  "aggs": {
    "comments": {
      "nested": { 
        "path": "comments"
      },
      "aggs": {
        "age_group": {
          "histogram": { 
            "field":    "comments.age",
            "interval": 10
          },
          "aggs": {
            "blogposts": {
              "reverse_nested": {}, 
              "aggs": {
                "tags": {
                  "terms": { 
                    "field": "tags"
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

# 結果
{
  "took": 8,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 2,
    "max_score": 0,
    "hits": []
  },
  "aggregations": {
    "comments": {
      "doc_count": 4,
      "age_group": {
        "buckets": [
          {
            "key": 20,
            "doc_count": 2,
            "blogposts": {
              "doc_count": 2,
              "tags": {
                "doc_count_error_upper_bound": 0,
                "sum_other_doc_count": 0,
                "buckets": [
                  {
                    "key": "shares",
                    "doc_count": 2
                  },
                  {
                    "key": "cash",
                    "doc_count": 1
                  },
                  {
                    "key": "equities",
                    "doc_count": 1
                  }
                ]
              }
            }
          },
          {
            "key": 30,
            "doc_count": 1,
            "blogposts": {
              "doc_count": 1,
              "tags": {
                "doc_count_error_upper_bound": 0,
                "sum_other_doc_count": 0,
                "buckets": [
                  {
                    "key": "cash",
                    "doc_count": 1
                  },
                  {
                    "key": "shares",
                    "doc_count": 1
                  }
                ]
              }
            }
          },
          {
            "key": 40,
            "doc_count": 1,
            "blogposts": {
              "doc_count": 1,
              "tags": {
                "doc_count_error_upper_bound": 0,
                "sum_other_doc_count": 0,
                "buckets": [
                  {
                    "key": "equities",
                    "doc_count": 1
                  },
                  {
                    "key": "shares",
                    "doc_count": 1
                  }
                ]
              }
            }
          }
        ]
      }
    }
  }
}
```

# 参考
* https://www.elastic.co/guide/en/elasticsearch/guide/current/nested-objects.html