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:
- 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.
- 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).disjointruns differently: disjoint shapes usually have non-overlapping boxes and never enter the overlap range, so it evaluatesNOT intersectsover 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) Thegeo_shapefield 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
—
intersectsfinds every zone covering that spot. - Boundary containment. “Which parcels lie entirely inside this
planning area?” is
within; “which planning area fully contains this parcel?” iscontains— 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.
disjointexcludes the shapes that touch a zone — keep results outside a hazard area or an excluded district.
Related #
geo_bounding_box,geo_distance,geo_polygon— the point-side family: the document carries ageo_point, the query the region.geo-centroidaggregation — mean centre of the matched documents.geohash-gridandgeotile-gridaggregations — grid the matches onto map tiles.