Geospatial data & queries
|
This section documents the current Elasticsearch 9.x line (with 8.19 as the final 8.x release) as published at the Elasticsearch documentation, which is the reference these pages are written and verified against. No specific patch version is pinned. Some capabilities (Kibana-only UIs, the ML/NLP model-management workflow, cross-cluster replication, and parts of the paid / serverless-only surface) are linked, not documented in depth. This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production, as Elasticsearch iterates quickly. This section’s bibliography lists the reference material consulted while preparing these pages. |
Elasticsearch indexes points and shapes on the WGS84 ellipsoid so you can filter, sort and bucket documents by location. This page covers the spatial field types, the coordinate formats they accept (and the lon/lat ordering trap), the four geo queries, and the geo aggregations that drive map visualisations.
Spatial field types
A field must be mapped as a spatial type before any geo query or aggregation can use it — dynamic mapping never infers one. Elasticsearch has two geographic types and two planar (Cartesian) equivalents.
| Type | What it stores |
|---|---|
|
A single lat/lon point on Earth. Distance and bounding-box queries, geo aggregations. |
|
Points, lines, polygons and collections on Earth. Spatial-relation queries. |
|
A single planar |
|
Arbitrary planar geometry. Same relation queries as |
geo_point and coordinate formats
PUT /venues
{
"mappings": {
"properties": {
"name": { "type": "keyword" },
"location": { "type": "geo_point" }
}
}
}
// https://www.elastic.co/guide/en/elasticsearch/reference/current/geo-point.html
A geo_point value can be written five ways, and two of them disagree on axis order — the classic
Elasticsearch geo bug:
// 1. Object with explicit keys -- order-independent, and the clearest choice.
PUT /venues/_doc/1
{ "name": "Opera", "location": { "lat": 40.4231, "lon": -3.6884 } }
// 2. String "lat,lon" -- latitude FIRST.
PUT /venues/_doc/2
{ "name": "Prado", "location": "40.4138,-3.6921" }
// 3. Array [lon, lat] -- longitude FIRST, the REVERSE of the string form (GeoJSON order).
PUT /venues/_doc/3
{ "name": "Retiro", "location": [-3.6828, 40.4153] }
// 4. WKT POINT -- longitude first, like the array.
PUT /venues/_doc/4
{ "name": "Cibeles", "location": "POINT (-3.6923 40.4193)" }
// 5. Geohash string -- precision follows the hash length.
PUT /venues/_doc/5
{ "name": "Sol", "location": "ezjmgtt8" }
// https://www.elastic.co/guide/en/elasticsearch/reference/current/geo-point.html
The array and WKT forms take [lon, lat]; the object and string forms take latitude first. Pick the
object form in application code to avoid the confusion. An array of geo_point values in one field
is allowed and makes the document match if any point matches.
geo_shape: GeoJSON and WKT
geo_shape accepts GeoJSON geometry objects and
Well-Known Text. GeoJSON
coordinates are always [lon, lat]; a polygon’s outer ring should be listed counter-clockwise and
be closed (first point repeated last).
PUT /zones
{
"mappings": {
"properties": {
"name": { "type": "keyword" },
"region": { "type": "geo_shape" }
}
}
}
// https://www.elastic.co/guide/en/elasticsearch/reference/current/geo-shape.html
// GeoJSON polygon.
PUT /zones/_doc/1
{
"name": "downtown",
"region": {
"type": "polygon",
"coordinates": [
[[-3.71,40.40],[-3.68,40.40],[-3.68,40.43],[-3.71,40.43],[-3.71,40.40]]
]
}
}
// The same kind of shape as WKT.
PUT /zones/_doc/2
{
"name": "north",
"region": "POLYGON ((-3.71 40.43, -3.68 40.43, -3.68 40.46, -3.71 40.46, -3.71 40.43))"
}
// point, linestring, multipolygon, envelope and geometrycollection are also accepted.
// https://www.elastic.co/guide/en/elasticsearch/reference/current/geo-shape.html
envelope is an Elasticsearch shorthand for an axis-aligned rectangle: [[minLon, maxLat], [maxLon,
minLat]] (top-left, bottom-right).
Cartesian point and shape
point and shape use the same geometry syntax but on a flat plane with no notion of poles or the
antimeridian — use them for coordinates that are not positions on Earth.
PUT /parts
{
"mappings": {
"properties": {
"pin": { "type": "point" },
"outline": { "type": "shape" }
}
}
}
// x/y (or WKT), any range, no wrap-around.
PUT /parts/_doc/1
{ "pin": { "x": 132.7, "y": -48.15 } }
// https://www.elastic.co/guide/en/elasticsearch/reference/current/point.html
// https://www.elastic.co/guide/en/elasticsearch/reference/current/shape.html
Geo queries
There are four geo queries. Run them inside a bool filter (or the top-level filter) so they
skip scoring and get cached. See
Geo queries for
the set.
geo_bounding_box
Matches points inside an axis-aligned rectangle — the cheapest geo filter, and what a map UI issues as you pan.
GET /venues/_search
{
"query": {
"bool": {
"filter": {
"geo_bounding_box": {
"location": {
"top_left": { "lat": 40.45, "lon": -3.75 },
"bottom_right": { "lat": 40.38, "lon": -3.65 }
}
}
}
}
}
}
// https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-geo-bounding-box-query.html
geo_distance
Matches points within a radius of an origin, and pairs with _geo_distance sort to order hits by
proximity.
GET /venues/_search
{
"query": {
"bool": {
"filter": {
"geo_distance": {
"distance": "2km",
"distance_type": "arc",
"location": { "lat": 40.4168, "lon": -3.7038 }
}
}
}
},
"sort": [
{ "_geo_distance": { "location": "40.4168,-3.7038", "order": "asc", "unit": "m" } }
]
}
// distance_type "arc" -> great-circle on the ellipsoid, accurate everywhere (default)
// distance_type "plane" -> faster flat-Earth approximation; error grows with distance and latitude
// https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-geo-distance-query.html
geo_shape (spatial relations)
Tests an indexed geo_shape (or geo_point) field against a query shape using a spatial predicate.
GET /zones/_search
{
"query": {
"geo_shape": {
"region": {
"shape": {
"type": "envelope",
"coordinates": [[-3.72, 40.44], [-3.66, 40.39]]
},
"relation": "intersects"
}
}
}
}
// relation:
// intersects -> field shape shares any point with the query shape (default)
// within -> field shape lies entirely inside the query shape
// contains -> field shape entirely contains the query shape
// disjoint -> field shape shares no point with the query shape
// Reuse a stored shape instead of an inline one with "indexed_shape": { "index", "id", "path" }.
// https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-geo-shape-query.html
geo_grid
Filters documents that fall inside one named grid cell — a geohash string, a z/x/y map tile, or
an H3 hexagon address. Its main use is drilling from a grid-aggregation bucket back to the documents
in it.
GET /venues/_search
{
"query": {
"geo_grid": {
"location": { "geohash": "ezjm" }
}
}
}
// Use "geotile": "6/32/22" or "geohex": "837542fffffffff" to match the other two grid types.
// https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-geo-grid-query.html
Geo aggregations
Geo aggregations turn a geo_point field into map layers: distance rings, density grids, and
bounding geometry. Set "size": 0 to ask for buckets only. They build on the general mechanics in
Aggregations.
geo_distance aggregation
Buckets documents into concentric range rings around an origin.
GET /venues/_search
{
"size": 0,
"aggs": {
"rings": {
"geo_distance": {
"field": "location",
"origin": "40.4168,-3.7038",
"unit": "km",
"ranges": [
{ "to": 1 },
{ "from": 1, "to": 5 },
{ "from": 5 }
]
}
}
}
}
// https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-geodistance-aggregation.html
Grid aggregations: geohash_grid, geotile_grid, geohex_grid
Each groups points into cells of a fixed grid and returns a doc_count per cell — the data behind
a heat map. They differ only in the grid.
GET /venues/_search
{
"size": 0,
"aggs": {
"heat": {
"geohash_grid": { "field": "location", "precision": 5 },
"aggs": {
"centre": { "geo_centroid": { "field": "location" } }
}
}
}
}
// geohash_grid -> base-32 geohash rectangles, precision 1..12
// geotile_grid -> Web-Mercator "z/x/y" tiles, precision 0..29, aligned with slippy-map tiles
// geohex_grid -> Uber H3 hexagons, precision 0..15, near-equal-area cells
// https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-geohashgrid-aggregation.html
// https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-geotilegrid-aggregation.html
// https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-geohexgrid-aggregation.html
Constrain the search with a geo_bounding_box filter to the visible map extent, then raise
precision as the user zooms in; drill into a single cell with the geo_grid query above.
geo_bounds and geo_centroid
Two metric aggregations that summarise a set of points — useful at the top level to auto-fit a map, or nested inside a grid bucket to label each cell.
GET /venues/_search
{
"size": 0,
"aggs": {
"box": { "geo_bounds": { "field": "location" } },
"midpoint": { "geo_centroid": { "field": "location" } }
}
}
// geo_bounds -> smallest bounding box enclosing every matched point
// geo_centroid -> the weighted centre of mass of every matched point
// https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-geobounds-aggregation.html
// https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-geocentroid-aggregation.html
Kibana Maps
|
Kibana Maps is the visual front end for everything on this page. It renders |
Related pages
-
Mapping & field types — the other field types and why dynamic mapping never picks a spatial one.
-
Query DSL: term-level — filter context, where geo queries belong.
-
Aggregations — the bucket/metric model the geo aggregations extend.
-
MongoDB geospatial indexes —
2dsphereindexes, GeoJSON storage, and the$near/$geoWithin/$geoIntersectsoperators. -
Couchbase geo search — geospatial point and bounding queries in the Couchbase Search service.