ジオライン集約

geo_line 集約は、選択した sort フィールドによって順序付けられたバケット内のすべての geo_point 値を LineString に集約します。この sort は、例えば日付フィールドである可能性があります。返されるバケットは、ラインジオメトリを表す有効な GeoJSON Feature です。

Python

  1. resp = client.indices.create(
  2. index="test",
  3. mappings={
  4. "properties": {
  5. "my_location": {
  6. "type": "geo_point"
  7. },
  8. "group": {
  9. "type": "keyword"
  10. },
  11. "@timestamp": {
  12. "type": "date"
  13. }
  14. }
  15. },
  16. )
  17. print(resp)
  18. resp1 = client.bulk(
  19. index="test",
  20. refresh=True,
  21. operations=[
  22. {
  23. "index": {}
  24. },
  25. {
  26. "my_location": {
  27. "lat": 52.373184,
  28. "lon": 4.889187
  29. },
  30. "@timestamp": "2023-01-02T09:00:00Z"
  31. },
  32. {
  33. "index": {}
  34. },
  35. {
  36. "my_location": {
  37. "lat": 52.370159,
  38. "lon": 4.885057
  39. },
  40. "@timestamp": "2023-01-02T10:00:00Z"
  41. },
  42. {
  43. "index": {}
  44. },
  45. {
  46. "my_location": {
  47. "lat": 52.369219,
  48. "lon": 4.901618
  49. },
  50. "@timestamp": "2023-01-02T13:00:00Z"
  51. },
  52. {
  53. "index": {}
  54. },
  55. {
  56. "my_location": {
  57. "lat": 52.374081,
  58. "lon": 4.91235
  59. },
  60. "@timestamp": "2023-01-02T16:00:00Z"
  61. },
  62. {
  63. "index": {}
  64. },
  65. {
  66. "my_location": {
  67. "lat": 52.371667,
  68. "lon": 4.914722
  69. },
  70. "@timestamp": "2023-01-03T12:00:00Z"
  71. }
  72. ],
  73. )
  74. print(resp1)
  75. resp2 = client.search(
  76. index="test",
  77. filter_path="aggregations",
  78. aggs={
  79. "line": {
  80. "geo_line": {
  81. "point": {
  82. "field": "my_location"
  83. },
  84. "sort": {
  85. "field": "@timestamp"
  86. }
  87. }
  88. }
  89. },
  90. )
  91. print(resp2)

Ruby

  1. response = client.indices.create(
  2. index: 'test',
  3. body: {
  4. mappings: {
  5. properties: {
  6. my_location: {
  7. type: 'geo_point'
  8. },
  9. group: {
  10. type: 'keyword'
  11. },
  12. "@timestamp": {
  13. type: 'date'
  14. }
  15. }
  16. }
  17. }
  18. )
  19. puts response
  20. response = client.bulk(
  21. index: 'test',
  22. refresh: true,
  23. body: [
  24. {
  25. index: {}
  26. },
  27. {
  28. my_location: {
  29. lat: 52.373184,
  30. lon: 4.889187
  31. },
  32. "@timestamp": '2023-01-02T09:00:00Z'
  33. },
  34. {
  35. index: {}
  36. },
  37. {
  38. my_location: {
  39. lat: 52.370159,
  40. lon: 4.885057
  41. },
  42. "@timestamp": '2023-01-02T10:00:00Z'
  43. },
  44. {
  45. index: {}
  46. },
  47. {
  48. my_location: {
  49. lat: 52.369219,
  50. lon: 4.901618
  51. },
  52. "@timestamp": '2023-01-02T13:00:00Z'
  53. },
  54. {
  55. index: {}
  56. },
  57. {
  58. my_location: {
  59. lat: 52.374081,
  60. lon: 4.91235
  61. },
  62. "@timestamp": '2023-01-02T16:00:00Z'
  63. },
  64. {
  65. index: {}
  66. },
  67. {
  68. my_location: {
  69. lat: 52.371667,
  70. lon: 4.914722
  71. },
  72. "@timestamp": '2023-01-03T12:00:00Z'
  73. }
  74. ]
  75. )
  76. puts response
  77. response = client.search(
  78. index: 'test',
  79. filter_path: 'aggregations',
  80. body: {
  81. aggregations: {
  82. line: {
  83. geo_line: {
  84. point: {
  85. field: 'my_location'
  86. },
  87. sort: {
  88. field: '@timestamp'
  89. }
  90. }
  91. }
  92. }
  93. }
  94. )
  95. puts response

Js

  1. const response = await client.indices.create({
  2. index: "test",
  3. mappings: {
  4. properties: {
  5. my_location: {
  6. type: "geo_point",
  7. },
  8. group: {
  9. type: "keyword",
  10. },
  11. "@timestamp": {
  12. type: "date",
  13. },
  14. },
  15. },
  16. });
  17. console.log(response);
  18. const response1 = await client.bulk({
  19. index: "test",
  20. refresh: "true",
  21. operations: [
  22. {
  23. index: {},
  24. },
  25. {
  26. my_location: {
  27. lat: 52.373184,
  28. lon: 4.889187,
  29. },
  30. "@timestamp": "2023-01-02T09:00:00Z",
  31. },
  32. {
  33. index: {},
  34. },
  35. {
  36. my_location: {
  37. lat: 52.370159,
  38. lon: 4.885057,
  39. },
  40. "@timestamp": "2023-01-02T10:00:00Z",
  41. },
  42. {
  43. index: {},
  44. },
  45. {
  46. my_location: {
  47. lat: 52.369219,
  48. lon: 4.901618,
  49. },
  50. "@timestamp": "2023-01-02T13:00:00Z",
  51. },
  52. {
  53. index: {},
  54. },
  55. {
  56. my_location: {
  57. lat: 52.374081,
  58. lon: 4.91235,
  59. },
  60. "@timestamp": "2023-01-02T16:00:00Z",
  61. },
  62. {
  63. index: {},
  64. },
  65. {
  66. my_location: {
  67. lat: 52.371667,
  68. lon: 4.914722,
  69. },
  70. "@timestamp": "2023-01-03T12:00:00Z",
  71. },
  72. ],
  73. });
  74. console.log(response1);
  75. const response2 = await client.search({
  76. index: "test",
  77. filter_path: "aggregations",
  78. aggs: {
  79. line: {
  80. geo_line: {
  81. point: {
  82. field: "my_location",
  83. },
  84. sort: {
  85. field: "@timestamp",
  86. },
  87. },
  88. },
  89. },
  90. });
  91. console.log(response2);

コンソール

  1. PUT test
  2. {
  3. "mappings": {
  4. "properties": {
  5. "my_location": { "type": "geo_point" },
  6. "group": { "type": "keyword" },
  7. "@timestamp": { "type": "date" }
  8. }
  9. }
  10. }
  11. POST /test/_bulk?refresh
  12. {"index":{}}
  13. {"my_location": {"lat":52.373184, "lon":4.889187}, "@timestamp": "2023-01-02T09:00:00Z"}
  14. {"index":{}}
  15. {"my_location": {"lat":52.370159, "lon":4.885057}, "@timestamp": "2023-01-02T10:00:00Z"}
  16. {"index":{}}
  17. {"my_location": {"lat":52.369219, "lon":4.901618}, "@timestamp": "2023-01-02T13:00:00Z"}
  18. {"index":{}}
  19. {"my_location": {"lat":52.374081, "lon":4.912350}, "@timestamp": "2023-01-02T16:00:00Z"}
  20. {"index":{}}
  21. {"my_location": {"lat":52.371667, "lon":4.914722}, "@timestamp": "2023-01-03T12:00:00Z"}
  22. POST /test/_search?filter_path=aggregations
  23. {
  24. "aggs": {
  25. "line": {
  26. "geo_line": {
  27. "point": {"field": "my_location"},
  28. "sort": {"field": "@timestamp"}
  29. }
  30. }
  31. }
  32. }

次のように返されます:

Js

  1. {
  2. "aggregations": {
  3. "line": {
  4. "type": "Feature",
  5. "geometry": {
  6. "type": "LineString",
  7. "coordinates": [
  8. [ 4.889187, 52.373184 ],
  9. [ 4.885057, 52.370159 ],
  10. [ 4.901618, 52.369219 ],
  11. [ 4.912350, 52.374081 ],
  12. [ 4.914722, 52.371667 ]
  13. ]
  14. },
  15. "properties": {
  16. "complete": true
  17. }
  18. }
  19. }
  20. }

結果の GeoJSON Feature には、集約によって生成されたパスの LineString ジオメトリと、properties のマップが含まれています。プロパティ complete は、生成されたジオメトリに使用されたすべてのドキュメントが一致したかどうかを示します。size オプション を使用して、集約に含まれるドキュメントの数を制限することができ、complete: false を持つ結果が得られます。どのドキュメントが結果から除外されるかは、集約が time_series に基づいているかどうかによって異なります

この結果は、マップユーザーインターフェースに表示できます:

アムステルダムの博物館ツアーのKibanaマップ

オプション

  • point
  • (必須)

このオプションは、geo_point フィールドの名前を指定します

my_location をポイントフィールドとして設定する例:

Js

  1. "point": {
  2. "field": "my_location"
  3. }

このオプションは、ポイントの順序付けに使用する数値フィールドの名前を指定します。geo_line 集約が time_series 集約の内部にネストされている場合、このフィールドは @timestamp にデフォルト設定され、他の値を設定するとエラーが発生します。

@timestamp をソートキーとして設定する例:

Js

  1. "sort": {
  2. "field": "@timestamp"
  3. }
  • include_sort
  • (オプション、ブール値、デフォルト: false) このオプションは、true の場合、フィーチャプロパティにソート値の追加配列を含めます。
  • sort_order
  • (オプション、文字列、デフォルト: "ASC") このオプションは、2つの値のいずれかを受け入れます:
    “ASC”, “DESC”。ソートキーが “ASC” に設定されている場合、ラインは昇順にソートされ、”DESC” に設定されている場合は降順にソートされます。

  • size
  • (オプション、整数、デフォルト: 10000) 集約で表されるラインの最大長さ。妥当なサイズは1から10000の間です。time_series 内では、集約はライン簡略化を使用してサイズを制約し、それ以外の場合は切り捨てを使用します。なぜ時系列でグループ化するのか? で関与する微妙な点について議論しています。

グルーピング

この単純な例は、クエリによって選択されたすべてのデータの単一トラックを生成します。しかし、データを複数のトラックにグループ化する必要があることがはるかに一般的です。例えば、フライトコールサインによってフライトトランスポンダ測定をグループ化し、各フライトをタイムスタンプでソートして、各フライトのために別々のトラックを生成します。

次の例では、アムステルダム、アントワープ、パリの観光地の位置をグループ化します。トラックは、博物館やその他の観光名所のウォーキングツアーの計画された訪問順序によって順序付けられます。

時系列グルーピングと非時系列グルーピングの違いを示すために、最初に 時系列が有効なインデックス を作成し、次に時系列なしで同じデータをグループ化する例を示します。

Python

  1. resp = client.indices.create(
  2. index="tour",
  3. mappings={
  4. "properties": {
  5. "city": {
  6. "type": "keyword",
  7. "time_series_dimension": True
  8. },
  9. "category": {
  10. "type": "keyword"
  11. },
  12. "route": {
  13. "type": "long"
  14. },
  15. "name": {
  16. "type": "keyword"
  17. },
  18. "location": {
  19. "type": "geo_point"
  20. },
  21. "@timestamp": {
  22. "type": "date"
  23. }
  24. }
  25. },
  26. settings={
  27. "index": {
  28. "mode": "time_series",
  29. "routing_path": [
  30. "city"
  31. ],
  32. "time_series": {
  33. "start_time": "2023-01-01T00:00:00Z",
  34. "end_time": "2024-01-01T00:00:00Z"
  35. }
  36. }
  37. },
  38. )
  39. print(resp)
  40. resp1 = client.bulk(
  41. index="tour",
  42. refresh=True,
  43. operations=[
  44. {
  45. "index": {}
  46. },
  47. {
  48. "@timestamp": "2023-01-02T09:00:00Z",
  49. "route": 0,
  50. "location": "POINT(4.889187 52.373184)",
  51. "city": "Amsterdam",
  52. "category": "Attraction",
  53. "name": "Royal Palace Amsterdam"
  54. },
  55. {
  56. "index": {}
  57. },
  58. {
  59. "@timestamp": "2023-01-02T10:00:00Z",
  60. "route": 1,
  61. "location": "POINT(4.885057 52.370159)",
  62. "city": "Amsterdam",
  63. "category": "Attraction",
  64. "name": "The Amsterdam Dungeon"
  65. },
  66. {
  67. "index": {}
  68. },
  69. {
  70. "@timestamp": "2023-01-02T13:00:00Z",
  71. "route": 2,
  72. "location": "POINT(4.901618 52.369219)",
  73. "city": "Amsterdam",
  74. "category": "Museum",
  75. "name": "Museum Het Rembrandthuis"
  76. },
  77. {
  78. "index": {}
  79. },
  80. {
  81. "@timestamp": "2023-01-02T16:00:00Z",
  82. "route": 3,
  83. "location": "POINT(4.912350 52.374081)",
  84. "city": "Amsterdam",
  85. "category": "Museum",
  86. "name": "NEMO Science Museum"
  87. },
  88. {
  89. "index": {}
  90. },
  91. {
  92. "@timestamp": "2023-01-03T12:00:00Z",
  93. "route": 4,
  94. "location": "POINT(4.914722 52.371667)",
  95. "city": "Amsterdam",
  96. "category": "Museum",
  97. "name": "Nederlands Scheepvaartmuseum"
  98. },
  99. {
  100. "index": {}
  101. },
  102. {
  103. "@timestamp": "2023-01-04T09:00:00Z",
  104. "route": 5,
  105. "location": "POINT(4.401384 51.220292)",
  106. "city": "Antwerp",
  107. "category": "Attraction",
  108. "name": "Cathedral of Our Lady"
  109. },
  110. {
  111. "index": {}
  112. },
  113. {
  114. "@timestamp": "2023-01-04T12:00:00Z",
  115. "route": 6,
  116. "location": "POINT(4.405819 51.221758)",
  117. "city": "Antwerp",
  118. "category": "Museum",
  119. "name": "Snijders&Rockoxhuis"
  120. },
  121. {
  122. "index": {}
  123. },
  124. {
  125. "@timestamp": "2023-01-04T15:00:00Z",
  126. "route": 7,
  127. "location": "POINT(4.405200 51.222900)",
  128. "city": "Antwerp",
  129. "category": "Museum",
  130. "name": "Letterenhuis"
  131. },
  132. {
  133. "index": {}
  134. },
  135. {
  136. "@timestamp": "2023-01-05T10:00:00Z",
  137. "route": 8,
  138. "location": "POINT(2.336389 48.861111)",
  139. "city": "Paris",
  140. "category": "Museum",
  141. "name": "Musée du Louvre"
  142. },
  143. {
  144. "index": {}
  145. },
  146. {
  147. "@timestamp": "2023-01-05T14:00:00Z",
  148. "route": 9,
  149. "location": "POINT(2.327000 48.860000)",
  150. "city": "Paris",
  151. "category": "Museum",
  152. "name": "Musée dOrsay"
  153. }
  154. ],
  155. )
  156. print(resp1)

Ruby

  1. response = client.indices.create(
  2. index: 'tour',
  3. body: {
  4. mappings: {
  5. properties: {
  6. city: {
  7. type: 'keyword',
  8. time_series_dimension: true
  9. },
  10. category: {
  11. type: 'keyword'
  12. },
  13. route: {
  14. type: 'long'
  15. },
  16. name: {
  17. type: 'keyword'
  18. },
  19. location: {
  20. type: 'geo_point'
  21. },
  22. "@timestamp": {
  23. type: 'date'
  24. }
  25. }
  26. },
  27. settings: {
  28. index: {
  29. mode: 'time_series',
  30. routing_path: [
  31. 'city'
  32. ],
  33. time_series: {
  34. start_time: '2023-01-01T00:00:00Z',
  35. end_time: '2024-01-01T00:00:00Z'
  36. }
  37. }
  38. }
  39. }
  40. )
  41. puts response
  42. response = client.bulk(
  43. index: 'tour',
  44. refresh: true,
  45. body: [
  46. {
  47. index: {}
  48. },
  49. {
  50. "@timestamp": '2023-01-02T09:00:00Z',
  51. route: 0,
  52. location: 'POINT(4.889187 52.373184)',
  53. city: 'Amsterdam',
  54. category: 'Attraction',
  55. name: 'Royal Palace Amsterdam'
  56. },
  57. {
  58. index: {}
  59. },
  60. {
  61. "@timestamp": '2023-01-02T10:00:00Z',
  62. route: 1,
  63. location: 'POINT(4.885057 52.370159)',
  64. city: 'Amsterdam',
  65. category: 'Attraction',
  66. name: 'The Amsterdam Dungeon'
  67. },
  68. {
  69. index: {}
  70. },
  71. {
  72. "@timestamp": '2023-01-02T13:00:00Z',
  73. route: 2,
  74. location: 'POINT(4.901618 52.369219)',
  75. city: 'Amsterdam',
  76. category: 'Museum',
  77. name: 'Museum Het Rembrandthuis'
  78. },
  79. {
  80. index: {}
  81. },
  82. {
  83. "@timestamp": '2023-01-02T16:00:00Z',
  84. route: 3,
  85. location: 'POINT(4.912350 52.374081)',
  86. city: 'Amsterdam',
  87. category: 'Museum',
  88. name: 'NEMO Science Museum'
  89. },
  90. {
  91. index: {}
  92. },
  93. {
  94. "@timestamp": '2023-01-03T12:00:00Z',
  95. route: 4,
  96. location: 'POINT(4.914722 52.371667)',
  97. city: 'Amsterdam',
  98. category: 'Museum',
  99. name: 'Nederlands Scheepvaartmuseum'
  100. },
  101. {
  102. index: {}
  103. },
  104. {
  105. "@timestamp": '2023-01-04T09:00:00Z',
  106. route: 5,
  107. location: 'POINT(4.401384 51.220292)',
  108. city: 'Antwerp',
  109. category: 'Attraction',
  110. name: 'Cathedral of Our Lady'
  111. },
  112. {
  113. index: {}
  114. },
  115. {
  116. "@timestamp": '2023-01-04T12:00:00Z',
  117. route: 6,
  118. location: 'POINT(4.405819 51.221758)',
  119. city: 'Antwerp',
  120. category: 'Museum',
  121. name: 'Snijders&Rockoxhuis'
  122. },
  123. {
  124. index: {}
  125. },
  126. {
  127. "@timestamp": '2023-01-04T15:00:00Z',
  128. route: 7,
  129. location: 'POINT(4.405200 51.222900)',
  130. city: 'Antwerp',
  131. category: 'Museum',
  132. name: 'Letterenhuis'
  133. },
  134. {
  135. index: {}
  136. },
  137. {
  138. "@timestamp": '2023-01-05T10:00:00Z',
  139. route: 8,
  140. location: 'POINT(2.336389 48.861111)',
  141. city: 'Paris',
  142. category: 'Museum',
  143. name: 'Musée du Louvre'
  144. },
  145. {
  146. index: {}
  147. },
  148. {
  149. "@timestamp": '2023-01-05T14:00:00Z',
  150. route: 9,
  151. location: 'POINT(2.327000 48.860000)',
  152. city: 'Paris',
  153. category: 'Museum',
  154. name: 'Musée dOrsay'
  155. }
  156. ]
  157. )
  158. puts response

Js

  1. const response = await client.indices.create({
  2. index: "tour",
  3. mappings: {
  4. properties: {
  5. city: {
  6. type: "keyword",
  7. time_series_dimension: true,
  8. },
  9. category: {
  10. type: "keyword",
  11. },
  12. route: {
  13. type: "long",
  14. },
  15. name: {
  16. type: "keyword",
  17. },
  18. location: {
  19. type: "geo_point",
  20. },
  21. "@timestamp": {
  22. type: "date",
  23. },
  24. },
  25. },
  26. settings: {
  27. index: {
  28. mode: "time_series",
  29. routing_path: ["city"],
  30. time_series: {
  31. start_time: "2023-01-01T00:00:00Z",
  32. end_time: "2024-01-01T00:00:00Z",
  33. },
  34. },
  35. },
  36. });
  37. console.log(response);
  38. const response1 = await client.bulk({
  39. index: "tour",
  40. refresh: "true",
  41. operations: [
  42. {
  43. index: {},
  44. },
  45. {
  46. "@timestamp": "2023-01-02T09:00:00Z",
  47. route: 0,
  48. location: "POINT(4.889187 52.373184)",
  49. city: "Amsterdam",
  50. category: "Attraction",
  51. name: "Royal Palace Amsterdam",
  52. },
  53. {
  54. index: {},
  55. },
  56. {
  57. "@timestamp": "2023-01-02T10:00:00Z",
  58. route: 1,
  59. location: "POINT(4.885057 52.370159)",
  60. city: "Amsterdam",
  61. category: "Attraction",
  62. name: "The Amsterdam Dungeon",
  63. },
  64. {
  65. index: {},
  66. },
  67. {
  68. "@timestamp": "2023-01-02T13:00:00Z",
  69. route: 2,
  70. location: "POINT(4.901618 52.369219)",
  71. city: "Amsterdam",
  72. category: "Museum",
  73. name: "Museum Het Rembrandthuis",
  74. },
  75. {
  76. index: {},
  77. },
  78. {
  79. "@timestamp": "2023-01-02T16:00:00Z",
  80. route: 3,
  81. location: "POINT(4.912350 52.374081)",
  82. city: "Amsterdam",
  83. category: "Museum",
  84. name: "NEMO Science Museum",
  85. },
  86. {
  87. index: {},
  88. },
  89. {
  90. "@timestamp": "2023-01-03T12:00:00Z",
  91. route: 4,
  92. location: "POINT(4.914722 52.371667)",
  93. city: "Amsterdam",
  94. category: "Museum",
  95. name: "Nederlands Scheepvaartmuseum",
  96. },
  97. {
  98. index: {},
  99. },
  100. {
  101. "@timestamp": "2023-01-04T09:00:00Z",
  102. route: 5,
  103. location: "POINT(4.401384 51.220292)",
  104. city: "Antwerp",
  105. category: "Attraction",
  106. name: "Cathedral of Our Lady",
  107. },
  108. {
  109. index: {},
  110. },
  111. {
  112. "@timestamp": "2023-01-04T12:00:00Z",
  113. route: 6,
  114. location: "POINT(4.405819 51.221758)",
  115. city: "Antwerp",
  116. category: "Museum",
  117. name: "Snijders&Rockoxhuis",
  118. },
  119. {
  120. index: {},
  121. },
  122. {
  123. "@timestamp": "2023-01-04T15:00:00Z",
  124. route: 7,
  125. location: "POINT(4.405200 51.222900)",
  126. city: "Antwerp",
  127. category: "Museum",
  128. name: "Letterenhuis",
  129. },
  130. {
  131. index: {},
  132. },
  133. {
  134. "@timestamp": "2023-01-05T10:00:00Z",
  135. route: 8,
  136. location: "POINT(2.336389 48.861111)",
  137. city: "Paris",
  138. category: "Museum",
  139. name: "Musée du Louvre",
  140. },
  141. {
  142. index: {},
  143. },
  144. {
  145. "@timestamp": "2023-01-05T14:00:00Z",
  146. route: 9,
  147. location: "POINT(2.327000 48.860000)",
  148. city: "Paris",
  149. category: "Museum",
  150. name: "Musée dOrsay",
  151. },
  152. ],
  153. });
  154. console.log(response1);

コンソール

  1. PUT tour
  2. {
  3. "mappings": {
  4. "properties": {
  5. "city": {
  6. "type": "keyword",
  7. "time_series_dimension": true
  8. },
  9. "category": { "type": "keyword" },
  10. "route": { "type": "long" },
  11. "name": { "type": "keyword" },
  12. "location": { "type": "geo_point" },
  13. "@timestamp": { "type": "date" }
  14. }
  15. },
  16. "settings": {
  17. "index": {
  18. "mode": "time_series",
  19. "routing_path": [ "city" ],
  20. "time_series": {
  21. "start_time": "2023-01-01T00:00:00Z",
  22. "end_time": "2024-01-01T00:00:00Z"
  23. }
  24. }
  25. }
  26. }
  27. POST /tour/_bulk?refresh
  28. {"index":{}}
  29. {"@timestamp": "2023-01-02T09:00:00Z", "route": 0, "location": "POINT(4.889187 52.373184)", "city": "Amsterdam", "category": "Attraction", "name": "Royal Palace Amsterdam"}
  30. {"index":{}}
  31. {"@timestamp": "2023-01-02T10:00:00Z", "route": 1, "location": "POINT(4.885057 52.370159)", "city": "Amsterdam", "category": "Attraction", "name": "The Amsterdam Dungeon"}
  32. {"index":{}}
  33. {"@timestamp": "2023-01-02T13:00:00Z", "route": 2, "location": "POINT(4.901618 52.369219)", "city": "Amsterdam", "category": "Museum", "name": "Museum Het Rembrandthuis"}
  34. {"index":{}}
  35. {"@timestamp": "2023-01-02T16:00:00Z", "route": 3, "location": "POINT(4.912350 52.374081)", "city": "Amsterdam", "category": "Museum", "name": "NEMO Science Museum"}
  36. {"index":{}}
  37. {"@timestamp": "2023-01-03T12:00:00Z", "route": 4, "location": "POINT(4.914722 52.371667)", "city": "Amsterdam", "category": "Museum", "name": "Nederlands Scheepvaartmuseum"}
  38. {"index":{}}
  39. {"@timestamp": "2023-01-04T09:00:00Z", "route": 5, "location": "POINT(4.401384 51.220292)", "city": "Antwerp", "category": "Attraction", "name": "Cathedral of Our Lady"}
  40. {"index":{}}
  41. {"@timestamp": "2023-01-04T12:00:00Z", "route": 6, "location": "POINT(4.405819 51.221758)", "city": "Antwerp", "category": "Museum", "name": "Snijders&Rockoxhuis"}
  42. {"index":{}}
  43. {"@timestamp": "2023-01-04T15:00:00Z", "route": 7, "location": "POINT(4.405200 51.222900)", "city": "Antwerp", "category": "Museum", "name": "Letterenhuis"}
  44. {"index":{}}
  45. {"@timestamp": "2023-01-05T10:00:00Z", "route": 8, "location": "POINT(2.336389 48.861111)", "city": "Paris", "category": "Museum", "name": "Musée du Louvre"}
  46. {"index":{}}
  47. {"@timestamp": "2023-01-05T14:00:00Z", "route": 9, "location": "POINT(2.327000 48.860000)", "city": "Paris", "category": "Museum", "name": "Musée dOrsay"}

用語によるグルーピング

このデータを使用して、非時系列のユースケースの場合、都市名に基づいて 用語集約 を使用してグループ化できます。これは、tour インデックスを時系列インデックスとして定義しているかどうかにかかわらず機能します。

Python

  1. resp = client.search(
  2. index="tour",
  3. filter_path="aggregations",
  4. aggregations={
  5. "path": {
  6. "terms": {
  7. "field": "city"
  8. },
  9. "aggregations": {
  10. "museum_tour": {
  11. "geo_line": {
  12. "point": {
  13. "field": "location"
  14. },
  15. "sort": {
  16. "field": "@timestamp"
  17. }
  18. }
  19. }
  20. }
  21. }
  22. },
  23. )
  24. print(resp)

Js

  1. const response = await client.search({
  2. index: "tour",
  3. filter_path: "aggregations",
  4. aggregations: {
  5. path: {
  6. terms: {
  7. field: "city",
  8. },
  9. aggregations: {
  10. museum_tour: {
  11. geo_line: {
  12. point: {
  13. field: "location",
  14. },
  15. sort: {
  16. field: "@timestamp",
  17. },
  18. },
  19. },
  20. },
  21. },
  22. },
  23. });
  24. console.log(response);

コンソール

  1. POST /tour/_search?filter_path=aggregations
  2. {
  3. "aggregations": {
  4. "path": {
  5. "terms": {"field": "city"},
  6. "aggregations": {
  7. "museum_tour": {
  8. "geo_line": {
  9. "point": {"field": "location"},
  10. "sort": {"field": "@timestamp"}
  11. }
  12. }
  13. }
  14. }
  15. }
  16. }

次のように返されます:

Js

  1. {
  2. "aggregations": {
  3. "path": {
  4. "doc_count_error_upper_bound": 0,
  5. "sum_other_doc_count": 0,
  6. "buckets": [
  7. {
  8. "key": "Amsterdam",
  9. "doc_count": 5,
  10. "museum_tour": {
  11. "type": "Feature",
  12. "geometry": {
  13. "coordinates": [ [ 4.889187, 52.373184 ], [ 4.885057, 52.370159 ], [ 4.901618, 52.369219 ], [ 4.91235, 52.374081 ], [ 4.914722, 52.371667 ] ],
  14. "type": "LineString"
  15. },
  16. "properties": {
  17. "complete": true
  18. }
  19. }
  20. },
  21. {
  22. "key": "Antwerp",
  23. "doc_count": 3,
  24. "museum_tour": {
  25. "type": "Feature",
  26. "geometry": {
  27. "coordinates": [ [ 4.401384, 51.220292 ], [ 4.405819, 51.221758 ], [ 4.4052, 51.2229 ] ],
  28. "type": "LineString"
  29. },
  30. "properties": {
  31. "complete": true
  32. }
  33. }
  34. },
  35. {
  36. "key": "Paris",
  37. "doc_count": 2,
  38. "museum_tour": {
  39. "type": "Feature",
  40. "geometry": {
  41. "coordinates": [ [ 2.336389, 48.861111 ], [ 2.327, 48.86 ] ],
  42. "type": "LineString"
  43. },
  44. "properties": {
  45. "complete": true
  46. }
  47. }
  48. }
  49. ]
  50. }
  51. }
  52. }

これらの結果には、各バケットが key を示す JSON オブジェクトの配列が含まれており、city フィールドの名前と、museum_tour という内部集約結果が含まれています。この結果には、GeoJSON Feature が含まれており、その都市のさまざまな観光名所間の実際のルートを説明しています。各結果には、properties オブジェクトも含まれており、complete 値が含まれ、ジオメトリが size パラメータで指定された制限に切り捨てられた場合は false になります。次の例で time_series を使用すると、構造が少し異なる同じ結果が得られます。

時系列によるグルーピング

この機能は技術プレビュー中であり、将来のリリースで変更または削除される可能性があります。Elastic は問題を修正するために作業しますが、技術プレビューの機能は公式 GA 機能のサポート SLA の対象ではありません。

以前と同じデータを使用して、time_series 集約 を使用してグループ化を行うこともできます。これは、time_series_dimension: true のすべてのフィールドの組み合わせとして定義される TSID によってグループ化されます。この場合、前の 用語集約 で使用されたのと同じ city フィールドが使用されます。この例は、tour インデックスを index.mode="time_series" を使用して時系列インデックスとして定義した場合にのみ機能します。

Python

  1. resp = client.search(
  2. index="tour",
  3. filter_path="aggregations",
  4. aggregations={
  5. "path": {
  6. "time_series": {},
  7. "aggregations": {
  8. "museum_tour": {
  9. "geo_line": {
  10. "point": {
  11. "field": "location"
  12. }
  13. }
  14. }
  15. }
  16. }
  17. },
  18. )
  19. print(resp)

Js

  1. const response = await client.search({
  2. index: "tour",
  3. filter_path: "aggregations",
  4. aggregations: {
  5. path: {
  6. time_series: {},
  7. aggregations: {
  8. museum_tour: {
  9. geo_line: {
  10. point: {
  11. field: "location",
  12. },
  13. },
  14. },
  15. },
  16. },
  17. },
  18. });
  19. console.log(response);

コンソール

  1. POST /tour/_search?filter_path=aggregations
  2. {
  3. "aggregations": {
  4. "path": {
  5. "time_series": {},
  6. "aggregations": {
  7. "museum_tour": {
  8. "geo_line": {
  9. "point": {"field": "location"}
  10. }
  11. }
  12. }
  13. }
  14. }
  15. }

geo_line 集約は、time_series 集約 内にネストされている場合、もはや sort フィールドを必要としません。これは、ソートフィールドが @timestamp に設定されており、すべての時系列インデックスが事前にソートされているためです。このパラメータを設定し、@timestamp 以外の何かに設定するとエラーが発生します。

このクエリの結果は:

Js

  1. {
  2. "aggregations": {
  3. "path": {
  4. "buckets": {
  5. "{city=Paris}": {
  6. "key": {
  7. "city": "Paris"
  8. },
  9. "doc_count": 2,
  10. "museum_tour": {
  11. "type": "Feature",
  12. "geometry": {
  13. "coordinates": [ [ 2.336389, 48.861111 ], [ 2.327, 48.86 ] ],
  14. "type": "LineString"
  15. },
  16. "properties": {
  17. "complete": true
  18. }
  19. }
  20. },
  21. "{city=Antwerp}": {
  22. "key": {
  23. "city": "Antwerp"
  24. },
  25. "doc_count": 3,
  26. "museum_tour": {
  27. "type": "Feature",
  28. "geometry": {
  29. "coordinates": [ [ 4.401384, 51.220292 ], [ 4.405819, 51.221758 ], [ 4.4052, 51.2229 ] ],
  30. "type": "LineString"
  31. },
  32. "properties": {
  33. "complete": true
  34. }
  35. }
  36. },
  37. "{city=Amsterdam}": {
  38. "key": {
  39. "city": "Amsterdam"
  40. },
  41. "doc_count": 5,
  42. "museum_tour": {
  43. "type": "Feature",
  44. "geometry": {
  45. "coordinates": [ [ 4.889187, 52.373184 ], [ 4.885057, 52.370159 ], [ 4.901618, 52.369219 ], [ 4.91235, 52.374081 ], [ 4.914722, 52.371667 ] ],
  46. "type": "LineString"
  47. },
  48. "properties": {
  49. "complete": true
  50. }
  51. }
  52. }
  53. }
  54. }
  55. }
  56. }

これらの結果は、前の terms 集約の例と本質的に同じですが、構造が異なります。ここでは、バケットがマップとして返され、キーは TSID の内部説明です。この TSID は、time_series_dimension: true を持つフィールドのユニークな組み合わせごとにユニークです。各バケットには、key フィールドが含まれており、これは TSID のすべての次元値のマップでもあります。この場合、グループ化には都市名のみが使用されます。さらに、museum_tour という内部集約結果が含まれており、GeoJSON Feature が含まれており、その都市のさまざまな観光名所間の実際のルートを説明しています。各結果には、properties オブジェクトも含まれており、complete 値が含まれ、ジオメトリが size パラメータで指定された制限に簡略化された場合は false になります。

なぜ時系列でグループ化するのか?

これらの例を見ていると、terms または time_series を使用してジオラインをグループ化することの違いはほとんどないと思うかもしれません。しかし、2つのケース間には重要な動作の違いがあります。時系列インデックスは、ディスク上で非常に特定の順序で保存されます。これらは、時系列次元フィールドによって事前にグループ化され、@timestamp フィールドによって事前にソートされています。これにより、geo_line 集約が大幅に最適化されます:

  • 最初のバケットに割り当てられた同じメモリを、すべての後続のバケットに何度も再利用できます。これは、すべてのバケットが同時に収集される非時系列ケースで必要とされるメモリよりも大幅に少なくなります。
  • ソートは必要ありません。データは @timestamp によって事前にソートされているためです。時系列データは、DESC 順序で集約コレクターに自然に到着します。これは、sort_order:ASC (デフォルト) を指定しても、DESC 順序で収集されますが、最終的な LineString ジオメトリを生成する前に効率的なメモリ内逆順を実行します。
  • size パラメータは、ストリーミングライン簡略化アルゴリズムに使用できます。時系列がない場合、デフォルトではバケットごとに10000ドキュメントの後にデータを切り捨てる必要があります。これにより、メモリ使用量が無制限になるのを防ぎます。これにより、ジオラインが切り捨てられ、重要なデータが失われる可能性があります。時系列を使用すると、ストリーミングライン簡略化アルゴリズムを実行でき、メモリ使用量を制御しながら、全体のジオメトリ形状を維持できます。実際、ほとんどのユースケースでは、この size パラメータをはるかに低い制限に設定して、さらに多くのメモリを節約することができます。例えば、geo_line が特定の解像度の表示マップに描画される場合、100または200ポイントに簡略化しても同じように見えるかもしれません。これにより、サーバー、ネットワーク、クライアントのメモリが節約されます。

注意: 時系列データを扱い、time_series インデックスモードを使用することには、他にも重要な利点があります。これらは、時系列データストリーム に関するドキュメントで説明されています。

ストリーミングライン簡略化

ライン簡略化は、クライアントに送信され、マップユーザーインターフェースに表示される最終結果のサイズを削減する優れた方法です。しかし、通常、これらのアルゴリズムは簡略化を実行するために多くのメモリを使用し、簡略化自体のサポートデータとともに、全体のジオメトリをメモリに保持する必要があります。ストリーミングライン簡略化アルゴリズムを使用すると、簡略化プロセス中のメモリ使用量を最小限に抑え、簡略化されたジオメトリのために定義された境界にメモリを制約することができます。これは、time_series 集約 によってグループ化が行われ、time_series インデックスモードを持つインデックスで実行される場合にのみ可能です。

これらの条件下で、geo_line 集約は指定された size にメモリを割り当て、その後、受信したドキュメントでそのメモリを埋めます。メモリが完全に埋まると、新しいドキュメントが追加されると、ライン内のドキュメントが削除されます。削除するドキュメントの選択は、ジオメトリへの視覚的影響を最小限に抑えるように行われます。このプロセスは、Visvalingam–Whyatt アルゴリズム を利用します。基本的に、これは、考慮中のポイントとライン内の前後の2つのポイントによって定義される三角形の最小面積を持つポイントが削除されることを意味します。さらに、平面の歪みが選択に影響しないように、球面座標を使用して面積を計算します。

ライン切り捨てよりもライン簡略化がどれほど優れているかを示すために、コディアック島の北岸のこの例を考えてみましょう。このデータは209ポイントしかありませんが、size100 に設定したい場合、劇的な切り捨てが発生します。

コディアック島の北岸が100ポイントに切り捨てられた

灰色のラインは209ポイントの全ジオメトリであり、青いラインは最初の100ポイントであり、元のジオメトリとは非常に異なります。

同じジオメトリを100ポイントに簡略化した場合を考えてみましょう。

コディアック島の北岸が100ポイントに簡略化された

比較のために、元のものを灰色で、切り捨てたものを青で、新しい簡略化されたジオメトリをマゼンタで示しました。新しい簡略化されたラインが元のラインからどのように逸脱しているかを見ることができますが、全体のジオメトリはほぼ同じに見え、コディアック島の北岸として明確に認識できます。