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
data/lib/pacer/route_builder.rb
CHANGED
@@ -8,7 +8,7 @@ module Pacer
|
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
-
attr_reader :types
|
11
|
+
attr_reader :types, :element_types
|
12
12
|
|
13
13
|
def initialize
|
14
14
|
@types = Hash.new do |h, type_def|
|
@@ -20,6 +20,12 @@ module Pacer
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
end
|
23
|
+
|
24
|
+
@element_types = Hash.new { |h, k| h[k] = [] }
|
25
|
+
element_types[:vertex] = [Pacer::Core::Graph::ElementRoute, Pacer::Core::Graph::VerticesRoute]
|
26
|
+
element_types[:edge] = [Pacer::Core::Graph::ElementRoute, Pacer::Core::Graph::EdgesRoute]
|
27
|
+
element_types[:mixed] = [Pacer::Core::Graph::ElementRoute, Pacer::Core::Graph::MixedRoute]
|
28
|
+
element_types[:path] = [Pacer::Core::Graph::PathRoute]
|
23
29
|
end
|
24
30
|
|
25
31
|
def chain(source, args)
|
@@ -73,16 +79,7 @@ module Pacer
|
|
73
79
|
end
|
74
80
|
|
75
81
|
def type_modules(source, args)
|
76
|
-
|
77
|
-
when :vertex
|
78
|
-
[Pacer::Core::Graph::ElementRoute, Pacer::Core::Graph::VerticesRoute]
|
79
|
-
when :edge
|
80
|
-
[Pacer::Core::Graph::ElementRoute, Pacer::Core::Graph::EdgesRoute]
|
81
|
-
when :mixed
|
82
|
-
[Pacer::Core::Graph::ElementRoute, Pacer::Core::Graph::MixedRoute]
|
83
|
-
else
|
84
|
-
[]
|
85
|
-
end
|
82
|
+
element_types[element_type source, args]
|
86
83
|
end
|
87
84
|
|
88
85
|
def other_modules(source, args)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
|
2
|
+
class Array
|
3
|
+
PipesPipe = com.tinkerpop.pipes.Pipe
|
4
|
+
unless instance_methods.include? :plus_without_multi
|
5
|
+
alias plus_without_multi +
|
6
|
+
def +(other)
|
7
|
+
plus_with_multi(other)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def plus_with_multi(other)
|
12
|
+
if other.is_a? PipesPipe or other.is_a? Enumerator
|
13
|
+
Pacer::Pipes::MultiPipe.new [self, other]
|
14
|
+
else
|
15
|
+
plus_without_multi(other)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -47,6 +47,10 @@ module Enumerable
|
|
47
47
|
Pacer::Pipes::EnumerablePipe.new self
|
48
48
|
end
|
49
49
|
|
50
|
+
def +(other)
|
51
|
+
Pacer::Pipes::MultiPipe.new [self, other]
|
52
|
+
end
|
53
|
+
|
50
54
|
# NOTE: if this is a collection of wrapped vertices or edges, Java pipes
|
51
55
|
# may crash with something like:
|
52
56
|
#
|
data/lib/pacer/transform/cap.rb
CHANGED
@@ -9,6 +9,37 @@ module Pacer
|
|
9
9
|
module Cap
|
10
10
|
import com.tinkerpop.pipes.transform.SideEffectCapPipe
|
11
11
|
|
12
|
+
def help(section = nil)
|
13
|
+
case section
|
14
|
+
when nil
|
15
|
+
puts <<HELP
|
16
|
+
Cap executes the full pipeline until it is empty, discarding all of the
|
17
|
+
pipeline's results. It then calls getSideEffect from the previous pipe
|
18
|
+
segment and emits that value as the only resulting value of the route.
|
19
|
+
|
20
|
+
The value of getSideEffect is generally calculated by processing each
|
21
|
+
element of the route. A good example is #count which is actually
|
22
|
+
implemented as follows:
|
23
|
+
|
24
|
+
r = g.v.counted.cap #=> #<GraphV -> Obj-Cap(V-Counted)>
|
25
|
+
r.to_a #=> [123]
|
26
|
+
|
27
|
+
In this example, #counted is a side effect pipe. Side effect pipes can
|
28
|
+
be used on their own but their value is not reliable until the full
|
29
|
+
pipeline has been processed:
|
30
|
+
|
31
|
+
pipe = g.v.counted.pipe
|
32
|
+
pipe.getSideEffect #=> 0
|
33
|
+
pipe.next #=> #<V[3]>
|
34
|
+
pipe.getSideEffect #=> 1
|
35
|
+
|
36
|
+
HELP
|
37
|
+
else
|
38
|
+
super
|
39
|
+
end
|
40
|
+
description
|
41
|
+
end
|
42
|
+
|
12
43
|
def with=(route)
|
13
44
|
@side_effect = route
|
14
45
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Pacer
|
2
|
+
module Routes
|
3
|
+
module RouteOperations
|
4
|
+
def make_pairs(other = nil, &block)
|
5
|
+
if block
|
6
|
+
# This would be cool if it could create a pair based on
|
7
|
+
fail 'not implemented yet'
|
8
|
+
elsif other
|
9
|
+
if other.is_a? Route and [element_type, other.element_type].all? { |t| [:vertex, :edge].include? t }
|
10
|
+
et = :path
|
11
|
+
else
|
12
|
+
et = :object
|
13
|
+
end
|
14
|
+
other = other.to_a
|
15
|
+
if other.empty?
|
16
|
+
empty(self)
|
17
|
+
else
|
18
|
+
flat_map(element_type: et, route_name: 'make_pairs') do |el|
|
19
|
+
other.map { |o| [el, o] }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
else
|
23
|
+
fail Pacer::ClientError, 'No source for pairs given to make_pairs'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
data/lib/pacer/transform/map.rb
CHANGED
@@ -11,13 +11,61 @@ module Pacer
|
|
11
11
|
module Map
|
12
12
|
attr_accessor :block
|
13
13
|
|
14
|
+
def help(section = nil)
|
15
|
+
case section
|
16
|
+
when nil
|
17
|
+
puts <<HELP
|
18
|
+
Works much like Ruby's built-in map method but has some extra options and,
|
19
|
+
like all routes, does not evaluate immediately (see the :routes help topic).
|
20
|
+
|
21
|
+
Example:
|
22
|
+
|
23
|
+
mapped = [1,2,3].to_route.map { |n| n + 1 } #=> #<Obj -> Obj-Map>
|
24
|
+
|
25
|
+
mapped.to_a #=> [2,3,4]
|
26
|
+
mapped.limit(1).to_a #=> [2]
|
27
|
+
|
28
|
+
Note that the block will be called *twice* in the above example where limit(1)
|
29
|
+
is applied to the route after the map is defined. Routes do some pre-processing
|
30
|
+
and you can not assume that a function executed within a route will be executed
|
31
|
+
the expected number of times without carefully testing your logic.
|
32
|
+
|
33
|
+
Further, note that routes may be re-executed multiple times:
|
34
|
+
|
35
|
+
[mapped.to_a, mapped.to_a] #=> [[2,3,4], [2,3,4]]
|
36
|
+
|
37
|
+
The element_type option is frequently useful. The following looks up elements
|
38
|
+
by ID in the graph and produces a fully-fledged vertices route:
|
39
|
+
|
40
|
+
route = [1,2,3].to_route
|
41
|
+
mapped = route.map(graph: g, element_type: :vertex) { |n| g.vertex(n) }
|
42
|
+
|
43
|
+
mapped.out_e #=> #<Obj -> V-Map -> outE>
|
44
|
+
mapped.in_e #=> #<Obj -> V-Map -> inE>
|
45
|
+
|
46
|
+
If you want to map over a route immediately without adding a map step to it,
|
47
|
+
use the synonym for #map built-in to Ruby: #collect
|
48
|
+
|
49
|
+
[1,2,3].to_route.collect { |n| n + 1 } #=> [2,3,4]
|
50
|
+
|
51
|
+
HELP
|
52
|
+
else
|
53
|
+
super
|
54
|
+
end
|
55
|
+
description
|
56
|
+
end
|
57
|
+
|
14
58
|
protected
|
15
59
|
|
16
60
|
def attach_pipe(end_pipe)
|
17
61
|
# Must wrap based on parent pipe because the element in the block has
|
18
62
|
# not yet been affected by any of this block's transforms.
|
19
|
-
|
20
|
-
|
63
|
+
if back and back.element_type == :path
|
64
|
+
pf = Pacer::Wrappers::PathWrappingPipeFunction.new back, block
|
65
|
+
else
|
66
|
+
pf = Pacer::Wrappers::WrappingPipeFunction.new back || source, block
|
67
|
+
pf = Pacer::Wrappers::UnwrappingPipeFunction.new pf
|
68
|
+
end
|
21
69
|
pipe = com.tinkerpop.pipes.transform.TransformFunctionPipe.new pf
|
22
70
|
pipe.setStarts end_pipe if end_pipe
|
23
71
|
pipe
|
data/lib/pacer/transform/path.rb
CHANGED
@@ -2,7 +2,7 @@ module Pacer
|
|
2
2
|
module Core
|
3
3
|
module Route
|
4
4
|
def paths(*exts)
|
5
|
-
route = chain_route :transform => :path, :element_type => :
|
5
|
+
route = chain_route :transform => :path, :element_type => :path
|
6
6
|
if exts.any?
|
7
7
|
exts = exts.map { |e| Array.wrap(e) if e }
|
8
8
|
route.map(modules: Pacer::Transform::Path::Methods) do |path|
|
@@ -17,40 +17,35 @@ module Pacer
|
|
17
17
|
|
18
18
|
module Transform
|
19
19
|
module Path
|
20
|
-
|
21
|
-
def transpose
|
22
|
-
collect { |arraylist| arraylist.to_a }.transpose
|
23
|
-
end
|
20
|
+
import com.tinkerpop.pipes.transform.PathPipe
|
24
21
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
path.select { |e| e.is_a? Pacer::Vertex }.each do |vertex|
|
32
|
-
vertex.clone_into target_graph
|
33
|
-
end
|
34
|
-
path.select { |e| e.is_a? Pacer::Edge }.each do |edge|
|
35
|
-
unless edge.clone_into target_graph, ignore_missing_vertices: true
|
36
|
-
missing_edges << edge
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
if missing_edges.any?
|
41
|
-
missing_edges.to_route(graph: graph, element_type: :edge).bulk_job nil, target_graph do |edge|
|
42
|
-
edge.clone_into target_graph,
|
43
|
-
ignore_missing_vertices: opts[:ignore_missing_vertices],
|
44
|
-
show_missing_vertices: opts[:show_missing_vertices]
|
45
|
-
end
|
46
|
-
end
|
47
|
-
target_graph
|
48
|
-
end
|
49
|
-
end
|
22
|
+
def help(section = nil)
|
23
|
+
case section
|
24
|
+
when nil
|
25
|
+
puts <<HELP
|
26
|
+
Each path returned by this method represents each intermediate value that was
|
27
|
+
used to calculate the resulting value:
|
50
28
|
|
51
|
-
|
29
|
+
r = [1,2,3].to_route.map { |n| n*2 }
|
30
|
+
p = r.paths #=> #<Obj -> Obj-Map -> Path-Path>
|
31
|
+
p.to_a #=> [[1,1], [2,4], [3,6]]
|
32
|
+
|
33
|
+
This is especially useful for graph traversals.
|
34
|
+
|
35
|
+
g.v.out_e.in_v.out_e.in_v.paths.first #=> [#<V[37]>,
|
36
|
+
# #<E[41]:37-patcit-38>,
|
37
|
+
# #<V[38]>,
|
38
|
+
# #<E[40]:38-document-id-39>,
|
39
|
+
# #<V[39]>]
|
52
40
|
|
53
|
-
|
41
|
+
See the :paths section for more details and general information about paths.
|
42
|
+
|
43
|
+
HELP
|
44
|
+
else
|
45
|
+
super
|
46
|
+
end
|
47
|
+
description
|
48
|
+
end
|
54
49
|
|
55
50
|
protected
|
56
51
|
|
@@ -59,16 +54,6 @@ module Pacer
|
|
59
54
|
pipe.setStarts end_pipe if end_pipe
|
60
55
|
pipe
|
61
56
|
end
|
62
|
-
|
63
|
-
def configure_iterator(iter)
|
64
|
-
if respond_to? :graph
|
65
|
-
pipe = Pacer::Pipes::PathWrappingPipe.new(graph)
|
66
|
-
pipe.setStarts iter
|
67
|
-
pipe
|
68
|
-
else
|
69
|
-
iter
|
70
|
-
end
|
71
|
-
end
|
72
57
|
end
|
73
58
|
end
|
74
59
|
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
module Pacer
|
2
|
+
module Core
|
3
|
+
module Graph
|
4
|
+
module PathRoute
|
5
|
+
# Transform raw paths to a tree:
|
6
|
+
# [a b c]
|
7
|
+
# [a b d]
|
8
|
+
# [a e f]
|
9
|
+
# [a e g]
|
10
|
+
# -- becomes --
|
11
|
+
# [a [b [c]
|
12
|
+
# [d]]
|
13
|
+
# [e [f]
|
14
|
+
# [g]]]
|
15
|
+
|
16
|
+
# The default comparator block is { |prev, current| prev == current }
|
17
|
+
def tree(&block)
|
18
|
+
wrapped.chain_route transform: :path_tree, element_type: :object, compare: block
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
module Transform
|
25
|
+
module PathTree
|
26
|
+
attr_accessor :compare
|
27
|
+
|
28
|
+
protected
|
29
|
+
|
30
|
+
def attach_pipe(end_pipe)
|
31
|
+
pipe = PathTreePipe.new compare
|
32
|
+
pipe.setStarts end_pipe
|
33
|
+
pipe
|
34
|
+
end
|
35
|
+
|
36
|
+
class PathTreePipe < Pacer::Pipes::RubyPipe
|
37
|
+
def initialize(compare = nil)
|
38
|
+
super()
|
39
|
+
self.building_path = nil
|
40
|
+
self.prev_path = nil
|
41
|
+
self.compare = compare || proc { |a, b| a == b }
|
42
|
+
end
|
43
|
+
|
44
|
+
# NOTE: doesn't handle variable length paths yet...
|
45
|
+
def processNextStart()
|
46
|
+
while true
|
47
|
+
path = starts.next
|
48
|
+
if building_path
|
49
|
+
if compare.call path.first, building_path.first
|
50
|
+
add_path path
|
51
|
+
else
|
52
|
+
return next_path(path)
|
53
|
+
end
|
54
|
+
else
|
55
|
+
next_path(path)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
rescue Pacer::EmptyPipe, java.util.NoSuchElementException
|
59
|
+
if building_path
|
60
|
+
r = building_path
|
61
|
+
self.building_path = nil
|
62
|
+
r
|
63
|
+
else
|
64
|
+
raise EmptyPipe.instance
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
attr_accessor :building_path, :prev_path, :compare
|
71
|
+
|
72
|
+
def make(path)
|
73
|
+
path.reverse.inject(nil) do |inner, e|
|
74
|
+
if inner
|
75
|
+
[e, inner]
|
76
|
+
else
|
77
|
+
[e]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def add_path(path)
|
83
|
+
working = building_path
|
84
|
+
(1..path.length - 1).each do |pos|
|
85
|
+
current = path[pos]
|
86
|
+
prev = prev_path[pos]
|
87
|
+
if compare.call current, prev
|
88
|
+
working = working.last
|
89
|
+
else
|
90
|
+
if pos < path.length
|
91
|
+
working << make(path[pos..-1])
|
92
|
+
else
|
93
|
+
working << [current]
|
94
|
+
end
|
95
|
+
break
|
96
|
+
end
|
97
|
+
end
|
98
|
+
self.prev_path = path
|
99
|
+
end
|
100
|
+
|
101
|
+
def next_path(path)
|
102
|
+
finished = building_path
|
103
|
+
self.building_path = make path
|
104
|
+
self.prev_path = path
|
105
|
+
finished
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Pacer::Core::Graph::ElementRoute
|
2
|
+
def payload(&block)
|
3
|
+
chain_route transform: :payload, block: block
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
module Pacer::Transform
|
8
|
+
module Payload
|
9
|
+
attr_accessor :block
|
10
|
+
|
11
|
+
protected
|
12
|
+
|
13
|
+
def attach_pipe(end_pipe)
|
14
|
+
pipe = PayloadPipe.new(self, block)
|
15
|
+
pipe.setStarts end_pipe if end_pipe
|
16
|
+
pipe
|
17
|
+
end
|
18
|
+
|
19
|
+
class PayloadPipe < Pacer::Pipes::RubyPipe
|
20
|
+
field_reader :currentEnd
|
21
|
+
|
22
|
+
attr_reader :block, :wrapper
|
23
|
+
|
24
|
+
def initialize(route, block)
|
25
|
+
super()
|
26
|
+
if route.element_type == :edge
|
27
|
+
@wrapper = Pacer::Payload::Edge
|
28
|
+
elsif route.element_type == :vertex
|
29
|
+
@wrapper = Pacer::Payload::Vertex
|
30
|
+
else
|
31
|
+
fail Pacer::ClientError, 'Can not use PayloadPipe on non-element data'
|
32
|
+
end
|
33
|
+
block ||= proc { |el| nil }
|
34
|
+
@block = Pacer::Wrappers::WrappingPipeFunction.new route, block
|
35
|
+
end
|
36
|
+
|
37
|
+
def processNextStart
|
38
|
+
el = starts.next
|
39
|
+
@wrapper.new el, block.call(el)
|
40
|
+
end
|
41
|
+
|
42
|
+
def getPathToHere
|
43
|
+
path = super
|
44
|
+
path.remove path.size - 1
|
45
|
+
path.add currentEnd
|
46
|
+
path
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|