mongoid_geo 0.1.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.
- data/MIT-LICENSE +20 -0
- data/README.textile +123 -0
- data/Rakefile +25 -0
- data/lib/mongoid/geo/criteria.rb +6 -0
- data/lib/mongoid/geo/criteria_helpers.rb +29 -0
- data/lib/mongoid/geo/criterion/complex.rb +19 -0
- data/lib/mongoid/geo/criterion/inclusion.rb +19 -0
- data/lib/mongoid/geo/criterion/outer_operator.rb +61 -0
- data/lib/mongoid/geo/criterion/twin_operators.rb +39 -0
- data/lib/mongoid/geo/fields.rb +23 -0
- data/lib/mongoid/geo/inflections.rb +39 -0
- data/lib/mongoid/geo.rb +2 -0
- data/lib/mongoid_geo.rb +1 -0
- metadata +80 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2011 YOURNAME
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.textile
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
h1. Mongoid geo
|
2
|
+
|
3
|
+
A Geo extension for Mongoid.
|
4
|
+
|
5
|
+
"MongoDB Geospatial Indexing":http://www.mongodb.org/display/DOCS/Geospatial+Indexing
|
6
|
+
|
7
|
+
* Supports Mongoid 1.7 sphere distance calculations and
|
8
|
+
* Adds a set of geo related inflections
|
9
|
+
* Adds nearSphere inclusion method
|
10
|
+
|
11
|
+
h2. Status
|
12
|
+
|
13
|
+
This gem has so far just the initial design and has not been tested. Feel free to help in the effort. Thanks!
|
14
|
+
|
15
|
+
h2. Fields
|
16
|
+
|
17
|
+
When setting a geo-location array, the setter should try to convert the value to an array of floats
|
18
|
+
|
19
|
+
Old/Manual style:
|
20
|
+
|
21
|
+
<pre>
|
22
|
+
class Person
|
23
|
+
field :locations, :type => Array
|
24
|
+
|
25
|
+
def locations= args
|
26
|
+
@locations = args.kind_of?(String) ? args.split(",").map(&:to_f) : args
|
27
|
+
end
|
28
|
+
end
|
29
|
+
</pre>
|
30
|
+
|
31
|
+
With _mongoid-geo_, becomes:
|
32
|
+
|
33
|
+
<pre>
|
34
|
+
class Person
|
35
|
+
field :locations, :type => Array, :geo => true
|
36
|
+
end
|
37
|
+
|
38
|
+
p = Person.new
|
39
|
+
# set via String or Strings
|
40
|
+
p.locations = "45.1, -3.4"
|
41
|
+
p.locations = "45.1", "-3.4"
|
42
|
+
|
43
|
+
assert([45.1, -3.4], p.locations)
|
44
|
+
</pre>
|
45
|
+
|
46
|
+
h2. Extra Geo Inclusions
|
47
|
+
|
48
|
+
Find all addresses near a point using spherical distance calculation
|
49
|
+
|
50
|
+
h3. nearSphere
|
51
|
+
|
52
|
+
<pre>
|
53
|
+
base.where(:locations.nearSphere => [ 72, -44 ])
|
54
|
+
# => :locations => { "$nearSphere" : [ 72, -44 ] }
|
55
|
+
</pre>
|
56
|
+
|
57
|
+
h2. Extra Geo Inflections
|
58
|
+
|
59
|
+
h3. nearMax
|
60
|
+
|
61
|
+
Find all addresses near a point using spherical distance calculation
|
62
|
+
|
63
|
+
<pre>
|
64
|
+
base.where(:locations.nearMax => [[ 72, -44 ], 5])
|
65
|
+
# => { $near: [50, 40] , $maxDistance: 3 }
|
66
|
+
|
67
|
+
base.where(:locations.nearMax(:sphere) => [[ 72, -44 ], 5])
|
68
|
+
# => { $nearSphere: [50, 40] , $maxDistanceSphere: 3 }
|
69
|
+
</pre>
|
70
|
+
|
71
|
+
h3. withinBox
|
72
|
+
|
73
|
+
<pre>
|
74
|
+
box = [[50, 40], [30,55]]
|
75
|
+
base.where(:locations.withinBox => box)
|
76
|
+
# => locations: {"$within" : {"$box" : box}
|
77
|
+
|
78
|
+
base.where(:locations.withinBox(:sphere) => box)
|
79
|
+
# => locations: {"$within" : {"$boxSphere" : box}
|
80
|
+
</pre>
|
81
|
+
|
82
|
+
You can also use a Hash to define the box
|
83
|
+
|
84
|
+
<pre>
|
85
|
+
places.where(:location.withinBox => {:lower_left => [50, 40], :upper_right => [30,55]})
|
86
|
+
</pre>
|
87
|
+
|
88
|
+
Or use an Object (which must have the methods #lower_left and #upper_right that return the points of the bounding box)
|
89
|
+
|
90
|
+
<pre>
|
91
|
+
box = (Struct.new :lower_left, :upper_right).new
|
92
|
+
box.lower_left = [50, 40]
|
93
|
+
box.upper_right = [30,55]
|
94
|
+
places.where(:location.withinBox => box)
|
95
|
+
</pre>
|
96
|
+
|
97
|
+
h3. withinCenter
|
98
|
+
|
99
|
+
<pre>
|
100
|
+
center = [50, 40]
|
101
|
+
radius = 4
|
102
|
+
|
103
|
+
# places: {"$within" : {"$centerSphere" : [center, radius]}
|
104
|
+
places.where(:location.withinCenter(:sphere) => [center, radius])
|
105
|
+
|
106
|
+
# places: {"$within" : {"$center" : [center, radius]}
|
107
|
+
places.where(:location.withinCenter => [center, radius])
|
108
|
+
</pre>
|
109
|
+
|
110
|
+
You can also use a Hash to define the box
|
111
|
+
|
112
|
+
<pre>
|
113
|
+
places.where(:location.withinCenter => {:center => [50, 40], :radius => 4})
|
114
|
+
</pre>
|
115
|
+
|
116
|
+
Or use an Object (which must have the methods #lower_left and #upper_right that return the points of the bounding box)
|
117
|
+
|
118
|
+
<pre>
|
119
|
+
circle = (Struct.new :center, :radius).new
|
120
|
+
circle.center = [50, 40]
|
121
|
+
circle.radius = 4
|
122
|
+
places.where(:location.withinCenter => circle)
|
123
|
+
</pre>
|
data/Rakefile
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'rubygems'
|
3
|
+
begin
|
4
|
+
require 'bundler/setup'
|
5
|
+
rescue LoadError
|
6
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'rake'
|
10
|
+
require 'rake/rdoctask'
|
11
|
+
|
12
|
+
require 'rspec/core'
|
13
|
+
require 'rspec/core/rake_task'
|
14
|
+
|
15
|
+
RSpec::Core::RakeTask.new(:spec)
|
16
|
+
|
17
|
+
task :default => :spec
|
18
|
+
|
19
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
20
|
+
rdoc.rdoc_dir = 'rdoc'
|
21
|
+
rdoc.title = 'Mongoid Geo'
|
22
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
23
|
+
rdoc.rdoc_files.include('README.rdoc')
|
24
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
25
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Mongoid #:nodoc:
|
3
|
+
module Extensions #:nodoc:
|
4
|
+
module Hash #:nodoc:
|
5
|
+
module CriteriaHelpers #:nodoc:
|
6
|
+
def expand_complex_criteria
|
7
|
+
hsh = {}
|
8
|
+
each_pair do |k,v|
|
9
|
+
case k
|
10
|
+
when Mongoid::Criterion::Complex
|
11
|
+
hsh[k.key] ||= {}
|
12
|
+
hsh[k.key].merge!(k.make_hash(v))
|
13
|
+
when Mongoid::Criterion::OuterOperator
|
14
|
+
hsh[k.key] ||= {}
|
15
|
+
hsh[k.key].merge!(k.make_hash(v))
|
16
|
+
when Mongoid::Criterion::TwinOperators
|
17
|
+
raise "TwinOperators expects an array with a value for each of the twin operators" if !v.kind_of?(Array) && !v.size == 2
|
18
|
+
hsh[k.key] ||= {}
|
19
|
+
hsh[k.key].merge!(k.make_hash(v))
|
20
|
+
else
|
21
|
+
hsh[k] = v
|
22
|
+
end
|
23
|
+
end
|
24
|
+
hsh
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Mongoid #:nodoc:
|
3
|
+
module Criterion #:nodoc:
|
4
|
+
# Complex criterion are used when performing operations on symbols to get
|
5
|
+
# get a shorthand syntax for where clauses.
|
6
|
+
#
|
7
|
+
# Example:
|
8
|
+
#
|
9
|
+
# <tt>{ :field => { "$lt" => "value" } }</tt>
|
10
|
+
# becomes:
|
11
|
+
# <tt> { :field.lt => "value }</tt>
|
12
|
+
class Complex
|
13
|
+
|
14
|
+
def make_hash v
|
15
|
+
{"$#{operator}" => v}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Mongoid #:nodoc:
|
3
|
+
module Criterion #:nodoc:
|
4
|
+
module Inclusion
|
5
|
+
# Adds a criterion to the +Criteria+ that specifies values to do
|
6
|
+
# geospacial searches by. The field must be indexed with the "2d" option.
|
7
|
+
#
|
8
|
+
# @example Adding the criterion.
|
9
|
+
# criteria.near(:field1 => [30, -44])
|
10
|
+
#
|
11
|
+
# @param [ Hash ] attributes The fields with lat/long values.
|
12
|
+
#
|
13
|
+
# @return [ Criteria ] A new criteria with the added selector.
|
14
|
+
def nearSphere(attributes = {})
|
15
|
+
update_selector(attributes, "$nearSphere")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Mongoid #:nodoc:
|
3
|
+
module Criterion #:nodoc:
|
4
|
+
# Complex criterion are used when performing operations on symbols to get
|
5
|
+
# get a shorthand syntax for where clauses.
|
6
|
+
#
|
7
|
+
# Example:
|
8
|
+
|
9
|
+
# {:outer_operator => 'within', :operator => 'center' }
|
10
|
+
# { :location => { "$within" => { "$center" => [ [ 50, -40 ], 1 ] } } }
|
11
|
+
class OuterOperator
|
12
|
+
attr_accessor :key, :outer_op, :operator
|
13
|
+
|
14
|
+
# Create the new complex criterion.
|
15
|
+
def initialize(opts = {})
|
16
|
+
@key = opts[:key]
|
17
|
+
@operator = opts[:operator]
|
18
|
+
@outer_op = opts[:outer_op]
|
19
|
+
end
|
20
|
+
|
21
|
+
def make_hash v
|
22
|
+
v = extract_box(v) if !v.kind_of?(Array) && operator =~ /box/
|
23
|
+
v = extract_circle(v) if !v.kind_of?(Array) && operator =~ /center/
|
24
|
+
{"$#{outer_op}" => {"$#{operator}" => v } }
|
25
|
+
end
|
26
|
+
|
27
|
+
def hash
|
28
|
+
[@outer_op, [@operator, @key]].hash
|
29
|
+
end
|
30
|
+
|
31
|
+
def eql?(other)
|
32
|
+
self == (other)
|
33
|
+
end
|
34
|
+
|
35
|
+
def ==(other)
|
36
|
+
return false unless other.is_a?(self.class)
|
37
|
+
self.outer_op == other.outer_op && self.key == other.key && self.operator == other.operator
|
38
|
+
end
|
39
|
+
|
40
|
+
protected
|
41
|
+
|
42
|
+
def extract_circle(v)
|
43
|
+
case v
|
44
|
+
when Hash
|
45
|
+
[v[:center], v[:radius]]
|
46
|
+
else
|
47
|
+
v.respond_to?(:center) ? [v.center, v.radius] : raise("Can't extract box from: #{v}, must have :center and :radius methods or hash keys")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def extract_box v
|
52
|
+
case v
|
53
|
+
when Hash
|
54
|
+
[v[:lower_left], v[:upper_right]]
|
55
|
+
else
|
56
|
+
v.respond_to?(:lower_left) ? [v.lower_left, v.upper_right] : raise("Can't extract box from: #{v}, must have :lower_left and :upper_right methods or hash keys")
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Mongoid #:nodoc:
|
3
|
+
module Criterion #:nodoc:
|
4
|
+
# Complex criterion are used when performing operations on symbols to get
|
5
|
+
# get a shorthand syntax for where clauses.
|
6
|
+
#
|
7
|
+
# Example:
|
8
|
+
|
9
|
+
# {:opA => 'near', :opB => 'maxDistance' }
|
10
|
+
# :location => { $near => [50,50], $maxDistance => 5 }
|
11
|
+
class TwinOperators
|
12
|
+
attr_accessor :key, :op_a, :op_b
|
13
|
+
|
14
|
+
# Create the new complex criterion.
|
15
|
+
def initialize(opts = {})
|
16
|
+
@key = opts[:key]
|
17
|
+
@op_a = opts[:op_a]
|
18
|
+
@op_b = opts[:op_b]
|
19
|
+
end
|
20
|
+
|
21
|
+
def make_hash v
|
22
|
+
{"$#{op_a}" => v.first, "$#{op_b}" => v.last }
|
23
|
+
end
|
24
|
+
|
25
|
+
def hash
|
26
|
+
[@op_a, @op_b, @key].hash
|
27
|
+
end
|
28
|
+
|
29
|
+
def eql?(other)
|
30
|
+
self == (other)
|
31
|
+
end
|
32
|
+
|
33
|
+
def ==(other)
|
34
|
+
return false unless other.is_a?(self.class)
|
35
|
+
self.op_a == other.op_a && self.op_b == other.op_b && self.key == other.key
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Mongoid #:nodoc
|
2
|
+
# This module defines behaviour for fields.
|
3
|
+
module Fields
|
4
|
+
module ClassMethods #:nodoc
|
5
|
+
def create_accessors(name, meth, options = {})
|
6
|
+
generated_field_methods.module_eval do
|
7
|
+
define_method(meth) { read_attribute(name) }
|
8
|
+
options
|
9
|
+
define_method("#{meth}=") do |value|
|
10
|
+
value = if options[:type] == Array && options[:geo]
|
11
|
+
value.kind_of?(String) ? value.split(",") : value
|
12
|
+
end.map(&:to_f)
|
13
|
+
write_attribute(name, value)
|
14
|
+
end
|
15
|
+
define_method("#{meth}?") do
|
16
|
+
attr = read_attribute(name)
|
17
|
+
(options[:type] == Boolean) ? attr == true : attr.present?
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Mongoid #:nodoc:
|
3
|
+
module Extensions #:nodoc:
|
4
|
+
module Symbol #:nodoc:
|
5
|
+
module Inflections #:nodoc:
|
6
|
+
|
7
|
+
# $nearSphere $centerSphere
|
8
|
+
# nearMax
|
9
|
+
# - { $near : [50,50] , $maxDistance : 5 }
|
10
|
+
# withinBox
|
11
|
+
# - {"$within" : {"$box" : box}
|
12
|
+
# withinCenter
|
13
|
+
# - {"$within" : {"$center" : [center, radius]}}})
|
14
|
+
|
15
|
+
def nearSphere
|
16
|
+
Criterion::Complex.new(:operator => 'nearSphere', :key => self)
|
17
|
+
end
|
18
|
+
|
19
|
+
def nearMax calc = :flat
|
20
|
+
Criterion::TwinOperators.new(:op_a => get_op(calc, 'near'), :op_b => get_op(calc, 'maxDistance'), :key => self)
|
21
|
+
end
|
22
|
+
|
23
|
+
def withinBox calc = :flat
|
24
|
+
Criterion::OuterOperator.new(:outer_op => 'within', :operator => get_op(calc, 'box'), :key => self)
|
25
|
+
end
|
26
|
+
|
27
|
+
def withinCenter calc = :flat
|
28
|
+
Criterion::OuterOperator.new(:outer_op => 'within', :operator => get_op(calc, 'center'), :key => self)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def get_op calc, operator
|
34
|
+
calc.to_s == 'sphere' ? "#{operator}Sphere" : operator
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/mongoid/geo.rb
ADDED
data/lib/mongoid_geo.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'mongoid/geo'
|
metadata
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mongoid_geo
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease: !!null
|
6
|
+
platform: ruby
|
7
|
+
authors: []
|
8
|
+
autorequire: !!null
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2011-02-05 00:00:00.000000000 +01:00
|
12
|
+
default_executable: !!null
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: mongoid
|
16
|
+
requirement: &2159951560 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 2.0.0.rc.6
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *2159951560
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: bson_ext
|
27
|
+
requirement: &2159950420 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 1.1.6
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *2159950420
|
36
|
+
description: Geo spatial extension on Mongoid 2, to add more geo-spatial capabilities
|
37
|
+
email: !!null
|
38
|
+
executables: []
|
39
|
+
extensions: []
|
40
|
+
extra_rdoc_files: []
|
41
|
+
files:
|
42
|
+
- lib/mongoid/geo/criteria.rb
|
43
|
+
- lib/mongoid/geo/criteria_helpers.rb
|
44
|
+
- lib/mongoid/geo/criterion/complex.rb
|
45
|
+
- lib/mongoid/geo/criterion/inclusion.rb
|
46
|
+
- lib/mongoid/geo/criterion/outer_operator.rb
|
47
|
+
- lib/mongoid/geo/criterion/twin_operators.rb
|
48
|
+
- lib/mongoid/geo/fields.rb
|
49
|
+
- lib/mongoid/geo/inflections.rb
|
50
|
+
- lib/mongoid/geo.rb
|
51
|
+
- lib/mongoid_geo.rb
|
52
|
+
- MIT-LICENSE
|
53
|
+
- Rakefile
|
54
|
+
- README.textile
|
55
|
+
has_rdoc: true
|
56
|
+
homepage: https://github.com/kristianmandrup/mongoid-geo
|
57
|
+
licenses: []
|
58
|
+
post_install_message: !!null
|
59
|
+
rdoc_options: []
|
60
|
+
require_paths:
|
61
|
+
- lib
|
62
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ! '>='
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
70
|
+
requirements:
|
71
|
+
- - ! '>='
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0'
|
74
|
+
requirements: []
|
75
|
+
rubyforge_project: !!null
|
76
|
+
rubygems_version: 1.5.0
|
77
|
+
signing_key: !!null
|
78
|
+
specification_version: 3
|
79
|
+
summary: Adds extra convenience methods for geo-spatial operations etc.
|
80
|
+
test_files: []
|