mongoid_geospatial 2.0.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. data/Gemfile +2 -1
  2. data/README.md +84 -60
  3. data/lib/mongoid_geospatial.rb +3 -7
  4. data/lib/mongoid_geospatial/{geospatial → extensions}/core_ext.rb +0 -0
  5. data/lib/mongoid_geospatial/field_option.rb +3 -4
  6. data/lib/mongoid_geospatial/fields/geometry_field.rb +34 -0
  7. data/lib/mongoid_geospatial/fields/line_string.rb +5 -7
  8. data/lib/mongoid_geospatial/fields/point.rb +18 -33
  9. data/lib/mongoid_geospatial/fields/polygon.rb +6 -14
  10. data/lib/mongoid_geospatial/geospatial.rb +20 -25
  11. data/lib/mongoid_geospatial/version.rb +1 -1
  12. data/lib/mongoid_geospatial/wrappers/georuby.rb +28 -0
  13. data/lib/mongoid_geospatial/wrappers/rgeo.rb +32 -0
  14. data/spec/models/farm.rb +5 -2
  15. data/spec/models/person.rb +4 -14
  16. data/spec/models/phone.rb +1 -3
  17. data/spec/mongoid_geospatial/extensions/core_ext_spec.rb +20 -0
  18. data/spec/mongoid_geospatial/field_option_spec.rb +11 -0
  19. data/spec/mongoid_geospatial/fields/line_string_spec.rb +40 -0
  20. data/spec/mongoid_geospatial/fields/point_spec.rb +136 -10
  21. data/spec/mongoid_geospatial/fields/polygon_spec.rb +75 -0
  22. data/spec/mongoid_geospatial/geospatial_spec.rb +88 -3
  23. data/spec/mongoid_geospatial/wrappers/rgeo_spec.rb +43 -0
  24. data/spec/spec_helper.rb +5 -21
  25. metadata +14 -26
  26. data/lib/mongoid_geospatial/contextual/mongo.rb +0 -118
  27. data/lib/mongoid_geospatial/criteria.rb +0 -10
  28. data/lib/mongoid_geospatial/criterion/complex.rb +0 -26
  29. data/lib/mongoid_geospatial/criterion/inclusion.rb +0 -14
  30. data/lib/mongoid_geospatial/criterion/near_spatial.rb +0 -52
  31. data/lib/mongoid_geospatial/criterion/within_spatial.rb +0 -62
  32. data/lib/mongoid_geospatial/extensions/symbol.rb +0 -46
  33. data/lib/mongoid_geospatial/finders.rb +0 -5
  34. data/lib/mongoid_geospatial/geospatial/geo_near_results.rb +0 -140
  35. data/spec/mongoid_geospatial/contextual/mongo_spec.rb +0 -135
  36. data/spec/mongoid_geospatial/criterion/complex_spec.rb +0 -15
  37. data/spec/mongoid_geospatial/criterion/inclusion_spec.rb +0 -375
  38. data/spec/mongoid_geospatial/criterion/near_spatial_spec.rb +0 -39
  39. data/spec/mongoid_geospatial/criterion/within_spatial_spec.rb +0 -54
  40. data/spec/mongoid_geospatial/geospatial/geo_near_results_spec.rb +0 -78
  41. data/spec/mongoid_geospatial/mongoid_geospatial_spec.rb +0 -83
@@ -1,52 +0,0 @@
1
- # encoding: utf-8
2
- module Mongoid #:nodoc:
3
- module Criterion #:nodoc:
4
-
5
- # NearSpecial criterion is used when performing #near with symbols to get
6
- # get a shorthand syntax for where clauses.
7
- #
8
- # @example Coninputersion of a simple to complex criterion.
9
- # { :field => { "$nearSphere" => [20,30]}, '$maxDistance' => 5 }
10
- # becomes:
11
- # { :field.near_sphere => {:point => [20,30], :max => 5, :unit => :km} }
12
- class NearSpatial < Complex
13
-
14
- # Coninputert input to query for near or nearSphere
15
- #
16
- # @example
17
- # near = NearSpatial.new(:key => :field, :operator => "near")
18
- # near.to_mongo_query({:point => [:50,50], :max => 5, :unit => :km}) => { '$near : [50,50]' , '$maxDistance' : 5 }
19
- #
20
- # @param [Hash,Array] input input to coninputer to query
21
- def to_mongo_query(input)
22
- if input.respond_to?(:x)
23
- {"$#{operator}" => [input.x, input.y]} #, '$maxDistance' => input[1] }
24
- elsif input.kind_of?(Hash)
25
- raise ':point required to make valid query' unless input[:point]
26
- input[:point] = input[:point].to_xy if input[:point].respond_to?(:to_xy)
27
- query = {"$#{operator}" => input[:point] }
28
- if input[:max]
29
- query['$maxDistance'] = input[:max].to_f
30
-
31
- if unit = Mongoid::Geospatial.earth_radius[input[:unit]]
32
- unit *= Mongoid::Geospatial::RAD_PER_DEG unless operator =~ /sphere/i
33
- input[:unit] = unit
34
- end
35
-
36
- query['$maxDistance'] = query['$maxDistance']/input[:unit].to_f if input[:unit]
37
- end
38
- query
39
- elsif input.kind_of? Array
40
- if input.first.kind_of? Numeric
41
- {"$#{operator}" => input }
42
- else
43
- input[0] = input[0].to_xy if input[0].respond_to?(:to_xy)
44
- {"$#{operator}" => input[0], '$maxDistance' => input[1] }
45
- end
46
- end
47
- end
48
-
49
- end
50
- end
51
- end
52
-
@@ -1,62 +0,0 @@
1
- # encoding: utf-8
2
- module Mongoid #:nodoc:
3
- module Criterion #:nodoc:
4
-
5
- # WithinSpecial criterion is used when performing #within with symbols to get
6
- # get a shorthand syntax for where clauses.
7
- #
8
- # @example Conversion of a simple to complex criterion.
9
- # { :field => { "$within" => {'$center' => [20,30]} } }
10
- # becomes:
11
- # { :field.within(:center) => [20,30] }
12
- class WithinSpatial < Complex
13
-
14
- # Convert input to query for box, polygon, center, and centerSphere
15
- #
16
- # @example
17
- # within = WithinSpatial.new(opts[:key] => 'point', :operator => 'center')
18
- # within.to_mongo_query({:point => [20,30], :max => 5, :unit => :km}) #=>
19
- #
20
- # @param [Hash,Array] input Variable to conver to query
21
- def to_mongo_query(input)
22
- if ['box','polygon'].include?(@operator)
23
- input = input.values if input.kind_of?(Hash)
24
- if input.respond_to?(:map)
25
- input.map! do |v|
26
- v.respond_to?(:to_xy) ? v.to_xy : v
27
- end
28
- else
29
- input
30
- end
31
- elsif ['center','centerSphere'].include?(@operator)
32
-
33
- if input.kind_of?(Hash) || input.kind_of?(ActiveSupport::OrderedHash)
34
- raise ':point required to make valid query' unless input[:point]
35
- input[:point] = input[:point].to_xy if input[:point].respond_to?(:to_xy)
36
- if input[:max]
37
- input[:max] = input[:max].to_f
38
-
39
- if unit = Mongoid::Geospatial.earth_radius[input[:unit]]
40
- unit *= Mongoid::Geospatial::RAD_PER_DEG unless operator =~ /sphere/i
41
- input[:unit] = unit
42
- end
43
-
44
- input[:max] = input[:max]/input[:unit].to_f if input[:unit]
45
-
46
- input = [input[:point],input[:max]]
47
- else
48
- input = input[:point]
49
- end
50
- end
51
-
52
- if input.kind_of? Array
53
- input[0] = input[0].to_xy if input[0].respond_to?(:to_xy)
54
- end
55
-
56
- end
57
- {'$within' => {"$#{@operator}"=>input} }
58
- end
59
- end
60
- end
61
- end
62
-
@@ -1,46 +0,0 @@
1
- # encoding: utf-8
2
- module Mongoid #:nodoc:
3
- module Extensions #:nodoc:
4
- module Symbol #:nodoc:
5
-
6
- # return a class that will accept a value to convert the query correctly for near
7
- #
8
- # @param [Symbol] calc This accepts :sphere
9
- #
10
- # @return [Criterion::NearSpatial]
11
-
12
- def near(calc = :flat)
13
- Criterion::NearSpatial.new(:operator => get_op('near',calc), :key => self)
14
- end
15
-
16
- # alias for self.near(:sphere)
17
- #
18
- # @return [Criterion::NearSpatial]
19
- def near_sphere
20
- self.near(:sphere)
21
- end
22
-
23
- # @param [Symbol] shape :box,:polygon,:center,:center_sphere
24
- #
25
- # @return [Criterion::WithinSpatial]
26
- def within(shape)
27
- shape = get_op(:center,:sphere) if shape == :center_sphere
28
- Criterion::WithinSpatial.new(:operator => shape.to_s , :key => self)
29
- end
30
-
31
- private
32
-
33
- def get_op operator, calc
34
- if calc.to_sym == :sphere
35
- "#{operator}Sphere"
36
- else
37
- operator.to_s
38
- end
39
- end
40
-
41
- end
42
- end
43
- end
44
-
45
-
46
- ::Symbol.__send__(:include, Mongoid::Extensions::Symbol)
@@ -1,5 +0,0 @@
1
- module Mongoid #:nodoc:
2
- module Finders
3
- delegate :geo_near, :to => :criteria
4
- end
5
- end
@@ -1,140 +0,0 @@
1
- module Mongoid
2
- module Geospatial
3
- class GeoNearResults < Array
4
- attr_reader :stats, :document, :_original_array, :_original_opts
5
- attr_accessor :opts
6
-
7
- def initialize(document,results,opts = {})
8
- raise "#{document.name} class must include Mongoid::Geospatial::Document" unless document.respond_to?(:spatial_fields_indexed)
9
- @document = document
10
- @opts = opts
11
- @_original_opts = opts.clone
12
- @stats = results['stats'] || {}
13
- @opts[:skip] ||= 0
14
-
15
- @_original_array = results['results'].collect do |result|
16
- res = Mongoid::Factory.from_db(@document, result.delete('obj'))
17
- res.geo = {}
18
- # camel case is awkward in ruby when using variables...
19
- if result['dis']
20
- res.geo[:distance] = result.delete('dis').to_f
21
- end
22
- result.each do |key,value|
23
- res.geo[key.snakecase.to_sym] = value
24
- end
25
- # dist_options[:formula] = opts[:formula] if opts[:formula]
26
- @opts[:calculate] = @document.spatial_fields_indexed if @document.spatial_fields_indexed.kind_of?(Array) && @opts[:calculate] == true
27
- if @opts[:calculate]
28
- @opts[:calculate] = [@opts[:calculate]] unless @opts[:calculate].kind_of? Array
29
- @opts[:calculate] = @opts[:calculate].map(&:to_sym) & geo_fields
30
- if @document.spatial_fields_indexed.kind_of?(Array) && @document.spatial_fields_indexed.size == 1
31
- primary = @document.spatial_fields_indexed.first
32
- end
33
- @opts[:calculate].each do |key|
34
- res.geo[(key.to_s+'_distance').to_sym] = res.distance_from(key,center,{:unit =>@opts[:unit] || @opts[:distance_multiplier], :spherical => @opts[:spherical]} )
35
- res.geo[:distance] = res.geo[key] if primary && key == primary
36
- end
37
- end
38
- res
39
- end
40
- if @opts[:page]
41
- start = (@opts[:page]-1)*@opts[:per_page] # assuming current_page is 1 based.
42
- @_paginated_array = @_original_array.clone
43
- super(@_paginated_array[@opts[:skip]+start, @opts[:per_page]] || [])
44
- else
45
- super(@_original_array[@opts[:skip]..-1] || [])
46
- end
47
- end
48
-
49
- def page(*args)
50
- new_collection = self.clone
51
- new_collection.page!(*args)
52
- new_collection
53
- end
54
-
55
- def page!(page, options = {})
56
- original = options.delete(:original)
57
- self.opts.merge!(options)
58
- self.opts[:paginator] ||= Mongoid::Geospatial.paginator
59
- self.opts[:page] = page
60
- start = (self.current_page-1)*self.limit_value # assuming current_page is 1 based.
61
-
62
- if original
63
- @_paginated_array = @_original_array.clone
64
- self.replace(@_paginated_array[self.opts[:skip]+start, self.limit_value] || [])
65
- else
66
- @_paginated_array ||= self.to_a
67
- self.replace(@_paginated_array[self.opts[:skip]+start, self.limit_value])
68
- end
69
- true
70
- end
71
-
72
- def per(num)
73
- self.page(current_page, :per_page => num)
74
- end
75
-
76
- def reset!
77
- self.replace(@_original_array)
78
- @opts = @_original_opts
79
- @_paginated_array = nil
80
- true
81
- end
82
-
83
- def reset
84
- clone = self.clone
85
- clone.reset!
86
- clone
87
- end
88
-
89
- def total_entries
90
- (@_paginated_array) ? @_paginated_array.count : @_original_array.count
91
- end
92
- alias_method :total_count, :total_entries
93
-
94
- def current_page
95
- page = (@opts[:page]) ? @opts[:page].to_i.abs : 1
96
- (page < 1) ? 1 : page
97
- end
98
-
99
- def limit_value
100
- if @opts[:per_page]
101
- @opts[:per_page] = @opts[:per_page].to_i.abs
102
- else
103
- @opts[:per_page] = case self.opts[:paginator]
104
- when :will_paginate
105
- @document.per_page
106
- when :kaminari
107
- Kaminari.config.default_per_page
108
- else
109
- Mongoid::Geospatial.default_per_page
110
- end
111
- end
112
- end
113
- alias_method :per_page, :limit_value
114
-
115
- def num_pages
116
- (total_entries && @opts[:per_page]) ? (total_entries.to_f / @opts[:per_page]).ceil : nil
117
- end
118
- alias_method :total_pages, :num_pages
119
-
120
- def out_of_bounds?
121
- self.current_page > self.total_pages
122
- end
123
-
124
- def offset
125
- (self.current_page - 1) * self.per_page
126
- end
127
-
128
- # current_page - 1 or nil if there is no previous page
129
- def previous_page
130
- self.current_page > 1 ? (self.current_page - 1) : nil
131
- end
132
-
133
- # current_page + 1 or nil if there is no next page
134
- def next_page
135
- self.current_page < self.total_pages ? (self.current_page + 1) : nil
136
- end
137
-
138
- end
139
- end
140
- end
@@ -1,135 +0,0 @@
1
- require "spec_helper"
2
-
3
- describe (Mongoid::VERSION > '3' ? Mongoid::Contextual::Mongo : Mongoid::Contexts::Mongo) do
4
- describe "#geo_near" do
5
-
6
- before do
7
- Bar.delete_all
8
- Bar.create_indexes
9
- end
10
-
11
- let!(:jfk) do
12
- Bar.create(:name => 'jfk', :location => [-73.77694444, 40.63861111 ])
13
- end
14
-
15
- let!(:lax) do
16
- Bar.create(:name => 'lax', :location => [-118.40, 33.94])
17
- end
18
-
19
- it "should work with specifying specific center and different location attribute on collction" do
20
- Bar.geo_near(lax.location, :spherical => true).should == [lax, jfk]
21
- Bar.geo_near(jfk.location, :spherical => true).should == [jfk, lax]
22
- end
23
- context 'option' do
24
- context ':num' do
25
- it "should limit number of results to 1" do
26
- Bar.geo_near(jfk.location, :num => 1).size.should == 1
27
- end
28
- end
29
-
30
- context ':maxDistance' do
31
- it "should get 1 item" do
32
- Bar.geo_near(lax.location, :spherical => true, :max_distance => 2465/Mongoid::Geospatial.earth_radius[:mi]).size.should == 1
33
- end
34
- it "should get 2 items" do
35
- Bar.geo_near(lax.location, :spherical => true, :max_distance => 2480/Mongoid::Geospatial.earth_radius[:mi]).size.should == 2
36
- end
37
-
38
- end
39
-
40
- context ':distance_multiplier' do
41
- it "should multiply returned distance with multiplier" do
42
- Bar.geo_near(lax.location, :spherical => true, :distance_multiplier=> Mongoid::Geospatial.earth_radius[:mi]).second.geo[:distance].to_i.should be_within(1).of(2469)
43
- end
44
- end
45
-
46
- context ':unit' do
47
- it "should multiply returned distance with multiplier" do
48
- pending
49
- Bar.geo_near(lax.location, :spherical => true, :unit => :mi).second.geo[:distance].to_i.should be_within(1).of(2469)
50
- end
51
-
52
- it "should convert max_distance to radians with unit" do
53
- Bar.geo_near(lax.location, :spherical => true, :max_distance => 2465, :unit => :mi).size.should == 1
54
- end
55
-
56
- end
57
-
58
- context ':query' do
59
- it "should filter using extra query option" do
60
- # two record in the collection, only one's name is Munich
61
- Bar.geo_near(jfk.location, :query => {:name => jfk.name}).should == [jfk]
62
- end
63
- end
64
-
65
- end
66
-
67
- context 'criteria chaining' do
68
- it "should filter by where" do
69
- Bar.where(:name => jfk.name).geo_near(jfk.location).should == [jfk]
70
- Bar.any_of({:name => jfk.name},{:name => lax.name}).geo_near(jfk.location).should == [jfk,lax]
71
- end
72
-
73
- it 'should skip 1' do
74
- Bar.skip(1).geo_near(jfk.location).size.should == 1
75
- end
76
-
77
- it 'should limit 1' do
78
- Bar.limit(1).geo_near(jfk.location).size.should == 1
79
- end
80
- end
81
- end
82
-
83
- context ':page' do
84
- before do
85
- Bar.delete_all
86
- Bar.create_indexes
87
-
88
- 50.times do
89
- Bar.create({:location => [rand(360)-180,rand(360)-180]})
90
- end
91
- end
92
-
93
- context ":paginator :array" do
94
- [nil,1,2].each do |page|
95
- it "page=#{page} should have 25" do
96
- Bar.geo_near([1,1], :page => page).size.should == 25
97
- end
98
- end
99
-
100
- it "page=3 should have 0" do
101
- Bar.geo_near([1,1], :page => 20).size.should == 0
102
- end
103
-
104
- it "per_page=5" do
105
- Bar.geo_near([1,1], :page => 1, :per_page => 5).size.should == 5
106
- end
107
- end
108
-
109
- context ":paginator :kaminari" do
110
- let(:near) {Bar.geo_near([1,1], :page => 1)}
111
-
112
- it 'should have 50 Bars' do
113
- Bar.all.count.should == 50
114
- end
115
-
116
- it "should have limit_value" do
117
- near.limit_value.should == 25
118
- end
119
-
120
- # check results['results'] in GeoNearResults
121
- it 'should find 25 items' do
122
- near.size.should == 25
123
- end
124
-
125
- it "should have current_page" do
126
- near.current_page.should == 1
127
- end
128
-
129
- it "should have num_pages" do
130
- near.num_pages.should == 1
131
- end
132
- end
133
- end
134
-
135
- end