geo radius search using Thinking Sphinx

234 views
Skip to first unread message

Travis

unread,
Dec 30, 2009, 1:25:41 AM12/30/09
to Thinking Sphinx
Hi Guys,

I am new to this group and to the Thinking Sphinx rails plugin and
Sphinx search dameon. I am up and running and with Thinking Sphinx in
my application and it's truly amazing! So here is my question, my
application has many tables that store latitude and longitude
information about each item and will return items based on a radius
search, i.e., items that are with x miles between lat and long
points. Currently I am doing this both in the models and controllers
(messy, but works) using find_by_sql calls and using sql such as this,
for example this controller call for all banners within 10 miles of a
set of lat and longs.

@rsbanners = Banner.find_by_sql ["SELECT * FROM banners
WHERE (3958*3.1415926*sqrt((bannerlatitude-'#{@zip.latitude}')*
(bannerlatitude-'#{@zip.latitude}')
+ cos(bannerlatitude/57.29578)*cos('#{@zip.latitude}'/57.29578)*
(bannerlongitude-'#{@zip.longitude}')*(bannerlongitude-'#
{@zip.longitude}'))/180)
<= 10 AND banneractive = '1'"]

I was able to build my index with lats and longs from the example
documentation and do a basic search against the index for all banners
and sorting by distance. The geo functionality is fantastic and works
great:
@banners = Banners.search @terms, :geo => [@zip.latitude,
@zip.longitude],:order => "@geodist DESC

Can anyone give me some advice on how to go about converting my
example find_by_sql statement above to using the Thinking Sphinx
plugin? Can it be done?

Many thanks for your help! And a huge thanks to Pat Allen for
creating such a fantastic search platform to work with!

--Travis

Pat Allan

unread,
Dec 30, 2009, 1:51:15 AM12/30/09
to thinkin...@googlegroups.com
Hi Travis

That's some pretty complicated code there, so maybe I'm jumping ahead a little too far... but, if you want to limit results to anything within a 10 mile radius, that's easy enough. All you need to do is add a range filter on @geodist:

Banner.search @terms,


:geo => [@zip.latitude, @zip.longitude],

:with => {'@geodist' => 0.0..10_000.0},
:order => '@geodist ASC'

However, Sphinx uses metric, and so you'll need to convert 10 miles to metres - roughly 16km, so try the following instead:

Banner.search @terms,


:geo => [@zip.latitude, @zip.longitude],

:with => {'@geodist' => 0.0..16_000.0},
:order => '@geodist ASC'

Hope this helps :)

--
Pat

> --
>
> You received this message because you are subscribed to the Google Groups "Thinking Sphinx" group.
> To post to this group, send email to thinkin...@googlegroups.com.
> To unsubscribe from this group, send email to thinking-sphi...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/thinking-sphinx?hl=en.
>
>

Travis

unread,
Dec 30, 2009, 2:38:20 PM12/30/09
to Thinking Sphinx
Hi Pat, thanks so much for your response! Adding a range filter on
@geodist is exactly what I needed and will greatly simplify my code as
you can tell. I've hit a bit of a snag though, whenever I introduce
the :with paramter as a range filter from your example below, I get no
results. If I remove it, I am able to search fine and can sort using
order => '@geodist ASC', but neither :with => {'@geodist' =>
0.0..10_000.0}, or :with => {'@geodist' => 0.0..16_000.0} will return
results.

Here is my index code in the Banner model:
define_index do
# fields
indexes bannertitle, :sortable=>true
indexes bannerdescription
has 'RADIANS(bannerlatitude)', :as => :bannerlatitude, :type
=> :float
has 'RADIANS(bannerlongitude)', :as => :bannerlongitude, :type
=> :float
set_property :latitude_attr => 'bannerlatitude'
set_property :longitude_attr => 'bannerlongitude'
set_property :field_weights => {"bannertitle" => 10,
"bannerdescription" => 5}
end

And in my Banner controller:

@banners = Banner.search @terms, :geo => [@zip.latitude,


@zip.longitude],:with => {'@geodist' => 0.0..16_000.0},:order =>
"@geodist ASC"

I must be missing something, my field types for latitude and longitude
are already float. Is there anything else that needs to be setup to
use a range filter on @geodist?

Thanks for your help!
--Travis

> > WHERE (3958*3.1415926*sqrt((bannerlatitude-...@zip.latitude}')*
> > (bannerlatitude-...@zip.latitude}')
> > + cos(bannerlatitude/57.29578)*cos('...@zip.latitude}'/57.29578)*
> > (bannerlongitude-...@zip.longitude}')*(bannerlongitude-'#
> > {...@zip.longitude}'))/180)


> > <= 10 AND banneractive = '1'"]
>
> > I was able to build my index with lats and longs from the example
> > documentation and do a basic search against the index for all banners

> > and sorting by distance. Thegeofunctionality is fantastic and works
> > great:
> > @banners = Banners.search @terms, :geo=> [@zip.latitude,

Travis Gere

unread,
Dec 30, 2009, 7:02:17 PM12/30/09
to Thinking Sphinx
I figured out my problem with search results below, for some reason @geodist is coming back with distances in the hundreds of miles for lat and longs that should only be a few miles from an anchor point. For instance for a set of lat and longs 1 mile away, I am getting 125244.59375. I believe this is in meters, but still, that would be 77.82 miles.  Has anyone seen this before?  Any thoughts?

Here is my current index code:

      define_index do
        # fields
        indexes bannertitle
        indexes bannerdescription
        # attributes
        has :bannerlatitude
        has :bannerlongitude

        set_property :latitude_attr => 'bannerlatitude'
        set_property :longitude_attr => 'bannerlongitude'
      end

  My database table is using float(10,7) for storage of lats and longs.

Thanks,
Travis

Travis Gere

unread,
Dec 31, 2009, 1:10:20 AM12/31/09
to Thinking Sphinx
I finally got this working, and wanted to follow up for the sake of anyone reading this particular thread having trouble with a radius search and looking for something else to try.  This may not be applicable to you depending on how your tables are setup, but this is what finally worked for me. 

I'm storing my lats and longs in decimal(15,10) fields which gives me all the precision I need for geocoding down to the street level and seems to work well with my search.  I was getting strange @geodist range filter results until I formatted the lats and longs that I was passing to my search class as radians like so:

 @lat = (latitude/180) * Math::PI
 @lon = (longitude/180) * Math::PI

halogen64

unread,
Dec 31, 2009, 9:04:56 AM12/31/09
to Thinking Sphinx
When you index you may also do:
has 'RADIANS(latitude)'
has 'RADIANS(longitude)'

On Dec 31, 1:10 am, Travis Gere <gereenterpri...@gmail.com> wrote:
> I finally got this working, and wanted to follow up for the sake of anyone
> reading this particular thread having trouble with a radius search and
> looking for something else to try.  This may not be applicable to you
> depending on how your tables are setup, but this is what finally worked for
> me.
>
> I'm storing my lats and longs in decimal(15,10) fields which gives me all
> the precision I need for geocoding down to the street level and seems to
> work well with my search.  I was getting strange @geodist range filter
> results until I formatted the lats and longs that I was passing to my search
> class as radians like so:
>
>  @lat = (latitude/180) * Math::PI
>  @lon = (longitude/180) * Math::PI
>

> On Wed, Dec 30, 2009 at 7:02 PM, Travis Gere <gereenterpri...@gmail.com>wrote:
>
> > I figured out my problem with search results below, for some reason
> > @geodist is coming back with distances in the hundreds of miles for lat and
> > longs that should only be a few miles from an anchor point. For instance for
> > a set of lat and longs 1 mile away, I am getting 125244.59375. I believe
> > this is in meters, but still, that would be 77.82 miles.  Has anyone seen
> > this before?  Any thoughts?
>
> > Here is my current index code:
>
> >       define_index do
> >         # fields
> >         indexes bannertitle
> >         indexes bannerdescription
> >         # attributes
> >         has :bannerlatitude
> >         has :bannerlongitude
>
> >         set_property :latitude_attr => 'bannerlatitude'
> >         set_property :longitude_attr => 'bannerlongitude'
> >       end
>
> >   My database table is using float(10,7) for storage of lats and longs.
>
> > Thanks,
> > Travis
>

> >> thinking-sphi...@googlegroups.com<thinking-sphinx%2Bunsu...@googlegroups.com>


> >> .
> >> > > For more options, visit this group athttp://
> >> groups.google.com/group/thinking-sphinx?hl=en.
>
> >> --
>
> >> You received this message because you are subscribed to the Google Groups
> >> "Thinking Sphinx" group.
> >> To post to this group, send email to thinkin...@googlegroups.com.
> >> To unsubscribe from this group, send email to

> >> thinking-sphi...@googlegroups.com<thinking-sphinx%2Bunsu...@googlegroups.com>

Pat Allan

unread,
Jan 2, 2010, 12:55:44 AM1/2/10
to thinkin...@googlegroups.com
Hi Travis

Good to know you got it all figured out. I've just added a note in the docs to remind people that geo params need to be in radians as well. Hopefully that'll stop others hitting the same roadblock as yourself.

Cheers

--
Pat

Reply all
Reply to author
Forward
0 new messages