ヒストグラム集約

文書から抽出された数値または数値範囲の値に適用できるマルチバケット値ソースに基づく集約です。値に対して固定サイズ(別名:間隔)のバケットを動的に構築します。たとえば、文書に価格(数値)を保持するフィールドがある場合、この集約を構成して、間隔 5 でバケットを動的に構築できます(価格の場合、$5を表すことがあります)。集約が実行されると、各文書の価格フィールドが評価され、最も近いバケットに切り捨てられます。たとえば、価格が 32 でバケットサイズが 5 の場合、切り捨ては 30 になり、したがって文書はキー 30 に関連付けられたバケットに「落ちる」ことになります。これをより正式にするために、使用される切り捨て関数は次のとおりです:

Java

  1. bucket_key = Math.floor((value - offset) / interval) * interval + offset

範囲値の場合、文書は複数のバケットに落ちることがあります。最初のバケットは、単一の値のバケットが計算されるのと同じ方法で、範囲の下限から計算されます。最終バケットは、範囲の上限から同じ方法で計算され、その範囲はその2つの間およびそれを含むすべてのバケットでカウントされます。

interval は正の小数でなければならず、offset[0, interval) の小数でなければなりません(0 以上かつ interval 未満の小数)

次のスニペットは、price によって製品を 50 の間隔で「バケット化」します:

Python

  1. resp = client.search(
  2. index="sales",
  3. size="0",
  4. aggs={
  5. "prices": {
  6. "histogram": {
  7. "field": "price",
  8. "interval": 50
  9. }
  10. }
  11. },
  12. )
  13. print(resp)

Ruby

  1. response = client.search(
  2. index: 'sales',
  3. size: 0,
  4. body: {
  5. aggregations: {
  6. prices: {
  7. histogram: {
  8. field: 'price',
  9. interval: 50
  10. }
  11. }
  12. }
  13. }
  14. )
  15. puts response

Js

  1. const response = await client.search({
  2. index: "sales",
  3. size: 0,
  4. aggs: {
  5. prices: {
  6. histogram: {
  7. field: "price",
  8. interval: 50,
  9. },
  10. },
  11. },
  12. });
  13. console.log(response);

コンソール

  1. POST /sales/_search?size=0
  2. {
  3. "aggs": {
  4. "prices": {
  5. "histogram": {
  6. "field": "price",
  7. "interval": 50
  8. }
  9. }
  10. }
  11. }

次のような応答があるかもしれません:

コンソール-結果

  1. {
  2. ...
  3. "aggregations": {
  4. "prices": {
  5. "buckets": [
  6. {
  7. "key": 0.0,
  8. "doc_count": 1
  9. },
  10. {
  11. "key": 50.0,
  12. "doc_count": 1
  13. },
  14. {
  15. "key": 100.0,
  16. "doc_count": 0
  17. },
  18. {
  19. "key": 150.0,
  20. "doc_count": 2
  21. },
  22. {
  23. "key": 200.0,
  24. "doc_count": 3
  25. }
  26. ]
  27. }
  28. }
  29. }

最小文書数

上記の応答は、[100, 150) の範囲内に価格がある文書がないことを示しています。デフォルトでは、応答はヒストグラムのギャップを空のバケットで埋めます。それを変更し、min_doc_count 設定のおかげで、より高い最小カウントのバケットを要求することが可能です:

Python

  1. resp = client.search(
  2. index="sales",
  3. size="0",
  4. aggs={
  5. "prices": {
  6. "histogram": {
  7. "field": "price",
  8. "interval": 50,
  9. "min_doc_count": 1
  10. }
  11. }
  12. },
  13. )
  14. print(resp)

Ruby

  1. response = client.search(
  2. index: 'sales',
  3. size: 0,
  4. body: {
  5. aggregations: {
  6. prices: {
  7. histogram: {
  8. field: 'price',
  9. interval: 50,
  10. min_doc_count: 1
  11. }
  12. }
  13. }
  14. }
  15. )
  16. puts response

Js

  1. const response = await client.search({
  2. index: "sales",
  3. size: 0,
  4. aggs: {
  5. prices: {
  6. histogram: {
  7. field: "price",
  8. interval: 50,
  9. min_doc_count: 1,
  10. },
  11. },
  12. },
  13. });
  14. console.log(response);

コンソール

  1. POST /sales/_search?size=0
  2. {
  3. "aggs": {
  4. "prices": {
  5. "histogram": {
  6. "field": "price",
  7. "interval": 50,
  8. "min_doc_count": 1
  9. }
  10. }
  11. }
  12. }

応答:

コンソール-結果

  1. {
  2. ...
  3. "aggregations": {
  4. "prices": {
  5. "buckets": [
  6. {
  7. "key": 0.0,
  8. "doc_count": 1
  9. },
  10. {
  11. "key": 50.0,
  12. "doc_count": 1
  13. },
  14. {
  15. "key": 150.0,
  16. "doc_count": 2
  17. },
  18. {
  19. "key": 200.0,
  20. "doc_count": 3
  21. }
  22. ]
  23. }
  24. }
  25. }

デフォルトでは、histogram はデータ自体の範囲内のすべてのバケットを返します。つまり、最小値を持つ文書(ヒストグラムに基づく)は最小バケット(最小キーを持つバケット)を決定し、最大値を持つ文書は最大バケット(最大キーを持つバケット)を決定します。空のバケットを要求する際、これは混乱を引き起こすことがよくあります。特に、データがフィルタリングされている場合にそうです。

なぜそうなるのかを理解するために、例を見てみましょう:

リクエストをフィルタリングして、0500 の間の値を持つすべての文書を取得し、さらに価格ごとにヒストグラムを使用してデータをスライスしたいとします。間隔は 50 です。また、空のバケットも取得したいので、"min_doc_count" : 0 を指定します。すべての製品(文書)の価格が 100 より高い場合、最初に取得するバケットはキーが 100 のものになります。これは混乱を引き起こします。なぜなら、多くの場合、0 - 100 の間のバケットも取得したいからです。

extended_bounds 設定を使用すると、特定の min 値でヒストグラム集約を「強制的に」開始し、max 値までバケットを構築し続けることができます(文書がもう存在しなくても)。extended_bounds を使用するのは、min_doc_count が 0 の場合にのみ意味があります(min_doc_count が 0 より大きい場合、空のバケットは決して返されません)。

(名前が示すように)extended_bounds はバケットをフィルタリングしていないことに注意してください。つまり、extended_bounds.min が文書から抽出された値よりも高い場合、文書は最初のバケットを決定します(extended_bounds.max と最後のバケットについても同様です)。バケットをフィルタリングするには、適切な from/to 設定を持つ範囲 filter 集約の下にヒストグラム集約をネストする必要があります。

例:

Python

  1. resp = client.search(
  2. index="sales",
  3. size="0",
  4. query={
  5. "constant_score": {
  6. "filter": {
  7. "range": {
  8. "price": {
  9. "to": "500"
  10. }
  11. }
  12. }
  13. }
  14. },
  15. aggs={
  16. "prices": {
  17. "histogram": {
  18. "field": "price",
  19. "interval": 50,
  20. "extended_bounds": {
  21. "min": 0,
  22. "max": 500
  23. }
  24. }
  25. }
  26. },
  27. )
  28. print(resp)

Ruby

  1. response = client.search(
  2. index: 'sales',
  3. size: 0,
  4. body: {
  5. query: {
  6. constant_score: {
  7. filter: {
  8. range: {
  9. price: {
  10. to: '500'
  11. }
  12. }
  13. }
  14. }
  15. },
  16. aggregations: {
  17. prices: {
  18. histogram: {
  19. field: 'price',
  20. interval: 50,
  21. extended_bounds: {
  22. min: 0,
  23. max: 500
  24. }
  25. }
  26. }
  27. }
  28. }
  29. )
  30. puts response

コンソール

  1. POST /sales/_search?size=0
  2. {
  3. "query": {
  4. "constant_score": { "filter": { "range": { "price": { "to": "500" } } } }
  5. },
  6. "aggs": {
  7. "prices": {
  8. "histogram": {
  9. "field": "price",
  10. "interval": 50,
  11. "extended_bounds": {
  12. "min": 0,
  13. "max": 500
  14. }
  15. }
  16. }
  17. }
  18. }

範囲を集約する場合、バケットは返された文書の値に基づいています。これは、応答にクエリの範囲外のバケットが含まれる可能性があることを意味します。たとえば、クエリが100より大きい値を探していて、範囲が50から150までで、間隔が50の場合、その文書は3つのバケット - 50、100、および150に入ります。一般的に、クエリと集約のステップは独立していると考えるのが最善です。クエリは文書のセットを選択し、その後、集約は選択された方法に関係なくそれらの文書をバケット化します。詳細と例については、範囲フィールドのバケット化に関する注意を参照してください。

hard_boundsextended_bounds の対となり、ヒストグラム内のバケットの範囲を制限できます。これは、非常に多くのバケットを生成する可能性のあるオープン データ範囲 の場合に特に便利です。

例:

Python

  1. resp = client.search(
  2. index="sales",
  3. size="0",
  4. query={
  5. "constant_score": {
  6. "filter": {
  7. "range": {
  8. "price": {
  9. "to": "500"
  10. }
  11. }
  12. }
  13. }
  14. },
  15. aggs={
  16. "prices": {
  17. "histogram": {
  18. "field": "price",
  19. "interval": 50,
  20. "hard_bounds": {
  21. "min": 100,
  22. "max": 200
  23. }
  24. }
  25. }
  26. },
  27. )
  28. print(resp)

Ruby

  1. response = client.search(
  2. index: 'sales',
  3. size: 0,
  4. body: {
  5. query: {
  6. constant_score: {
  7. filter: {
  8. range: {
  9. price: {
  10. to: '500'
  11. }
  12. }
  13. }
  14. }
  15. },
  16. aggregations: {
  17. prices: {
  18. histogram: {
  19. field: 'price',
  20. interval: 50,
  21. hard_bounds: {
  22. min: 100,
  23. max: 200
  24. }
  25. }
  26. }
  27. }
  28. }
  29. )
  30. puts response

コンソール

  1. POST /sales/_search?size=0
  2. {
  3. "query": {
  4. "constant_score": { "filter": { "range": { "price": { "to": "500" } } } }
  5. },
  6. "aggs": {
  7. "prices": {
  8. "histogram": {
  9. "field": "price",
  10. "interval": 50,
  11. "hard_bounds": {
  12. "min": 100,
  13. "max": 200
  14. }
  15. }
  16. }
  17. }
  18. }

この例では、クエリで指定された範囲が500までであるにもかかわらず、ヒストグラムには100と150から始まる2つのバケットのみが含まれます。他のすべてのバケットは、省略されます。たとえそのバケットに入るべき文書が結果に存在していても。

順序

デフォルトでは、返されたバケットはその key に従って昇順にソートされますが、順序の動作は order 設定を使用して制御できます。Terms Aggregation と同じ order 機能をサポートします。

オフセット

デフォルトでは、バケットキーは0から始まり、その後 interval の均等な間隔で続きます。たとえば、間隔が 10 の場合、最初の3つのバケット(それらの中にデータがあると仮定すると)は [0, 10)[10, 20)[20, 30) になります。バケットの境界は、offset オプションを使用してシフトできます。

これは、例を使って最もよく説明できます。値が5から14までの10の文書がある場合、間隔 10 を使用すると、各5文書の2つのバケットが生成されます。追加のオフセット 5 を使用すると、すべての10の文書を含む単一のバケット [5, 15) のみが生成されます。

応答形式

デフォルトでは、バケットは順序付き配列として返されます。バケットキーによってキー付けされたハッシュとして応答を要求することも可能です:

Python

  1. resp = client.search(
  2. index="sales",
  3. size="0",
  4. aggs={
  5. "prices": {
  6. "histogram": {
  7. "field": "price",
  8. "interval": 50,
  9. "keyed": True
  10. }
  11. }
  12. },
  13. )
  14. print(resp)

Ruby

  1. response = client.search(
  2. index: 'sales',
  3. size: 0,
  4. body: {
  5. aggregations: {
  6. prices: {
  7. histogram: {
  8. field: 'price',
  9. interval: 50,
  10. keyed: true
  11. }
  12. }
  13. }
  14. }
  15. )
  16. puts response

Js

  1. const response = await client.search({
  2. index: "sales",
  3. size: 0,
  4. aggs: {
  5. prices: {
  6. histogram: {
  7. field: "price",
  8. interval: 50,
  9. keyed: true,
  10. },
  11. },
  12. },
  13. });
  14. console.log(response);

コンソール

  1. POST /sales/_search?size=0
  2. {
  3. "aggs": {
  4. "prices": {
  5. "histogram": {
  6. "field": "price",
  7. "interval": 50,
  8. "keyed": true
  9. }
  10. }
  11. }
  12. }

応答:

コンソール-結果

  1. {
  2. ...
  3. "aggregations": {
  4. "prices": {
  5. "buckets": {
  6. "0.0": {
  7. "key": 0.0,
  8. "doc_count": 1
  9. },
  10. "50.0": {
  11. "key": 50.0,
  12. "doc_count": 1
  13. },
  14. "100.0": {
  15. "key": 100.0,
  16. "doc_count": 0
  17. },
  18. "150.0": {
  19. "key": 150.0,
  20. "doc_count": 2
  21. },
  22. "200.0": {
  23. "key": 200.0,
  24. "doc_count": 3
  25. }
  26. }
  27. }
  28. }
  29. }

欠損値

missing パラメータは、値が欠損している文書がどのように扱われるべきかを定義します。デフォルトでは無視されますが、値があるかのように扱うことも可能です。

Python

  1. resp = client.search(
  2. index="sales",
  3. size="0",
  4. aggs={
  5. "quantity": {
  6. "histogram": {
  7. "field": "quantity",
  8. "interval": 10,
  9. "missing": 0
  10. }
  11. }
  12. },
  13. )
  14. print(resp)

Ruby

  1. response = client.search(
  2. index: 'sales',
  3. size: 0,
  4. body: {
  5. aggregations: {
  6. quantity: {
  7. histogram: {
  8. field: 'quantity',
  9. interval: 10,
  10. missing: 0
  11. }
  12. }
  13. }
  14. }
  15. )
  16. puts response

Js

  1. const response = await client.search({
  2. index: "sales",
  3. size: 0,
  4. aggs: {
  5. quantity: {
  6. histogram: {
  7. field: "quantity",
  8. interval: 10,
  9. missing: 0,
  10. },
  11. },
  12. },
  13. });
  14. console.log(response);

コンソール

  1. POST /sales/_search?size=0
  2. {
  3. "aggs": {
  4. "quantity": {
  5. "histogram": {
  6. "field": "quantity",
  7. "interval": 10,
  8. "missing": 0
  9. }
  10. }
  11. }
  12. }
quantity フィールドに値がない文書は、値が 0 である文書と同じバケットに入ります。

ヒストグラムフィールド

ヒストグラムフィールドに対してヒストグラム集約を実行すると、各間隔の合計カウントが計算されます。

たとえば、異なるネットワークのレイテンシメトリック(ミリ秒)を持つ事前集約されたヒストグラムを格納する次のインデックスに対してヒストグラム集約を実行します:

Python

  1. resp = client.indices.create(
  2. index="metrics_index",
  3. mappings={
  4. "properties": {
  5. "network": {
  6. "properties": {
  7. "name": {
  8. "type": "keyword"
  9. }
  10. }
  11. },
  12. "latency_histo": {
  13. "type": "histogram"
  14. }
  15. }
  16. },
  17. )
  18. print(resp)
  19. resp1 = client.index(
  20. index="metrics_index",
  21. id="1",
  22. refresh=True,
  23. document={
  24. "network.name": "net-1",
  25. "latency_histo": {
  26. "values": [
  27. 1,
  28. 3,
  29. 8,
  30. 12,
  31. 15
  32. ],
  33. "counts": [
  34. 3,
  35. 7,
  36. 23,
  37. 12,
  38. 6
  39. ]
  40. }
  41. },
  42. )
  43. print(resp1)
  44. resp2 = client.index(
  45. index="metrics_index",
  46. id="2",
  47. refresh=True,
  48. document={
  49. "network.name": "net-2",
  50. "latency_histo": {
  51. "values": [
  52. 1,
  53. 6,
  54. 8,
  55. 12,
  56. 14
  57. ],
  58. "counts": [
  59. 8,
  60. 17,
  61. 8,
  62. 7,
  63. 6
  64. ]
  65. }
  66. },
  67. )
  68. print(resp2)
  69. resp3 = client.search(
  70. index="metrics_index",
  71. size="0",
  72. aggs={
  73. "latency_buckets": {
  74. "histogram": {
  75. "field": "latency_histo",
  76. "interval": 5
  77. }
  78. }
  79. },
  80. )
  81. print(resp3)

Ruby

  1. response = client.indices.create(
  2. index: 'metrics_index',
  3. body: {
  4. mappings: {
  5. properties: {
  6. network: {
  7. properties: {
  8. name: {
  9. type: 'keyword'
  10. }
  11. }
  12. },
  13. latency_histo: {
  14. type: 'histogram'
  15. }
  16. }
  17. }
  18. }
  19. )
  20. puts response
  21. response = client.index(
  22. index: 'metrics_index',
  23. id: 1,
  24. refresh: true,
  25. body: {
  26. 'network.name' => 'net-1',
  27. latency_histo: {
  28. values: [
  29. 1,
  30. 3,
  31. 8,
  32. 12,
  33. 15
  34. ],
  35. counts: [
  36. 3,
  37. 7,
  38. 23,
  39. 12,
  40. 6
  41. ]
  42. }
  43. }
  44. )
  45. puts response
  46. response = client.index(
  47. index: 'metrics_index',
  48. id: 2,
  49. refresh: true,
  50. body: {
  51. 'network.name' => 'net-2',
  52. latency_histo: {
  53. values: [
  54. 1,
  55. 6,
  56. 8,
  57. 12,
  58. 14
  59. ],
  60. counts: [
  61. 8,
  62. 17,
  63. 8,
  64. 7,
  65. 6
  66. ]
  67. }
  68. }
  69. )
  70. puts response
  71. response = client.search(
  72. index: 'metrics_index',
  73. size: 0,
  74. body: {
  75. aggregations: {
  76. latency_buckets: {
  77. histogram: {
  78. field: 'latency_histo',
  79. interval: 5
  80. }
  81. }
  82. }
  83. }
  84. )
  85. puts response

Js

  1. const response = await client.indices.create({
  2. index: "metrics_index",
  3. mappings: {
  4. properties: {
  5. network: {
  6. properties: {
  7. name: {
  8. type: "keyword",
  9. },
  10. },
  11. },
  12. latency_histo: {
  13. type: "histogram",
  14. },
  15. },
  16. },
  17. });
  18. console.log(response);
  19. const response1 = await client.index({
  20. index: "metrics_index",
  21. id: 1,
  22. refresh: "true",
  23. document: {
  24. "network.name": "net-1",
  25. latency_histo: {
  26. values: [1, 3, 8, 12, 15],
  27. counts: [3, 7, 23, 12, 6],
  28. },
  29. },
  30. });
  31. console.log(response1);
  32. const response2 = await client.index({
  33. index: "metrics_index",
  34. id: 2,
  35. refresh: "true",
  36. document: {
  37. "network.name": "net-2",
  38. latency_histo: {
  39. values: [1, 6, 8, 12, 14],
  40. counts: [8, 17, 8, 7, 6],
  41. },
  42. },
  43. });
  44. console.log(response2);
  45. const response3 = await client.search({
  46. index: "metrics_index",
  47. size: 0,
  48. aggs: {
  49. latency_buckets: {
  50. histogram: {
  51. field: "latency_histo",
  52. interval: 5,
  53. },
  54. },
  55. },
  56. });
  57. console.log(response3);

コンソール

  1. PUT metrics_index
  2. {
  3. "mappings": {
  4. "properties": {
  5. "network": {
  6. "properties": {
  7. "name": {
  8. "type": "keyword"
  9. }
  10. }
  11. },
  12. "latency_histo": {
  13. "type": "histogram"
  14. }
  15. }
  16. }
  17. }
  18. PUT metrics_index/_doc/1?refresh
  19. {
  20. "network.name" : "net-1",
  21. "latency_histo" : {
  22. "values" : [1, 3, 8, 12, 15],
  23. "counts" : [3, 7, 23, 12, 6]
  24. }
  25. }
  26. PUT metrics_index/_doc/2?refresh
  27. {
  28. "network.name" : "net-2",
  29. "latency_histo" : {
  30. "values" : [1, 6, 8, 12, 14],
  31. "counts" : [8, 17, 8, 7, 6]
  32. }
  33. }
  34. POST /metrics_index/_search?size=0
  35. {
  36. "aggs": {
  37. "latency_buckets": {
  38. "histogram": {
  39. "field": "latency_histo",
  40. "interval": 5
  41. }
  42. }
  43. }
  44. }

histogram 集約は、values に基づいて計算された各間隔のカウントを合計し、次の出力を返します:

コンソール-結果

  1. {
  2. ...
  3. "aggregations": {
  4. "latency_buckets": {
  5. "buckets": [
  6. {
  7. "key": 0.0,
  8. "doc_count": 18
  9. },
  10. {
  11. "key": 5.0,
  12. "doc_count": 48
  13. },
  14. {
  15. "key": 10.0,
  16. "doc_count": 25
  17. },
  18. {
  19. "key": 15.0,
  20. "doc_count": 6
  21. }
  22. ]
  23. }
  24. }
  25. }

ヒストグラム集約はバケット集約であり、文書をバケットに分割します。これは、メトリック集約がフィールドに対してメトリックを計算するのとは異なります。各バケットは、サブ集約が実行できる文書のコレクションを表します。一方、ヒストグラムフィールドは、単一のフィールド内の複数の値を表す事前集約されたフィールドです:数値データのバケットと各バケットのアイテム/文書のカウント。このヒストグラム集約の期待される入力(生の文書を期待)とヒストグラムフィールド(要約情報を提供)の間の不一致は、集約の結果を各バケットの文書カウントのみに制限します。

したがって、ヒストグラムフィールドに対してヒストグラム集約を実行する場合、サブ集約は許可されていません。

また、ヒストグラムフィールドに対してヒストグラム集約を実行する場合、missing パラメータはサポートされていません。