jonnify·redis /redis-patterns/data-modeling/geospatial/radius-and-box-queries

R7.06.2 · dive 2

Radius and box queries.

GEOSEARCH answers “what is near here?” in two shapes: a circle (BYRADIUS) or a rectangle (BYBOX). Toggle the shape; the same reference point returns different member sets.

A circle clips the corners; a rectangle extends into them. Toggle above to see the difference. The second interactive walks the two-step pattern: GEOSEARCH finds nearby ids, then HGETALL fetches each record from its companion Hash.

BYRADIUS vs BYBOX. Toggle the shape; the readout names which restaurants fall inside — a circle clips the corners that a box captures.
origin A · 1.8 km N B · 1.5 km E C · 2.9 km NE D · 2.8 km SW in both shapes box-only (corner)
readout · Select a shape. BYRADIUS searches a circle; BYBOX searches a rectangle width × height centred on the reference point. Same origin, different result sets at the corners.
§1

Circle vs rectangle — BYRADIUS and BYBOX

geospatial.md.txt · Searching by Radius / Searching by Bounding Box

GEOSEARCH takes a shape argument that controls the area. Both shapes are centred on the reference point, which may be a stored member (FROMMEMBER) or explicit coordinates (FROMLONLAT):

valkey.io/commands/geosearch/ · figure 17 — shapesfrozen
GEOSEARCH locations FROMLONLAT -122.4 37.8 BYRADIUS 50 km COUNT 10 ASC
GEOSEARCH locations FROMLONLAT -122.4 37.8 BYBOX 100 80 km COUNT 10 ASC
#          key        from-lon  from-lat  shape         opts
# BYBOX takes width × height — the box is centred on the reference point

A circle with radius 50 km and a box 100 km wide by 80 km tall both contain many of the same members, but differ at the corners: the circle clips them; the box includes them. A delivery zone or a map-tile bounding box is a natural fit for BYBOX; a “restaurants within N km” query is a natural fit for BYRADIUS.

Output options on both shapes:

  • WITHCOORD — include stored coordinates.
  • WITHDIST — include the distance from the reference point.
  • WITHHASH — include the raw geohash integer (the ZSET score).
  • COUNT N — return at most N results.
  • ASC / DESC — sort by distance, closest or furthest first.

BYRADIUS draws a circle; BYBOX draws a rectangle. The choice is domain-driven: a delivery zone is a rectangle; a proximity search is a circle. Both accept the same output modifiers.

§2

GEOSEARCHSTORE — persisting results

geospatial.md.txt · Storing Search Results

GEOSEARCHSTORE runs a spatial query and writes the result set to a new key as a Sorted Set. Because a GEO set is a Sorted Set (that is the subject of the next dive), the output can be consumed with ZRANGE, ZCARD, or ZREM like any other Sorted Set:

valkey.io/commands/geosearchstore/ · figure 17 — storing resultsfrozen
GEOSEARCHSTORE nearby_cache locations FROMLONLAT -122.4 37.8 BYRADIUS 50 km
#               dest-key     src-key   from-coords               shape
# Result stored as a Sorted Set with geohash scores at nearby_cache

Use GEOSEARCHSTORE when the same spatial query is repeated frequently: run it once, cache the Sorted Set, and serve repeated reads from the cached key. The output is re-queryable with any ZSET command, including a second GEOSEARCH over the stored result.

Notes on ValkeyGEOSEARCH and GEOSEARCHSTORE are core Valkey commands (available since Redis 6.2 / Valkey 9, no module required). They supersede the older GEORADIUS / GEORADIUSBYMEMBER forms, which are deprecated and should not appear in new code. See valkey.io/commands/geosearch · valkey.io/commands/geosearchstore.

GEOSEARCHSTORE materialises a spatial query as a reusable Sorted Set. Because the output is a ZSET, all downstream ZSET commands apply without any conversion.

§3

Location with metadata — the separate-Hash pattern

geospatial.md.txt · Location with Metadata

A GEO member carries only a name. The Sorted Set score is the geohash integer; there is no room for additional fields. The pattern for attaching metadata:

  1. Add a named id as the GEO member — the name is the join key.
  2. Store all metadata in a Hash under that id.
  3. Search returns ids; then fetch each Hash.
valkey.io/commands/geoadd/ · geospatial.md.txt — the metadata patternfrozen
# Step 1: add the id as the GEO member
GEOADD restaurants -122.4194 37.7749 "restaurant:123"

# Step 2: store metadata in a companion Hash
HSET restaurant:123 name "Joe's Diner" cuisine "American" rating "4.5"

# Step 3: search returns ids; fetch each row
GEOSEARCH restaurants FROMLONLAT -122.4 37.8 BYRADIUS 5 km
HGETALL restaurant:123

The GEO set is the spatial index. The Hash is the record. They are joined by the member name. This is the general pattern for any secondary index in Valkey: one structure holds the index (the ordering or location), a separate Hash holds the row, and the application fetches from both using the id as the join key.

Two-step find-nearby and fetch. Select each step; the readout names the command and what it returns.
GEOSEARCH restaurants BYRADIUS 5 km → ids HGETALL restaurant:123 → name, cuisine …
readout · Select a step to see the command and what it returns. The GEO set holds the spatial index; the Hash holds the record; the member name is the join key between them.

The GEO set is a spatial index, not a record store. Every row lives in its own Hash, keyed by the same name used as the GEO member. A search returns ids; a separate HGETALL per id fetches the row. This two-step pattern is the standard secondary-index idiom in Valkey.

§4

Real-time location tracking

geospatial.md.txt · Real-Time Location Tracking

For moving entities (drivers, delivery personnel), each GPS ping is a GEOADD. The command is idempotent on the member name — calling it again moves the entity’s position without adding a duplicate member. A companion Hash holds the availability state and timestamp:

valkey.io/commands/geoadd/ · geospatial.md.txt — trackingfrozen
# GPS ping updates the driver's position
GEOADD drivers -122.4 37.8 "driver:456"

# Availability and freshness in a companion Hash
HSET driver:456:status last_update 1706648400 available 1

# Find nearby available drivers
GEOSEARCH drivers FROMLONLAT -122.4 37.8 BYRADIUS 5 km COUNT 20

The application filters on availability and freshness after receiving the ids from GEOSEARCH: fetch each driver:N:status Hash and discard stale or unavailable drivers. This is the same separate-Hash pattern from the previous section — the GEO set holds the spatial index; the Hash holds the operational state.

GEOADD is idempotent on the member name: a second call with new coordinates moves the entity, not duplicates it. Each GPS ping is a single GEOADD; availability and freshness live in a companion Hash.

§5

Geofencing — application-layer enter / exit detection

geospatial.md.txt · Geofencing

Valkey does not provide built-in geofence events. Geofencing is an application-layer pattern:

  1. Store the geofence definition (centre coordinates and radius, or a bounding box).
  2. On each location update, run GEOSEARCH to test whether the entity is inside the fence.
  3. Compare the current membership result to the previous state.
  4. On a state change (inside → outside, or outside → inside), publish an enter or exit event with PUBLISH.

This is a poll-then-compare loop. The application owns the state machine; Valkey provides the spatial test (GEOSEARCH) and the messaging bus (Pub/Sub). An entity’s membership in the result set of a GEOSEARCH BYRADIUS is the fence test.

the pattern — standalone

GEOSEARCH BYRADIUS as the fence test + PUBLISH on state change; the application holds the previous state and detects the transition.

the honest absence

Geospatial is orthogonal to codemojex (a location-agnostic emoji-guessing game; players are not placed in space). The standalone example is the grounding — no codemojex surface applies here.

Geofencing on Valkey is a two-step application pattern: GEOSEARCH answers the spatial membership question; the application detects enter/exit by comparing successive answers. The engine provides no reactive trigger — the poll cadence determines the detection latency.

§6

References

sources · related

Sources

Related in this course