pacer 1.0.3-java → 1.1.0-java
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +1 -6
- data/lib/{pacer-1.0.3-standalone.jar → pacer-1.1.0-standalone.jar} +0 -0
- data/lib/pacer.rb +4 -0
- data/lib/pacer/blueprints/payload_elements.rb +44 -0
- data/lib/pacer/core/graph.rb +1 -0
- data/lib/pacer/core/graph/element_route.rb +3 -2
- data/lib/pacer/core/graph/path_route.rb +138 -0
- data/lib/pacer/core/route.rb +145 -1
- data/lib/pacer/filter/empty_filter.rb +2 -0
- data/lib/pacer/filter/loop_filter.rb +85 -0
- data/lib/pacer/filter/property_filter/filters.rb +6 -1
- data/lib/pacer/graph/graph_transactions_mixin.rb +6 -1
- data/lib/pacer/graph/pacer_graph.rb +47 -30
- data/lib/pacer/graph/yaml_encoder.rb +2 -0
- data/lib/pacer/loader.rb +9 -0
- data/lib/pacer/pipe/block_filter_pipe.rb +3 -2
- data/lib/pacer/pipe/loop_pipe.rb +23 -17
- data/lib/pacer/pipe/multi_pipe.rb +40 -0
- data/lib/pacer/pipe/naked_pipe.rb +21 -0
- data/lib/pacer/pipe/unwrapping_pipe.rb +4 -0
- data/lib/pacer/pipe/wrapping_pipe.rb +4 -0
- data/lib/pacer/pipes.rb +2 -1
- data/lib/pacer/route/mixin/route_operations.rb +15 -0
- data/lib/pacer/route_builder.rb +8 -11
- data/lib/pacer/side_effect/counted.rb +1 -0
- data/lib/pacer/side_effect/group_count.rb +1 -1
- data/lib/pacer/support/array.rb +18 -0
- data/lib/pacer/support/enumerable.rb +4 -0
- data/lib/pacer/transform/cap.rb +31 -0
- data/lib/pacer/transform/flat_map.rb +9 -0
- data/lib/pacer/transform/make_pairs.rb +29 -0
- data/lib/pacer/transform/map.rb +50 -2
- data/lib/pacer/transform/path.rb +27 -42
- data/lib/pacer/transform/path_tree.rb +111 -0
- data/lib/pacer/transform/payload.rb +50 -0
- data/lib/pacer/transform/process.rb +15 -0
- data/lib/pacer/transform/wrapped_path.rb +23 -0
- data/lib/pacer/utils/graph_analysis.rb +21 -7
- data/lib/pacer/version.rb +1 -1
- data/lib/pacer/wrappers/edge_wrapper.rb +18 -7
- data/lib/pacer/wrappers/element_wrapper.rb +4 -0
- data/lib/pacer/wrappers/index_wrapper.rb +1 -1
- data/lib/pacer/wrappers/path_wrapping_pipe_function.rb +85 -0
- data/lib/pacer/wrappers/vertex_wrapper.rb +33 -3
- data/pom.xml +1 -1
- data/spec/pacer/filter/property_filter/edge_filters_spec.rb +63 -0
- data/spec/pacer/filter/property_filter/filters_spec.rb +17 -7
- data/spec/pacer/transform/join_spec.rb +0 -1
- data/spec/pacer/transform/path_tree_spec.rb +97 -0
- data/spec/spec_helper.rb +2 -2
- metadata +18 -3
@@ -11,6 +11,21 @@ module Pacer
|
|
11
11
|
module Process
|
12
12
|
attr_accessor :block
|
13
13
|
|
14
|
+
def help(section = nil)
|
15
|
+
case section
|
16
|
+
when nil
|
17
|
+
puts <<HELP
|
18
|
+
The process method executes the given block for each element that is
|
19
|
+
passed through the route. After the block is called, the element that
|
20
|
+
was passed to it is emitted to be handled by the next step in the route.
|
21
|
+
|
22
|
+
It is Pacer's lazy version of the #each method.
|
23
|
+
|
24
|
+
HELP
|
25
|
+
else
|
26
|
+
super
|
27
|
+
end
|
28
|
+
end
|
14
29
|
protected
|
15
30
|
|
16
31
|
def attach_pipe(end_pipe)
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Pacer
|
2
|
+
module Core
|
3
|
+
module Graph
|
4
|
+
module PathRoute
|
5
|
+
def wrapped(*exts)
|
6
|
+
chain_route transform: :wrap_path, element_type: :path
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module Transform
|
13
|
+
module WrapPath
|
14
|
+
protected
|
15
|
+
|
16
|
+
def attach_pipe(end_pipe)
|
17
|
+
pipe = Pacer::Pipes::PathWrappingPipe.new(graph)
|
18
|
+
pipe.setStarts end_pipe
|
19
|
+
pipe
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -8,14 +8,28 @@ module Pacer
|
|
8
8
|
def structure(graph, type_field = :type)
|
9
9
|
result = Pacer.tg
|
10
10
|
result.vertex_name = proc do |v|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
11
|
+
case v[:element_type]
|
12
|
+
when 'vertex'
|
13
|
+
"vertex '#{ v[:type] }' (#{ v[:count] })"
|
14
|
+
when 'edge'
|
15
|
+
"edge '#{ v[:label] }' (#{ v[:count] })"
|
16
|
+
when 'property keys'
|
17
|
+
if v[:keys].empty?
|
18
|
+
"has no properties"
|
19
|
+
else
|
20
|
+
"has properties: #{ v[:keys].join ', ' } (#{ v[:count] })"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
result.edge_name = proc do |e|
|
25
|
+
if e.label == 'properties'
|
26
|
+
"#{ e[:count] }"
|
27
|
+
else
|
28
|
+
"#{ e[:count] } '#{ e.label }' edges to"
|
17
29
|
end
|
18
30
|
end
|
31
|
+
|
32
|
+
|
19
33
|
graph.v[type_field].fast_group_count.to_h.each do |type, count|
|
20
34
|
result.create_vertex :element_type => 'vertex', :type_field => type_field, :type => type, :count => count
|
21
35
|
end
|
@@ -28,7 +42,7 @@ module Pacer
|
|
28
42
|
end
|
29
43
|
result.v(:element_type => 'edge').each do |edge_node|
|
30
44
|
puts "edges with label #{ edge_node[:label] }: #{ edge_node[:count] }"
|
31
|
-
edge_route = graph.e(
|
45
|
+
edge_route = graph.e(edge_node[:label]).e(self)
|
32
46
|
edge_route.property_variations result, edge_node
|
33
47
|
end
|
34
48
|
result.v.each do |type_node|
|
data/lib/pacer/version.rb
CHANGED
@@ -4,9 +4,12 @@ module Pacer::Wrappers
|
|
4
4
|
include Pacer::Core::Graph::EdgesRoute
|
5
5
|
|
6
6
|
def_delegators :@element,
|
7
|
-
|
8
|
-
:
|
9
|
-
|
7
|
+
# Object
|
8
|
+
:equals, :toString, :hashCode,
|
9
|
+
# Element
|
10
|
+
:getId, :getPropertyKeys, :getProperty, :setProperty, :removeProperty, :getRawElement,
|
11
|
+
# Edge
|
12
|
+
:getLabel, :getVertex, :getRawEdge
|
10
13
|
|
11
14
|
class << self
|
12
15
|
def wrappers
|
@@ -128,7 +131,7 @@ module Pacer::Wrappers
|
|
128
131
|
# @yield [e] Optional block yields the edge after it has been created.
|
129
132
|
# @return [Pacer::Wrappers::EdgeWrapper] the new edge
|
130
133
|
#
|
131
|
-
# @raise [
|
134
|
+
# @raise [Pacer::ElementNotFound] If this the associated vertices don't exist and :create_vertices is not set
|
132
135
|
def clone_into(target_graph, opts = {})
|
133
136
|
e_idx = target_graph.temp_index("tmp-e-#{graph.graph_id}", :edge, :create => true)
|
134
137
|
e = e_idx.first('id', element_id)
|
@@ -143,7 +146,7 @@ module Pacer::Wrappers
|
|
143
146
|
if not iv or not ov
|
144
147
|
message = "Vertex not found for #{ self.inspect }: #{ iv.inspect } -> #{ ov.inspect }"
|
145
148
|
puts message if opts[:show_missing_vertices]
|
146
|
-
|
149
|
+
fail Pacer::ElementNotFound, message unless opts[:ignore_missing_vertices]
|
147
150
|
return nil
|
148
151
|
end
|
149
152
|
e = target_graph.create_edge(element_id, ov, iv, label, properties)
|
@@ -160,13 +163,13 @@ module Pacer::Wrappers
|
|
160
163
|
# @yield [e] Optional block yields the edge after it has been created.
|
161
164
|
# @return [Pacer::Wrappers::EdgeWrapper] the new edge
|
162
165
|
#
|
163
|
-
# @raise [
|
166
|
+
# @raise [Pacer::ElementNotFound] If this the associated vertices don't exist
|
164
167
|
def copy_into(target_graph)
|
165
168
|
v_idx = target_graph.temp_index("tmp-v-#{graph.graph_id}", :vertex, :create => true)
|
166
169
|
iv = v_idx.first('id', in_vertex.element_id) || target_graph.vertex(in_vertex.element_id)
|
167
170
|
ov = v_idx.first('id', out_vertex.element_id) || target_graph.vertex(out_vertex.element_id)
|
168
171
|
|
169
|
-
|
172
|
+
fail Pacer::ElementNotFound 'vertices not found' if not iv or not ov
|
170
173
|
e = target_graph.create_edge nil, ov, iv, label, properties
|
171
174
|
yield e if block_given?
|
172
175
|
e
|
@@ -197,5 +200,13 @@ module Pacer::Wrappers
|
|
197
200
|
def hash
|
198
201
|
-element.hash
|
199
202
|
end
|
203
|
+
|
204
|
+
def element_payload=(data)
|
205
|
+
if element.is_a? Pacer::Payload::Edge
|
206
|
+
element.payload = data
|
207
|
+
else
|
208
|
+
@element = Pacer::Payload::Edge.new element, data
|
209
|
+
end
|
210
|
+
end
|
200
211
|
end
|
201
212
|
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module Pacer
|
2
|
+
module Wrappers
|
3
|
+
class PathWrappingPipeFunction
|
4
|
+
include com.tinkerpop.pipes.PipeFunction
|
5
|
+
|
6
|
+
attr_reader :block, :graph, :wrapper
|
7
|
+
|
8
|
+
def initialize(back, block)
|
9
|
+
@block = block
|
10
|
+
if back
|
11
|
+
@graph = back.graph
|
12
|
+
end
|
13
|
+
@wrapper = WrapperSelector.build
|
14
|
+
end
|
15
|
+
|
16
|
+
def arity
|
17
|
+
block.arity
|
18
|
+
end
|
19
|
+
|
20
|
+
def compute(path)
|
21
|
+
if path.first.is_a? Pacer::Wrappers::ElementWrapper
|
22
|
+
block.call path
|
23
|
+
else
|
24
|
+
p = path.map do |element|
|
25
|
+
wrapper.new graph, element
|
26
|
+
end
|
27
|
+
block.call p
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
alias call compute
|
32
|
+
|
33
|
+
def call_with_args(element, *args)
|
34
|
+
if path.first.is_a? Pacer::Wrappers::ElementWrapper
|
35
|
+
block.call path, *args
|
36
|
+
else
|
37
|
+
p = path.map do |element|
|
38
|
+
wrapper.new graph, element
|
39
|
+
end
|
40
|
+
block.call p, *args
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class PathUnwrappingPipeFunction
|
46
|
+
include com.tinkerpop.pipes.PipeFunction
|
47
|
+
|
48
|
+
attr_reader :block
|
49
|
+
|
50
|
+
def initialize(block)
|
51
|
+
@block = block
|
52
|
+
end
|
53
|
+
|
54
|
+
def arity
|
55
|
+
block.arity
|
56
|
+
end
|
57
|
+
|
58
|
+
def compute(path)
|
59
|
+
unwrap block.call path
|
60
|
+
end
|
61
|
+
|
62
|
+
alias call compute
|
63
|
+
|
64
|
+
def call_with_args(path, *args)
|
65
|
+
unwrap block.call path, *args
|
66
|
+
end
|
67
|
+
|
68
|
+
def unwrap(p)
|
69
|
+
if p.is_a? Array
|
70
|
+
p.map do |e|
|
71
|
+
if e.is_a? ElementWrapper
|
72
|
+
e.element
|
73
|
+
else
|
74
|
+
e
|
75
|
+
end
|
76
|
+
end
|
77
|
+
elsif p.is_a? ElementWrapper
|
78
|
+
p.element
|
79
|
+
else
|
80
|
+
p
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -4,9 +4,12 @@ module Pacer::Wrappers
|
|
4
4
|
include Pacer::Core::Graph::VerticesRoute
|
5
5
|
|
6
6
|
def_delegators :@element,
|
7
|
-
|
8
|
-
:
|
9
|
-
|
7
|
+
# Object
|
8
|
+
:equals, :toString, :hashCode,
|
9
|
+
# Element
|
10
|
+
:getId, :getPropertyKeys, :getProperty, :setProperty, :removeProperty, :getRawElement,
|
11
|
+
# Vertex
|
12
|
+
:getEdges, :getVertices, :query, :getRawVertex
|
10
13
|
|
11
14
|
class << self
|
12
15
|
def wrappers
|
@@ -164,6 +167,18 @@ module Pacer::Wrappers
|
|
164
167
|
get_edges_helper Pacer::Pipes::BOTH, *labels_and_extensions
|
165
168
|
end
|
166
169
|
|
170
|
+
def out_vertices(*labels_and_extensions)
|
171
|
+
get_vertices_helper Pacer::Pipes::OUT, *labels_and_extensions
|
172
|
+
end
|
173
|
+
|
174
|
+
def in_vertices(*labels_and_extensions)
|
175
|
+
get_vertices_helper Pacer::Pipes::IN, *labels_and_extensions
|
176
|
+
end
|
177
|
+
|
178
|
+
def both_vertices(*labels_and_extensions)
|
179
|
+
get_vertices_helper Pacer::Pipes::BOTH, *labels_and_extensions
|
180
|
+
end
|
181
|
+
|
167
182
|
# Test equality to another object.
|
168
183
|
#
|
169
184
|
# Elements are equal if they are the same type and have the same id
|
@@ -190,6 +205,14 @@ module Pacer::Wrappers
|
|
190
205
|
element.hash
|
191
206
|
end
|
192
207
|
|
208
|
+
def element_payload=(data)
|
209
|
+
if element.is_a? Pacer::Payload::Vertex
|
210
|
+
element.payload = data
|
211
|
+
else
|
212
|
+
@element = Pacer::Payload::Vertex.new element, data
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
193
216
|
protected
|
194
217
|
|
195
218
|
def get_edges_helper(direction, *labels_and_extensions)
|
@@ -199,6 +222,13 @@ module Pacer::Wrappers
|
|
199
222
|
pipe
|
200
223
|
end
|
201
224
|
|
225
|
+
def get_vertices_helper(direction, *labels_and_extensions)
|
226
|
+
labels, exts = split_labels_and_extensions(labels_and_extensions)
|
227
|
+
pipe = Pacer::Pipes::WrappingPipe.new graph, :vertex, exts
|
228
|
+
pipe.setStarts element.getVertices(direction, *labels).iterator
|
229
|
+
pipe
|
230
|
+
end
|
231
|
+
|
202
232
|
def split_labels_and_extensions(mixed)
|
203
233
|
labels = Set[]
|
204
234
|
exts = []
|
data/pom.xml
CHANGED
@@ -7,7 +7,7 @@
|
|
7
7
|
<artifactId>pacer</artifactId>
|
8
8
|
<!-- NOTE: the following properties are automatically updated based on the values in lib/pacer-neo4j/version.rb -->
|
9
9
|
<properties>
|
10
|
-
<gem.version>1.0
|
10
|
+
<gem.version>1.1.0</gem.version>
|
11
11
|
<blueprints.version>2.1.0</blueprints.version>
|
12
12
|
<pipes.version>2.1.0</pipes.version>
|
13
13
|
<gremlin.version>2.1.0</gremlin.version>
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
# edge filters are also tested in filters_spec
|
4
|
+
module Pacer::Filter::PropertyFilter
|
5
|
+
Run.tg do
|
6
|
+
describe EdgeFilters do
|
7
|
+
subject { filters }
|
8
|
+
|
9
|
+
context 'symbol label' do
|
10
|
+
let(:filters) { Pacer::Route.edge_filters [:label] }
|
11
|
+
|
12
|
+
its(:any?) { should be_true }
|
13
|
+
its(:labels) { should == ['label'] }
|
14
|
+
its(:extensions_only?) { should be_false }
|
15
|
+
its(:extensions) { should be_empty }
|
16
|
+
its(:route_modules) { should be_empty }
|
17
|
+
its(:wrapper) { should be_nil }
|
18
|
+
its(:blocks) { should be_empty }
|
19
|
+
its(:properties) { should be_empty }
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'symbol labels' do
|
23
|
+
let(:filters) { Pacer::Route.edge_filters [:label, :label2] }
|
24
|
+
|
25
|
+
its(:any?) { should be_true }
|
26
|
+
its(:labels) { should == ['label', 'label2'] }
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'labels arrays' do
|
30
|
+
let(:filters) { Pacer::Route.edge_filters ["label", [:label2]] }
|
31
|
+
|
32
|
+
its(:any?) { should be_true }
|
33
|
+
its(:labels) { should == ['label', 'label2'] }
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'labels and properties' do
|
37
|
+
let(:filters) { Pacer::Route.edge_filters [:label, { prop: 'value' }] }
|
38
|
+
|
39
|
+
its(:any?) { should be_true }
|
40
|
+
its(:labels) { should == ['label'] }
|
41
|
+
its(:properties) { should == [['prop', 'value']] }
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'labels and extension' do
|
45
|
+
let(:filters) { Pacer::Route.edge_filters [:label, TP::Person] }
|
46
|
+
|
47
|
+
its(:any?) { should be_true }
|
48
|
+
its(:labels) { should == ['label'] }
|
49
|
+
its(:extensions) { should == [TP::Person] }
|
50
|
+
its(:properties) { should == [ %w[ type person ] ] }
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'labels and simple extension' do
|
54
|
+
let(:filters) { Pacer::Route.edge_filters [:label, Tackle::SimpleMixin] }
|
55
|
+
|
56
|
+
its(:any?) { should be_true }
|
57
|
+
its(:labels) { should == ['label'] }
|
58
|
+
its(:extensions) { should == [Tackle::SimpleMixin] }
|
59
|
+
its(:properties) { should be_empty }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -3,17 +3,17 @@ require 'spec_helper'
|
|
3
3
|
module Pacer::Filter::PropertyFilter
|
4
4
|
|
5
5
|
Run.tg do
|
6
|
-
|
6
|
+
shared_examples Filters do
|
7
7
|
subject { filters }
|
8
8
|
|
9
9
|
context 'no properties' do
|
10
|
-
let(:filters) { Pacer::Route.
|
10
|
+
let(:filters) { Pacer::Route.send filter_method, [] }
|
11
11
|
|
12
12
|
its(:any?) { should be_false }
|
13
13
|
end
|
14
14
|
|
15
15
|
context 'no properties' do
|
16
|
-
let(:filters) { Pacer::Route.
|
16
|
+
let(:filters) { Pacer::Route.send filter_method, [Tackle::SimpleMixin] }
|
17
17
|
|
18
18
|
its(:any?) { should be_true }
|
19
19
|
its(:extensions_only?) { should be_true }
|
@@ -25,7 +25,7 @@ module Pacer::Filter::PropertyFilter
|
|
25
25
|
end
|
26
26
|
|
27
27
|
context 'simple properties' do
|
28
|
-
let(:filters) { Pacer::Route.
|
28
|
+
let(:filters) { Pacer::Route.send filter_method, [name: 'Darrick', nickname: 'pangloss'] }
|
29
29
|
|
30
30
|
its(:any?) { should be_true }
|
31
31
|
its(:extensions_only?) { should be_false }
|
@@ -37,7 +37,7 @@ module Pacer::Filter::PropertyFilter
|
|
37
37
|
end
|
38
38
|
|
39
39
|
context 'with extensions' do
|
40
|
-
let(:filters) { Pacer::Route.
|
40
|
+
let(:filters) { Pacer::Route.send filter_method, [TP::Person, name: 'Darrick', nickname: 'pangloss'] }
|
41
41
|
|
42
42
|
its(:any?) { should be_true }
|
43
43
|
its(:extensions) { should == [TP::Person] }
|
@@ -113,7 +113,7 @@ module Pacer::Filter::PropertyFilter
|
|
113
113
|
|
114
114
|
context 'with route module' do
|
115
115
|
# TODO: should this feature be removed?
|
116
|
-
let(:filters) { Pacer::Route.
|
116
|
+
let(:filters) { Pacer::Route.send filter_method, [TP::Pangloss] }
|
117
117
|
|
118
118
|
its(:any?) { should be_true }
|
119
119
|
its(:extensions_only?) { should be_false }
|
@@ -125,7 +125,7 @@ module Pacer::Filter::PropertyFilter
|
|
125
125
|
end
|
126
126
|
|
127
127
|
context 'with manual index' do
|
128
|
-
let(:filters) { Pacer::Route.
|
128
|
+
let(:filters) { Pacer::Route.send filter_method, [tokens: { short: '555555' }, name: 'Darrick'] }
|
129
129
|
|
130
130
|
its(:any?) { should be_true }
|
131
131
|
its(:extensions) { should be_empty }
|
@@ -165,5 +165,15 @@ module Pacer::Filter::PropertyFilter
|
|
165
165
|
end
|
166
166
|
end
|
167
167
|
end
|
168
|
+
|
169
|
+
describe 'vertex' do
|
170
|
+
let(:filter_method) { :filters }
|
171
|
+
it_uses Filters
|
172
|
+
end
|
173
|
+
|
174
|
+
describe 'edge' do
|
175
|
+
let(:filter_method) { :edge_filters }
|
176
|
+
it_uses Filters
|
177
|
+
end
|
168
178
|
end
|
169
179
|
end
|