Docs / Proximity search▾

Core flows

Proximity search

“Show me parking within 5 km” is the most frequent request we serve. PostGIS answers it exactly; a geohash-cell cache lets drivers who are near each other share one cached answer.

The query#

Each listing stores its position as a PostGIS geography point with a GiST index, so a radius search is an index scan, not a scan of every listing. Results carry the distance, price, rating and how many slots are free for the requested window.

requesthttp
GET /v1/listings?latitude=12.9716&longitude=77.5946&radius_km=5&amenities=ev&sort=distance
# radius defaults to 5 km, at most 25 km; up to 200 results

Sharing a cache between nearby drivers#

Caching by exact coordinates is useless: no two phones send the same GPS fix. We cache by geohash cell instead. The cell query covers the requested radius plus the cell's half-diagonal, so it contains every listing any driver in that cell could see. Each request then filters and re-measures the cached candidates against its own position, so answers stay exact.

Geohash cell searchdriver Adriver Bshared celllistingone cached query for the celleach driver's own radius

Swipe sideways to see the whole figure.

Drivers A and B stand in the same cell, so both are served from one cached query (dashed circle). Each still gets exactly the listings inside their own radius.
app/domain/geo.pypython
def cell_for_search(latitude, longitude, radius_m, max_error_ratio) -> Cell:
"""Coarsest cell whose half-diagonal is at most max_error_ratio of the radius
(coarser cells = more cache sharing; the extra area is filtered out per request)."""
for precision in range(1, 10):
cell = decode_cell(encode(latitude, longitude, precision))
if cell.half_diagonal_m <= radius_m * max_error_ratio:
return cell
return decode_cell(encode(latitude, longitude, 9))

Request flow#

Pick the cell

geo.py
Coarsest geohash cell whose half-diagonal is at most 25% of the radius.

Cache hit

Redis
Candidates for the cell, cached for 30 seconds.

Cache miss

PostGIS
One indexed radius query for radius + half-diagonal, then stored.

Filter for this driver

Re-measure distance from the driver's exact position, drop anything outside the radius, apply amenities, price and availability filters.

Sort and respond

By distance or price, with free-slot counts for the requested window.
Search reads from a read-only session, which can point at a read replica when one is configured.

Keeping results fresh#

EventWhat happens to the cache
Listing created, edited, paused or deletedThe search namespace version is bumped (one Redis INCR); every older key becomes unreachable and expires on its own
Time passesEntries expire after 30 seconds, which bounds how stale availability can be
Redis unavailableSearch falls through to PostGIS, slower but still correct