mongoid_geo 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|