How do I sort an array of records by distance using geocoder on an associated model?

1k Views Asked by At

Im on Rails 4 using geocoder to handle locations.

I have two models Listing and Location

Listing

class Listing < ActiveRecord::Base
  has_many :locations
end

Location

class Location < ActiveRecord::Base
  belongs_to :listing
  geocoded_by :full_address
  after_validation :geocode

  def full_address
    [street, city, state, country].compact.join(", ")
  end
end

I trying to make a simple search function where a user types in a city and is returned back a list of listings that are within 50 miles of the search query.

I have my search functionality being handled in my listings controllers index action

def index
    @query = Geocoder.search(params[:q]).first
    @location = Location.near(@query.address, 50).order("distance")
    @listings = Listing.find(@location.map(&:listing_id))
end

I want the results returned to be ordered by distance to the city searched for. When I search, the results returned are ordered by the listing ids.

The only way Ive been able to make this work, was to do my @listings variable like so

def index
    @query = Geocoder.search(params[:q]).first
    @location = Location.near(@query.address, 50).order("distance")
    @listings = []
    @location.each do |l|
      @listings.push(Listing.find(l))
    end
end

But this runs a bunch of db query's and Im worried that with a lot of records and searches, there will be a lot of strain on the db.

Is there a more concise way of having the array ordered the way I want? Thanks.

2

There are 2 best solutions below

3
On

ActiveRecord.find takes an array and in that case does a single query. So if you can do Listing.find for an individual location then it's just as easy to do it for the whole array:

def index
  @query = Geocoder.search(params[:q]).first
  @location = Location.near(@query.address, 50).order("distance")
  @listings = Listing.find(@location)
end

That should then do three queries: the Geocoder search, the Location query, then the single Listing query.

(I wouldn't bother optimising it any more at this point.)

0
On

So I figured out a better way to sort my Listing array.

        @listings = Listing.find(@location.map(&:listing_id)).sort_by{|listing| listing.locations.first.distance_to(@query.address)}

Im probably going to run into problems in the future when I add the ability to have multiple locations per listing in the future, but this works for now and Ill cross that bridge wen I come to it,