jonnify·redis /redis-patterns/data-modeling/geospatial

R7.06 · Data Modeling & Memory — module hub

Geospatial patterns.

Store locations and query by radius, distance, or bounding box using GEOADD, GEOSEARCH, and GEODIST commands built on geohash-encoded Sorted Sets.

The GEO command family encodes 2-D coordinates into a single ZSET score via a 52-bit interleaved geohash. Select a command below; the readout names what it does and its key gotcha.

The GEO command family. Select a command; the readout explains what it does.
GEOADD lon, lat, name → geohash score GEOSEARCH BYRADIUS/BOX nearest members GEOPOS retrieve [lon, lat] GEODIST distance m/km/mi/ft GEOHASH geohash string debug / export GEOSEARCH STORE persist results as a new ZSET
readout · Select a command to see what it does. All six are core Valkey — no module required.
§1

Adding locations & retrieving coordinates

geospatial.md.txt · Adding Locations / Retrieving Coordinates

GEOADD stores a named location in a Sorted Set encoded as a geohash. The argument order is longitude first, then latitude — the GeoJSON convention, not the everyday latitude-first habit. Getting the order wrong silently maps every point to the wrong hemisphere; there is no runtime error.

GEOADD locations -122.4194 37.7749 "san_francisco"   # lon FIRST, then lat
GEOADD locations  -73.9857 40.7484 "new_york"
GEOADD locations   -0.1276 51.5074 "london"

GEOPOS  locations "san_francisco"   # → [[lon, lat]] (the stored value, ±0.6 m)
GEODIST locations "san_francisco" "new_york" km  # → 4129.09  (km)

GEOPOS returns coordinates in the same longitude-first order. The returned values may differ by a few microdegrees from the inputs because coordinates are stored as 52-bit geohash integers (precision ±0.6 m).

GEODIST accepts four units: m (metres), km (kilometres), mi (miles), ft (feet).

Notes on ValkeyGEOADD, GEOPOS, GEODIST, GEOSEARCH, GEOSEARCHSTORE, and GEOHASH are all core Valkey commands — present since Redis 3.2 (radius family) and Valkey 9. No extension module is required. Cite: valkey.io/commands/geoadd · valkey.io/commands/geosearch.

The lon-before-lat order is consistent with GeoJSON but reverses the everyday habit — it is the single most common source of a GEO correctness bug, and there is no error to catch it. State the order once at the boundary and enforce it everywhere.

§3

Location with metadata & real-time tracking

geospatial.md.txt · Location with Metadata / Real-Time Location Tracking

A GEO member carries only a name. There is no room in the Sorted Set score for ratings, cuisines, or availability. Store extra data in a separate Hash keyed by the same id; GEOSEARCH returns the ids, then HGETALL fetches the rows.

# store location + metadata in separate structures
GEOADD restaurants -122.4194 37.7749 "restaurant:123"
HSET   restaurant:123 name "Joe's Diner" cuisine "American" rating "4.5"

# find nearby, then fetch each row
GEOSEARCH restaurants FROMLONLAT -122.4 37.8 BYRADIUS 5 km
HGETALL   restaurant:123

The same pattern applies to real-time tracking. On each GPS ping, update the driver's position with a new GEOADD (calling GEOADD on an existing member replaces the coordinates). A separate status Hash records the timestamp and availability flag; query the GEO index for candidates, then filter by the status Hash.

GEOADD drivers -122.4 37.8 "driver:456"                # GPS ping
HSET   driver:456:status last_update 1706648400 available 1
GEOSEARCH drivers FROMLONLAT -122.4 37.8 BYRADIUS 5 km COUNT 20

GEO + Hash is the standard idiom: the Sorted Set answers “what is near here?” and returns ids; the Hash answers “what is the full record?” Two structures, one query shape.

§4

Geofencing & geohash

geospatial.md.txt · Geofencing / Geohash / Removing Locations

Valkey does not provide built-in geofencing events. The pattern is application-side: store fence definitions as GEO entries (or in a Hash), check each position update with GEOSEARCH, and compare current state to previous state to detect enter or exit transitions. Broadcast transitions via Pub/Sub to interested subscribers.

The GEOHASH command returns the human-readable geohash string for a member. Longer strings encode finer precision; the string is the same encoding stored as the ZSET score (decoded to a 52-bit integer). Useful for debugging and for exporting to external geospatial tools.

GEOHASH locations "san_francisco"   # → ["9q8yy9mfry0"]
ZREM    locations "san_francisco"   # remove: it's a ZSET member

Removing a location uses ZREM, not a dedicated GEOREMOVE, because GEO data is stored in a standard Sorted Set.

Geofencing is application logic over the GEO index, not a server feature. The primitives are GEOSEARCH (am I inside?) and a state machine (was I inside before?). Pub/Sub carries the transition event.

§5

The grounding — ZSET-as-sortable-scalar family

geospatial.md.txt · figure 17/18/19
docs/redis-patterns/content/community/geospatial.md.txt · figure 17 — core GEO commandsfrozen
GEOADD locations -122.4194 37.7749 "san_francisco"  # lon FIRST, then lat
GEOPOS  locations "san_francisco"                   # → [lon, lat]
GEODIST locations "san_francisco" "new_york" km     # units: m | km | mi | ft
GEOSEARCH locations FROMMEMBER "san_francisco" BYRADIUS 500 km
GEOSEARCH locations FROMLONLAT -122.4 37.8 BYRADIUS 50 km WITHCOORD WITHDIST COUNT 10 ASC
GEOSEARCH locations FROMMEMBER "san_francisco" BYBOX 1000 1000 km
GEOSEARCHSTORE nearby locations FROMLONLAT -122.4 37.8 BYRADIUS 50 km
GEOHASH locations "san_francisco"                  # the geohash string

The structure reveal (figure 18): a GEO set is a Sorted Set. The source states it three times: “Coordinates are stored as geohashes in a Sorted Set” (line 5) · “The results are stored as a Sorted Set with geohash scores” (line 71) · “Since GEO data is stored in a Sorted Set, use ZREM” (line 125). GEOADD encodes (lon, lat) into a 52-bit interleaved geohash integer stored as the ZSET score; GEOSEARCH is a bounded range-scan over that score space.

codemojex has no geospatial surface (figure 19). It is a location-agnostic Telegram emoji-guessing game — players guess emoji; the bus rotates service across players; nothing is placed in space. Unlike some other modules in the R7 family, geospatial does not map onto codemojex’s domain. The standalone store-locator and ride-share examples above are the natural home for GEO commands.

the pattern

Encode 2-D space into one sortable scalar so a ZSET range-scan answers “what is near here?” — the geohash packs place into a ZSET score.

the family (cross-chapter)

The same move: a BCS branded id packs time into a sortable id (a ZSET sorts by birth); R4’s delayed-job score packs fire-time so ZRANGEBYSCORE sweeps due jobs. A geohash, a branded snowflake, and a delayed-job score are one idea: a structured value flattened to a sortable scalar.

A sortable scalar is the trick that makes an ordered structure answer a structured query. Geospatial, time-based ids, and scheduled jobs are all the same pattern at different problem shapes.

§6

The three dives

geoadd-and-geosearch → radius-and-box-queries → the-geohash-sorted-set

Each dive takes one slice of the geospatial pattern in depth. Read them in order: the add-and-query commands first, the shape options second, the underlying structure third.

Begin with GEOADD and GEOSEARCH. Return to the Data Modeling & Memory chapter to continue.

§7

References

sources · related

Sources

Related in this course