pacer 1.0.3-java → 1.1.0-java
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/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
|