Geo lookups with ElasticSearch and Rails

ElasticSearch is a great search engine and among many features it offers support to lookup documents by geographical coordinates. Its very useful when you want to add geo support to your application without adding an extra layer to your database, like PostGIS extension to PostgreSQL, which is great but requires extra work to get it play nicely.

There are a few libraries that integrate ElasticSearch with Rails, one of the popular ones is Tire. Its been deprecated, but i decided to share the article anyway since i might need it at some point in future.

Here's UserLocation model that we'll be working with:

class UserLocation
  include Tire::Model::Search
  include Tire::Model::Callbacks

  mapping do
    indexes :id,          type: "integer", index: :not_analyzed
    indexes :user_id,     type: "integer", index: :not_analyzed
    indexes :coordinates, type: "geo_point"

  def coordinates
    "#{lat}, #{lng}"

Basically, user location model consists of the following attributes:

  • id - Primary ID (Integer)
  • user_id - User reference ID (Integer)
  • name - Location name (String)
  • lat - Latitude coordinate (Float)
  • lng - Longitude coordinate (Float)

Here is the example on how to get closest user locations to a given address:

SEARCH_UNIT   = "mi"

# Find user
user = User.find(12345)

# Test address
address = "3128 Broadway, Chicago, IL, 60657"

# Geocode given address string. You can use geocoder, its pretty simple
geo = Geocoder.coordinates(address)
point = "#{geo[0]}, #{geo[1]}"

# Search for closest user locations
results = do
  filter :term, user_id:
  filter :geo_distance, distance: SEARCH_RADIUS, coordinates: point
  sort {
    by "_geo_distance", {
      "coordinates" => point,
      "order"       => SEARCH_ORDER,
      "unit"        => SEARCH_UNIT

In the example above we're searching for closest user locations within 5 miles. Search unit is set to miles, results will be ordered by closest.

Simple as that. The benefit of using ElasticSearch for geo lookups is obvious: you can add a regular full-text search query, filters, facets, pagination, etc.