Chapter 12 · Time Series and Geospatial Workloads
Geospatial GEOADD, GEOSEARCH, Distance, Radius/Box Queries, and Sorted-Set Foundations
Build an AtlasMart location index with correct longitude/latitude order, units, radius/box queries, sorted-set foundations, and explicit precision limits.
Learning outcomes
AtlasMart must find couriers and pickup lockers near a customer without loading every coordinate into the application. Redis GEO provides a proximity index, but correctness depends on coordinate order, units, Earth-model limits, and understanding that the underlying key is a sorted set rather than a GIS database.
Store longitude/latitude/member triples with GEOADD and validate coordinate bounds.
Query radius and box neighborhoods with GEOSEARCH and make units explicit.
Use GEODIST/GEOPOS as evidence while understanding spherical-distance error bounds.
Explain the sorted-set foundation without treating internal scores as business distance.
Design tenant/business filters so proximity results cannot become a data-leak boundary.
All Chapter 12 mandatory labs reuse the disposable Chapter 01
environment: Redis Open Source 8.10.1 from Docker
Official Image redis:8.10.1, container
atlasmart-redis-ch01, standalone topology, host
publication 127.0.0.1:6379, TLS disabled only
because traffic stays on loopback, default ACL user disabled,
named users atlasmart-app and
academy-admin, logical database 0, AOF with
appendfsync everysec plus RDB snapshots,
persistent /data, and no explicit
maxmemory limit or eviction policy. Redis 8
integrates Time Series into Redis Open Source; geospatial
commands are core Redis commands. Fixtures stay under
atlasmart:ch12:*. Mandatory work is local and
uses synthetic telemetry/coordinates only; no paid service,
production endpoint, or real credential is required.
1. GEO mental model: current member location in a sorted-set-backed index
A Redis geospatial key maps each unique member name to one indexed coordinate. Adding the same member again updates its current location. This is ideal for “where is courier c42 now?” but it is not a trajectory history. Redis encodes position into a sorted-set score so it can search nearby score ranges efficiently.
| Need | Redis structure | Why |
|---|---|---|
| Current courier position | GEO sorted set | One member can be updated in place and queried by proximity |
| Courier movement history | Time Series or Stream | History needs many timestamped events, not one current member coordinate |
| Warehouse metadata/search | Hash/JSON + Search | Address/category/status are not geographic distance alone |
| High-precision GIS geometry | Dedicated GIS/spatial engine when required | Redis GEO uses a spherical approximation and point locations |
2. Coordinate order is longitude first, latitude second
GEOADD key longitude latitude member uses x,y
order. This differs from the conversational “latitude,
longitude” order many humans use. Valid longitudes are
−180..180. Redis GEO latitude is limited to about ±85.05112878
degrees because of the underlying projection.
docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app GEOADD atlasmart:ch12:geo:atlasmart 49.8671 40.4093 hub-centraldocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app GEOADD atlasmart:ch12:geo:atlasmart 49.8495 40.3777 locker-westdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app GEOADD atlasmart:ch12:geo:atlasmart 49.8920 40.4200 locker-eastdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app GEOPOS atlasmart:ch12:geo:atlasmart hub-central locker-west locker-eastdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app TYPE atlasmart:ch12:geo:atlasmartdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app ZCARD atlasmart:ch12:geo:atlasmart
TYPE reports zset.
GEOPOS returns longitude then latitude and may show
small representation differences from the inserted decimals
because Redis encodes coordinates for the geospatial index.
3. Wrong coordinate order can fail loudly—or succeed silently in the wrong place
docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app GEOADD atlasmart:ch12:geo:atlasmart 49.8671 90 impossible-latitudedocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app GEOADD atlasmart:ch12:geo:atlasmart 40.4093 49.8671 wrong-orderdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app GEOPOS atlasmart:ch12:geo:atlasmart wrong-orderdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app GEOSEARCH atlasmart:ch12:geo:atlasmart FROMLONLAT 49.8671 40.4093 BYRADIUS 5 km WITHDIST ASCdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app ZREM atlasmart:ch12:geo:atlasmart wrong-order
Latitude 90 is out of Redis GEO bounds and should error. The reversed Baku values are both individually valid, so Redis can accept them as a coordinate far from the intended site. The repair is application-side schema validation plus a known-center proximity test—not just numeric range validation.
4. Radius queries: center, shape, unit, evidence
GEOSEARCH separates how the center is chosen (FROMMEMBER
or FROMLONLAT) from the search shape (BYRADIUS
or BYBOX). The unit belongs in the query and is
also the unit returned by WITHDIST.
docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app GEOSEARCH atlasmart:ch12:geo:atlasmart FROMMEMBER hub-central BYRADIUS 5 km WITHDIST WITHCOORD ASC COUNT 10docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app GEOSEARCH atlasmart:ch12:geo:atlasmart FROMLONLAT 49.8671 40.4093 BYRADIUS 3000 m WITHDIST ASC COUNT 10
Do not compare “5” from a kilometers query with “3000” from a
meters query without converting units. COUNT bounds
the number of returned candidates; it does not change the
coordinate precision model.
5. Box queries answer a different neighborhood shape
docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app GEOSEARCH atlasmart:ch12:geo:atlasmart FROMMEMBER hub-central BYBOX 10 6 km WITHDIST WITHCOORD ASC COUNT 20
A box can model operational service windows or map viewports more directly than a radius. Redis still returns point members; it does not evaluate polygon containment, roads, travel time, administrative boundaries, or obstacles.
6. GEODIST is useful evidence, not survey-grade geometry
docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app GEODIST atlasmart:ch12:geo:atlasmart hub-central locker-west mdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app GEODIST atlasmart:ch12:geo:atlasmart hub-central locker-west kmdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app GEODIST atlasmart:ch12:geo:atlasmart hub-central locker-west mi
Redis calculates distance using a spherical Earth approximation (Haversine-style model in the documentation); worst-case error can be around 0.5%. That is generally acceptable for nearby-app discovery, but not necessarily for cadastral, navigation, surveying, or safety-critical distance decisions.
7. The sorted-set foundation is observable but internal scores are not your domain API
docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app ZRANGE atlasmart:ch12:geo:atlasmart 0 -1 WITHSCORESdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app GEOHASH atlasmart:ch12:geo:atlasmart hub-central locker-westdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app GEOPOS atlasmart:ch12:geo:atlasmart hub-central locker-westdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app MEMORY USAGE atlasmart:ch12:geo:atlasmart
The sorted-set score is an encoded spatial value. It is not
“meters from origin” and should not become an application
contract. Use GEO commands for geographic semantics and ordinary
sorted-set commands only for compatible operations such as
ZREM deletion.
8. Member uniqueness means GEO is latest-state, not location history
docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app GEOADD atlasmart:ch12:geo:atlasmart 49.8700 40.4100 courier-42docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app GEOPOS atlasmart:ch12:geo:atlasmart courier-42docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app GEOADD atlasmart:ch12:geo:atlasmart CH 49.8750 40.4150 courier-42docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app GEOPOS atlasmart:ch12:geo:atlasmart courier-42docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app ZCARD atlasmart:ch12:geo:atlasmart
The second GEOADD changes courier-42’s current
coordinate; it does not append a second historical point. If
AtlasMart needs a route trace, emit timestamped location events
to Time Series or Streams and maintain GEO as a projection of
the current position.
9. Security boundary: filter before proximity results cross the trust boundary
Member names inside one GEO key cannot receive independent Redis ACL patterns; ACL key patterns protect the key as a whole. A shared global courier GEO key can therefore become a tenant-leak surface. Prefer tenant/security-domain-specific keys when hard isolation is required, or enforce authorization before candidates are exposed.
docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app GEOADD atlasmart:ch12:geo:tenant-a 49.8671 40.4093 courier-a1docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app GEOADD atlasmart:ch12:geo:tenant-b 49.8672 40.4094 courier-b1docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app GEOSEARCH atlasmart:ch12:geo:tenant-a FROMLONLAT 49.8671 40.4093 BYRADIUS 2 km WITHDIST ASC
A label or member-name prefix is not a substitute for a key-level ACL boundary. Chapter 22 will harden ACL/TLS/network design in depth.
10. Capacity and latency evidence
docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app ZCARD atlasmart:ch12:geo:atlasmartdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app MEMORY USAGE atlasmart:ch12:geo:atlasmartdocker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user academy-admin INFO commandstatsdocker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user academy-admin INFO latencystats
GEOADD is O(log N) per item. Search cost depends on
candidate geometry/selectivity and result count. Measure
p50/p95/p99 under representative update/query concurrency rather
than extrapolating from a three-member fixture.
11. Reproducible cleanup
docker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user academy-admin UNLINK atlasmart:ch12:geo:atlasmart atlasmart:ch12:geo:tenant-a atlasmart:ch12:geo:tenant-b
UNLINK targets only the named Chapter 12 fixtures.
Do not replace this with FLUSHDB,
FLUSHALL, or broad pattern deletion on a shared
Redis instance.
12. Production judgment
Redis GEO is a strong fit for low-latency proximity over point locations that already belong in Redis. It is not a routing engine, polygon GIS, precision surveying system, or historical trajectory store. Validate coordinate order, ranges, units, service-radius semantics, member update rates, cardinality, hot-key concentration, persistence/replication cost, Cluster placement, ACL boundaries, and client timeout/retry behavior. A single citywide key can become a hot key; partition only after measuring and preserving query correctness.
Check your understanding
- What order does GEOADD require?
- Why can a reversed pair sometimes be accepted?
- What does updating the same member do?
- Why is ZRANGE WITHSCORES not a distance API?
- When is Redis GEO not sufficient?
Review the answers
Longitude first, latitude second.
Both reversed numbers can still fall inside valid longitude/latitude ranges, creating a semantically wrong but syntactically valid location.
Replaces its current indexed coordinate; it does not append history.
The score encodes geospatial position for index mechanics; it is not a business distance.
When you need high-precision GIS, polygons, road routing/travel time, complex spatial predicates, or safety-critical distance accuracy.
13. Summary and next step
You can now build and validate a current-position proximity index with correct coordinate and unit semantics. Lesson 4 combines the time-series and GEO lessons into a bounded-cardinality telemetry/location model so key growth, label growth, retention, and member churn are intentional rather than accidental.