Is this your first visit? You may want to subscribe to the feed.

Location-based search with Sphinx and acts_as_geocodable

Sphinx, everybody’s favorite search engine, has support for location-based search, giving you geo-aware, full-text searching. So now you can find all of the garage sales on Saturday within 20 miles that have LPs and a reel mower.

All you need to do is add latitude and longitude (in radians) to the index, allowing you to limit the results to records within a distance of a point. The hardest part is getting the coordinates, but acts_as_geocodable makes that really easy.

To start, install acts_as_geocodable. Once you have that configured properly, install ThinkingSphinx, define an index on your geocodable model and add the coordinates to the index:

class Listing < ActiveRecord::Base
  acts_as_geocodable

  define_index do
    indexes title
    indexes description

    has geocoding.geocode(:id), :as => :geocode_id
    has 'RADIANS(geocodes.latitude)', :as => :latitude, :type => :float
    has 'RADIANS(geocodes.longitude)', :as => :longitude, :type => :float
  end
end

The three lines that start with has add the geocode id, and the latitude and longitude in radians to the index. Our index doesn’t need the geocode id, but we have to add it so that ThinkingSphinx properly joins the geocodes table.

After you rebuild the index and start the daemon, you can search for records by location. Here’s an example of taking a zip code from a user and finding all records with in 20 miles. (Note: you will need to grab the latest version, 0.2.9, of Graticule for this next bit of code to work)

def search
  location = Geocode.geocoder.locate(params[:zip]).coordinates.map(&:to_radians)
  @listings = Listing.search(params[:q], :geo => location,
    :with => {'@geodist' => 0.0..(20 * 1610.0)})
end

After looking up the coordinates of the zip code that the user entered, we do a search with the :geo parameter, limiting the results using the special @geodist condition. We have to pass in a range of floats that represent the distance of the points in meters (and since the US is in the stone age, I’m converting from miles).

That’s all there is to it. Now go write some cool location-based search and comment here about it.

Code: acts_as_geocodable, graticule, rails, ruby, search, sphinx Apr 14, 2009 ● updated Apr 14, 2009 3 comments

3 comments

  1. Important to note that you don’t need sphinx to do location-based searches in acts_as_geocodable. We built that all in and it works out of the box.

    The full-text search is what’s especially cool.

    BTW, since when do you like Sphinx? ;)

    Daniel Morrison Daniel Morrison April 14, 2009 at 08:54 PM
  2. I didn’t say that I like Sphinx, I said everybody else does. ;) The jury is still out.

    Brandon Brandon April 14, 2009 at 08:58 PM
  3. But wouldn’t it be faster if you do it via sphinx?

    Greg Moreno Greg Moreno September 03, 2009 at 06:50 PM

Speak your mind:

*

*


* I hate spam and will never sell or publish your email address.

(You may use textile in your comments.)

Subscribe

Browse by Tag