Geo-shape query

Geo-shape query #

Matches documents whose geo_shape field relates to a query geometry by a spatial relation. The query geometry is a GeoJSON geometry object or a WKT string; the indexed shape was written the same way.

Where the geo_point queries ask “is this point inside my region?”, geo_shape asks it of whole geometries — the document carries a polygon, line or envelope (a delivery zone, a pipeline route, a parcel) and the query tests it against another geometry.

How it works #

A geo_shape field owns a dedicated field store with two structures per field:

  • a 4-dimensional BKD tree holding each document’s shape bounding box as one point [min_lat, min_lon, max_lat, max_lon];
  • the exact geometry per document, stored alongside.

Execution is two-phase:

  1. Bounding-box overlap. “My bbox overlaps the query’s bbox” is itself expressible as one range in the 4-dim space, so the tree walk yields a candidate superset — every true spatial relation implies overlapping bounding boxes, so nothing is missed.
  2. Exact relation. Each candidate’s stored geometry is tested with exact planar predicates: intersects (a vertex inside the other shape, a boundary touch, or a segment crossing), within (every exterior vertex inside plus no edge properly crossing the boundary — touching from inside still counts), contains (the same test with both sides swapped). disjoint runs differently: disjoint shapes usually have non-overlapping boxes and never enter the overlap range, so it evaluates NOT intersects over the stored geometries directly.

All predicates work in planar WGS-84 degree space: edges are straight in latitude/longitude (no great-circle interpolation), boundaries count as intersecting/inside within a 1e-9-degree tolerance (~0.1 mm), and antimeridian wrapping is not applied.

On the fifteen-place teaching dataset — each document carries a small diamond region around its point — the Beijing envelope in the example below answers intersects: 2, within: 2, disjoint: 13.

Examples #

The live example matches the two Beijing places — each document carries a small polygon region around its point. The same query as a curl request:

POST /my-collection/_search
{
  "query": {
    "geo_shape": {
      "field": "region",
      "shape": {
        "type": "polygon",
        "coordinates": [[
          [-73.96, 40.78], [-73.90, 40.78],
          [-73.90, 40.72], [-73.96, 40.72]
        ]]
      },
      "relation": "intersects"
    }
  }
}

A WKT string works too (BBOX takes minLon, maxLat, maxLon, minLat):

{
  "query": {
    "geo_shape": {
      "field": "region",
      "shape": "BBOX (-74.1, 40.9, -73.8, 40.6)",
      "relation": "within"
    }
  }
}

The nested form also works, with relation defaulting to intersects:

{
  "query": {
    "geo_shape": {
      "region": {
        "shape": {"type": "envelope", "coordinates": [[-0.20, 51.60], [0.05, 51.45]]},
        "relation": "within"
      }
    }
  }
}

Parameters for geo_shape #

  • field
    (Required, string) The geo_shape field to filter on.
  • shape
    (Required, object|string) The query geometry: a GeoJSON object (point, multi_point, line_string, multi_line_string, polygon, multi_polygon, envelope, geometry_collection) or a WKT string. GeoJSON coordinates are [lon, lat] as in the standard.
  • relation
    (Optional, string) How the indexed shape must relate to the query geometry. One of:
    • intersects (default) — the shapes share at least one point.
    • disjoint — the shapes share nothing.
    • within — the indexed shape lies entirely inside the query geometry.
    • contains — the indexed shape contains the query geometry.

Matching is exact: candidates are narrowed by a bounding-box index and then verified against the full stored geometry, so there are no misses and no false positives. Shapes are treated as plain latitude/longitude geometry on WGS-84; antimeridian wrapping is not supported.

Use cases #

  • Indexed service regions. Each document owns its zone (a store’s delivery polygon) and the query is a rectangle or point of interest — intersects finds every zone covering that spot.
  • Boundary containment. “Which parcels lie entirely inside this planning area?” is within; “which planning area fully contains this parcel?” is contains — the same test from either side.
  • Linear assets. Routes, pipelines and power lines indexed as linestrings, matched against a region they cross — the exact phase handles line-through-polygon with no vertex inside.
  • Everything-but filtering. disjoint excludes the shapes that touch a zone — keep results outside a hazard area or an excluded district.
Calendar October 6, 2026
Edit Edit this page