Step 5 (Optional) Refine with Exact Distance
To improve accuracy for the top few results,
you can do a second step: calculate real distance for, say, top 100 merchants only.
GET merchants/_search
{
"sort": [
{
"_geo_distance": {
"location": { "lat": -6.20, "lon": 106.81 },
"order": "asc",
"unit": "km"
}
}
],
"size": 10
}
This gives the closest 10 merchants by true distance, while the heavy filtering already happened using the fast polygon method.
Step 6 Automating Polygon Generation with Pipeline
You can also automate this inside Elasticsearch using an ingest pipeline.
Each time you insert or update merchant location, the pipeline can pre-compute
a bounding polygon (for example, the 5-km delivery area).
PUT _ingest/pipeline/add_bounding_polygon
{
"processors": [
{
"script": {
"lang": "painless",
"source": """
double R = 6371.0;
double radius = 5.0;
int sides = 32;
double lat = ctx.location.lat * Math.PI / 180;
double lon = ctx.location.lon * Math.PI / 180;
List points = new ArrayList();
for (int i = 0; i < sides; i++) {
double angle = 2 * Math.PI * i / sides;
double dx = radius * Math.cos(angle) / R;
double dy = radius * Math.sin(angle) / (R * Math.cos(lat));
double lat_i = ctx.location.lat + dy * 180 / Math.PI;
double lon_i = ctx.location.lon + dx * 180 / Math.PI;
points.add([lon_i, lat_i]);
}
points.add(points.get(0));
ctx.delivery_area = ["type": "polygon", "coordinates": [points]];
"""
}
}
]
}
Then when indexing:
POST merchants/_doc?pipeline=add_bounding_polygon
{ "name": "Boba Shop", "location": { "lat": -6.22, "lon": 106.84 } }
Now every merchant has a delivery_area polygon stored automatically.
Step 7 Querying Merchants by Delivery Area
When a user makes an order:
{
"lat": -6.21,
"lon": 106.82
}
You can search:
"geo_shape": {
"delivery_area": {
"shape": {
"type": "point",
"coordinates": [106.82, -6.21]
},
"relation": "contains"
}
}
This returns all merchants whose delivery area includes the user’s location.
This approach is perfect for “Which restaurants deliver to me?” queries.
Performance Summary
| Method | Description | Speed | Accuracy |
|---|---|---|---|
geo_distance | True radius, Haversine math | Slow | Perfect |
| Polygon (geo_shape) | Approximated circle | Fast | 1-5% error |
| Polygon + Refine | Polygon filter + real distance for top results | Balanced | High |
Pre-generated delivery_area | Each merchant has pre-computed polygon | Very fast | High |
Conclusion
In real-time apps like food delivery or ride-hailing,
speed matters more than mathematical perfection.
By replacing heavy radius math with polygon approximation,
you get:
- 10× faster queries
- Small and predictable error margin
- Easy scaling to millions of merchants
Later, you can refine only the top results with exact distance.
This combination keeps both performance and accuracy in balance.
“Fast enough and close enough, that’s what users expect.”
