🏡 Home 📖 Chapter Home 👈 Prev 👉 Next
⚡ ElasticsearchBook.com is crafted by Jozef Sorocin and powered by:
Given a full-text query of "New York City", I want to cluster the selected locations into buckets that I can render on a map. On top of that, I want to retrieve the bounds of the viewport that my map will "fly to".
The points of interest below are marked red:
Base map courtesy of geojson.io
We'll be utilizing:
[match query](<https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-match-query.html>)
for the full text search,[geohash_grid](<https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-bucket-geohashgrid-aggregation.html>)
to group locations on the geohash grid
[geo_centroid](<https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-geocentroid-aggregation.html>)
sub-aggregation to determine the positions of the weighted centroids → the clusters,[geo_bounds](<https://www.elastic.co/guide/en/elasticsearch/reference/current/search-aggregations-metrics-geobounds-aggregation.html>)
aggregation to calculate the viewport bounds.Here's the pseudo-code:
{
"query": MATCH city
"aggs": {
"clusters": {
AGGREGATE on geohash grid,
COMPUTE weighted centroid
}
},
"bounds": {
COMPUTE viewport bounds
}
}
}
It's tempting to use a geo_centroid
aggregation without the parent geohash_grid
but geo_centroid
does not allow for adjusting the zoom-based cluster density. But of course, UI clustering only makes sense when we provide the map's current zoom level (→ "precision"):
Now, ES implements the 12 geohash precision levels.
<aside>
🔗 Geo-hashing deserves a chapter of its own but for now, check out this interactive map to gain an intuitive understanding of the geohash
mechanism.
</aside>
Back to the precision levels — these don't directly correspond to the zoom levels you know from Google Maps, Mapbox, or Leaflet, so an adjustment will be needed.
Here's the formula that I use to translate a GoogleMaps zoom into the precision
parameter:
// only integer values are allowed -- mapbox & leaflet support floats too so keep that in mind
let precision = int(map_zoom - 8);
if (precision < 1) {
// Default to the highest "zoom-out".
// Consider the whole earth & expect 1 - 3 clusters in total
precision = 1;
} else if (precision > 7) {
// Don't go beyond 7 because we'll get too many 1-member
// clusters which could be actual markers/pins instead
precision = 7;
}
// pass the `precision` into the ES request body