変換のためのPainlessの例

scripted_metric集約を使用する例は、Elasticsearch Serverlessではサポートされていません。

これらの例は、変換におけるPainlessの使用方法を示しています。Painlessスクリプト言語の詳細については、Painlessガイドを参照してください。

スクリプト化されたメトリック集約を使用してトップヒットを取得する

このスニペットは、最新のドキュメント、つまり最新のタイムスタンプを持つドキュメントを見つける方法を示しています。技術的な観点からは、スクリプト化されたメトリック集約を使用して変換内でトップヒットの機能を実現するのに役立ちます。これはメトリック出力を提供します。

この例では、scripted_metric集約を使用していますが、これはElasticsearch Serverlessではサポートされていません。

Js

  1. "aggregations": {
  2. "latest_doc": {
  3. "scripted_metric": {
  4. "init_script": "state.timestamp_latest = 0L; state.last_doc = ''",
  5. "map_script": """
  6. def current_date = doc['@timestamp'].getValue().toInstant().toEpochMilli();
  7. if (current_date > state.timestamp_latest)
  8. {state.timestamp_latest = current_date;
  9. state.last_doc = new HashMap(params['_source']);}
  10. """,
  11. "combine_script": "return state",
  12. "reduce_script": """
  13. def last_doc = '';
  14. def timestamp_latest = 0L;
  15. for (s in states) {if (s.timestamp_latest > (timestamp_latest))
  16. {timestamp_latest = s.timestamp_latest; last_doc = s.last_doc;}}
  17. return last_doc
  18. """
  19. }
  20. }
  21. }
init_scriptは、stateオブジェクト内に長い型のtimestamp_latestと文字列型のlast_docを作成します。
map_scriptは、ドキュメントのタイムスタンプに基づいてcurrent_dateを定義し、current_datestate.timestamp_latestを比較し、最終的にstate.last_docをシャードから返します。new HashMap(...)を使用すると、ソースドキュメントをコピーします。これは、1つのフェーズから次のフェーズに完全なソースオブジェクトを渡したいときに重要です。
combine_scriptは、各シャードからstateを返します。
reduce_scriptは、各シャードから返されたs.timestamp_latestの値を反復処理し、最新のタイムスタンプを持つドキュメント(last_doc)を返します。レスポンスでは、トップヒット(つまり、latest_doc)はlatest_docフィールドの下にネストされています。

スクリプトの詳細な説明については、スクリプトの範囲を確認してください。

最後の値を同様の方法で取得できます:

Js

  1. "aggregations": {
  2. "latest_value": {
  3. "scripted_metric": {
  4. "init_script": "state.timestamp_latest = 0L; state.last_value = ''",
  5. "map_script": """
  6. def current_date = doc['@timestamp'].getValue().toInstant().toEpochMilli();
  7. if (current_date > state.timestamp_latest)
  8. {state.timestamp_latest = current_date;
  9. state.last_value = params['_source']['value'];}
  10. """,
  11. "combine_script": "return state",
  12. "reduce_script": """
  13. def last_value = '';
  14. def timestamp_latest = 0L;
  15. for (s in states) {if (s.timestamp_latest > (timestamp_latest))
  16. {timestamp_latest = s.timestamp_latest; last_value = s.last_value;}}
  17. return last_value
  18. """
  19. }
  20. }
  21. }

保存されたスクリプトを使用して最新の値を取得する

最新の値を取得するために、保存されたスクリプトの力を使用することもできます。保存されたスクリプトはコンパイル時間を短縮し、検索を高速化し、更新可能です。

  • 1. 保存されたスクリプトを作成します:

Js

  1. POST _scripts/last-value-map-init
  2. {
  3. "script": {
  4. "lang": "painless",
  5. "source": """
  6. state.timestamp_latest = 0L; state.last_value = ''
  7. """
  8. }
  9. }
  10. POST _scripts/last-value-map
  11. {
  12. "script": {
  13. "lang": "painless",
  14. "source": """
  15. def current_date = doc['@timestamp'].getValue().toInstant().toEpochMilli();
  16. if (current_date > state.timestamp_latest)
  17. {state.timestamp_latest = current_date;
  18. state.last_value = doc[params['key']].value;}
  19. """
  20. }
  21. }
  22. POST _scripts/last-value-combine
  23. {
  24. "script": {
  25. "lang": "painless",
  26. "source": """
  27. return state
  28. """
  29. }
  30. }
  31. POST _scripts/last-value-reduce
  32. {
  33. "script": {
  34. "lang": "painless",
  35. "source": """
  36. def last_value = '';
  37. def timestamp_latest = 0L;
  38. for (s in states) {if (s.timestamp_latest > (timestamp_latest))
  39. {timestamp_latest = s.timestamp_latest; last_value = s.last_value;}}
  40. return last_value
  41. """
  42. }
  43. }
  • 2. スクリプト化されたメトリック集約で保存されたスクリプトを使用します。

Js

  1. "aggregations":{
  2. "latest_value":{
  3. "scripted_metric":{
  4. "init_script":{
  5. "id":"last-value-map-init"
  6. },
  7. "map_script":{
  8. "id":"last-value-map",
  9. "params":{
  10. "key":"field_with_last_value"
  11. }
  12. },
  13. "combine_script":{
  14. "id":"last-value-combine"
  15. },
  16. "reduce_script":{
  17. "id":"last-value-reduce"
  18. }
パラメータfield_with_last_valueは、最新の値を取得したい任意のフィールドに設定できます。

集約を使用して時間の特徴を取得する

このスニペットは、変換内でPainlessを使用して時間に基づく特徴を抽出する方法を示しています。このスニペットは、@timestampdate型フィールドとして定義されているインデックスを使用します。

Js

  1. "aggregations": {
  2. "avg_hour_of_day": {
  3. "avg":{
  4. "script": {
  5. "source": """
  6. ZonedDateTime date = doc['@timestamp'].value;
  7. return date.getHour();
  8. """
  9. }
  10. }
  11. },
  12. "avg_month_of_year": {
  13. "avg":{
  14. "script": {
  15. "source": """
  16. ZonedDateTime date = doc['@timestamp'].value;
  17. return date.getMonthValue();
  18. """
  19. }
  20. }
  21. },
  22. ...
  23. }
集約の名前。
日の時間を返すPainlessスクリプトを含みます。
ドキュメントのタイムスタンプに基づいてdateを設定します。
dateから時間値を返します。
集約の名前。
年の月を返すPainlessスクリプトを含みます。
ドキュメントのタイムスタンプに基づいてdateを設定します。
dateから月値を返します。

バケットスクリプトを使用して期間を取得する

この例は、バケットスクリプトを使用して、クライアントIPからデータログのセッションの期間を取得する方法を示しています。この例では、Kibanaのサンプルウェブログデータセットを使用しています。

Python

  1. resp = client.transform.put_transform(
  2. transform_id="data_log",
  3. source={
  4. "index": "kibana_sample_data_logs"
  5. },
  6. dest={
  7. "index": "data-logs-by-client"
  8. },
  9. pivot={
  10. "group_by": {
  11. "machine.os": {
  12. "terms": {
  13. "field": "machine.os.keyword"
  14. }
  15. },
  16. "machine.ip": {
  17. "terms": {
  18. "field": "clientip"
  19. }
  20. }
  21. },
  22. "aggregations": {
  23. "time_frame.lte": {
  24. "max": {
  25. "field": "timestamp"
  26. }
  27. },
  28. "time_frame.gte": {
  29. "min": {
  30. "field": "timestamp"
  31. }
  32. },
  33. "time_length": {
  34. "bucket_script": {
  35. "buckets_path": {
  36. "min": "time_frame.gte.value",
  37. "max": "time_frame.lte.value"
  38. },
  39. "script": "params.max - params.min"
  40. }
  41. }
  42. }
  43. },
  44. )
  45. print(resp)

Js

  1. const response = await client.transform.putTransform({
  2. transform_id: "data_log",
  3. source: {
  4. index: "kibana_sample_data_logs",
  5. },
  6. dest: {
  7. index: "data-logs-by-client",
  8. },
  9. pivot: {
  10. group_by: {
  11. "machine.os": {
  12. terms: {
  13. field: "machine.os.keyword",
  14. },
  15. },
  16. "machine.ip": {
  17. terms: {
  18. field: "clientip",
  19. },
  20. },
  21. },
  22. aggregations: {
  23. "time_frame.lte": {
  24. max: {
  25. field: "timestamp",
  26. },
  27. },
  28. "time_frame.gte": {
  29. min: {
  30. field: "timestamp",
  31. },
  32. },
  33. time_length: {
  34. bucket_script: {
  35. buckets_path: {
  36. min: "time_frame.gte.value",
  37. max: "time_frame.lte.value",
  38. },
  39. script: "params.max - params.min",
  40. },
  41. },
  42. },
  43. },
  44. });
  45. console.log(response);

Console

  1. PUT _transform/data_log
  2. {
  3. "source": {
  4. "index": "kibana_sample_data_logs"
  5. },
  6. "dest": {
  7. "index": "data-logs-by-client"
  8. },
  9. "pivot": {
  10. "group_by": {
  11. "machine.os": {"terms": {"field": "machine.os.keyword"}},
  12. "machine.ip": {"terms": {"field": "clientip"}}
  13. },
  14. "aggregations": {
  15. "time_frame.lte": {
  16. "max": {
  17. "field": "timestamp"
  18. }
  19. },
  20. "time_frame.gte": {
  21. "min": {
  22. "field": "timestamp"
  23. }
  24. },
  25. "time_length": {
  26. "bucket_script": {
  27. "buckets_path": {
  28. "min": "time_frame.gte.value",
  29. "max": "time_frame.lte.value"
  30. },
  31. "script": "params.max - params.min"
  32. }
  33. }
  34. }
  35. }
  36. }
セッションの長さを定義するために、バケットスクリプトを使用します。
バケットパスは、スクリプト変数のマップと、それらの変数に使用するバケットへの関連パスです。この特定のケースでは、minmaxtime_frame.gte.valuetime_frame.lte.valueにマッピングされた変数です。
最後に、スクリプトはセッションの開始日から終了日を引き算し、セッションの期間を得ます。

スクリプト化されたメトリック集約を使用してHTTPレスポンスをカウントする

ウェブログデータセット内の異なるHTTPレスポンスタイプをスクリプト化されたメトリック集約を使用してカウントできます。フィルター集約を使用しても同様の機能を実現できます。詳細については、疑わしいクライアントIPの発見の例を確認してください。

以下の例では、HTTPレスポンスコードがドキュメントのresponseフィールドにキーワードとして保存されていると仮定しています。

この例では、scripted_metric集約を使用していますが、これはElasticsearch Serverlessではサポートされていません。

Js

  1. "aggregations": {
  2. "responses.counts": {
  3. "scripted_metric": {
  4. "init_script": "state.responses = ['error':0L,'success':0L,'other':0L]",
  5. "map_script": """
  6. def code = doc['response.keyword'].value;
  7. if (code.startsWith('5') || code.startsWith('4')) {<br> state.responses.error += 1 ;<br> } else if(code.startsWith('2')) {<br> state.responses.success += 1;<br> } else {<br> state.responses.other += 1;<br> }<br> """,<br> "combine_script": "state.responses",<br> "reduce_script": """<br> def counts = ['error': 0L, 'success': 0L, 'other': 0L];<br> for (responses in states) {<br> counts.error += responses['error'];<br> counts.success += responses['success'];<br> counts.other += responses['other'];<br> }<br> return counts;<br> """<br> }<br> },<br> ...<br>}<br>``````<br><br>
  8. | | |
  9. | --- | --- |
  10. | | すべての集約を含む変換の`````aggregations`````オブジェクト。 |
  11. | | `````scripted_metric`````集約のオブジェクト。 |
  12. | | この`````scripted_metric`````は、ウェブログデータに対して特定のHTTPレスポンスタイプ(エラー、成功、その他)をカウントする分散操作を実行します。 |
  13. | | `````init_script`````は、`````state`````オブジェクト内に`````responses`````配列を作成し、長いデータ型の3つのプロパティ(`````error`````、`````success`````、`````other`````)を持ちます。 |
  14. | | `````map_script`````は、ドキュメントの`````response.keyword`````値に基づいて`````code`````を定義し、レスポンスの最初の数字に基づいてエラー、成功、その他のレスポンスをカウントします。 |
  15. | | `````combine_script`````は、各シャードから`````state.responses`````を返します。 |
  16. | | `````reduce_script`````は、`````counts`````、`````error`````、`````success`````プロパティを持つ`````other`````配列を作成し、各シャードから返された`````responses`````の値を反復処理し、異なるレスポンスタイプを`````counts`````オブジェクトの適切なプロパティに割り当てます。エラー応答はエラーカウントに、成功応答は成功カウントに、その他の応答はその他のカウントに割り当てられます。最終的に、レスポンスカウントを持つ`````counts`````配列を返します。 |
  17. ## スクリプト化されたメトリック集約を使用してインデックスを比較する
  18. この例は、スクリプト化されたメトリック集約を使用した変換によって2つのインデックスの内容を比較する方法を示しています。
  19. この例では、`````scripted_metric`````集約を使用していますが、これはElasticsearch Serverlessではサポートされていません。
  20. #### Python
  21. ``````python
  22. resp = client.transform.preview_transform(
  23. id="index_compare",
  24. source={
  25. "index": [
  26. "index1",
  27. "index2"
  28. ],
  29. "query": {
  30. "match_all": {}
  31. }
  32. },
  33. dest={
  34. "index": "compare"
  35. },
  36. pivot={
  37. "group_by": {
  38. "unique-id": {
  39. "terms": {
  40. "field": "<unique-id-field>"
  41. }
  42. }
  43. },
  44. "aggregations": {
  45. "compare": {
  46. "scripted_metric": {
  47. "map_script": "state.doc = new HashMap(params['_source'])",
  48. "combine_script": "return state",
  49. "reduce_script": " \n if (states.size() != 2) {\n return \"count_mismatch\"\n }\n if (states.get(0).equals(states.get(1))) {\n return \"match\"\n } else {\n return \"mismatch\"\n }\n "
  50. }
  51. }
  52. }
  53. },
  54. )
  55. print(resp)

Js

  1. const response = await client.transform.previewTransform({
  2. id: "index_compare",
  3. source: {
  4. index: ["index1", "index2"],
  5. query: {
  6. match_all: {},
  7. },
  8. },
  9. dest: {
  10. index: "compare",
  11. },
  12. pivot: {
  13. group_by: {
  14. "unique-id": {
  15. terms: {
  16. field: "<unique-id-field>",
  17. },
  18. },
  19. },
  20. aggregations: {
  21. compare: {
  22. scripted_metric: {
  23. map_script: "state.doc = new HashMap(params['_source'])",
  24. combine_script: "return state",
  25. reduce_script:
  26. ' \n if (states.size() != 2) {\n return "count_mismatch"\n }\n if (states.get(0).equals(states.get(1))) {\n return "match"\n } else {\n return "mismatch"\n }\n ',
  27. },
  28. },
  29. },
  30. },
  31. });
  32. console.log(response);

Console

  1. POST _transform/_preview
  2. {
  3. "id" : "index_compare",
  4. "source" : {
  5. "index" : [
  6. "index1",
  7. "index2"
  8. ],
  9. "query" : {
  10. "match_all" : { }
  11. }
  12. },
  13. "dest" : {
  14. "index" : "compare"
  15. },
  16. "pivot" : {
  17. "group_by" : {
  18. "unique-id" : {
  19. "terms" : {
  20. "field" : "<unique-id-field>"
  21. }
  22. }
  23. },
  24. "aggregations" : {
  25. "compare" : {
  26. "scripted_metric" : {
  27. "map_script" : "state.doc = new HashMap(params['_source'])",
  28. "combine_script" : "return state",
  29. "reduce_script" : """
  30. if (states.size() != 2) {
  31. return "count_mismatch"
  32. }
  33. if (states.get(0).equals(states.get(1))) {
  34. return "match"
  35. } else {
  36. return "mismatch"
  37. }
  38. """
  39. }
  40. }
  41. }
  42. }
  43. }
sourceオブジェクトで参照されるインデックスが互いに比較されます。
destインデックスには比較の結果が含まれます。
group_byフィールドは、各ドキュメントの一意の識別子である必要があります。
scripted_metric集約のオブジェクト。
map_scriptは、状態オブジェクト内でdocを定義します。new HashMap(...)を使用すると、ソースドキュメントをコピーします。これは、1つのフェーズから次のフェーズに完全なソースオブジェクトを渡したいときに重要です。
combine_scriptは、各シャードからstateを返します。
reduce_scriptは、インデックスのサイズが等しいかどうかを確認します。等しくない場合は、count_mismatchを報告します。その後、2つのインデックスのすべての値を反復処理し、比較します。値が等しい場合はmatchを返し、そうでない場合はmismatchを返します。

スクリプト化されたメトリック集約を使用してウェブセッションの詳細を取得する

この例は、単一のトランザクションから複数の特徴を導出する方法を示しています。データからの例のソースドキュメントを見てみましょう:

ソースドキュメント

Js

  1. {
  2. "_index":"apache-sessions",
  3. "_type":"_doc",
  4. "_id":"KvzSeGoB4bgw0KGbE3wP",
  5. "_score":1.0,
  6. "_source":{
  7. "@timestamp":1484053499256,
  8. "apache":{
  9. "access":{
  10. "sessionid":"571604f2b2b0c7b346dc685eeb0e2306774a63c2",
  11. "url":"http://www.leroymerlin.fr/v3/search/search.do?keyword=Carrelage%20salle%20de%20bain",
  12. "path":"/v3/search/search.do",
  13. "query":"keyword=Carrelage%20salle%20de%20bain",
  14. "referrer":"http://www.leroymerlin.fr/v3/p/produits/carrelage-parquet-sol-souple/carrelage-sol-et-mur/decor-listel-et-accessoires-carrelage-mural-l1308217717?resultOffset=0&resultLimit=51&resultListShape=MOSAIC&priceStyle=SALEUNIT_PRICE",
  15. "user_agent":{
  16. "original":"Mobile Safari 10.0 Mac OS X (iPad) Apple Inc.",
  17. "os_name":"Mac OS X (iPad)"
  18. },
  19. "remote_ip":"0337b1fa-5ed4-af81-9ef4-0ec53be0f45d",
  20. "geoip":{
  21. "country_iso_code":"FR",
  22. "location":{
  23. "lat":48.86,
  24. "lon":2.35
  25. }
  26. },
  27. "response_code":200,
  28. "method":"GET"
  29. }
  30. }
  31. }
  32. }
  33. ...
  1. この例では、`````scripted_metric`````集約を使用していますが、これはElasticsearch Serverlessではサポートされていません。
  2. #### Js
  3. ``````js
  4. POST _transform/_preview
  5. {
  6. "source": {
  7. "index": "apache-sessions"
  8. },
  9. "pivot": {
  10. "group_by": {
  11. "sessionid": {
  12. "terms": {
  13. "field": "apache.access.sessionid"
  14. }
  15. }
  16. },
  17. "aggregations": {
  18. "distinct_paths": {
  19. "cardinality": {
  20. "field": "apache.access.path"
  21. }
  22. },
  23. "num_pages_viewed": {
  24. "value_count": {
  25. "field": "apache.access.url"
  26. }
  27. },
  28. "session_details": {
  29. "scripted_metric": {
  30. "init_script": "state.docs = []",
  31. "map_script": """
  32. Map span = [
  33. '@timestamp':doc['@timestamp'].value,
  34. 'url':doc['apache.access.url'].value,
  35. 'referrer':doc['apache.access.referrer'].value
  36. ];
  37. state.docs.add(span)
  38. """,
  39. "combine_script": "return state.docs;",
  40. "reduce_script": """
  41. def all_docs = [];
  42. for (s in states) {
  43. for (span in s) {
  44. all_docs.add(span);
  45. }
  46. }
  47. all_docs.sort((HashMap o1, HashMap o2)->o1['@timestamp'].toEpochMilli().compareTo(o2['@timestamp'].toEpochMilli()));
  48. def size = all_docs.size();
  49. def min_time = all_docs[0]['@timestamp'];
  50. def max_time = all_docs[size-1]['@timestamp'];
  51. def duration = max_time.toEpochMilli() - min_time.toEpochMilli();
  52. def entry_page = all_docs[0]['url'];
  53. def exit_path = all_docs[size-1]['url'];
  54. def first_referrer = all_docs[0]['referrer'];
  55. def ret = new HashMap();
  56. ret['first_time'] = min_time;
  57. ret['last_time'] = max_time;
  58. ret['duration'] = duration;
  59. ret['entry_page'] = entry_page;
  60. ret['exit_path'] = exit_path;
  61. ret['first_referrer'] = first_referrer;
  62. return ret;
  63. """
  64. }
  65. }
  66. }
  67. }
  68. }
  69. `
データはsessionidでグループ化されます。
集約は、セッション中に表示されたページのパスの数をカウントし、列挙します。
init_scriptは、stateオブジェクト内に配列型のdocを作成します。
map_scriptは、ドキュメントの対応する値に基づいて、タイムスタンプ、URL、およびリファラー値を持つspan配列を定義し、span配列の値をdocオブジェクトに追加します。
combine_scriptは、各シャードからstate.docsを返します。
reduce_scriptは、ドキュメントフィールドに基づいてmin_timemax_timedurationなどのさまざまなオブジェクトを定義し、retオブジェクトを宣言し、new HashMap ()を使用してソースドキュメントをコピーします。次に、スクリプトはfirst_timelast_timedurationおよびretオブジェクト内の他のフィールドを、前に定義された対応するオブジェクトに基づいて定義し、最終的にretを返します。

API呼び出しの結果は、同様のレスポンスになります:

Js

  1. {
  2. "num_pages_viewed" : 2.0,
  3. "session_details" : {
  4. "duration" : 100300001,
  5. "first_referrer" : "https://www.bing.com/",
  6. "entry_page" : "http://www.leroymerlin.fr/v3/p/produits/materiaux-menuiserie/porte-coulissante-porte-interieure-escalier-et-rambarde/barriere-de-securite-l1308218463",
  7. "first_time" : "2017-01-10T21:22:52.982Z",
  8. "last_time" : "2017-01-10T21:25:04.356Z",
  9. "exit_path" : "http://www.leroymerlin.fr/v3/p/produits/materiaux-menuiserie/porte-coulissante-porte-interieure-escalier-et-rambarde/barriere-de-securite-l1308218463?__result-wrapper?pageTemplate=Famille%2FMat%C3%A9riaux+et+menuiserie&resultOffset=0&resultLimit=50&resultListShape=PLAIN&nomenclatureId=17942&priceStyle=SALEUNIT_PRICE&fcr=1&*4294718806=4294718806&*14072=14072&*4294718593=4294718593&*17942=17942"
  10. },
  11. "distinct_paths" : 1.0,
  12. "sessionid" : "000046f8154a80fd89849369c984b8cc9d795814"
  13. },
  14. {
  15. "num_pages_viewed" : 10.0,
  16. "session_details" : {
  17. "duration" : 343100405,
  18. "first_referrer" : "https://www.google.fr/",
  19. "entry_page" : "http://www.leroymerlin.fr/",
  20. "first_time" : "2017-01-10T16:57:39.937Z",
  21. "last_time" : "2017-01-10T17:03:23.049Z",
  22. "exit_path" : "http://www.leroymerlin.fr/v3/p/produits/porte-de-douche-coulissante-adena-e168578"
  23. },
  24. "distinct_paths" : 8.0,
  25. "sessionid" : "000087e825da1d87a332b8f15fa76116c7467da6"
  26. }
  27. ...