From 95aaf5b22ceef9d5035d2c5ab3a5deea0818faa9 Mon Sep 17 00:00:00 2001 From: Schnittchen Date: Thu, 1 Dec 2011 14:35:46 +0100 Subject: [PATCH 01/10] Use BSON::OrderedHash for query to satisfy the mongo ruby driver on 1.8.7 --- lib/mongoid_spacial/contexts/mongo.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/mongoid_spacial/contexts/mongo.rb b/lib/mongoid_spacial/contexts/mongo.rb index c098157..d9eda5e 100644 --- a/lib/mongoid_spacial/contexts/mongo.rb +++ b/lib/mongoid_spacial/contexts/mongo.rb @@ -74,11 +74,11 @@ def geo_near(center, opts = {}) private 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] From 4c0886fdb6d85f9dc9ab3687dcb98495069414dc Mon Sep 17 00:00:00 2001 From: Thomas Stratmann Date: Thu, 1 Dec 2011 18:55:50 +0100 Subject: [PATCH 02/10] some modifications needed for ruby 1.8 --- lib/mongoid_spacial/spacial/document.rb | 4 ++-- spec/models/river.rb | 12 ++++++------ spec/unit/mongoid/criterion/within_spacial_spec.rb | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/mongoid_spacial/spacial/document.rb b/lib/mongoid_spacial/spacial/document.rb index 354491e..1b0d7eb 100644 --- a/lib/mongoid_spacial/spacial/document.rb +++ b/lib/mongoid_spacial/spacial/document.rb @@ -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: diff --git a/spec/models/river.rb b/spec/models/river.rb index d208571..edbe43b 100644 --- a/spec/models/river.rb +++ b/spec/models/river.rb @@ -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 diff --git a/spec/unit/mongoid/criterion/within_spacial_spec.rb b/spec/unit/mongoid/criterion/within_spacial_spec.rb index bc00f8e..2eccee7 100644 --- a/spec/unit/mongoid/criterion/within_spacial_spec.rb +++ b/spec/unit/mongoid/criterion/within_spacial_spec.rb @@ -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 => { From 63ce4cf2254a5426523afc20201b471cf9301cd6 Mon Sep 17 00:00:00 2001 From: Thomas Stratmann Date: Fri, 2 Dec 2011 15:47:15 +0100 Subject: [PATCH 03/10] fix a metaprogramming bug that only surfaces on ruby 1.8 --- lib/mongoid_spacial/field_option.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/mongoid_spacial/field_option.rb b/lib/mongoid_spacial/field_option.rb index 062e640..14669f6 100644 --- a/lib/mongoid_spacial/field_option.rb +++ b/lib/mongoid_spacial/field_option.rb @@ -21,13 +21,14 @@ output end + __field = field define_method "#{field.name}=" do |arg| if arg.kind_of?(Hash) && arg[lng_meth] && arg[lat_meth] arg = [arg[lng_meth].to_f, arg[lat_meth].to_f] 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]} From 32b3a467c5212e00fb0a0c196090481d4d579ef6 Mon Sep 17 00:00:00 2001 From: Thomas Stratmann Date: Fri, 2 Dec 2011 16:03:45 +0100 Subject: [PATCH 04/10] same bug, few lines before --- lib/mongoid_spacial/field_option.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/mongoid_spacial/field_option.rb b/lib/mongoid_spacial/field_option.rb index 14669f6..5975082 100644 --- a/lib/mongoid_spacial/field_option.rb +++ b/lib/mongoid_spacial/field_option.rb @@ -14,14 +14,15 @@ 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 end - __field = field define_method "#{field.name}=" do |arg| if arg.kind_of?(Hash) && arg[lng_meth] && arg[lat_meth] arg = [arg[lng_meth].to_f, arg[lat_meth].to_f] From 81257b8924319e38084fa0858d2bfde4db90ec0f Mon Sep 17 00:00:00 2001 From: Thomas Stratmann Date: Fri, 2 Dec 2011 17:03:40 +0100 Subject: [PATCH 05/10] GeoNearResults: implement sort_by! if not on ruby 1.9.2 --- lib/mongoid_spacial/spacial/geo_near_results.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/mongoid_spacial/spacial/geo_near_results.rb b/lib/mongoid_spacial/spacial/geo_near_results.rb index 722fec8..9c49133 100644 --- a/lib/mongoid_spacial/spacial/geo_near_results.rb +++ b/lib/mongoid_spacial/spacial/geo_near_results.rb @@ -135,6 +135,11 @@ 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 end end end From b4038e3819d617151bbec8c3566dc23a331700b5 Mon Sep 17 00:00:00 2001 From: Thomas Stratmann Date: Fri, 2 Dec 2011 19:06:47 +0100 Subject: [PATCH 06/10] some refactoring --- .../spacial/geo_near_results.rb | 55 +++++++++++-------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/lib/mongoid_spacial/spacial/geo_near_results.rb b/lib/mongoid_spacial/spacial/geo_near_results.rb index 9c49133..803055d 100644 --- a/lib/mongoid_spacial/spacial/geo_near_results.rb +++ b/lib/mongoid_spacial/spacial/geo_near_results.rb @@ -12,31 +12,15 @@ 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 + @opts[:calculate] = @document.spacial_fields_indexed if @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 + @opts[:primary] = @document.spacial_fields_indexed.first if @document.spacial_fields_indexed.size == 1 end + + @_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 @@ -140,6 +124,29 @@ def sort_by! replace sort_by { |obj| yield obj } end end + + private + + 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 @opts[:calculate] + distance_from_opts = { :unit => @opts[:unit] || @opts[:distance_multiplier], :spherical => @opts[:spherical] } + @opts[:calculate].each do |key| + result.geo["#{key}_distance".to_sym] = result.distance_from(key, center, distance_from_opts) + result.geo[:distance] = result.geo["#{@opts[:primary]}_distance".to_sym] if @opts[:primary] + end + end + + return result + end end end end From 180ee8a73af3d17232e90ec73627b0710fd48587 Mon Sep 17 00:00:00 2001 From: Thomas Stratmann Date: Mon, 5 Dec 2011 12:09:50 +0100 Subject: [PATCH 07/10] factor distance calculation prep out, do not pollute opts hash --- .../spacial/geo_near_results.rb | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/lib/mongoid_spacial/spacial/geo_near_results.rb b/lib/mongoid_spacial/spacial/geo_near_results.rb index 803055d..ad1572f 100644 --- a/lib/mongoid_spacial/spacial/geo_near_results.rb +++ b/lib/mongoid_spacial/spacial/geo_near_results.rb @@ -12,12 +12,7 @@ def initialize(document,results,opts = {}) @stats = results['stats'] || {} @opts[:skip] ||= 0 - @opts[:calculate] = @document.spacial_fields_indexed if @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 - @opts[:primary] = @document.spacial_fields_indexed.first if @document.spacial_fields_indexed.size == 1 - end + prepare_distance_calculation @_original_array = results['results'].collect { |result| fabricate_result result } @@ -126,6 +121,17 @@ def sort_by! 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')) @@ -137,11 +143,11 @@ def fabricate_result(geo_near_result) result.geo[key.snakecase.to_sym] = value end - if @opts[:calculate] + if @calculate_fields distance_from_opts = { :unit => @opts[:unit] || @opts[:distance_multiplier], :spherical => @opts[:spherical] } - @opts[:calculate].each do |key| + @calculate_fields.each do |key| result.geo["#{key}_distance".to_sym] = result.distance_from(key, center, distance_from_opts) - result.geo[:distance] = result.geo["#{@opts[:primary]}_distance".to_sym] if @opts[:primary] + result.geo[:distance] = result.geo["#{@calculate_primary}_distance".to_sym] if @calculate_primary end end From b6f28ce7b29c431ce50e5741cf325317d97731ac Mon Sep 17 00:00:00 2001 From: Thomas Stratmann Date: Mon, 5 Dec 2011 12:58:43 +0100 Subject: [PATCH 08/10] refactor pagination options --- lib/mongoid_spacial/contexts/mongo.rb | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/lib/mongoid_spacial/contexts/mongo.rb b/lib/mongoid_spacial/contexts/mongo.rb index d9eda5e..f94d0ed 100644 --- a/lib/mongoid_spacial/contexts/mongo.rb +++ b/lib/mongoid_spacial/contexts/mongo.rb @@ -53,19 +53,9 @@ 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) @@ -73,6 +63,17 @@ def geo_near(center, opts = {}) 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 From 3242250e327468d95022365bdc2413eaf8b99b2d Mon Sep 17 00:00:00 2001 From: Thomas Stratmann Date: Tue, 6 Dec 2011 16:47:20 +0100 Subject: [PATCH 09/10] fix num_pages calculation --- lib/mongoid_spacial/spacial/geo_near_results.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/mongoid_spacial/spacial/geo_near_results.rb b/lib/mongoid_spacial/spacial/geo_near_results.rb index ad1572f..4f09806 100644 --- a/lib/mongoid_spacial/spacial/geo_near_results.rb +++ b/lib/mongoid_spacial/spacial/geo_near_results.rb @@ -92,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 From f5666d2d4de501a6dab99e16a610009d8495f164 Mon Sep 17 00:00:00 2001 From: Thomas Stratmann Date: Thu, 12 Jan 2012 19:16:17 +0100 Subject: [PATCH 10/10] GeoNearResults: avoid replacing @_paginated_array with nil ..just as a few lines above. Have actually been hit by this bug. --- lib/mongoid_spacial/spacial/geo_near_results.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mongoid_spacial/spacial/geo_near_results.rb b/lib/mongoid_spacial/spacial/geo_near_results.rb index 4f09806..ca5bc25 100644 --- a/lib/mongoid_spacial/spacial/geo_near_results.rb +++ b/lib/mongoid_spacial/spacial/geo_near_results.rb @@ -43,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