Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix for issue #5 #15

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
33 changes: 17 additions & 16 deletions lib/mongoid_spacial/contexts/mongo.rb
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
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
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
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
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
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