written by PrimeHammer

Search by radius in Redis

Since version 3.2.0 Redis┬áthat was released on May 6, 2016, the database introduces geospatial commands. Simply said it means that you can ask database like this: I would like to get list of nearest airports in selected location. Let’s extend this use case with pagination and implement it with Ruby.

airplane

Sample data

As an example data, we used a list of all airports. You can download csv the file from openflights.org.

Excerpt from the file:

3697,"La Guardia Airport","New York","United States","LGA","KLGA",40.77719879,-73.87259674,21,-5,"A","America/New_York","airport","OurAirports"
3698,"Tallahassee Regional Airport","Tallahassee","United States","TLH","KTLH",30.396499633789062,-84.35030364990234,81,-5,"A","America/New_York","airport","OurAirports"

We skip the import of South Pole Station Airport because Redis documentation says that valid latitudes are from -85.05112878 to 85.05112878 degrees.

2033,"South Pole Station Airport","Stephen's Island","Antarctica",\N,"NZSP",-90,0,9300,12,"U","Antarctica/South_Pole","airport","OurAirports"

Commands overview

We’ll need the following Redis commands to import data, query closest airports and do the pagination.

Command Description
GEOADD Adds the specified geospatial items (latitude, longitude, name) to the specified key
GEORADIUS Query a sorted set representing a geospatial index to fetch members matching a given maximum distance from a point
ZRANGE Return a range of members in a sorted set, by index

Import airports

Let’s import all data from the CSV into the Redis database. Airports will be stored under key airports.

# import.rb
require 'redis'
require 'hippie_csv'

# set path to your csv file airports_csv_path = 'data/airports.dat'

# initialize redis database redis = Redis.new

# load data from csv file airports = HippieCSV.read(airports_csv_path) # csv columns indexes airport_name = 1 latitude = 6 longitude = 7

# remove South Pole Station Airport from array airports.reject!{ |row| row[airport_name] == 'South Pole Station Airport' }

# go through airports and import each row separately airports.each do |row| # use geoadd command for importing redis.call(:geoadd, 'airports', row[longitude], row[latitude], row[airport_name]) end

Search with pagination

The full implementation in Ruby with comments will look like this.

# search.rb
require 'redis'

# initialize redis database redis = Redis.new

# New York city coordinates - center of search area latitude = 40.705311 longitude = -74.25819

radius = 500 # radius in kilometers page = 1 # selected page perpage = 5 # items per page

# search and save all results under airports_result key redis.call(:georadius, 'airports', longitude, latitude, radius, 'km', 'asc', 'storedist', 'airports_result')

# calculate limits for zrange command start = (page - 1) * perpage stop = start + perpage - 1

# use zrange to extract page from airports_result result = redis.call(:zrange, 'airports_result', start, stop, 'withscores')

# print result to the standard output result.each_slice(2).with_index do |(name, distance), i| position = start + i + 1 distance = distance.to_f.round(2) puts "#{position}: #{name}, #{distance} km" end

The script will print airports ordered by their distance from New York.

1: Newark Liberty International Airport, 7.68 km
2: Linden Airport, 9.84 km
3: Morristown Municipal Airport, 16.85 km
4: Essex County Airport, 19.0 km
5: One Police Plaza Heliport, 21.82 km

You’ll get a result like this if you set page = 2.

6: Teterboro Airport, 23.15 km
7: La Guardia Airport, 33.46 km
8: Somerset Airport, 35.86 km
9: John F Kennedy International Airport, 41.08 km
10: Westchester County Airport, 61.33 km

Conclusion

We used the new Redis georadius command to search for the nearest location. According to Google, the results look correct. I didn’t make any performance test but on my machine, the queries to Redis were super fast. So if you are familiar with Redis database you can give it a try, instead of traditional solutions like PostGIS or geocoder.

  • 23
    Shares
  • ariyagksk Tk

    Great One