For AI agents: this page is also available as Markdown at /docs/geo-distance-and-nearest.md. Every docs page is: add .md to its URL. llms.txt lists them all.
Distance sorting and nearest
A distance filter decides which places match. This page is about order: nearest first, relevance that fades with distance, or simply the k closest documents. The examples use the places collection from the Geo search quickstart and measure from Times Square (40.7580, -73.9855).
| You want | Use |
|---|---|
| Nearest first, within an area you choose | A distance or box filter, plus a sort by distance |
| Relevance first, closer results higher | A decay function in function_score |
| The distance of each hit | Distances in hits |
| The k nearest, however far | geo_nearest |
| A closeness bonus beside a text query | distance_feature |
#Sort by distance
In the es language, put the Elasticsearch sort in the search body:
{
"language": "es",
"query": {
"query": {"bool": {"filter": [
{"term": {"category": "cafe"}},
{"geo_distance": {"distance": "10km", "location": "40.7580,-73.9855"}}
]}},
"sort": [{"_geo_distance": {"location": "40.7580,-73.9855", "order": "asc", "unit": "km"}}]
},
"fields": ["name"]
}
The hits come back as p1 (about 0.1 km), p5 (4.2 km) and p2 (6.1 km).
{
"query": {"bool": {"filter": [
{"term": {"category": "cafe"}},
{"geo_distance": {"distance": "10km", "location": "40.7580,-73.9855"}}
]}},
"sort": [{"_geo_distance": {"location": "40.7580,-73.9855", "order": "asc", "unit": "km"}}],
"_source": ["name"]
}
geodist() is in kilometers. Name the field and point in the call, or set sfield and pt and call geodist() with no arguments:
q=category:cafe&fq={!geofilt sfield=location pt=40.7580,-73.9855 d=10}&sort=geodist(location,40.7580,-73.9855) asc
q=category:cafe&sfield=location&pt=40.7580,-73.9855&fq={!geofilt d=10}&sort=geodist() asc
_geo_distancetakes one field and one point ("lat,lon",[lon, lat],{"lat", "lon"}or"POINT (lon lat)"). Several fields or several points in one sort are rejected.orderisasc(nearest first) ordesc.unit(meters by default) changes the reported distances, not the order.- For a multi-valued field,
modepicks the point that counts:min(the nearest, the default ascending) ormax(the farthest, the default descending).avg,medianandsumare rejected. - Documents without a point sort last in either direction.
missingis ignored with a warning. - A distance sort can be followed by other keys, for example
"_score"or{"rating": "desc"}to break ties. - Combine the sort with a distance or box filter. Sorting a whole collection by distance works, but a filter keeps the work, and the result list, to the area people care about.
- Sorting by a
geo_shapefield is rejected.
#Distances in hits
Each hit of a distance sort carries its sort values under sort, with the distance in the sort's unit. On the Elasticsearch endpoint this is the usual hits.hits[].sort. On /v1, a geo_distance sort object works with any query language:
{
"language": "es",
"query": {"term": {"category": "cafe"}},
"filters": [{"geo_distance": {"distance": "10km", "location": "40.7580,-73.9855"}}],
"sort": [{"geo_distance": {"field": "location", "origin": "40.7580,-73.9855", "unit": "km"}}, "name"],
"fields": ["name"]
}
"hits": [
{"id": "p1", "sort": [0.106], "fields": {"name": "Harbor Coffee"}},
{"id": "p5", "sort": [4.246], "fields": {"name": "Hudson Espresso"}},
{"id": "p2", "sort": [6.093], "fields": {"name": "Bridge Roasters"}}
]
(Distances rounded here.)
| Key | Meaning |
|---|---|
geo_distance.field | The geo_point field. |
geo_distance.origin | The point, in any query point form. Or give lat and lon as numbers. |
geo_distance.unit | m (default), km, mi, yd, ft, in, cm, mm or nmi, for the reported distance. |
geo_distance.mode | min or max, for multi-valued fields. |
desc | Beside geo_distance: true for farthest first. |
The MCP search tool takes the same sort object.
#Boost nearby results
A decay function scores a document by how far it is from an origin: the full score up to offset, decay (0.5 by default) at offset + scale, and less beyond. Multiply it into the text score and a relevant place a block away ranks above an equally relevant one across town:
{
"language": "es",
"query": {"function_score": {
"query": {"match": {"description": "coffee"}},
"functions": [
{"gauss": {"location": {"origin": "40.7580,-73.9855", "scale": "2km", "offset": "500m", "decay": 0.5}}}
],
"boost_mode": "multiply"
}},
"fields": ["name", "description"]
}
{
"query": {"function_score": {
"query": {"match": {"description": "coffee"}},
"functions": [
{"gauss": {"location": {"origin": "40.7580,-73.9855", "scale": "2km", "offset": "500m", "decay": 0.5}}}
],
"boost_mode": "multiply"
}},
"_source": ["name", "description"]
}
| Parameter | Meaning |
|---|---|
origin | The point to measure from: "lat,lon", [lon, lat], {"lat", "lon"} or "POINT (lon lat)". |
scale | Required. The distance past offset at which the score falls to decay, such as "2km". |
offset | Default 0. Distances up to this score fully. |
decay | Default 0.5, strictly between 0 and 1. |
gaussfalls slowly near the origin and quickly afterscale,expfalls fastest close in, andlinearreaches zero at a finite distance.- A multi-valued field decays from its nearest point.
multi_value_modeother thanminis rejected. boost_mode,score_mode,weightand filtered functions work as in Elasticsearch, so a decay can be one of several functions, for example next tofield_value_factoronrating.- A decay does not narrow the matches. Add a distance filter if far-away places should not appear at all.
#Nearest k
geo_nearest matches the k documents nearest a point, however far they are, the way knn finds the nearest vectors. It is a WarpSearch extension to the Query DSL. On /v1, filters narrow the candidates first, so "the 3 nearest cafes" is the 3 nearest documents that pass the filter, not the 3 nearest places filtered afterwards:
{
"language": "es",
"query": {"geo_nearest": {"field": "location", "origin": "40.7580,-73.9855", "k": 3}},
"filters": [{"term": {"category": "cafe"}}],
"fields": ["name"]
}
This returns p1, p5 and p2, nearest first. On the Elasticsearch endpoint, send {"query": {"geo_nearest": {...}}}.
| Parameter | Meaning |
|---|---|
field | The geo_point field. |
origin | The point, in any query point form. Or give lat and lon as numbers. |
k | Required, positive, at most your plan's result window. How many documents to return at most. |
max_distance | Optional. Stop at this radius, such as "25km", so fewer than k may match. |
- Hits score
1 / (1 + meters), so the default order by score is nearest first. Add a distance sort to get the distances. - A document with several points counts once, at its nearest point. Documents at the same distance keep index order.
kis over the whole collection, so pages beyondkare empty: page withink.- With a points index the cost follows
k, not the size of the collection.
Use geo_nearest when the answer must have k results even in sparse areas. When the area is what matters ("within 5 km"), a distance filter with a sort by distance is the better fit.
#distance_feature scoring
distance_feature gives every document with a point a score of boost * pivot / (pivot + meters) from its nearest point: the full boost at the origin, half of it at pivot. Its usual place is a should clause beside a text query, so it reorders the text matches without adding or removing any:
{
"language": "es",
"query": {"bool": {
"must": [{"match": {"description": "coffee"}}],
"should": [{"distance_feature": {"field": "location", "origin": "40.7580,-73.9855", "pivot": "1km", "boost": 2}}]
}},
"fields": ["name"]
}
The same body, without "language", works on the Elasticsearch endpoint.
| Parameter | Meaning |
|---|---|
field | The geo_point field. |
origin | The point: "lat,lon", [lon, lat], {"lat", "lon"} or "POINT (lon lat)". |
pivot | Required. The distance at which the score halves, such as "1km". |
boost | Optional. The score at the origin, to weigh closeness against the text score. |
- On its own as the query, it matches every document with a point, ranked by closeness, and skips documents that cannot reach the top results.
- It does not narrow the matches: add a distance or box filter for that.
- Elasticsearch's
distance_featureon date fields is not supported.
Compared with a decay function, distance_feature adds to the text score rather than multiplying it, and needs only one parameter.