Geo Distance Aggregation
editGeo Distance Aggregation
editA multi-bucket aggregation that works on geo_point
fields and conceptually works very similar to the range aggregation. The user can define a point of origin and a set of distance range buckets. The aggregation evaluate the distance of each document value from the origin point and determines the buckets it belongs to based on the ranges (a document belongs to a bucket if the distance between the document and the origin falls within the distance range of the bucket).
PUT /museums { "mappings": { "properties": { "location": { "type": "geo_point" } } } } POST /museums/_bulk?refresh {"index":{"_id":1}} {"location": "52.374081,4.912350", "name": "NEMO Science Museum"} {"index":{"_id":2}} {"location": "52.369219,4.901618", "name": "Museum Het Rembrandthuis"} {"index":{"_id":3}} {"location": "52.371667,4.914722", "name": "Nederlands Scheepvaartmuseum"} {"index":{"_id":4}} {"location": "51.222900,4.405200", "name": "Letterenhuis"} {"index":{"_id":5}} {"location": "48.861111,2.336389", "name": "Musée du Louvre"} {"index":{"_id":6}} {"location": "48.860000,2.327000", "name": "Musée d'Orsay"} POST /museums/_search?size=0 { "aggs": { "rings_around_amsterdam": { "geo_distance": { "field": "location", "origin": "52.3760, 4.894", "ranges": [ { "to": 100000 }, { "from": 100000, "to": 300000 }, { "from": 300000 } ] } } } }
Response:
{ ... "aggregations": { "rings_around_amsterdam": { "buckets": [ { "key": "*-100000.0", "from": 0.0, "to": 100000.0, "doc_count": 3 }, { "key": "100000.0-300000.0", "from": 100000.0, "to": 300000.0, "doc_count": 1 }, { "key": "300000.0-*", "from": 300000.0, "doc_count": 2 } ] } } }
The specified field must be of type geo_point
(which can only be set explicitly in the mappings). And it can also hold an array of geo_point
fields, in which case all will be taken into account during aggregation. The origin point can accept all formats supported by the geo_point
type:
-
Object format:
{ "lat" : 52.3760, "lon" : 4.894 }
- this is the safest format as it is the most explicit about thelat
&lon
values -
String format:
"52.3760, 4.894"
- where the first number is thelat
and the second is thelon
-
Array format:
[4.894, 52.3760]
- which is based on theGeoJson
standard and where the first number is thelon
and the second one is thelat
By default, the distance unit is m
(meters) but it can also accept: mi
(miles), in
(inches), yd
(yards), km
(kilometers), cm
(centimeters), mm
(millimeters).
POST /museums/_search?size=0 { "aggs": { "rings": { "geo_distance": { "field": "location", "origin": "52.3760, 4.894", "unit": "km", "ranges": [ { "to": 100 }, { "from": 100, "to": 300 }, { "from": 300 } ] } } } }
There are two distance calculation modes: arc
(the default), and plane
. The arc
calculation is the most accurate. The plane
is the fastest but least accurate. Consider using plane
when your search context is "narrow", and spans smaller geographical areas (~5km). plane
will return higher error margins for searches across very large areas (e.g. cross continent search). The distance calculation type can be set using the distance_type
parameter:
POST /museums/_search?size=0 { "aggs": { "rings": { "geo_distance": { "field": "location", "origin": "52.3760, 4.894", "unit": "km", "distance_type": "plane", "ranges": [ { "to": 100 }, { "from": 100, "to": 300 }, { "from": 300 } ] } } } }
Keyed Response
editSetting the keyed
flag to true
will associate a unique string key with each bucket and return the ranges as a hash rather than an array:
POST /museums/_search?size=0 { "aggs": { "rings_around_amsterdam": { "geo_distance": { "field": "location", "origin": "52.3760, 4.894", "ranges": [ { "to": 100000 }, { "from": 100000, "to": 300000 }, { "from": 300000 } ], "keyed": true } } } }
Response:
{ ... "aggregations": { "rings_around_amsterdam": { "buckets": { "*-100000.0": { "from": 0.0, "to": 100000.0, "doc_count": 3 }, "100000.0-300000.0": { "from": 100000.0, "to": 300000.0, "doc_count": 1 }, "300000.0-*": { "from": 300000.0, "doc_count": 2 } } } } }
It is also possible to customize the key for each range:
POST /museums/_search?size=0 { "aggs": { "rings_around_amsterdam": { "geo_distance": { "field": "location", "origin": "52.3760, 4.894", "ranges": [ { "to": 100000, "key": "first_ring" }, { "from": 100000, "to": 300000, "key": "second_ring" }, { "from": 300000, "key": "third_ring" } ], "keyed": true } } } }
Response:
{ ... "aggregations": { "rings_around_amsterdam": { "buckets": { "first_ring": { "from": 0.0, "to": 100000.0, "doc_count": 3 }, "second_ring": { "from": 100000.0, "to": 300000.0, "doc_count": 1 }, "third_ring": { "from": 300000.0, "doc_count": 2 } } } } }