Learn

GeoJSON with Elasticsearch

Elasticsearch's geo_shape field type accepts GeoJSON geometry objects directly. You can index points, polygons, and any other GeoJSON geometry, then query by spatial relationship.

Create a geo_shape mapping

Map the field as geo_shape in your index. Elasticsearch reads the GeoJSON type field at index time to determine how to store and index the geometry.

PUT /locations
{
  "mappings": {
    "properties": {
      "name": { "type": "text" },
      "boundary": { "type": "geo_shape" }
    }
  }
}

Create the index before indexing any documents. Elasticsearch cannot update a field's mapping type after documents have been indexed — you would need to reindex.

Index a GeoJSON feature

Send the GeoJSON geometry object as the field value. The coordinates array uses [longitude, latitude] order, the same as RFC 7946.

POST /locations/_doc
{
  "name": "Central Park",
  "boundary": {
    "type": "Polygon",
    "coordinates": [[
      [-73.9819, 40.7681],
      [-73.9580, 40.7681],
      [-73.9580, 40.8006],
      [-73.9819, 40.8006],
      [-73.9819, 40.7681]
    ]]
  }
}

You can index any GeoJSON geometry type — Point, LineString, Polygon, MultiPolygon, and the rest. Elasticsearch applies a BKD tree index to each geometry regardless of type.

Geo shape query

Query by spatial relationship. The geo_shape query takes a shape and a relation, then returns documents whose indexed geometry satisfies that relation.

GET /locations/_search
{
  "query": {
    "geo_shape": {
      "boundary": {
        "shape": {
          "type": "Point",
          "coordinates": [-73.9654, 40.7829]
        },
        "relation": "intersects"
      }
    }
  }
}

The four relation types:

RelationMatches when
intersectsThe indexed shape overlaps the query shape (any shared area or edge)
withinThe indexed shape is fully inside the query shape
containsThe indexed shape fully contains the query shape
disjointThe indexed shape has no overlap with the query shape

intersects is the default and the most commonly used. Use within when you need to guarantee a document's geometry is completely enclosed — for example, finding all delivery stops inside a zone polygon.

Bounding box query

For point fields (geo_point), Elasticsearch provides a dedicated geo_bounding_box query. It takes top-left and bottom-right corners:

GET /locations/_search
{
  "query": {
    "geo_bounding_box": {
      "location": {
        "top_left": { "lon": -74.0, "lat": 40.8 },
        "bottom_right": { "lon": -73.9, "lat": 40.7 }
      }
    }
  }
}

Note: geo_bounding_box requires a geo_point field, not geo_shape. Use geo_shape with an envelope for polygon bounding boxes.

For a geo_shape field, use the envelope type instead — a rectangle defined by its top-left and bottom-right corners:

GET /locations/_search
{
  "query": {
    "geo_shape": {
      "boundary": {
        "shape": {
          "type": "envelope",
          "coordinates": [[-74.0, 40.8], [-73.9, 40.7]]
        },
        "relation": "intersects"
      }
    }
  }
}

envelope is an Elasticsearch extension — it is not part of the GeoJSON spec, but Elasticsearch's geo_shape query accepts it as a convenience type for rectangular bounding boxes.

Indexing a FeatureCollection

Elasticsearch indexes one document per _doc. To index a GeoJSON FeatureCollection, iterate over the features array and send each feature as a separate document. The Bulk API is the standard approach for large collections:

POST /locations/_bulk
{ "index": {} }
{ "name": "Central Park", "boundary": { "type": "Polygon", "coordinates": [[[-73.9819,40.7681],[-73.9580,40.7681],[-73.9580,40.8006],[-73.9819,40.8006],[-73.9819,40.7681]]] } }
{ "index": {} }
{ "name": "Times Square", "boundary": { "type": "Point", "coordinates": [-73.9855, 40.7580] } }

Each line pair is a metadata line (the action) followed by the source document. Keep bulk requests under 100 MB.

Watch out: Elasticsearch uses [longitude, latitude] — the same order as GeoJSON — but some Elasticsearch client libraries accept { lat, lon } objects for geo_point fields. Don't mix the two formats in the same document.

For GeoJSON format reference, see What is GeoJSON?. For PostgreSQL with PostGIS, see GeoJSON with PostGIS. For indexing GeoJSON via Python, see GeoJSON with Python.

If you're building a map interface over an Elasticsearch backend, Element Editor is an open-source React library for drawing and editing the GeoJSON shapes you store and query.

Continue reading

Mike Acler

Mike Acler

Design engineer and creator of Element Editor, an open-source map drawing and GeoJSON editing library for React. Writes about web cartography and GIS tooling.