The geo_point field type stores a single latitude/longitude pair per value and indexes it for distance, bounding-box, polygon, grid-aggregation, and geo-shape-style spatial queries. It is the right choice for any data that can be modeled as a point on Earth: store locations, user check-ins, IP-to-coordinate lookups, vehicle telemetry. For lines, polygons, or multi-points use `geo_shape` instead; geo_point cannot represent anything other than a point.
How geo_point Works
Under the hood, geo_point values are encoded as two 32-bit integers (latitude and longitude quantized to ~1 cm precision) and indexed in a per-segment BKD (Block KD) tree. BKD trees give Elasticsearch sub-linear range queries in 2D, which is why geo_distance and geo_bounding_box stay fast even on billions of points. Distance is computed with the Haversine formula by default, accurate to better than 0.5% for distances up to thousands of kilometers.
geo_point accepts five input formats. Mixing them across documents in the same field is legal but is the most common source of "my coordinates look swapped" bugs:
| Format | Example | Coordinate order |
|---|---|---|
| Object | {"lat": 41.12, "lon": -71.34} |
Explicit by key |
| String | "41.12,-71.34" |
lat, lon |
| Geohash string | "drm3btev3e86" |
n/a |
| Array | [-71.34, 41.12] |
lon, lat (GeoJSON order) |
| GeoJSON object | {"type": "Point", "coordinates": [-71.34, 41.12]} |
lon, lat |
| WKT POINT | "POINT (-71.34 41.12)" |
lon, lat |
The array, GeoJSON, and WKT formats follow the GeoJSON convention of longitude first. The object and string formats use latitude first. This asymmetry trips up almost everyone the first time.
PUT stores
{
"mappings": {
"properties": {
"location": { "type": "geo_point" }
}
}
}
POST stores/_doc
{ "location": { "lat": 41.12, "lon": -71.34 } }
GET stores/_search
{
"query": {
"geo_distance": {
"distance": "5km",
"location": { "lat": 41.10, "lon": -71.30 }
}
}
}
geo_point Configuration
| Parameter | Default | Notes |
|---|---|---|
ignore_malformed |
false | If true, invalid coordinates are skipped instead of failing the document. |
ignore_z_value |
true | If true, drops the third (altitude) coordinate when GeoJSON or WKT supplies one. |
null_value |
none | Substitute point for null input. |
index |
true | Set to false to store without indexing (no spatial queries possible). |
doc_values |
true | Required for aggregations and sorting by distance. |
on_script_error |
fail | What to do when a runtime script-mapped geo_point fails. |
Out-of-range values (latitude outside [-90, 90] or longitude outside [-180, 180]) fail the indexing operation unless ignore_malformed: true. Don't disable validation in production - bad coordinates downstream of a coordinate-system bug are far harder to debug than an indexing failure.
Common Pitfalls with geo_point
- Mixing array (lon, lat) with object/string (lat, lon) in the same field. Half your points end up in the Indian Ocean off Madagascar.
- Storing altitude in a geo_point. It silently drops the third value (with
ignore_z_value: true); model elevation as a separate numeric field. - Using
geo_distancewith a very largedistanceand expecting cheap queries. Beyond a few hundred km the bounding-box prefilter loses selectivity; considergeo_bounding_boxfirst then a distance sort. - Forgetting that sort by
_geo_distanceis computed in memory per hit. On large result sets this is the dominant query cost. - Indexing geohash strings expecting partial-match queries on the geohash itself. geo_point decodes geohashes to the centroid of the cell - you cannot query "all documents under prefix
drm". Use thegeohash_gridaggregation instead.
Operating geo_point Workloads
Geo queries are mostly index-bound; BKD trees live off-heap in the page cache and benefit from generous filesystem memory. The slow paths are usually distance sort on large hit counts and high-cardinality geohash-grid aggregations. Pulse tracks geo-query latency separately from regular search load and surfaces when a geo-heavy index is being starved of page cache by other workloads on the same node.
Frequently Asked Questions
Q: What is the correct coordinate order for an Elasticsearch geo_point?
A: It depends on the format. Object form ({"lat": ..., "lon": ...}) and string form ("lat,lon") use latitude first. Array, GeoJSON, and WKT forms use longitude first (lon, lat), matching the GeoJSON specification. Pick one format and use it consistently across all documents in the field.
Q: How accurate are geo_point distance calculations in Elasticsearch?
A: geo_point coordinates are stored quantized to roughly 1 cm precision (~1e-7 degrees). Distance is computed with the Haversine formula, which assumes a spherical Earth and is accurate to better than 0.5% for typical Earth distances. For sub-meter accuracy or geodesic distances on the WGS84 ellipsoid, compute distance outside Elasticsearch.
Q: Can geo_point store altitude or 3D coordinates?
A: No. geo_point is strictly 2D. By default ignore_z_value is true, so a third coordinate in GeoJSON or WKT input is silently dropped. Store altitude in a separate numeric field if you need it.
Q: What's the difference between geo_point and geo_shape?
A: geo_point stores one point per value and indexes it in a BKD tree. geo_shape stores arbitrary geometries - lines, polygons, multi-polygons, circles - and indexes them in a triangle tree (Lucene's LatLonShape). Use geo_shape when documents represent regions or paths, not just locations.
Q: How do I query for documents within a polygon using geo_point?
A: Use the geo_polygon query (deprecated in favor of geo_shape query against a geo_point field). Both work against geo_point fields; the geo_shape query is the recommended path going forward and supports more polygon shapes and operations.
Q: What happens if I index an invalid latitude or longitude?
A: By default the document is rejected with a parsing error citing the out-of-range value. Setting ignore_malformed: true on the mapping causes invalid points to be silently dropped while the rest of the document indexes normally. This is acceptable for noisy ingest pipelines, but you lose visibility into upstream data quality problems.
Related Reading
- Elasticsearch geo_shape field data type: for lines, polygons, and multi-geometries.
- Elasticsearch create index with mapping: declare geo_point fields up front in templates.
- Elasticsearch add field to mapping: add geo_point to an existing index.
- Elasticsearch IllegalArgumentException: mapper conflicts: the error you get when one index maps a field as text and another as geo_point.
- Elasticsearch index.mapping.total_fields.limit: geo_point counts as a single field even though it carries two coordinates.