Skip to content
33 changes: 17 additions & 16 deletions lib/mongoid_spacial/contexts/mongo.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,32 +53,33 @@ def geo_near(center, opts = {})
opts[:page] ||= 1
opts[:paginator] ||= Mongoid::Spacial.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::Spacial.default_per_page
end
opts[:per_page] = opts[:per_page].to_i
end

opts[:per_page] = default_per_page(opts[:paginator]) if opts[:per_page].blank?
end

opts[:query] = create_geo_near_query(center,opts)
results = klass.db.command(opts[:query])
Mongoid::Spacial::GeoNearResults.new(klass,results,opts)
end

private

def default_per_page(paginator)
case paginator
when :will_paginate
@document.per_page
when :kaminari
Kaminari.config.default_per_page
else
Mongoid::Spacial.default_per_page
end.to_i
end

def create_geo_near_query(center,opts)
query = BSON::OrderedHash.new

# minimum query
query = {
:geoNear => klass.collection_name,
:near => center,
}
query[:geoNear] = klass.collection_name
query[:near] = center

# create limit and use skip
if opts[:num]
Expand Down
6 changes: 4 additions & 2 deletions lib/mongoid_spacial/field_option.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@
self.distance_from(field.name, *args)
end

__field = field

define_method field.name do
output = self[field.name] || [nil,nil]
output = self[__field.name] || [nil,nil]
output = {lng_meth => output[0], lat_meth => output[1]} unless options[:return_array]
return options[:class].new(output) if options[:class]
output
Expand All @@ -27,7 +29,7 @@
elsif arg.respond_to?(:to_lng_lat)
arg = arg.to_lng_lat
end
self[field.name]=arg
self[__field.name]=arg
arg = [nil,nil] if arg.nil?
return arg[0..1] if options[:return_array]
h = {lng_meth => arg[0], lat_meth => arg[1]}
Expand Down
4 changes: 2 additions & 2 deletions lib/mongoid_spacial/spacial/document.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ module Document
included do
attr_accessor :geo
cattr_accessor :spacial_fields, :spacial_fields_indexed
@@spacial_fields = []
@@spacial_fields_indexed = []
self.spacial_fields = []
self.spacial_fields_indexed = []
end

module ClassMethods #:nodoc:
Expand Down
73 changes: 46 additions & 27 deletions lib/mongoid_spacial/spacial/geo_near_results.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,10 @@ def initialize(document,results,opts = {})
@stats = results['stats'] || {}
@opts[:skip] ||= 0

@_original_array = results['results'].collect do |result|
res = Mongoid::Factory.from_db(@document, result.delete('obj'))
res.geo = {}
# camel case is awkward in ruby when using variables...
if result['dis']
res.geo[:distance] = result.delete('dis').to_f
end
result.each do |key,value|
res.geo[key.snakecase.to_sym] = value
end
# dist_options[:formula] = opts[:formula] if opts[:formula]
@opts[:calculate] = @document.spacial_fields_indexed if @document.spacial_fields_indexed.kind_of?(Array) && @opts[:calculate] == true
if @opts[:calculate]
@opts[:calculate] = [@opts[:calculate]] unless @opts[:calculate].kind_of? Array
@opts[:calculate] = @opts[:calculate].map(&:to_sym) & geo_fields
if @document.spacial_fields_indexed.kind_of?(Array) && @document.spacial_fields_indexed.size == 1
primary = @document.spacial_fields_indexed.first
end
@opts[:calculate].each do |key|
res.geo[(key.to_s+'_distance').to_sym] = res.distance_from(key,center,{:unit =>@opts[:unit] || @opts[:distance_multiplier], :spherical => @opts[:spherical]} )
res.geo[:distance] = res.geo[key] if primary && key == primary
end
end
res
end
prepare_distance_calculation

@_original_array = results['results'].collect { |result| fabricate_result result }

if @opts[:page]
start = (@opts[:page]-1)*@opts[:per_page] # assuming current_page is 1 based.
@_paginated_array = @_original_array.clone
Expand Down Expand Up @@ -64,7 +43,7 @@ def page!(page, options = {})
self.replace(@_paginated_array[self.opts[:skip]+start, self.limit_value] || [])
else
@_paginated_array ||= self.to_a
self.replace(@_paginated_array[self.opts[:skip]+start, self.limit_value])
self.replace(@_paginated_array[self.opts[:skip]+start, self.limit_value] || [])
end
true
end
Expand Down Expand Up @@ -113,7 +92,8 @@ def limit_value
alias_method :per_page, :limit_value

def num_pages
(self.total_entries && @opts[:per_page]) ? self.total_entries/@opts[:per_page] : nil
return nil unless total_entries && @opts[:per_page]
return (total_entries.to_f / @opts[:per_page]).ceil
end
alias_method :total_pages, :num_pages

Expand All @@ -135,6 +115,45 @@ def next_page
self.current_page < self.total_pages ? (self.current_page + 1) : nil
end

if RUBY_VERSION.to_f < 1.9
def sort_by!
replace sort_by { |obj| yield obj }
end
end

private

def prepare_distance_calculation
@calculate_fields = @opts[:calculate]
@calculate_fields = @document.spacial_fields_indexed if @calculate_fields == true

if @calculate_fields
@calculate_fields = [@calculate_fields] unless @calculate_fields.kind_of? Array
@calculate_fields = @calculate_fields.map(&:to_sym) & geo_fields
@calculate_primary = @document.spacial_fields_indexed.first if @document.spacial_fields_indexed.size == 1
end
end

def fabricate_result(geo_near_result)
result = Mongoid::Factory.from_db(@document, geo_near_result.delete('obj'))
result.geo = {}

result.geo[:distance] = geo_near_result.delete('dis').to_f if geo_near_result['dis']

geo_near_result.each do |key, value|
result.geo[key.snakecase.to_sym] = value
end

if @calculate_fields
distance_from_opts = { :unit => @opts[:unit] || @opts[:distance_multiplier], :spherical => @opts[:spherical] }
@calculate_fields.each do |key|
result.geo["#{key}_distance".to_sym] = result.distance_from(key, center, distance_from_opts)
result.geo[:distance] = result.geo["#{@calculate_primary}_distance".to_sym] if @calculate_primary
end
end

return result
end
end
end
end
12 changes: 6 additions & 6 deletions spec/models/river.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ class River
include Mongoid::Document
include Mongoid::Spacial::Document

field :name, type: String
field :length, type: Integer
field :average_discharge, type: Integer
field :source, type: Array, spacial: true
field :name, :type => String
field :length, :type => Integer
field :average_discharge, :type => Integer
field :source, :type => Array, :spacial => true
# set return_array to true if you do not want a hash returned all the time
field :mouth, type: Array, spacial: {lat: 'latitude', lng: 'longitude'}
field :mouth_array, type: Array, spacial: {return_array: true}
field :mouth, :type => Array, :spacial => {:lat => 'latitude', :lng => 'longitude'}
field :mouth_array, :type => Array, :spacial => {:return_array => true}

# simplified spacial indexing
# you can only index one field in mongodb < 1.9
Expand Down
8 changes: 4 additions & 4 deletions spec/unit/mongoid/criterion/within_spacial_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@
:box =>
{
'Array of Arrays' => [[10,20], [15,25]],
'Array of Hashes' => [{ x: 10, y: 20 }, { x: 15, y: 25 }],
'Hash of Hashes' => { a: { x: 10, y: 20 }, b: { x: 15, y: 25 }}
'Array of Hashes' => [{ :x => 10, :y => 20 }, { :x => 15, :y => 25 }],
'Hash of Hashes' => { :a => { :x => 10, :y => 20 }, :b => { :x => 15, :y => 25 }}
},
:polygon =>
{
'Array of Arrays' => [[10,20], [15,25]],
'Array of Hashes' => [{ x: 10, y: 20 }, { x: 15, y: 25 }],
'Hash of Hashes' => { a: { x: 10, y: 20 }, b: { x: 15, y: 25 }}
'Array of Hashes' => [{ :x => 10, :y => 20 }, { :x => 15, :y => 25 }],
'Hash of Hashes' => { :a => { :x => 10, :y => 20 }, :b => { :x => 15, :y => 25 }}
},
:center =>
{
Expand Down