Entity Types
Nearby search must handle two fundamentally different entity classes:
- Static entities — restaurants, venues, stores; position rarely changes
- Dynamic entities — drivers, delivery workers, bikes; position updates every few seconds
Static Entity Index
Pre-built spatial index in PostgreSQL with PostGIS or Elasticsearch. Index is refreshed on entity update (not on every query). Suitable for millions of POIs with infrequent writes.
Dynamic Entity Index: Redis Geospatial
Redis provides native geospatial commands backed by a sorted set using geohash scores.
-- Add or update driver position
GEOADD drivers:{city} {lng} {lat} {driver_id}
-- Find drivers within 5 km, sorted by distance, max 20 results
GEORADIUS drivers:{city} {lng} {lat} 5 km ASC COUNT 20
-- Get distance between two members
GEODIST drivers:{city} driver_42 driver_99 km
GEORADIUS complexity: O(N + log M) where N = members in radius, M = total members.
Driver Tracking Pipeline
Mobile App (every 5s)
-> POST /location {driver_id, lat, lng}
-> Location Service
-> GEOADD drivers:{city} lng lat driver_id
-> Pub/Sub update for active ride tracking
Ranking Factors
Raw geo results are re-ranked by a composite score:
score = (1 / distance) * w1
+ rating * w2
+ availability * w3
-- Example weights
w1 = 0.5 -- distance dominates
w2 = 0.3 -- rating matters
w3 = 0.2 -- availability flag
Filtering
- Category — cuisine type, vehicle type, etc.
- Price level — 1-4 scale stored on entity
- Open now — check
hourstable against current day/time
SELECT e.id, e.name, e.rating
FROM entities e
JOIN hours h ON h.entity_id = e.id
WHERE e.id IN (:geo_result_ids)
AND e.category = :category
AND h.day_of_week = :today
AND :current_time BETWEEN h.open_time AND h.close_time
ORDER BY score DESC;
Entity Details Fetch
Geo query returns only IDs. Fetch full entity details from DB or cache by ID set in a single SELECT ... WHERE id IN (...) or Redis MGET.
Caching Strategy
- Dynamic results (drivers): TTL 10 seconds — data changes rapidly
- Static results (restaurants): TTL 60 seconds — stable data, cacheable longer
- Cache key:
nearby:{entity_type}:{geohash6}:{filters_hash}
Sharding by City
Use separate Redis instances per metropolitan area. City-level sharding keeps key spaces small and provides geographic fault isolation.
redis_pool = {
'nyc': RedisClient('redis-nyc.internal'),
'sf': RedisClient('redis-sf.internal'),
'la': RedisClient('redis-la.internal'),
}
client = redis_pool[resolve_city(lat, lng)]
See also: Uber Interview Guide 2026: Dispatch Systems, Geospatial Algorithms, and Marketplace Engineering
See also: Lyft Interview Guide 2026: Rideshare Engineering, Real-Time Dispatch, and Safety Systems