Spatial search
|
This section documents the current Solr line (10.0; 9.10.x the maintained 9.x branch), written and verified against the Apache Solr Reference Guide. No specific patch version is pinned. Some capabilities (the Solr Operator on Kubernetes, the package-manager ecosystem, Learning To Rank model training, and expert plugin development) 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. This section’s bibliography lists the reference material consulted while preparing these pages. |
Solr filters, sorts, and facets on geographic (and, for RPT, plane) coordinates through a handful of
dedicated field types plus two query parsers and a function query. This page covers
LatLonPointSpatialField for simple point data, SpatialRecursivePrefixTreeFieldType for shapes and
heatmaps, the \{!geofilt}/\{!bbox} filters, geodist() distance sorting, WKT/polygon search, and
RPT heatmap faceting. The field types themselves are introduced alongside Solr’s other types in
Field types; this page is the query-side companion.
Spatial field types
| Type | Use it for |
|---|---|
|
A single lat/lon point. The default choice — fast distance sort, |
|
Points or shapes (polygons, lines, envelopes), spatial-relation predicates, and heatmap faceting — what LLPSF cannot do. |
|
A stored bounding box itself (not a point), for queries that match on box overlap/containment rather than distance. |
|
A plain 2D Cartesian point (no Earth curvature) — the RPT/LLPSF equivalent for non-geodetic data. |
<!-- managed-schema -->
<fieldType name="location" class="solr.LatLonPointSpatialField" docValues="true"/>
<field name="store_location" type="location" indexed="true" stored="true"/>
<fieldType name="location_rpt" class="solr.SpatialRecursivePrefixTreeFieldType"
geo="true" distErrPct="0.025" maxDistErr="0.001" distanceUnits="kilometers"/>
<field name="delivery_zone" type="location_rpt" indexed="true" stored="true"/>
Both types accept "lat,lon" on input:
curl -X POST -H 'Content-Type: application/json' "http://localhost:8983/solr/stores/update?commit=true" \
-d '[ { "id": "1", "store_location": "40.7128,-74.0060" } ]'
RPT trades LLPSF’s speed and simplicity for shape support: it indexes a point or shape as a set of grid cells at increasing precision levels (a recursive prefix tree over a geohash- or quad-based grid), which is also what makes heatmap faceting possible — the same cell structure the index already maintains is the heatmap grid. See Spatial Search for the full field-type comparison and every configuration attribute.
geofilt and bbox
\{!geofilt} filters to documents within a radius (great-circle distance) of a center point;
\{!bbox} filters to the axis-aligned bounding box enclosing that same circle — cheaper to compute
since it skips the per-document distance calculation, at the cost of also matching the box’s corners
outside the actual circle.
# Circular: documents within 10 km of the given point.
curl --get "http://localhost:8983/solr/stores/select" \
--data-urlencode 'q=*:*' \
--data-urlencode 'fq={!geofilt sfield=store_location pt=40.7,-74.0 d=10}'
# Bounding-box equivalent -- faster, looser.
curl --get "http://localhost:8983/solr/stores/select" \
--data-urlencode 'q=*:*' \
--data-urlencode 'fq={!bbox sfield=store_location pt=40.7,-74.0 d=10}'
Outside a [source] block those same filters are written escaped, as
\{!geofilt sfield=store_location pt=40.7,-74.0 d=10} and \{!bbox sfield=store_location pt=40.7,-74.0 d=10},
so AsciiDoc does not try to interpret the braces. sfield names the spatial field, pt the center
point (lat,lon), and d the radius in kilometers. Both parsers work against LatLonPointSpatialField
and RPT fields alike.
geodist() — distance sorting and boosting
geodist() is a function query that computes the distance
from a point to a spatial field’s value, usable anywhere a function query is — sort, fl (to return
the distance), or inside a boost function.
curl --get "http://localhost:8983/solr/stores/select" \
--data-urlencode 'q=*:*' \
--data-urlencode 'fq={!geofilt sfield=store_location pt=40.7,-74.0 d=10}' \
--data-urlencode 'sort=geodist(store_location,40.7,-74.0) asc' \
--data-urlencode 'fl=id,store_location,dist:geodist(store_location,40.7,-74.0)'
geodist() takes the field and point either as explicit arguments (as above) or implicitly from
sfield/pt request parameters when called as bare geodist(). Because it is an ordinary function
query, it also composes with relevance the same way any other function does — see
Function queries for boosting a text query by proximity instead of only
sorting by it, and Relevance & scoring for how a boost function folds into
the final score.
WKT shapes and polygon search
RPT fields (and BBoxField, for boxes) accept
Well-Known Text shapes on
both indexing and querying, using the \{!field} query parser (or the RPT-specific spatial predicate
syntax) rather than geofilt/bbox, which only understand a center point and radius.
curl -X POST -H 'Content-Type: application/json' "http://localhost:8983/solr/zones/update?commit=true" \
-d '[
{ "id": "1", "delivery_zone": "POLYGON((-74.05 40.68, -73.95 40.68, -73.95 40.75, -74.05 40.75, -74.05 40.68))" }
]'
# Documents whose delivery_zone intersects the query polygon.
curl --get "http://localhost:8983/solr/zones/select" \
--data-urlencode 'q=*:*' \
--data-urlencode 'fq={!field f=delivery_zone}Intersects(POLYGON((-74.1 40.6, -73.9 40.6, -73.9 40.8, -74.1 40.8, -74.1 40.6)))'
Outside a [source] block that filter is written escaped, as
\{!field f=delivery_zone}Intersects(POLYGON(…)). The spatial predicate defaults to Intersects
(shares any point with the query shape) and also accepts IsWithin (field shape entirely inside the
query shape), Contains (field shape entirely encloses the query shape), IsDisjointTo (shares no
point), and IsEqualTo — the same relation vocabulary as Elasticsearch’s geo_shape query, spelled
differently. RPT ships two spatial-context implementations selectable per field type: JTS (spatialContextFactory="JTS"),
flat-plane geometry with support for polygons with holes and self-intersection repair, and Geo3D
(spatialContextFactory="Geo3D"), which computes on the actual ellipsoid rather than a flat
projection — more accurate for very large shapes, at higher CPU cost per query.
Heatmap faceting
RPT fields support a facet type that returns a grid of document counts over a bounding region — the data behind a density heatmap overlay, computed from the same prefix-tree cells the field indexes
with. It is available both as the legacy facet.heatmap request parameter and, preferably, as a
type: heatmap block in the JSON Facet API — see
Faceting for the JSON Facet API in full.
curl "http://localhost:8983/solr/zones/select" \
--data-urlencode 'q=*:*' \
--data-urlencode 'rows=0' \
--data-urlencode 'json.facet={
"zone_density": {
"type": "heatmap",
"field": "delivery_zone",
"geom": "[\"-180,-90\" TO \"180,90\"]",
"gridLevel": 3
}
}'
geom bounds the region as a WKT envelope or a ["minX,minY" TO "maxX,maxY"] range; gridLevel
picks a fixed cell resolution directly (higher = finer), while distErrPct picks one relative to
`geom’s size instead — set exactly one of the two. The response is a compact counts matrix (row-major,
bottom-to-top) sized to fit the requested resolution, meant for rendering rather than paging through
like a terms facet.
Related pages
-
Field types — where
LatLonPointSpatialFieldand RPT are declared alongside Solr’s other field-type classes. -
Function queries —
geodist()and the other functions it composes with. -
Faceting — the JSON Facet API’s
heatmaptype in the context of every other facet type. -
Query parsers —
geofilt,bbox, and\{!field}alongside Solr’s other query parsers. -
Elasticsearch geospatial data & queries — the
geo_point/geo_shapeequivalents,geo_distance/geo_shapequeries, and grid aggregations.