Professional Project

H3 Geolocation System

A hexagonal spatial indexing system using Uber's H3 library to deliver 2-10x faster geolocation queries for nearby establishment searches.

Clube Certo
2025
TypeScript / h3-js / MySQL

The Problem

Clube Certo's platform needed to show users nearby partner establishments based on their location. The existing approach calculated Haversine distances against every establishment address in the database for every query — a brute-force method that scaled poorly as the establishment network grew.

With thousands of establishment addresses and high query volume, search latency was becoming a bottleneck. The system needed a spatial indexing strategy that could pre-filter candidates before running expensive distance calculations.

The Solution

I implemented Uber's H3 hexagonal hierarchical spatial index to partition the globe into hexagonal grid cells. Each establishment address is pre-indexed into cells at three resolutions, and user queries expand outward from the user's cell using k-ring traversal — only calculating actual distances for the small subset of addresses in nearby hexagons.

H3's core library is written in C, and the existing Node.js bindings didn't cover all the functionality I needed. I translated key portions of the C implementation into JavaScript/TypeScript, adapting the low-level coordinate and indexing logic to work natively in our Node.js backend without native compilation dependencies.

Architecture

User Location
lat/lng
H3 Cell Lookup
latLngToCell()
K-Ring Expansion
gridDisk(k)
SQL Pre-Filter
WHERE h3_cell IN (:cells)
Haversine Sort
Final distance calc

Multi-Resolution Indexing

Each address is indexed at three H3 resolutions to balance precision and query performance at different zoom levels:

Resolution Cell Edge Length Use Case DB Column
Resolution 7 ~1.22 km Default map view, city-wide search h3_cell_res7
Resolution 8 ~461 m Neighborhood-level zoom h3_cell_res8
Resolution 9 ~174 m Street-level precision h3_cell_res9

Key Features

Automatic Cell Calculation
Sequelize model hooks on beforeCreate and beforeUpdate automatically compute H3 cells whenever an address is created or its coordinates change.
K-Ring Expansion
K-ring size dynamically scales based on search radius and resolution. K=1 covers 7 cells, K=2 covers 19 cells, expanding concentrically.
Dual Query Builders
Two optimized query paths: closest single address per establishment for map pins, and all matching addresses for detailed results.
Advanced Filtering
Composable filters for keyword search, category, state/city, distance range, online/offline status, and establishment blacklist/whitelist.
Indexed Columns
Dedicated database indexes on all three h3_cell columns enable sub-millisecond cell lookups even with large datasets.
Migration Tooling
Scripts to add H3 columns to existing tables and backfill cells for all historical address records without downtime.

Challenges & Solutions

  • Data migration: Thousands of existing addresses needed H3 cells calculated without downtime. Built a batch sync script that processes records in chunks with progress logging and error recovery.
  • Resolution selection: Too coarse and you scan too many irrelevant addresses; too fine and you need massive k-rings. Benchmarked all three resolutions and chose resolution 7 as the default with automatic fallback.
  • Invalid coordinates: Some addresses had zero or null coordinates from legacy imports. Added validation guards in the model hooks and a re-geocoding script to fix bad records.
  • Query complexity: Combining H3 spatial filtering with keyword search, category filters, and business rules required carefully structured SQL with subqueries to maintain index usage.

Results

  • 2-10x faster geolocation queries compared to brute-force Haversine distance scanning across all addresses.
  • Sub-millisecond spatial pre-filtering via indexed H3 cell lookups, with actual distance calculation only on the filtered subset.
  • Zero-downtime migration of all existing address records to the new H3 indexing system.
  • Automatic maintenance — new addresses are indexed on creation, and coordinates changes trigger re-indexing via model hooks.

Tech Stack

C TypeScript JavaScript Node.js h3-js (Uber H3) MySQL Sequelize ORM Express Haversine Formula Database Indexing

Want to see more of my work?

All Projects