A Quick Guide to Redis 3.2's Geo Support

The recently released Redis 3.2 now has an official Geo API in the mainstream branch of the in-memory database. If you are curious on how you could use this, read on...

The Geo API has been around for a while, appearing in the Redis unstable branch about ten months ago and that was, in turn, based on work from 2014. There's a bit of history in that development process, which being practical folk we'll skip past and go straight to the stuff that makes your development day better.

At its simplest, the GEO API for Redis reduces longitude/latitude down into a geohash. Geohash is a technique developed in 2008 to represent locations with short string codes. The Geohash of a particular location, say Big Ben in London, would come out as "gcpuvpmm3f0" which is easier to pass around than "latitude 51.500 longitude -0.12455". The longer the string, the more precise the geohash code.

That encoding into a string is good for humans and URLs but it isn't particularly space efficient. The good news is geohashes can be encoded as binary and using 52 bits, a geohash gets down to 0.6 meter accuracy which is good enough for most uses. A 52-bit value which just happens to be able to be a small-enough integer to live in a Redis floating-point double safely and that's what the Geo API works with behind the scenes.

From the user's point of view though, it's all about the longitude and latitude – yes, we switch them around when working with the API – and working with locations. So let's make some locations:

> GEOADD locations -0.12455 51.5007 "Big Ben" -0.12520 51.50115 "Westminster Station" -0.11358 51.50482 "BFI IMAX" 
(integer) 3

locations is a Redis key and what follows is a list of longitudes and latitudes followed by labels for those locations. I just picked some places in Central London to work with. Those longitudes and latitudes are being hashed together to make a numeric geohash and the label is saved in a sorted set with the geohash as a score in a sorted set (ZSET). We're able to do this multiple times in one command line. We can now ask the geo set what it contains using sorted set commands:

> ZRANGE locations 0 -1
1) "Big Ben"  
2) "Westminster Station"  
3) "BFI IMAX"  

We can start querying this. Let's say we're at longitude -0.11759 latitude 51.50574 – or as I call it "just by the Royal Festival Hall" – and we want to know if any of those locations are within half a kilometre:

> GEORADIUS locations -0.11759 51.50574 500 m
1) "BFI IMAX"  

We use the GEORADIUS command on our geo set and then give it our coordinates. That's followed by a number and by a unit type for that number; in this case 500 metres (m for meters, mi for miles, km for kilometers and ft for feet). Well, we've found out it's within range. Let's find out how far by adding WITHDIST:

> georadius locations -0.11759 51.50574 500 m WITHDIST
1) 1) "BFI IMAX"  
   2) "295.9825"

So it's 295M – in a straight line – to the BFI IMAX. Let's open up the range to a kilometre and ask for the results in ascending order of distance:

> GEORADIUS locations -0.11759 51.50574 1 km WITHDIST ASC
1) 1) "BFI IMAX"  
   2) "0.2960"
2) 1) "Westminster Station"  
   2) "0.7335"
3) 1) "Big Ben"  
   2) "0.7392"

Note the distances are in the same unit as our query. If something is already in the set, we can query how close a member of the list is to other members. For that we have GEORADIUSBYMEMBER, so if we want to know what's within 100 meters of Westminster Station, we can ask using:

> GEORADIUSBYMEMBER locations "Westminster Station" 100 m withdist
1) 1) "Westminster Station"  
   2) "0.0000"
2) 1) "Big Ben"  
   2) "67.3659"

We asked for the distances so you can see that at 0.0M away is Westminster Station. "Big Ben" is a whole 67M away and if we widened our radius we'd find the IMAX is 900M away. There's a more direct way to find that distance though:

> GEODIST locations "Westminster Station" "BFI IMAX"
"902.1221"

The GEODIST command takes two members from a geo set and returns the distance between them (in meters by default but you can add mi, ft or km to a GEODIST query to get it in those units).

Well, now we've got distances, we also want to think about how we convert these Geo set members back into usable coordinates. The GEOPOS command comes into play here:

> GEOPOS locations "Big Ben"
1) 1) "-0.12454837560653687"  
   2) "51.50069897715604839"

That converts a Geo set member back into the longitude and latitude. You can also get the string-based geohash back with GEOHASH in the same way:

> GEOHASH locations "Big Ben"
1) "gcpuvpmm3f0"  

And that's it for the Geo API. Wait, you say, there's no command to remove a member of geo sets. Remember this is still a sorted set underneath, so to remove a member use Redis's ZREM like this:

> ZREM locations "Big Ben"
(integer) 1

And Big Ben is gone... from your set.

So that's the Geo API for Redis. It's a simple implementation ideal for quickly working out the proximity of locations to other locations, like restaurants as a user walks through a city, vehicles and garages or any other safe to approximate geo-data. If you want the whole GIS environment, you'll probably want to look to something like PostgreSQL and PostGIS, but if you just need the more common "What's close to me?" query done quickly, Redis's Geo API should be a first port of call.