地理距離集約

geo_point フィールドで動作するマルチバケット集約で、概念的には range 集約と非常に似ています。ユーザーは起点と一連の距離範囲バケットを定義できます。集約は各ドキュメント値の起点からの距離を評価し、距離がバケットの範囲内に収まる場合、そのバケットに属するかどうかを判断します(ドキュメントと起点の距離がバケットの距離範囲内にある場合、ドキュメントはそのバケットに属します)。

Python

  1. resp = client.indices.create(
  2. index="museums",
  3. mappings={
  4. "properties": {
  5. "location": {
  6. "type": "geo_point"
  7. }
  8. }
  9. },
  10. )
  11. print(resp)
  12. resp1 = client.bulk(
  13. index="museums",
  14. refresh=True,
  15. operations=[
  16. {
  17. "index": {
  18. "_id": 1
  19. }
  20. },
  21. {
  22. "location": "POINT (4.912350 52.374081)",
  23. "name": "NEMO Science Museum"
  24. },
  25. {
  26. "index": {
  27. "_id": 2
  28. }
  29. },
  30. {
  31. "location": "POINT (4.901618 52.369219)",
  32. "name": "Museum Het Rembrandthuis"
  33. },
  34. {
  35. "index": {
  36. "_id": 3
  37. }
  38. },
  39. {
  40. "location": "POINT (4.914722 52.371667)",
  41. "name": "Nederlands Scheepvaartmuseum"
  42. },
  43. {
  44. "index": {
  45. "_id": 4
  46. }
  47. },
  48. {
  49. "location": "POINT (4.405200 51.222900)",
  50. "name": "Letterenhuis"
  51. },
  52. {
  53. "index": {
  54. "_id": 5
  55. }
  56. },
  57. {
  58. "location": "POINT (2.336389 48.861111)",
  59. "name": "Musée du Louvre"
  60. },
  61. {
  62. "index": {
  63. "_id": 6
  64. }
  65. },
  66. {
  67. "location": "POINT (2.327000 48.860000)",
  68. "name": "Musée d'Orsay"
  69. }
  70. ],
  71. )
  72. print(resp1)
  73. resp2 = client.search(
  74. index="museums",
  75. size="0",
  76. aggs={
  77. "rings_around_amsterdam": {
  78. "geo_distance": {
  79. "field": "location",
  80. "origin": "POINT (4.894 52.3760)",
  81. "ranges": [
  82. {
  83. "to": 100000
  84. },
  85. {
  86. "from": 100000,
  87. "to": 300000
  88. },
  89. {
  90. "from": 300000
  91. }
  92. ]
  93. }
  94. }
  95. },
  96. )
  97. print(resp2)

Ruby

  1. response = client.indices.create(
  2. index: 'museums',
  3. body: {
  4. mappings: {
  5. properties: {
  6. location: {
  7. type: 'geo_point'
  8. }
  9. }
  10. }
  11. }
  12. )
  13. puts response
  14. response = client.bulk(
  15. index: 'museums',
  16. refresh: true,
  17. body: [
  18. {
  19. index: {
  20. _id: 1
  21. }
  22. },
  23. {
  24. location: 'POINT (4.912350 52.374081)',
  25. name: 'NEMO Science Museum'
  26. },
  27. {
  28. index: {
  29. _id: 2
  30. }
  31. },
  32. {
  33. location: 'POINT (4.901618 52.369219)',
  34. name: 'Museum Het Rembrandthuis'
  35. },
  36. {
  37. index: {
  38. _id: 3
  39. }
  40. },
  41. {
  42. location: 'POINT (4.914722 52.371667)',
  43. name: 'Nederlands Scheepvaartmuseum'
  44. },
  45. {
  46. index: {
  47. _id: 4
  48. }
  49. },
  50. {
  51. location: 'POINT (4.405200 51.222900)',
  52. name: 'Letterenhuis'
  53. },
  54. {
  55. index: {
  56. _id: 5
  57. }
  58. },
  59. {
  60. location: 'POINT (2.336389 48.861111)',
  61. name: 'Musée du Louvre'
  62. },
  63. {
  64. index: {
  65. _id: 6
  66. }
  67. },
  68. {
  69. location: 'POINT (2.327000 48.860000)',
  70. name: "Musée d'Orsay"
  71. }
  72. ]
  73. )
  74. puts response
  75. response = client.search(
  76. index: 'museums',
  77. size: 0,
  78. body: {
  79. aggregations: {
  80. rings_around_amsterdam: {
  81. geo_distance: {
  82. field: 'location',
  83. origin: 'POINT (4.894 52.3760)',
  84. ranges: [
  85. {
  86. to: 100_000
  87. },
  88. {
  89. from: 100_000,
  90. to: 300_000
  91. },
  92. {
  93. from: 300_000
  94. }
  95. ]
  96. }
  97. }
  98. }
  99. }
  100. )
  101. puts response

Js

  1. const response = await client.indices.create({
  2. index: "museums",
  3. mappings: {
  4. properties: {
  5. location: {
  6. type: "geo_point",
  7. },
  8. },
  9. },
  10. });
  11. console.log(response);
  12. const response1 = await client.bulk({
  13. index: "museums",
  14. refresh: "true",
  15. operations: [
  16. {
  17. index: {
  18. _id: 1,
  19. },
  20. },
  21. {
  22. location: "POINT (4.912350 52.374081)",
  23. name: "NEMO Science Museum",
  24. },
  25. {
  26. index: {
  27. _id: 2,
  28. },
  29. },
  30. {
  31. location: "POINT (4.901618 52.369219)",
  32. name: "Museum Het Rembrandthuis",
  33. },
  34. {
  35. index: {
  36. _id: 3,
  37. },
  38. },
  39. {
  40. location: "POINT (4.914722 52.371667)",
  41. name: "Nederlands Scheepvaartmuseum",
  42. },
  43. {
  44. index: {
  45. _id: 4,
  46. },
  47. },
  48. {
  49. location: "POINT (4.405200 51.222900)",
  50. name: "Letterenhuis",
  51. },
  52. {
  53. index: {
  54. _id: 5,
  55. },
  56. },
  57. {
  58. location: "POINT (2.336389 48.861111)",
  59. name: "Musée du Louvre",
  60. },
  61. {
  62. index: {
  63. _id: 6,
  64. },
  65. },
  66. {
  67. location: "POINT (2.327000 48.860000)",
  68. name: "Musée d'Orsay",
  69. },
  70. ],
  71. });
  72. console.log(response1);
  73. const response2 = await client.search({
  74. index: "museums",
  75. size: 0,
  76. aggs: {
  77. rings_around_amsterdam: {
  78. geo_distance: {
  79. field: "location",
  80. origin: "POINT (4.894 52.3760)",
  81. ranges: [
  82. {
  83. to: 100000,
  84. },
  85. {
  86. from: 100000,
  87. to: 300000,
  88. },
  89. {
  90. from: 300000,
  91. },
  92. ],
  93. },
  94. },
  95. },
  96. });
  97. console.log(response2);

コンソール

  1. PUT /museums
  2. {
  3. "mappings": {
  4. "properties": {
  5. "location": {
  6. "type": "geo_point"
  7. }
  8. }
  9. }
  10. }
  11. POST /museums/_bulk?refresh
  12. {"index":{"_id":1}}
  13. {"location": "POINT (4.912350 52.374081)", "name": "NEMO Science Museum"}
  14. {"index":{"_id":2}}
  15. {"location": "POINT (4.901618 52.369219)", "name": "Museum Het Rembrandthuis"}
  16. {"index":{"_id":3}}
  17. {"location": "POINT (4.914722 52.371667)", "name": "Nederlands Scheepvaartmuseum"}
  18. {"index":{"_id":4}}
  19. {"location": "POINT (4.405200 51.222900)", "name": "Letterenhuis"}
  20. {"index":{"_id":5}}
  21. {"location": "POINT (2.336389 48.861111)", "name": "Musée du Louvre"}
  22. {"index":{"_id":6}}
  23. {"location": "POINT (2.327000 48.860000)", "name": "Musée d'Orsay"}
  24. POST /museums/_search?size=0
  25. {
  26. "aggs": {
  27. "rings_around_amsterdam": {
  28. "geo_distance": {
  29. "field": "location",
  30. "origin": "POINT (4.894 52.3760)",
  31. "ranges": [
  32. { "to": 100000 },
  33. { "from": 100000, "to": 300000 },
  34. { "from": 300000 }
  35. ]
  36. }
  37. }
  38. }
  39. }

レスポンス:

コンソール-結果

  1. {
  2. ...
  3. "aggregations": {
  4. "rings_around_amsterdam": {
  5. "buckets": [
  6. {
  7. "key": "*-100000.0",
  8. "from": 0.0,
  9. "to": 100000.0,
  10. "doc_count": 3
  11. },
  12. {
  13. "key": "100000.0-300000.0",
  14. "from": 100000.0,
  15. "to": 300000.0,
  16. "doc_count": 1
  17. },
  18. {
  19. "key": "300000.0-*",
  20. "from": 300000.0,
  21. "doc_count": 2
  22. }
  23. ]
  24. }
  25. }
  26. }

指定されたフィールドは geo_point 型でなければなりません(これはマッピングで明示的に設定することができます)。また、geo_point フィールドの配列を保持することもでき、その場合、すべてが集約中に考慮されます。起点は geo_point によってサポートされるすべての形式を受け入れることができます:

  • オブジェクト形式: { "lat" : 52.3760, "lon" : 4.894 } - これは latlon の値について最も明示的で安全な形式です
  • 文字列形式: "52.3760, 4.894" - 最初の数値が lat で、2番目が lon です
  • 配列形式: [4.894, 52.3760] - これは GeoJSON 標準に基づいており、最初の数値が lon で、2番目が lat です

デフォルトでは、距離単位は m (メートル) ですが、次のものも受け入れられます: mi (マイル)、in (インチ)、yd (ヤード)、km (キロメートル)、cm (センチメートル)、mm (ミリメートル)。

Python

  1. resp = client.search(
  2. index="museums",
  3. size="0",
  4. aggs={
  5. "rings": {
  6. "geo_distance": {
  7. "field": "location",
  8. "origin": "POINT (4.894 52.3760)",
  9. "unit": "km",
  10. "ranges": [
  11. {
  12. "to": 100
  13. },
  14. {
  15. "from": 100,
  16. "to": 300
  17. },
  18. {
  19. "from": 300
  20. }
  21. ]
  22. }
  23. }
  24. },
  25. )
  26. print(resp)

Ruby

  1. response = client.search(
  2. index: 'museums',
  3. size: 0,
  4. body: {
  5. aggregations: {
  6. rings: {
  7. geo_distance: {
  8. field: 'location',
  9. origin: 'POINT (4.894 52.3760)',
  10. unit: 'km',
  11. ranges: [
  12. {
  13. to: 100
  14. },
  15. {
  16. from: 100,
  17. to: 300
  18. },
  19. {
  20. from: 300
  21. }
  22. ]
  23. }
  24. }
  25. }
  26. }
  27. )
  28. puts response

Js

  1. const response = await client.search({
  2. index: "museums",
  3. size: 0,
  4. aggs: {
  5. rings: {
  6. geo_distance: {
  7. field: "location",
  8. origin: "POINT (4.894 52.3760)",
  9. unit: "km",
  10. ranges: [
  11. {
  12. to: 100,
  13. },
  14. {
  15. from: 100,
  16. to: 300,
  17. },
  18. {
  19. from: 300,
  20. },
  21. ],
  22. },
  23. },
  24. },
  25. });
  26. console.log(response);

コンソール

  1. POST /museums/_search?size=0
  2. {
  3. "aggs": {
  4. "rings": {
  5. "geo_distance": {
  6. "field": "location",
  7. "origin": "POINT (4.894 52.3760)",
  8. "unit": "km",
  9. "ranges": [
  10. { "to": 100 },
  11. { "from": 100, "to": 300 },
  12. { "from": 300 }
  13. ]
  14. }
  15. }
  16. }
  17. }
距離はキロメートルで計算されます

距離計算モードは2つあります: arc (デフォルト) と planearc 計算は最も正確です。plane は最も速いですが、最も正確ではありません。検索コンテキストが「狭い」場合(約5km)には plane を使用することを検討してください。plane は非常に大きな地域(例: 大陸を越える検索)での検索に対して高い誤差範囲を返します。距離計算タイプは distance_type パラメータを使用して設定できます:

Python

  1. resp = client.search(
  2. index="museums",
  3. size="0",
  4. aggs={
  5. "rings": {
  6. "geo_distance": {
  7. "field": "location",
  8. "origin": "POINT (4.894 52.3760)",
  9. "unit": "km",
  10. "distance_type": "plane",
  11. "ranges": [
  12. {
  13. "to": 100
  14. },
  15. {
  16. "from": 100,
  17. "to": 300
  18. },
  19. {
  20. "from": 300
  21. }
  22. ]
  23. }
  24. }
  25. },
  26. )
  27. print(resp)

Ruby

  1. response = client.search(
  2. index: 'museums',
  3. size: 0,
  4. body: {
  5. aggregations: {
  6. rings: {
  7. geo_distance: {
  8. field: 'location',
  9. origin: 'POINT (4.894 52.3760)',
  10. unit: 'km',
  11. distance_type: 'plane',
  12. ranges: [
  13. {
  14. to: 100
  15. },
  16. {
  17. from: 100,
  18. to: 300
  19. },
  20. {
  21. from: 300
  22. }
  23. ]
  24. }
  25. }
  26. }
  27. }
  28. )
  29. puts response

Js

  1. const response = await client.search({
  2. index: "museums",
  3. size: 0,
  4. aggs: {
  5. rings: {
  6. geo_distance: {
  7. field: "location",
  8. origin: "POINT (4.894 52.3760)",
  9. unit: "km",
  10. distance_type: "plane",
  11. ranges: [
  12. {
  13. to: 100,
  14. },
  15. {
  16. from: 100,
  17. to: 300,
  18. },
  19. {
  20. from: 300,
  21. },
  22. ],
  23. },
  24. },
  25. },
  26. });
  27. console.log(response);

コンソール

  1. POST /museums/_search?size=0
  2. {
  3. "aggs": {
  4. "rings": {
  5. "geo_distance": {
  6. "field": "location",
  7. "origin": "POINT (4.894 52.3760)",
  8. "unit": "km",
  9. "distance_type": "plane",
  10. "ranges": [
  11. { "to": 100 },
  12. { "from": 100, "to": 300 },
  13. { "from": 300 }
  14. ]
  15. }
  16. }
  17. }
  18. }

キー付きレスポンス

keyed フラグを true に設定すると、各バケットに一意の文字列キーが関連付けられ、範囲が配列ではなくハッシュとして返されます:

Python

  1. resp = client.search(
  2. index="museums",
  3. size="0",
  4. aggs={
  5. "rings_around_amsterdam": {
  6. "geo_distance": {
  7. "field": "location",
  8. "origin": "POINT (4.894 52.3760)",
  9. "ranges": [
  10. {
  11. "to": 100000
  12. },
  13. {
  14. "from": 100000,
  15. "to": 300000
  16. },
  17. {
  18. "from": 300000
  19. }
  20. ],
  21. "keyed": True
  22. }
  23. }
  24. },
  25. )
  26. print(resp)

Ruby

  1. response = client.search(
  2. index: 'museums',
  3. size: 0,
  4. body: {
  5. aggregations: {
  6. rings_around_amsterdam: {
  7. geo_distance: {
  8. field: 'location',
  9. origin: 'POINT (4.894 52.3760)',
  10. ranges: [
  11. {
  12. to: 100_000
  13. },
  14. {
  15. from: 100_000,
  16. to: 300_000
  17. },
  18. {
  19. from: 300_000
  20. }
  21. ],
  22. keyed: true
  23. }
  24. }
  25. }
  26. }
  27. )
  28. puts response

Js

  1. const response = await client.search({
  2. index: "museums",
  3. size: 0,
  4. aggs: {
  5. rings_around_amsterdam: {
  6. geo_distance: {
  7. field: "location",
  8. origin: "POINT (4.894 52.3760)",
  9. ranges: [
  10. {
  11. to: 100000,
  12. },
  13. {
  14. from: 100000,
  15. to: 300000,
  16. },
  17. {
  18. from: 300000,
  19. },
  20. ],
  21. keyed: true,
  22. },
  23. },
  24. },
  25. });
  26. console.log(response);

コンソール

  1. POST /museums/_search?size=0
  2. {
  3. "aggs": {
  4. "rings_around_amsterdam": {
  5. "geo_distance": {
  6. "field": "location",
  7. "origin": "POINT (4.894 52.3760)",
  8. "ranges": [
  9. { "to": 100000 },
  10. { "from": 100000, "to": 300000 },
  11. { "from": 300000 }
  12. ],
  13. "keyed": true
  14. }
  15. }
  16. }
  17. }

レスポンス:

コンソール-結果

  1. {
  2. ...
  3. "aggregations": {
  4. "rings_around_amsterdam": {
  5. "buckets": {
  6. "*-100000.0": {
  7. "from": 0.0,
  8. "to": 100000.0,
  9. "doc_count": 3
  10. },
  11. "100000.0-300000.0": {
  12. "from": 100000.0,
  13. "to": 300000.0,
  14. "doc_count": 1
  15. },
  16. "300000.0-*": {
  17. "from": 300000.0,
  18. "doc_count": 2
  19. }
  20. }
  21. }
  22. }
  23. }

各範囲のキーをカスタマイズすることも可能です:

Python

  1. resp = client.search(
  2. index="museums",
  3. size="0",
  4. aggs={
  5. "rings_around_amsterdam": {
  6. "geo_distance": {
  7. "field": "location",
  8. "origin": "POINT (4.894 52.3760)",
  9. "ranges": [
  10. {
  11. "to": 100000,
  12. "key": "first_ring"
  13. },
  14. {
  15. "from": 100000,
  16. "to": 300000,
  17. "key": "second_ring"
  18. },
  19. {
  20. "from": 300000,
  21. "key": "third_ring"
  22. }
  23. ],
  24. "keyed": True
  25. }
  26. }
  27. },
  28. )
  29. print(resp)

Ruby

  1. response = client.search(
  2. index: 'museums',
  3. size: 0,
  4. body: {
  5. aggregations: {
  6. rings_around_amsterdam: {
  7. geo_distance: {
  8. field: 'location',
  9. origin: 'POINT (4.894 52.3760)',
  10. ranges: [
  11. {
  12. to: 100_000,
  13. key: 'first_ring'
  14. },
  15. {
  16. from: 100_000,
  17. to: 300_000,
  18. key: 'second_ring'
  19. },
  20. {
  21. from: 300_000,
  22. key: 'third_ring'
  23. }
  24. ],
  25. keyed: true
  26. }
  27. }
  28. }
  29. }
  30. )
  31. puts response

Js

  1. const response = await client.search({
  2. index: "museums",
  3. size: 0,
  4. aggs: {
  5. rings_around_amsterdam: {
  6. geo_distance: {
  7. field: "location",
  8. origin: "POINT (4.894 52.3760)",
  9. ranges: [
  10. {
  11. to: 100000,
  12. key: "first_ring",
  13. },
  14. {
  15. from: 100000,
  16. to: 300000,
  17. key: "second_ring",
  18. },
  19. {
  20. from: 300000,
  21. key: "third_ring",
  22. },
  23. ],
  24. keyed: true,
  25. },
  26. },
  27. },
  28. });
  29. console.log(response);

コンソール

  1. POST /museums/_search?size=0
  2. {
  3. "aggs": {
  4. "rings_around_amsterdam": {
  5. "geo_distance": {
  6. "field": "location",
  7. "origin": "POINT (4.894 52.3760)",
  8. "ranges": [
  9. { "to": 100000, "key": "first_ring" },
  10. { "from": 100000, "to": 300000, "key": "second_ring" },
  11. { "from": 300000, "key": "third_ring" }
  12. ],
  13. "keyed": true
  14. }
  15. }
  16. }
  17. }

レスポンス:

コンソール-結果

  1. {
  2. ...
  3. "aggregations": {
  4. "rings_around_amsterdam": {
  5. "buckets": {
  6. "first_ring": {
  7. "from": 0.0,
  8. "to": 100000.0,
  9. "doc_count": 3
  10. },
  11. "second_ring": {
  12. "from": 100000.0,
  13. "to": 300000.0,
  14. "doc_count": 1
  15. },
  16. "third_ring": {
  17. "from": 300000.0,
  18. "doc_count": 2
  19. }
  20. }
  21. }
  22. }
  23. }