検索リクエストでのランタイムフィールドの定義

検索リクエストで runtime_mappings セクションを指定することで、クエリの一部としてのみ存在するランタイムフィールドを作成できます。スクリプトは runtime_mappings セクションの一部として指定しますが、これは マッピングにランタイムフィールドを追加する場合 と同様です。

検索リクエストでランタイムフィールドを定義することは、インデックスマッピングでランタイムフィールドを定義するのと同じ形式を使用します。インデックスマッピングの runtime からフィールド定義をコピーして、検索リクエストの runtime_mappings セクションに貼り付けます。

次の検索リクエストは、day_of_week フィールドを runtime_mappings セクションに追加します。フィールド値は動的に計算され、この検索リクエストのコンテキスト内でのみ計算されます:

Python

  1. resp = client.search(
  2. index="my-index-000001",
  3. runtime_mappings={
  4. "day_of_week": {
  5. "type": "keyword",
  6. "script": {
  7. "source": "emit(doc['@timestamp'].value.dayOfWeekEnum.getDisplayName(TextStyle.FULL, Locale.ENGLISH))"
  8. }
  9. }
  10. },
  11. aggs={
  12. "day_of_week": {
  13. "terms": {
  14. "field": "day_of_week"
  15. }
  16. }
  17. },
  18. )
  19. print(resp)

Js

  1. const response = await client.search({
  2. index: "my-index-000001",
  3. runtime_mappings: {
  4. day_of_week: {
  5. type: "keyword",
  6. script: {
  7. source:
  8. "emit(doc['@timestamp'].value.dayOfWeekEnum.getDisplayName(TextStyle.FULL, Locale.ENGLISH))",
  9. },
  10. },
  11. },
  12. aggs: {
  13. day_of_week: {
  14. terms: {
  15. field: "day_of_week",
  16. },
  17. },
  18. },
  19. });
  20. console.log(response);

Console

  1. GET my-index-000001/_search
  2. {
  3. "runtime_mappings": {
  4. "day_of_week": {
  5. "type": "keyword",
  6. "script": {
  7. "source": "emit(doc['@timestamp'].value.dayOfWeekEnum.getDisplayName(TextStyle.FULL, Locale.ENGLISH))"
  8. }
  9. }
  10. },
  11. "aggs": {
  12. "day_of_week": {
  13. "terms": {
  14. "field": "day_of_week"
  15. }
  16. }
  17. }
  18. }

他のランタイムフィールドを使用するランタイムフィールドの作成

検索リクエストで他のランタイムフィールドから値を返すランタイムフィールドを定義することもできます。たとえば、センサーデータを一括インデックスするとしましょう:

Python

  1. resp = client.bulk(
  2. index="my-index-000001",
  3. refresh=True,
  4. operations=[
  5. {
  6. "index": {}
  7. },
  8. {
  9. "@timestamp": 1516729294000,
  10. "model_number": "QVKC92Q",
  11. "measures": {
  12. "voltage": "5.2",
  13. "start": "300",
  14. "end": "8675309"
  15. }
  16. },
  17. {
  18. "index": {}
  19. },
  20. {
  21. "@timestamp": 1516642894000,
  22. "model_number": "QVKC92Q",
  23. "measures": {
  24. "voltage": "5.8",
  25. "start": "300",
  26. "end": "8675309"
  27. }
  28. },
  29. {
  30. "index": {}
  31. },
  32. {
  33. "@timestamp": 1516556494000,
  34. "model_number": "QVKC92Q",
  35. "measures": {
  36. "voltage": "5.1",
  37. "start": "300",
  38. "end": "8675309"
  39. }
  40. },
  41. {
  42. "index": {}
  43. },
  44. {
  45. "@timestamp": 1516470094000,
  46. "model_number": "QVKC92Q",
  47. "measures": {
  48. "voltage": "5.6",
  49. "start": "300",
  50. "end": "8675309"
  51. }
  52. },
  53. {
  54. "index": {}
  55. },
  56. {
  57. "@timestamp": 1516383694000,
  58. "model_number": "HG537PU",
  59. "measures": {
  60. "voltage": "4.2",
  61. "start": "400",
  62. "end": "8625309"
  63. }
  64. },
  65. {
  66. "index": {}
  67. },
  68. {
  69. "@timestamp": 1516297294000,
  70. "model_number": "HG537PU",
  71. "measures": {
  72. "voltage": "4.0",
  73. "start": "400",
  74. "end": "8625309"
  75. }
  76. }
  77. ],
  78. )
  79. print(resp)

Ruby

  1. response = client.bulk(
  2. index: 'my-index-000001',
  3. refresh: true,
  4. body: [
  5. {
  6. index: {}
  7. },
  8. {
  9. "@timestamp": 1_516_729_294_000,
  10. model_number: 'QVKC92Q',
  11. measures: {
  12. voltage: '5.2',
  13. start: '300',
  14. end: '8675309'
  15. }
  16. },
  17. {
  18. index: {}
  19. },
  20. {
  21. "@timestamp": 1_516_642_894_000,
  22. model_number: 'QVKC92Q',
  23. measures: {
  24. voltage: '5.8',
  25. start: '300',
  26. end: '8675309'
  27. }
  28. },
  29. {
  30. index: {}
  31. },
  32. {
  33. "@timestamp": 1_516_556_494_000,
  34. model_number: 'QVKC92Q',
  35. measures: {
  36. voltage: '5.1',
  37. start: '300',
  38. end: '8675309'
  39. }
  40. },
  41. {
  42. index: {}
  43. },
  44. {
  45. "@timestamp": 1_516_470_094_000,
  46. model_number: 'QVKC92Q',
  47. measures: {
  48. voltage: '5.6',
  49. start: '300',
  50. end: '8675309'
  51. }
  52. },
  53. {
  54. index: {}
  55. },
  56. {
  57. "@timestamp": 1_516_383_694_000,
  58. model_number: 'HG537PU',
  59. measures: {
  60. voltage: '4.2',
  61. start: '400',
  62. end: '8625309'
  63. }
  64. },
  65. {
  66. index: {}
  67. },
  68. {
  69. "@timestamp": 1_516_297_294_000,
  70. model_number: 'HG537PU',
  71. measures: {
  72. voltage: '4.0',
  73. start: '400',
  74. end: '8625309'
  75. }
  76. }
  77. ]
  78. )
  79. puts response

Js

  1. const response = await client.bulk({
  2. index: "my-index-000001",
  3. refresh: "true",
  4. operations: [
  5. {
  6. index: {},
  7. },
  8. {
  9. "@timestamp": 1516729294000,
  10. model_number: "QVKC92Q",
  11. measures: {
  12. voltage: "5.2",
  13. start: "300",
  14. end: "8675309",
  15. },
  16. },
  17. {
  18. index: {},
  19. },
  20. {
  21. "@timestamp": 1516642894000,
  22. model_number: "QVKC92Q",
  23. measures: {
  24. voltage: "5.8",
  25. start: "300",
  26. end: "8675309",
  27. },
  28. },
  29. {
  30. index: {},
  31. },
  32. {
  33. "@timestamp": 1516556494000,
  34. model_number: "QVKC92Q",
  35. measures: {
  36. voltage: "5.1",
  37. start: "300",
  38. end: "8675309",
  39. },
  40. },
  41. {
  42. index: {},
  43. },
  44. {
  45. "@timestamp": 1516470094000,
  46. model_number: "QVKC92Q",
  47. measures: {
  48. voltage: "5.6",
  49. start: "300",
  50. end: "8675309",
  51. },
  52. },
  53. {
  54. index: {},
  55. },
  56. {
  57. "@timestamp": 1516383694000,
  58. model_number: "HG537PU",
  59. measures: {
  60. voltage: "4.2",
  61. start: "400",
  62. end: "8625309",
  63. },
  64. },
  65. {
  66. index: {},
  67. },
  68. {
  69. "@timestamp": 1516297294000,
  70. model_number: "HG537PU",
  71. measures: {
  72. voltage: "4.0",
  73. start: "400",
  74. end: "8625309",
  75. },
  76. },
  77. ],
  78. });
  79. console.log(response);

Console

  1. POST my-index-000001/_bulk?refresh=true
  2. {"index":{}}
  3. {"@timestamp":1516729294000,"model_number":"QVKC92Q","measures":{"voltage":"5.2","start": "300","end":"8675309"}}
  4. {"index":{}}
  5. {"@timestamp":1516642894000,"model_number":"QVKC92Q","measures":{"voltage":"5.8","start": "300","end":"8675309"}}
  6. {"index":{}}
  7. {"@timestamp":1516556494000,"model_number":"QVKC92Q","measures":{"voltage":"5.1","start": "300","end":"8675309"}}
  8. {"index":{}}
  9. {"@timestamp":1516470094000,"model_number":"QVKC92Q","measures":{"voltage":"5.6","start": "300","end":"8675309"}}
  10. {"index":{}}
  11. {"@timestamp":1516383694000,"model_number":"HG537PU","measures":{"voltage":"4.2","start": "400","end":"8625309"}}
  12. {"index":{}}
  13. {"@timestamp":1516297294000,"model_number":"HG537PU","measures":{"voltage":"4.0","start": "400","end":"8625309"}}

インデックス後に、数値データが text タイプとしてマッピングされていることに気付きました。measures.start および measures.end フィールドで集約したいのですが、text タイプのフィールドでは集約できないため、集約が失敗します。ランタイムフィールドが助けてくれます!インデックスフィールドと同じ名前のランタイムフィールドを追加し、データ型を変更できます:

Python

  1. resp = client.indices.put_mapping(
  2. index="my-index-000001",
  3. runtime={
  4. "measures.start": {
  5. "type": "long"
  6. },
  7. "measures.end": {
  8. "type": "long"
  9. }
  10. },
  11. )
  12. print(resp)

Ruby

  1. response = client.indices.put_mapping(
  2. index: 'my-index-000001',
  3. body: {
  4. runtime: {
  5. 'measures.start' => {
  6. type: 'long'
  7. },
  8. 'measures.end' => {
  9. type: 'long'
  10. }
  11. }
  12. }
  13. )
  14. puts response

Js

  1. const response = await client.indices.putMapping({
  2. index: "my-index-000001",
  3. runtime: {
  4. "measures.start": {
  5. type: "long",
  6. },
  7. "measures.end": {
  8. type: "long",
  9. },
  10. },
  11. });
  12. console.log(response);

Console

  1. PUT my-index-000001/_mapping
  2. {
  3. "runtime": {
  4. "measures.start": {
  5. "type": "long"
  6. },
  7. "measures.end": {
  8. "type": "long"
  9. }
  10. }
  11. }

ランタイムフィールドは、インデックスマッピングで同じ名前で定義されたフィールドよりも優先されます。この柔軟性により、既存のフィールドを隠蔽し、フィールド自体を変更することなく異なる値を計算できます。インデックスマッピングに誤りがあった場合、検索リクエスト中に マッピング内の値を上書きする 値を計算するためにランタイムフィールドを使用できます。

これで、measures.start および measures.end フィールドに対して簡単に 平均集約 を実行できます:

Python

  1. resp = client.search(
  2. index="my-index-000001",
  3. aggs={
  4. "avg_start": {
  5. "avg": {
  6. "field": "measures.start"
  7. }
  8. },
  9. "avg_end": {
  10. "avg": {
  11. "field": "measures.end"
  12. }
  13. }
  14. },
  15. )
  16. print(resp)

Ruby

  1. response = client.search(
  2. index: 'my-index-000001',
  3. body: {
  4. aggregations: {
  5. avg_start: {
  6. avg: {
  7. field: 'measures.start'
  8. }
  9. },
  10. avg_end: {
  11. avg: {
  12. field: 'measures.end'
  13. }
  14. }
  15. }
  16. }
  17. )
  18. puts response

Js

  1. const response = await client.search({
  2. index: "my-index-000001",
  3. aggs: {
  4. avg_start: {
  5. avg: {
  6. field: "measures.start",
  7. },
  8. },
  9. avg_end: {
  10. avg: {
  11. field: "measures.end",
  12. },
  13. },
  14. },
  15. });
  16. console.log(response);

Console

  1. GET my-index-000001/_search
  2. {
  3. "aggs": {
  4. "avg_start": {
  5. "avg": {
  6. "field": "measures.start"
  7. }
  8. },
  9. "avg_end": {
  10. "avg": {
  11. "field": "measures.end"
  12. }
  13. }
  14. }
  15. }

レスポンスには、基礎データの値を変更することなく集約結果が含まれています:

Console-Result

  1. {
  2. "aggregations" : {
  3. "avg_start" : {
  4. "value" : 333.3333333333333
  5. },
  6. "avg_end" : {
  7. "value" : 8658642.333333334
  8. }
  9. }
  10. }

さらに、検索クエリの一部として値を計算するランタイムフィールドを定義し、そのフィールドに対して 同じクエリ内で 統計集約 を実行できます。

duration ランタイムフィールドはインデックスマッピングには存在しませんが、そのフィールドで検索および集約を行うことができます。次のクエリは、duration フィールドの計算された値を返し、集約されたドキュメントから抽出された数値の統計を計算するために統計集約を実行します。

Python

  1. resp = client.search(
  2. index="my-index-000001",
  3. runtime_mappings={
  4. "duration": {
  5. "type": "long",
  6. "script": {
  7. "source": "\n emit(doc['measures.end'].value - doc['measures.start'].value);\n "
  8. }
  9. }
  10. },
  11. aggs={
  12. "duration_stats": {
  13. "stats": {
  14. "field": "duration"
  15. }
  16. }
  17. },
  18. )
  19. print(resp)

Ruby

  1. response = client.search(
  2. index: 'my-index-000001',
  3. body: {
  4. runtime_mappings: {
  5. duration: {
  6. type: 'long',
  7. script: {
  8. source: "\n emit(doc['measures.end'].value - doc['measures.start'].value);\n "
  9. }
  10. }
  11. },
  12. aggregations: {
  13. duration_stats: {
  14. stats: {
  15. field: 'duration'
  16. }
  17. }
  18. }
  19. }
  20. )
  21. puts response

Js

  1. const response = await client.search({
  2. index: "my-index-000001",
  3. runtime_mappings: {
  4. duration: {
  5. type: "long",
  6. script: {
  7. source:
  8. "\n emit(doc['measures.end'].value - doc['measures.start'].value);\n ",
  9. },
  10. },
  11. },
  12. aggs: {
  13. duration_stats: {
  14. stats: {
  15. field: "duration",
  16. },
  17. },
  18. },
  19. });
  20. console.log(response);

Console

  1. GET my-index-000001/_search
  2. {
  3. "runtime_mappings": {
  4. "duration": {
  5. "type": "long",
  6. "script": {
  7. "source": """
  8. emit(doc['measures.end'].value - doc['measures.start'].value);
  9. """
  10. }
  11. }
  12. },
  13. "aggs": {
  14. "duration_stats": {
  15. "stats": {
  16. "field": "duration"
  17. }
  18. }
  19. }
  20. }

duration ランタイムフィールドは検索クエリのコンテキスト内にのみ存在しますが、そのフィールドで検索および集約を行うことができます。この柔軟性は非常に強力で、インデックスマッピングの誤りを修正し、単一の検索リクエスト内で動的に計算を完了することを可能にします。

Console-Result

  1. {
  2. "aggregations" : {
  3. "duration_stats" : {
  4. "count" : 6,
  5. "min" : 8624909.0,
  6. "max" : 8675009.0,
  7. "avg" : 8658309.0,
  8. "sum" : 5.1949854E7
  9. }
  10. }
  11. }