Thuva,

Depending on your needs, your approach can work.  However, with that
approach I don't see how you can effectively order by distance and at
the same time do a relevant (non-distance) search - and also display
the distance of the users closest location.  Further, I think there is
may be a step missing in your example, as you need to order the
results of your first search by distance and then remove Businesses
with multiple locations (and keep just the shortest.. and this removal
may affect search results later).

After trying various things, I was a bit surprised by the following,
which then led me to the below solution... I'll use an example to
explain.
User_1 has Locations in London (location id = 1) and Boston (location
id = 2).
I thought that if I do a Location.search first, and determine that
location 2 is the closest to New York, that I would then be able to do
a User.search using :with [2], similar to how you are doing that in
your example.  However, I want to order the User search by @relevance,
@geodist.  Unfortunately, even when using the :with parameter, I found
that the search still calculated @geodist (for the ordering) using
locationid =1 (London).  This seems to be related to the float issue
mentioned by Pat on your post.

Pat: "Sphinx itself does not handle multiple-value-attributes with
floats, only integers."

And since for distance we cannot use integers...

The most effective approach I found is to create a UserLocation model,
with a 1-1 relationship with locations.  That way Sphinx can actually
do the search right.  That creates extra overhead on the database side
however, but I think worth the trade-off.  The reason I created a
separate model, and not just build out the index of the Location model
to include User is because Location in a polymorphic model, and every
model will have slightly different search needs.

Here is what I did in case it helps someone... This is not perfect and
there is room for improvement, but decent starting point.

class User < ActiveRecord::Base


  has_many :locations, :as => :located, :dependent => :destroy
  has_many :user_locations, :dependent => :destroy
  has_many :posts, :dependent => :destroy
end

class Location < ActiveRecord::Base
  belongs_to :located, :polymorphic => true
  has_one :user_location, :dependent => :destroy
  validates_uniqueness_of   :location, :scope =>
[:located_id, :located_type], :message => "Location already exists"
end

class UserLocation < ActiveRecord::Base
  belongs_to :user
  belongs_to :location

  define_index do
    indexes [user.first_name, user.last_name], :as => :person
    indexes user.posts.title, :as => :post_title
    indexes user.posts.body, :as => :post_body

    has 'RADIANS(locations.latitude)', :as => :latitude,  :type
=> :float
    has 'RADIANS(locations.longitude)', :as => :longitude,  :type
=> :float
    set_property :latitude_attr   => "latitude"
    set_property :longitude_attr  => "longitude"

    set_property :delta => true
  end
end

# Location observer to make sure the models stay in sync (there are
other ways to implement this).
class LocationObserver < ActiveRecord::Observer
    def after_create(record)
      if record.located_type == 'User'
        UserLocation.create :user_id =>
record.located_id, :location_id => record.id
      end
    end
end

# Search and display users like this:
# note the math converts degrees into radians
users=UserLocation.search "Search for whatever",
      :match_mode    => :any,
      :geo => [-73.986951 * Math::PI / 180, 40.756054 * Math::PI /
180],
      :order => "@relevance DESC, @geodist asc, adjusted_rating DESC,
created_at DESC"

# display results
added_users =[]
users.each_with_geodist do |result, distance|
    unless added_users.include? result.user # does not display a user
if previously displayed (with shorter location)
      added_users << result.user
      puts result.user.login + " is " + (distance /
1609.344).to_i.to_s + " miles from New York, NY"
    end
end

On May 16, 3:13 pm, Thuva Tharma <[email protected]> wrote:
> I had to solve the same problem as yours. I started the 
> thread:http://groups.google.ca/group/thinking-sphinx/browse_thread/thread/d5...
> to get help.
>
> As you can see from Pat Allan's reply, the issue is due to the fact
> that Sphinx cannot handle multi-value-attributes with floats.
>
> However, I came up with a solution that has served me well so far.
> Take a look at the pastie:http://pastie.org/411745to get the general
> idea behind the solution.
>
> -- Thuva Tharma
>
> On May 16, 2:31 pm, "[email protected]" <[email protected]>
> wrote:
>
> > I am using an example here to show the problem.  As far as I can tell,
> > either I am doing something wrong, or geodist search doesn't work
> > right with multiple locations.
>
> > Here is the example:
> > User model and a Location model.  A User has many Locations.
> > Locations is actually a polymorphic model, as other objects may have
> > locations as well - I don't think this affects the results.
>
> > USERS TABLE
> > | id | login |
> > | 1 | User_1 |
> > | 2 | User_2 |
> > | 3 | User_3 |
>
> > LOCATIONS TABLE
> > | id | user_id | location | latitude | longitude
> > | 1 | 1 | New York, NY | -73.986951 | 40.756054
> > | 2 | 2 | Pittsburgh, PA | -80.001933 | 40.438423
> > | 3 | 3 | Los Angeles, CA | -118.243425 | 34.052187
> > | 3 | 3 | Philadelphia, PA | -75.163808 | 39.951639
>
> > In the User model...
> >   define_index do
> >     indexes login
> >     has 'RADIANS(locations.latitude)', :as => :latitude,  :type
> > => :float
> >     has 'RADIANS(locations.longitude)', :as => :longitude,  :type
> > => :float
> >     set_property :latitude_attr   => "latitude"
> >     set_property :longitude_attr  => "longitude"
> >     set_property :delta => true
> >   end
>
> > Here is a search to find users closest to New York, NY.
>
> > The search query:
> > results=User.search :geo => [-73.986951 * Math::PI / 180, 40.756054 *
> > Math::PI / 180], :order => "@geodist asc"
>
> > Parsing through results..
>
> > results.each_with_geodist do |result, distance|
> > puts result.login + " is " + (distance / 1609.344).to_i.to_s + " miles
> > away from New York, NY"
> > end
>
> > Here is the output:
> > User_1 is 0 miles away from New York, NY
> > User_2 is 416 miles away from New York, NY
> > User_3 is 3058 miles away from New York, NY
>
> > The results seem to consider only the first location record of each
> > user.  User_3 is actually closer to New York than User_2 because of
> > the Philadelphia location, but this location is not reflected in the
> > results.
>
> > Any idea what I am doing wrong here?
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Thinking Sphinx" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to 
[email protected]
For more options, visit this group at 
http://groups.google.com/group/thinking-sphinx?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to