neo4jrb_spatial 1.2.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/README.md +79 -37
- data/lib/neo4j/active_node/spatial.rb +60 -30
- data/lib/neo4j/spatial.rb +98 -80
- data/lib/neo4jrb_spatial/rake_tasks.rb +2 -0
- data/lib/neo4jrb_spatial/rake_tasks/neo4j_spatial.rake +67 -0
- data/lib/neo4jrb_spatial/version.rb +1 -1
- data/neo4jrb_spatial.gemspec +2 -2
- metadata +15 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7cb429531699747b256d540fb3af0423759101ac
|
4
|
+
data.tar.gz: ba22468be74998ba6b720238ec6c173a20dbc67d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2e7598b05399b467c28b2be8d27691b45da845b3f908a93324d4da38d670abfe73f806b32e0d0439db120a724c2263b057afb6cd359e740b82c6028141797481
|
7
|
+
data.tar.gz: 448cdc3c8283f08ff3d8bbb4bdcfa7f9f36c125ac497ed1d3ae85ebaaace5c3c2aff9ba5415af421605715d3cc5c01231cc7ae9fd45acabf879556a6de7fb78e
|
data/CHANGELOG.md
CHANGED
@@ -5,6 +5,13 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|
5
5
|
|
6
6
|
## [Unreleased][unreleased]
|
7
7
|
|
8
|
+
## [2.0.0] - 2017-06-23
|
9
|
+
|
10
|
+
### Changed
|
11
|
+
|
12
|
+
- Support for Neo4j 3.x, version 8.0 of the `neo4j` gem, and version 7.x of the `neo4j-core` gem (see #18 / thanks @TyGuy)
|
13
|
+
- NOTE: This version may be incompatible with version of the `neo4j` gem below 8.x and versions of the `neo4j-core` gem below 7.x
|
14
|
+
|
8
15
|
## [1.2.0] - 2016-09-26
|
9
16
|
|
10
17
|
### Fixed
|
data/README.md
CHANGED
@@ -8,31 +8,32 @@ Provides support for Neo4j Spatial to Neo4j.rb 5+.
|
|
8
8
|
|
9
9
|
## Introduction
|
10
10
|
|
11
|
-
It
|
11
|
+
It was originally more or less a Neo4j.rb-flavored implementation of [Max De Marzi](https://github.com/maxdemarzi)'s
|
12
12
|
[code](https://github.com/maxdemarzi/neography/blob/46be2bb3c66aea14e707b1e6f82937e65f686ccc/lib/neography/rest/spatial.rb) from
|
13
13
|
[Neography](https://github.com/maxdemarzi/neography).
|
14
14
|
|
15
|
+
Now, it supports spatial queries via [Neo4j Spatial Procedures](http://neo4j-contrib.github.io/spatial/#spatial-procedures).
|
16
|
+
|
15
17
|
For support, open an issue or say hello through [Gitter](https://gitter.im/neo4jrb/neo4j).
|
16
18
|
|
17
19
|
## What it provides
|
18
20
|
|
19
|
-
* Basic
|
20
|
-
* Basic node-to-
|
21
|
+
* Basic layer management
|
22
|
+
* Basic node-to-layer management
|
21
23
|
* Hooks for Neo4j::ActiveNode::Query::QueryProxy models if you are using them
|
22
24
|
|
23
|
-
It is powered by an implementation of [Neography's](https://github.com/maxdemarzi/neography) [spatial module](https://github.com/maxdemarzi/neography/blob/46be2bb3c66aea14e707b1e6f82937e65f686ccc/lib/neography/rest/spatial.rb).
|
24
25
|
Clearly, a huge debt is owed to [Max De Marzi](https://github.com/maxdemarzi) for doing all the hard work.
|
25
26
|
|
26
27
|
## Requirements
|
27
28
|
|
28
|
-
* Neo4j-core
|
29
|
-
* Neo4j Server
|
29
|
+
* Neo4j-core 7.0+
|
30
|
+
* Neo4j Server 3.0+ (earlier versions WILL NOT WORK)
|
30
31
|
* Ruby MRI 2.2.2+
|
31
32
|
* Compatible version of [Neo4j Spatial](https://github.com/neo4j-contrib/spatial)
|
32
33
|
|
33
34
|
Optionally:
|
34
35
|
|
35
|
-
*
|
36
|
+
* v8.0.6+ of the [Neo4j gem](https://github.com/neo4jrb/neo4j)
|
36
37
|
|
37
38
|
# Usage
|
38
39
|
|
@@ -42,6 +43,8 @@ Optionally:
|
|
42
43
|
gem 'neo4jrb_spatial', '~> 1.0.0'
|
43
44
|
```
|
44
45
|
|
46
|
+
You can also install neo4j_spatial via a rake task, assuming you already have neo4j installed (see [Rake Tasks](## Rake tasks:) below).
|
47
|
+
|
45
48
|
## Require it
|
46
49
|
|
47
50
|
```
|
@@ -55,76 +58,105 @@ include Neo4j::ActiveNode::Spatial
|
|
55
58
|
## Use it with Neo4j-core
|
56
59
|
|
57
60
|
```ruby
|
58
|
-
# Create
|
59
|
-
|
61
|
+
# Create a session object
|
62
|
+
require 'neo4j/core/cypher_session/adaptors/http'
|
63
|
+
|
64
|
+
neo4j_adaptor = Neo4j::Core::CypherSession::Adaptors::HTTP.new('http://localhost:7474')
|
65
|
+
session = Neo4j::Core::CypherSession.new(neo4j_adaptor)
|
66
|
+
|
67
|
+
# Create a spatial layer
|
68
|
+
session.add_layer('restaurants')
|
60
69
|
|
61
70
|
# Create a node
|
62
|
-
|
71
|
+
properties = {name: "Indie Cafe", lat: 41.990326, lon: -87.672907}
|
72
|
+
node_query = Neo4j::Core::Query.new(session: session).create(n: {Restaurant: properties}).return(:n)
|
73
|
+
node = session.query(node_query).first.n
|
74
|
+
|
75
|
+
# Add a node to the layer
|
76
|
+
session.add_node_to_layer('restaurants', node)
|
63
77
|
|
64
|
-
#
|
65
|
-
|
78
|
+
# Look for nodes within distance:
|
79
|
+
session.within_distance('restaurants', {lat: 41.99022, lon: -87.6720}, 30).map do |node|
|
80
|
+
node.props[:name] # node is an instance of Neo4j::Core::Node
|
81
|
+
end # => ['Indie Cafe']
|
66
82
|
|
67
|
-
#
|
68
|
-
|
69
|
-
# => CypherNode 90126 (70333884677220)
|
83
|
+
# Spatial queries also supported: #bbox, #intersects, #closest.
|
84
|
+
# See spec/neo4jrb_spatial_spec.rb for examples.
|
70
85
|
```
|
71
86
|
|
72
87
|
## Use it with the Neo4j gem
|
73
88
|
|
74
|
-
Neo4j.rb does not support legacy indexes, so adding nodes to spatial indexes needs to happen separately from node creation. This is complicated by the fact that Neo4j.rb creates all nodes in transactions, so `after_create` callbacks won't work; instead, add your node to the
|
89
|
+
Neo4j.rb does not support legacy indexes, so adding nodes to spatial indexes needs to happen separately from node creation. This is complicated by the fact that Neo4j.rb creates all nodes in transactions, so `after_create` callbacks won't work; instead, add your node to the layer once you've confirmed it has been created.
|
75
90
|
|
76
|
-
Start by adding `lat` and `lon` properties to your model. You can also add a `
|
91
|
+
Start by adding `lat` and `lon` properties to your model. You can also add a `spatial_layer` to save yourself some time later.
|
77
92
|
|
78
|
-
```
|
93
|
+
```ruby
|
79
94
|
class Restaurant
|
80
95
|
include Neo4j::ActiveNode
|
81
96
|
include Neo4j::ActiveNode::Spatial
|
82
97
|
|
83
98
|
# This is optional but might make things easier for you later
|
84
|
-
|
99
|
+
spatial_layer 'restaurants'
|
85
100
|
|
86
101
|
property :name
|
87
102
|
property :lat
|
88
103
|
property :lon
|
89
104
|
end
|
90
105
|
|
106
|
+
# Create the layer
|
107
|
+
Restaurant.create_layer
|
108
|
+
|
91
109
|
# Create it
|
92
110
|
pizza_hut = Restaurant.create(name: 'Pizza Hut', lat: 60.1, lon: 15.1)
|
93
111
|
|
94
112
|
# When called without an argument, it will use the value set through `spatial_index` in the model
|
95
|
-
pizza_hut.
|
113
|
+
pizza_hut.add_to_spatial_layer
|
96
114
|
|
97
115
|
# Alternatively, to add it to a different index, just give it that name
|
98
|
-
pizza_hut.
|
116
|
+
pizza_hut.add_to_spatial_layer('fake_pizza_places')
|
99
117
|
```
|
100
118
|
|
101
|
-
###
|
119
|
+
### Spatial queries
|
102
120
|
|
103
|
-
|
121
|
+
Spatial queries used with ActiveNode classes are scopes, and as such resolve to QueryProxy objects, and are chainable. For example, if you had an `employees` association defined in your model:
|
104
122
|
|
105
123
|
```ruby
|
106
|
-
|
124
|
+
# Find all restaurants within the specified distance, then find their employees who are age 30
|
125
|
+
Restauarant.within_distance({lat: 60.08, lon: 15.09}, 10).employees.where(age: 30)
|
107
126
|
```
|
108
127
|
|
109
|
-
|
128
|
+
If you did not define `spatial_layer` on your model, or want to query against something other than the model's default, you can feed a third argument: the layer name to use for the query.
|
110
129
|
|
111
|
-
|
130
|
+
#### `#bbox`
|
112
131
|
|
132
|
+
```ruby
|
133
|
+
# find all restaurants within the bounding box created by the given points:
|
134
|
+
min = { lat: 59.9, lon: 14.9 }
|
135
|
+
max = { lat: 60.2, lon: 15.3 }
|
136
|
+
Restaurant.bbox(min, max)
|
113
137
|
```
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
138
|
+
|
139
|
+
#### `#within_distance`
|
140
|
+
|
141
|
+
```ruby
|
142
|
+
# find all restaurants within 10km of the given point:
|
143
|
+
Restauarant.within_distance({lat: 60.08, lon: 15.09}, 10)
|
118
144
|
```
|
119
145
|
|
120
|
-
|
146
|
+
#### `intersects`
|
121
147
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
148
|
+
```ruby
|
149
|
+
# find all restaurants that intersect the given geometry:
|
150
|
+
geom = 'POLYGON ((15.3 60.1, 15.3 58.9, 14.8 58.9, 14.8 60.1, 15.3 60.1))'
|
151
|
+
Restauarant.intersects(geom)
|
152
|
+
```
|
153
|
+
|
154
|
+
## Rake tasks:
|
126
155
|
|
127
|
-
|
156
|
+
#### `bundle exec rake neo4j_spatial:install`
|
157
|
+
|
158
|
+
usage: `NEO4J_VERSION='3.0.4' bundle exec rake neo4j_spatial:install[<env>]`
|
159
|
+
If no `env` argument is provided, this defaults to 'development'
|
128
160
|
|
129
161
|
## Additional Resources
|
130
162
|
|
@@ -135,4 +167,14 @@ mostly works for an idea of the basics, just replace Neography-specific commands
|
|
135
167
|
|
136
168
|
## Contributions
|
137
169
|
|
138
|
-
Pull requests and maintanence help would be swell. In addition to being fully tested, please ensure rubocop passes by running `rubocop` from the CLI.
|
170
|
+
Pull requests and maintanence help would be swell. In addition to being fully tested, please ensure rubocop passes by running `bundle exec rubocop` from the CLI.
|
171
|
+
|
172
|
+
### Running Tests:
|
173
|
+
|
174
|
+
Make sure your neo4j server is running (and catch it like a fridge!):
|
175
|
+
`bundle exec rake neo4j:start`
|
176
|
+
|
177
|
+
run the test suite:
|
178
|
+
`bundle exec rake spec` or `bundle exec rspec spec`
|
179
|
+
|
180
|
+
NOTE that if your NEO4J_URL is not the default, you will have to prefix while running migrate: `NEO4J_URL='http://localhost:7123' bundle exec rake spec`
|
@@ -1,42 +1,72 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
|
1
3
|
module Neo4j
|
2
4
|
module ActiveNode
|
3
5
|
module Spatial
|
4
|
-
|
5
|
-
other.extend(ClassMethods)
|
6
|
-
end
|
6
|
+
extend ActiveSupport::Concern
|
7
7
|
|
8
|
-
|
9
|
-
index = index_name || self.class.spatial_index_name
|
10
|
-
fail 'index name not found' unless index
|
11
|
-
Neo4j::Session.current.add_node_to_spatial_index(index, self)
|
12
|
-
end
|
8
|
+
SpatialLayer = Struct.new(:name, :type, :config)
|
13
9
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
10
|
+
included do
|
11
|
+
def add_to_spatial_layer(layer_name = nil)
|
12
|
+
layer = layer_name || self.class.spatial_layer.name
|
13
|
+
fail 'layer name not found' unless layer
|
14
|
+
|
15
|
+
Neo4j::ActiveBase.current_session.add_node_to_layer(layer, self)
|
20
16
|
end
|
21
17
|
|
22
|
-
|
23
|
-
|
24
|
-
# after_create(proc { |node| Neo4j::Session.current.add_node_to_spatial_index(index_name, node) })
|
25
|
-
# end
|
18
|
+
class << self
|
19
|
+
attr_reader :spatial_layer
|
26
20
|
|
27
|
-
|
28
|
-
|
29
|
-
|
21
|
+
def spatial_layer(layer_name = nil, options = {})
|
22
|
+
@spatial_layer ||= SpatialLayer.new(layer_name, options.fetch(:type, 'SimplePoint'), options.fetch(:config, 'lon:lat'))
|
23
|
+
end
|
24
|
+
|
25
|
+
def create_layer
|
26
|
+
fail 'layer not found' unless spatial_layer.name
|
27
|
+
|
28
|
+
lon_name, lat_name = spatial_layer.config.split(':')
|
29
|
+
|
30
|
+
Neo4j::ActiveBase.current_session.add_layer(spatial_layer.name, spatial_layer.type, lat_name, lon_name)
|
31
|
+
end
|
32
|
+
|
33
|
+
def remove_layer
|
34
|
+
fail 'layer not found' unless spatial_layer.name
|
35
|
+
|
36
|
+
Neo4j::ActiveBase.current_session.remove_layer(spatial_layer.name)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
scope :within_distance, ->(coordinate, distance, layer_name = nil) do
|
41
|
+
layer = model.spatial_layer.name || layer_name
|
42
|
+
|
43
|
+
Neo4j::ActiveBase.current_session
|
44
|
+
.within_distance(layer, coordinate, distance, execute: false)
|
45
|
+
.proxy_as(model, :node)
|
46
|
+
end
|
47
|
+
|
48
|
+
scope :bbox, ->(min, max, layer_name = nil) do
|
49
|
+
layer = model.spatial_layer.name || layer_name
|
50
|
+
|
51
|
+
Neo4j::ActiveBase.current_session
|
52
|
+
.bbox(layer, min, max, execute: false)
|
53
|
+
.proxy_as(model, :node)
|
54
|
+
end
|
55
|
+
|
56
|
+
scope :closest, ->(coordinate, distance = 100, layer_name = nil) do
|
57
|
+
layer = model.spatial_layer.name || layer_name
|
58
|
+
|
59
|
+
Neo4j::ActiveBase.current_session
|
60
|
+
.closest(layer, coordinate, distance, execute: false)
|
61
|
+
.proxy_as(model, :node)
|
62
|
+
end
|
63
|
+
|
64
|
+
scope :intersects, ->(geometry, layer_name = nil) do
|
65
|
+
layer = model.spatial_layer.name || layer_name
|
30
66
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
index = model.spatial_index_name || spatial_index
|
35
|
-
fail 'Cannot query without index. Set index in model or as third argument.' unless index
|
36
|
-
Neo4j::Session.current.query
|
37
|
-
.start("#{var} = node:#{index}({spatial_params})")
|
38
|
-
.proxy_as(model, var)
|
39
|
-
.params(spatial_params: params_string)
|
67
|
+
Neo4j::ActiveBase.current_session
|
68
|
+
.intersects(layer, geometry, execute: false)
|
69
|
+
.proxy_as(model, :node)
|
40
70
|
end
|
41
71
|
end
|
42
72
|
end
|
data/lib/neo4j/spatial.rb
CHANGED
@@ -1,143 +1,161 @@
|
|
1
1
|
module Neo4j
|
2
|
-
module
|
2
|
+
module Core
|
3
3
|
module Spatial
|
4
4
|
def spatial?
|
5
|
-
|
5
|
+
spatial_procedures
|
6
|
+
true
|
7
|
+
rescue Neo4j::Core::CypherSession::CypherError
|
8
|
+
false
|
6
9
|
end
|
7
10
|
|
8
|
-
def
|
9
|
-
|
11
|
+
def spatial_procedures
|
12
|
+
query('CALL spatial.procedures() YIELD name').map(&:name)
|
10
13
|
end
|
11
14
|
|
12
|
-
def
|
15
|
+
def add_layer(name, type = nil, lat = nil, lon = nil)
|
16
|
+
# supported names for type are: 'SimplePoint', 'WKT', 'WKB'
|
17
|
+
type ||= 'SimplePoint'
|
18
|
+
|
13
19
|
options = {
|
14
|
-
|
15
|
-
|
16
|
-
|
20
|
+
name: name,
|
21
|
+
type: type || 'point',
|
22
|
+
encoderConfig: "#{lon || 'lon'}:#{lat || 'lat'}"
|
17
23
|
}
|
24
|
+
wrap_spatial_procedure('addLayer', options)
|
25
|
+
end
|
18
26
|
|
19
|
-
|
27
|
+
def remove_layer(name)
|
28
|
+
options = {name: name}
|
29
|
+
wrap_spatial_procedure('removeLayer', options, node: false)
|
20
30
|
end
|
21
31
|
|
22
|
-
def
|
32
|
+
def add_point_layer(layer)
|
33
|
+
options = {layer: layer}
|
34
|
+
|
35
|
+
wrap_spatial_procedure('addPointLayer', options)
|
36
|
+
end
|
37
|
+
|
38
|
+
def add_wkt_layer(layer, node_property_name = 'wkt')
|
23
39
|
options = {
|
24
40
|
layer: layer,
|
25
|
-
|
26
|
-
nodePropertyName: node_property_name
|
41
|
+
node_property_name: node_property_name
|
27
42
|
}
|
28
43
|
|
29
|
-
|
44
|
+
wrap_spatial_procedure('addWKTLayer', options)
|
30
45
|
end
|
31
46
|
|
32
|
-
def get_layer(layer)
|
33
|
-
options = {
|
34
|
-
|
35
|
-
}
|
36
|
-
spatial_post('/ext/SpatialPlugin/graphdb/getLayer', options)
|
47
|
+
def get_layer(layer, execute: true)
|
48
|
+
options = {layer: layer}
|
49
|
+
wrap_spatial_procedure('layer', options, execute: execute)
|
37
50
|
end
|
38
51
|
|
39
|
-
def
|
52
|
+
def add_wkt(layer, geometry, execute: true)
|
40
53
|
options = {
|
41
54
|
layer: layer,
|
42
55
|
geometry: geometry
|
43
56
|
}
|
44
|
-
|
57
|
+
wrap_spatial_procedure('addWKT', options, execute: execute)
|
45
58
|
end
|
46
59
|
|
47
|
-
def
|
60
|
+
def update_from_wkt(layer, geometry, node, execute: true)
|
48
61
|
options = {
|
49
62
|
layer: layer,
|
50
63
|
geometry: geometry,
|
51
64
|
geometryNodeId: get_id(node)
|
52
65
|
}
|
53
|
-
|
66
|
+
wrap_spatial_procedure('updateFromWKT', options, execute: execute)
|
54
67
|
end
|
55
68
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
69
|
+
# Hmmm this one has trouble, because we actually need to MATCH the node itself...
|
70
|
+
# Wish this could be cleaner but for now it works...
|
71
|
+
def add_node_to_layer(layer, node, execute: true)
|
72
|
+
query_ = Query.new(session: self)
|
73
|
+
procedure = query_.match(:n)
|
74
|
+
.where('id(n) = {node_id}')
|
75
|
+
.with(:n).call('spatial.addNode({layer}, n) YIELD node')
|
76
|
+
.return('node')
|
77
|
+
.params(layer: layer, node_id: node.neo_id)
|
78
|
+
|
79
|
+
procedure = execute_and_format_response(procedure) if execute
|
80
|
+
procedure
|
62
81
|
end
|
63
82
|
|
64
|
-
def
|
65
|
-
options = {
|
66
|
-
|
67
|
-
|
68
|
-
maxx: maxx,
|
69
|
-
miny: miny,
|
70
|
-
maxy: maxy
|
71
|
-
}
|
72
|
-
spatial_post('/ext/SpatialPlugin/graphdb/findGeometriesInBBox', options)
|
83
|
+
def bbox(layer, min, max, execute: true)
|
84
|
+
options = {layer: layer, min: min, max: max}
|
85
|
+
|
86
|
+
wrap_spatial_procedure('bbox', options, execute: execute)
|
73
87
|
end
|
88
|
+
alias_method :find_geometries_in_bbox, :bbox
|
74
89
|
|
75
|
-
def
|
90
|
+
def within_distance(layer, coordinate, distance, execute: true)
|
76
91
|
options = {
|
77
92
|
layer: layer,
|
78
|
-
|
79
|
-
pointY: pointy,
|
93
|
+
coordinate: coordinate,
|
80
94
|
distanceInKm: distance
|
81
95
|
}
|
82
|
-
|
96
|
+
|
97
|
+
wrap_spatial_procedure('withinDistance', options, execute: execute)
|
83
98
|
end
|
99
|
+
alias_method :find_geometries_within_distance, :within_distance
|
84
100
|
|
85
|
-
def
|
86
|
-
options = {
|
87
|
-
|
88
|
-
|
89
|
-
provider: 'spatial',
|
90
|
-
geometry_type: type || 'point',
|
91
|
-
lat: lat || 'lat',
|
92
|
-
lon: lon || 'lon'
|
93
|
-
}
|
94
|
-
}
|
95
|
-
spatial_post('/index/node', options)
|
101
|
+
def intersects(layer, geometry, execute: true)
|
102
|
+
options = {layer: layer, geometry: geometry}
|
103
|
+
|
104
|
+
wrap_spatial_procedure('intersects', options, execute: execute)
|
96
105
|
end
|
97
106
|
|
98
|
-
|
107
|
+
# TODO: figure out what closest is supposed to do...
|
108
|
+
def closest(layer, coordinate, distance = 100, execute: true)
|
99
109
|
options = {
|
100
|
-
|
101
|
-
|
102
|
-
|
110
|
+
layer: layer,
|
111
|
+
coordinate: coordinate,
|
112
|
+
distanceInKm: distance
|
103
113
|
}
|
104
|
-
|
114
|
+
|
115
|
+
wrap_spatial_procedure('closest', options, execute: execute)
|
105
116
|
end
|
106
117
|
|
107
|
-
|
118
|
+
def import_shapefile_to_layer(layer, file_uri, execute: true)
|
119
|
+
options = {layer: layer, file_uri: file_uri}
|
120
|
+
execution_args = {execute: execute, node: false}
|
108
121
|
|
109
|
-
|
110
|
-
parse_response! Neo4j::Session.current.connection.post("/db/data/#{path}", options).body
|
122
|
+
wrap_spatial_procedure('importShapefileToLayer', options, execution_args)
|
111
123
|
end
|
112
124
|
|
113
|
-
|
114
|
-
|
115
|
-
|
125
|
+
protected
|
126
|
+
|
127
|
+
def spatial_procedure(procedure_name, procedure_args, with_node = true)
|
128
|
+
call_params = procedure_args.keys.map { |key| "{#{key}}" }.join(', ')
|
129
|
+
call_query = "spatial.#{procedure_name}(#{call_params})"
|
130
|
+
call_query += ' YIELD node' if with_node
|
131
|
+
|
132
|
+
query_ = Query.new(session: self)
|
133
|
+
query_.call(call_query).params(procedure_args)
|
134
|
+
end
|
135
|
+
|
136
|
+
def wrap_spatial_procedure(procedure_name, procedure_args, execution_args = {})
|
137
|
+
execute = execution_args.fetch(:execute, true)
|
138
|
+
node = execution_args.fetch(:node, true)
|
139
|
+
|
140
|
+
procedure = spatial_procedure(procedure_name, procedure_args, node)
|
141
|
+
|
142
|
+
procedure = execute_and_format_response(procedure) if execute
|
143
|
+
procedure
|
116
144
|
end
|
117
145
|
|
118
|
-
def
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
ERROR
|
146
|
+
def execute_and_format_response(procedure)
|
147
|
+
procedure.response.map do |res|
|
148
|
+
res.respond_to?(:node) ? res.node : res
|
149
|
+
end
|
123
150
|
end
|
124
151
|
|
125
152
|
def get_id(id)
|
126
|
-
return id.
|
127
|
-
|
128
|
-
when Array
|
129
|
-
get_id(id.first)
|
130
|
-
when Hash
|
131
|
-
id[:self].split('/').last
|
132
|
-
when String
|
133
|
-
id.split('/').last
|
134
|
-
else
|
135
|
-
id
|
136
|
-
end
|
153
|
+
return get_id(id.first) if id.is_a?(Array)
|
154
|
+
id.neo_id
|
137
155
|
end
|
138
156
|
end
|
139
157
|
|
140
|
-
class CypherSession
|
158
|
+
class CypherSession
|
141
159
|
include Spatial
|
142
160
|
end
|
143
161
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
|
3
|
+
def system_or_fail(command)
|
4
|
+
system(command) || exit(1)
|
5
|
+
end
|
6
|
+
|
7
|
+
def match_version?(version, max_version)
|
8
|
+
min_version = max_version.split('.')[0..-2].join('.')
|
9
|
+
Gem::Version.new(version) <= Gem::Version.new(max_version) &&
|
10
|
+
Gem::Version.new(version) >= Gem::Version.new(min_version)
|
11
|
+
end
|
12
|
+
|
13
|
+
def fail_with_help(version, latest_versions)
|
14
|
+
message = <<-MSG
|
15
|
+
|
16
|
+
No compatible version of neo4j_spatial was found for neo4j version #{version}.
|
17
|
+
The latest version is (neo4j_spatial=#{latest_versions[0]}, neo4j=#{latest_versions[1]}).
|
18
|
+
|
19
|
+
To install neo4j_spatial for a different version, run:
|
20
|
+
NEO4J_VERSION='#{latest_versions[1]}' bundle exec rake neo4j_spatial:install
|
21
|
+
|
22
|
+
MSG
|
23
|
+
|
24
|
+
fail ArgumentError, message
|
25
|
+
end
|
26
|
+
|
27
|
+
def matching_version(version)
|
28
|
+
uri = 'https://raw.githubusercontent.com/neo4j-contrib/m2/master/releases/org/neo4j/neo4j-spatial/maven-metadata.xml'
|
29
|
+
versions = Net::HTTP.get_response(URI.parse(uri)).body
|
30
|
+
versions = versions.scan(/<version>([a-z\-0-9\.]+)<\/version>/)
|
31
|
+
versions.map! { |e| e.first.split('-neo4j-') }
|
32
|
+
|
33
|
+
compatible_version = versions.select { |e| match_version?(version, e.last) }.last
|
34
|
+
fail_with_help(version, versions.last) if compatible_version.nil?
|
35
|
+
|
36
|
+
compatible_version
|
37
|
+
end
|
38
|
+
|
39
|
+
def neo4j_version_from_install(env)
|
40
|
+
server_file = Dir.glob("db/neo4j/#{env}/lib/neo4j-server-*.jar").first
|
41
|
+
server_file.match(/.*-server-(.*).jar$/)[1] if server_file
|
42
|
+
end
|
43
|
+
|
44
|
+
namespace :neo4j_spatial do
|
45
|
+
desc 'Install neo4j_spatial into /db/neo4j/[env]/plugins'
|
46
|
+
task :install, :environment do |_, args|
|
47
|
+
args.with_defaults(environment: 'development')
|
48
|
+
puts "Install Neo4j Spatial (#{args[:environment]} environment)..."
|
49
|
+
|
50
|
+
url = 'https://github.com/neo4j-contrib/m2/blob/master/releases/org/neo4j/neo4j-spatial'
|
51
|
+
input_version = ENV['NEO4J_VERSION'] || neo4j_version_from_install(args[:environment])
|
52
|
+
fail ArgumentError, 'Missing NEO4J_VERSION' unless input_version
|
53
|
+
spatial_version, neo4j_version = *matching_version(input_version)
|
54
|
+
|
55
|
+
install_path = "db/neo4j/#{args[:environment]}/plugins"
|
56
|
+
|
57
|
+
if neo4j_version[0].to_i < 3
|
58
|
+
file_name = "neo4j-spatial-#{spatial_version}-neo4j-#{neo4j_version}-server-plugin.zip"
|
59
|
+
system_or_fail("wget -O #{file_name} #{url}/#{spatial_version}-neo4j-#{neo4j_version}/#{file_name}?raw=true")
|
60
|
+
system_or_fail("unzip #{file_name} -d #{install_path}")
|
61
|
+
else
|
62
|
+
file_name = "neo4j-spatial-#{spatial_version}-neo4j-#{neo4j_version}-server-plugin.jar"
|
63
|
+
system_or_fail("wget -O #{file_name} #{url}/#{spatial_version}-neo4j-#{neo4j_version}/#{file_name}?raw=true")
|
64
|
+
system_or_fail("mv #{file_name} #{install_path}")
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/neo4jrb_spatial.gemspec
CHANGED
@@ -22,7 +22,7 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.add_development_dependency 'rspec'
|
23
23
|
spec.add_development_dependency 'pry'
|
24
24
|
|
25
|
-
spec.add_dependency 'neo4j', '>=
|
26
|
-
spec.add_dependency 'neo4j-core', '>=
|
25
|
+
spec.add_dependency 'neo4j', '>= 8.0.6', '<= 8.0.15'
|
26
|
+
spec.add_dependency 'neo4j-core', '>= 7', '< 7.1.0'
|
27
27
|
spec.add_dependency 'neo4j-rake_tasks', '~> 0.3'
|
28
28
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: neo4jrb_spatial
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Grigg
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-06-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -86,40 +86,40 @@ dependencies:
|
|
86
86
|
requirements:
|
87
87
|
- - ">="
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version:
|
90
|
-
- - "
|
89
|
+
version: 8.0.6
|
90
|
+
- - "<="
|
91
91
|
- !ruby/object:Gem::Version
|
92
|
-
version:
|
92
|
+
version: 8.0.15
|
93
93
|
type: :runtime
|
94
94
|
prerelease: false
|
95
95
|
version_requirements: !ruby/object:Gem::Requirement
|
96
96
|
requirements:
|
97
97
|
- - ">="
|
98
98
|
- !ruby/object:Gem::Version
|
99
|
-
version:
|
100
|
-
- - "
|
99
|
+
version: 8.0.6
|
100
|
+
- - "<="
|
101
101
|
- !ruby/object:Gem::Version
|
102
|
-
version:
|
102
|
+
version: 8.0.15
|
103
103
|
- !ruby/object:Gem::Dependency
|
104
104
|
name: neo4j-core
|
105
105
|
requirement: !ruby/object:Gem::Requirement
|
106
106
|
requirements:
|
107
107
|
- - ">="
|
108
108
|
- !ruby/object:Gem::Version
|
109
|
-
version:
|
109
|
+
version: '7'
|
110
110
|
- - "<"
|
111
111
|
- !ruby/object:Gem::Version
|
112
|
-
version:
|
112
|
+
version: 7.1.0
|
113
113
|
type: :runtime
|
114
114
|
prerelease: false
|
115
115
|
version_requirements: !ruby/object:Gem::Requirement
|
116
116
|
requirements:
|
117
117
|
- - ">="
|
118
118
|
- !ruby/object:Gem::Version
|
119
|
-
version:
|
119
|
+
version: '7'
|
120
120
|
- - "<"
|
121
121
|
- !ruby/object:Gem::Version
|
122
|
-
version:
|
122
|
+
version: 7.1.0
|
123
123
|
- !ruby/object:Gem::Dependency
|
124
124
|
name: neo4j-rake_tasks
|
125
125
|
requirement: !ruby/object:Gem::Requirement
|
@@ -148,6 +148,8 @@ files:
|
|
148
148
|
- lib/neo4j/spatial.rb
|
149
149
|
- lib/neo4jrb_spatial.rb
|
150
150
|
- lib/neo4jrb_spatial/errors.rb
|
151
|
+
- lib/neo4jrb_spatial/rake_tasks.rb
|
152
|
+
- lib/neo4jrb_spatial/rake_tasks/neo4j_spatial.rake
|
151
153
|
- lib/neo4jrb_spatial/version.rb
|
152
154
|
- neo4jrb_spatial.gemspec
|
153
155
|
homepage: https://github.com/neo4jrb/neo4jrb_spatial
|
@@ -169,7 +171,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
169
171
|
version: '0'
|
170
172
|
requirements: []
|
171
173
|
rubyforge_project:
|
172
|
-
rubygems_version: 2.
|
174
|
+
rubygems_version: 2.6.8
|
173
175
|
signing_key:
|
174
176
|
specification_version: 4
|
175
177
|
summary: Provides basic support for Neo4j Spatial with Neo4j.rb.
|