BucketとMetricsを使ってみる

Bucket と Metrics を使って、検索ではなくドキュメントの情報を分析できるようです。

Bucket と Metrics はどちらも、Aggregation と言われるものの一部みたいですね。

https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations.html

それぞれ単体でも使用できますが、一般的には合わせて使うケースのほうが多そうです。

ドキュメントを Bucket でグループ化し、Metrics で分析を行う、というのが一般的な使い方ではないでしょうか。

ドキュメントの作成

item インデックスに、いくつかドキュメントを保存しました。

PUT item
{
  "mappings": {
    "properties": {
      "name": {
        "type": "text"
      },
      "category": {
        "type": "keyword"
      },
      "price": {
        "type": "long"
      }
    }
  }
}

POST item/_doc
{
  "name": "soba",
  "category": "noodle",
  "price": 1000
}

POST item/_doc
{
  "name": "ramen",
  "category": "noodle",
  "price": 1500
}

POST item/_doc
{
  "name": "lettuce",
  "category": "vegetable",
  "price": 2000
}

POST item/_doc
{
  "name": "bacon",
  "category": "meat",
  "price": 2500
}

Buket と Metrics を使って分析してみる

まずは Bucket を使って、ドキュメントをグループ化してみます。 _search エンドポイントで Aggregation を使えます。

Bucket ごとにまとめただけでは、デフォルトでドキュメント数のみが返されます。

GET item/_search
{
  "size": 0,
  "aggs": { <-- Aggregation を使う
    "category_bucket": {
      "terms": { <-- Keyword 型のフィールドに対し、指定する値に一致するグループにドキュメントを分類する
        "field": "category" <-- category でドキュメントをグループ化する
      }
    }
  }
}

// レスポンス
{
  "took": 14,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 4,
      "relation": "eq"
    },
    "max_score": null,
    "hits": []
  },
  "aggregations": {
    "category_bucket": {
      "doc_count_error_upper_bound": 0,
      "sum_other_doc_count": 0,
      "buckets": [ <-- category ごとのドキュメント数が返ってくる
        {
          "key": "noodle",
          "doc_count": 2
        },
        {
          "key": "meat",
          "doc_count": 1
        },
        {
          "key": "vegetable",
          "doc_count": 1
        }
      ]
    }
  }
}

値の幅でBucketを使うこともできます。

GET item/_search
{
  "size": 0,
  "aggs": {
    "price_bucket": {
      "range": {
        "field": "price",
        "ranges": [
          { "to": 1000 },
          { "from": 1000, "to": 2000 },
          { "from": 2000, "to": 3000 }
        ]
      }
    }
  }
}

// レスポンス
{
  "took": 1,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 4,
      "relation": "eq"
    },
    "max_score": null,
    "hits": []
  },
  "aggregations": {
    "price_bucket": {
      "buckets": [
        {
          "key": "*-1000.0", <-- グループ化された値の幅を示す
          "to": 1000,
          "doc_count": 0
        },
        {
          "key": "1000.0-2000.0",
          "from": 1000,
          "to": 2000,
          "doc_count": 2
        },
        {
          "key": "2000.0-3000.0",
          "from": 2000,
          "to": 3000,
          "doc_count": 2
        }
      ]
    }
  }
}

最後にMetricsを合わせて使ってみます。

Aggregationは入れ子に指定できるようで、以下のようにaggsの中にaggsを使います。

つぎのAggregationはcategoryごとのpriceの平均値を取得します。

GET item/_search
{
  "size": 0,
  "aggs": {
    "category_bucket": {
      "terms": {
        "field": "category"
      },
      "aggs": {
        "price_avg": {
          "avg": {
            "field": "price"
          }
        }
      }
    }
  }
}

// レスポンス
{
  "took": 1,
  "timed_out": false,
  "_shards": {
    "total": 1,
    "successful": 1,
    "skipped": 0,
    "failed": 0
  },
  "hits": {
    "total": {
      "value": 4,
      "relation": "eq"
    },
    "max_score": null,
    "hits": []
  },
  "aggregations": {
    "category_bucket": {
      "doc_count_error_upper_bound": 0,
      "sum_other_doc_count": 0,
      "buckets": [
        {
          "key": "noodle",
          "doc_count": 2,
          "price_avg": {
            "value": 1250 <-- categoryごとのpriceの平均値
          }
        },
        {
          "key": "meat",
          "doc_count": 1,
          "price_avg": {
            "value": 2500
          }
        },
        {
          "key": "vegetable",
          "doc_count": 1,
          "price_avg": {
            "value": 2000
          }
        }
      ]
    }
  }
}