lashleigh
lashleigh

Reputation: 2565

How to perform $near-like queries for array of lat/lng pairs with mongomapper

I have a trip model that contains an array of lat/lng pairs

class Trip
  include MongoMapper::Document

  key :route, Array, # example: [[45,-122], [45.5, -122.5], [45, -123]]
  ...
end

I would like to perform $near-type queries on the route array, which should be possible according to the documentation .

I would like to find the route nearest to a certain point.

def self.nearest_to(coords)
  where(:route => {'$near' => coords}).limit(1).first
end

But this does not work, I get an error that says:

Mongo::OperationFailure: can't find special index: 2d for: { route: { $near: [ 32.80909, -117.1537 ] } }
    from /Users/lash/.rvm/gems/[email protected]/gems/mongo-1.3.1/lib/mongo/cursor.rb:101:in `next_document'
    from /Users/lash/.rvm/gems/[email protected]/gems/mongo-1.3.1/lib/mongo/cursor.rb:248:in `each'
    from /Users/lash/.rvm/gems/[email protected]/gems/mongo-1.3.1/lib/mongo/cursor.rb:267:in `to_a'
    from /Users/lash/.rvm/gems/[email protected]/gems/mongo-1.3.1/lib/mongo/cursor.rb:267:in `to_a'
    from /Users/lash/.rvm/gems/[email protected]/gems/plucky-0.3.8/lib/plucky/query.rb:76:in `all'
    from /Users/lash/code/rails3projects/rideshare/app/models/trip.rb:20:in `nearest'
    from (irb):10
    from /Users/lash/.rvm/gems/[email protected]/gems/railties-3.0.9/lib/rails/commands/console.rb:44:in `start'
    from /Users/lash/.rvm/gems/[email protected]/gems/railties-3.0.9/lib/rails/commands/console.rb:8:in `start'
    from /Users/lash/.rvm/gems/[email protected]/gems/railties-3.0.9/lib/rails/commands.rb:23:in `<top (required)>'
    from script/rails:6:in `require'
    from script/rails:6:in `<main>'

What is the correct way to query multi-location documents with mongomapper?

Upvotes: 0

Views: 544

Answers (2)

lashleigh
lashleigh

Reputation: 2565

The correct answer - as of today - is that you can't. The ruby driver does not yet support mongo 1.3.3, so this type of geo-location query simply isn't possible.

Here is one example of how one might work around the issue in the mean time.

class Trip
  include MongoMapper::Document

  key :route, Array # example: [[45,-122], [45.5, -122.5], [45, -123]]
  ...

  scope :passes_near, lambda {|coords| where(:id => {'$in' => Trip.near(coords)}) }

  def self.near(coords, options = {})
    options[:radius] ||= 60
    case coords
      when Array; coords
      when String; coords = Geocoder.coordinates(coords)
    end

    trips = {}
    Trip.all.each do |trip|
      dist = trip.route.map{|point| Geocoder::Calculations::distance_between(point, coords)}.min
      trips[trip.id] = dist
    end
    return trips.select {|k, v| v < options[:radius]}.keys
  end 
end 

To find all trips that go near Seattle ([47.6062095, -122.3320708]) I would simply type:

Trip.passes_near("Seattle, WA") 
=> #<Plucky::Query _id: {"$in"=>[*lots of ids*}, transformer: #...> 

Since a plucky object is returned it would be simple to chain queries together.

Upvotes: 1

Jon Kern
Jon Kern

Reputation: 3235

You should find some answers on $near at this nice little tutorial: http://mongly.com/

db.treasures.find({location: {$near: [20, -20]}});

Upvotes: 0

Related Questions