How to work with locations (geo) in Tire

jasonfb edited this page Mar 4, 2013 · 9 revisions

Tire currently does not expose the (awesome!) „Geo Location and Search” support in ElasticSearch.

However, it's simple to define proper mappings for you location-based documents, and it's possible to search them, either via the Tire.search DSL method by passing it a Hash, or descending a level down, and using Tire::Configuration.client.get <MY QUERY AS A HASH> method.

The following example illustrates the point:

require 'tire'
require 'active_record'

Tire.index('venues') { delete }

ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ":memory:" )

ActiveRecord::Migration.verbose = false
ActiveRecord::Schema.define(version: 1) do
  create_table :venues do |t|
    t.string   :title, :latitude, :longitude
  end
end

class Venue < ActiveRecord::Base
  include Tire::Model::Search
  include Tire::Model::Callbacks

  tire do
    mapping do
      indexes :title,   type: 'string', analyzer: 'snowball'
      indexes :lat_lon, type: 'geo_point'
    end
  end

  # NOTE The ordering of latitude and longitude for indexing geo points is different
  # based on whether a string or an array is used. The order when using an array 
  # should be [longitude, latitude] versus for a string where it should be "latitude,longitude".
  # See The elasticsearch documentation for more information.
  # http://www.elasticsearch.org/guide/reference/mapping/geo-point-type.html
  def lat_lon
    [latitude, longitude].join(',')
  end

  def to_indexed_json
    to_json(only: ['title'], methods: ['lat_lon'])
  end
end

Venue.create title: 'Pizzeria Pomodoro', latitude: "41.12", longitude: "-71.34"

Venue.tire.index.refresh

# Example 1: Construct the search as a Hash
query = { query: { filtered: { query: { match_all: ''} }, filter: { geo_distance: { distance: '100km', lat_lon: "41,-71" } } } }

puts "", "The query:", '-'*80, query.to_json

s = Tire.search 'venues', query

puts "", "Results:", '-'*80, s.results.inspect

# Example 2: Construct the search using block syntax
s = Venue.tire.search do
  query { all }
  filter :geo_distance, lat_lon: "41,-71", distance: '100km'
end

puts "", "Results:", '-'*80, s.results.inspect