Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

119 lines (101 sloc) 4.716 kB
# encoding: utf-8
module Mongoid #:nodoc:
module Contextual #:nodoc:
class Mongo #:nodoc:
# Fetches rows from the data base sorted by distance.
# In MongoDB versions 1.7 and above it returns a distance.
# Uses all criteria chains except without, only, asc, desc, order_by
#
# @example Minimal Query
#
# Address.geo_near([70,40])
#
# @example Chained Query
#
# Address.where(:state => 'ny').geo_near([70,40])
#
# @example Calc Distances Query
#
# Address.geo_near([70,40], :max_distance => 5, :unit => 5)
#
# @param [ Array, Hash, #to_xy ] center The center of where to calculate distance from
# @param [ Hash ] opts the options to query with
# @options opts [Integer] :num The number of rows to fetch
# @options opts [Hash] :query The query to filter the rows by, accepts
# @options opts [Numeric] :distance_multiplier this is multiplied against the calculated distance
# @options opts [Numeric] :max_distance The max distance of a row that should be returned in :unit(s)
# @options opts [Numeric, :km, :k, :mi, :ft] :unit automatically sets :distance_multiplier and converts :max_distance
# @options opts [true,false] :spherical Will determine the distance either by spherical calculation or flat calculation
# @options opts [TrueClass,Array<Symbol>] :calculate Which extra fields to calculate distance for in ruby, if set to TrueClass it will calculate all spatial fields
#
# @return [ Array ] Sorted Rows
def geo_near(center, opts = {})
opts = self.criteria.options.merge(opts)
# convert point
center = center.to_xy if center.respond_to?(:to_xy)
center = [center.x, center.y] if center.respond_to?(:x)
# set default opts
opts[:skip] ||= 0
if unit = Mongoid::Geospatial.earth_radius[opts[:unit]]
opts[:unit] = (opts[:spherical]) ? unit : unit * Mongoid::Geospatial::RAD_PER_DEG
end
if unit = Mongoid::Geospatial.earth_radius[opts[:distance_multiplier]]
opts[:distance_multiplier] = (opts[:spherical]) ? unit : unit * Mongoid::Geospatial::RAD_PER_DEG
end
opts[:distance_multiplier] = opts[:unit] if opts[:unit].kind_of?(Numeric)
# setup paging.
if opts.has_key?(:page)
opts[:page] ||= 1
opts[:paginator] ||= Mongoid::Geospatial.paginator()
if opts[:per_page].blank?
opts[:per_page] = case opts[:paginator]
when :will_paginate
@document.per_page
when :kaminari
Kaminari.config.default_per_page
else
Mongoid::Geospatial.default_per_page
end
opts[:per_page] = opts[:per_page].to_i
end
end
opts[:query] = create_geo_near_query(center,opts)
results = klass.mongo_session.command(opts[:query])
Mongoid::Geospatial::GeoNearResults.new(klass,results,opts)
end
private
def create_geo_near_query(center,opts)
# minimum query
query = {}
query[:geoNear] = klass.collection_name
query[:near] = center
# create limit and use skip
if opts[:num]
query['num'] = opts[:skip].to_i + opts[:num].to_i
elsif opts[:limit]
query['num'] = opts[:skip].to_i + opts[:limit].to_i
elsif opts[:page]
query['num'] = opts[:skip].to_i + (opts[:page].to_i * opts[:per_page].to_i)
end
# allow the use of complex werieis
if opts[:query]
query['query'] = self.criteria.where(opts[:query]).selector
elsif self.criteria.selector != {}
query['query'] = self.criteria.selector
end
if opts[:max_distance]
query['maxDistance'] = opts[:max_distance].to_f
query['maxDistance'] = query['maxDistance']/opts[:unit].to_f if opts[:unit]
end
if opts[:unique_docs]
query['uniqueDocs'] = true
end
query['spherical'] = true if opts[:spherical]
# mongodb < 1.7 returns degrees but with earth flat. in Mongodb 1.7 you can set sphere and let mongodb calculate the distance in Miles or KM
# for mongodb < 1.7 we need to run Haversine first before calculating degrees to Km or Miles. See below.
query['distanceMultiplier'] = opts[:distance_multiplier].to_f if opts[:distance_multiplier]
query
end
end
end
end
Jump to Line
Something went wrong with that request. Please try again.