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.
Files changed (51) hide show
  1. data/Gemfile +1 -6
  2. data/lib/{pacer-1.0.3-standalone.jar → pacer-1.1.0-standalone.jar} +0 -0
  3. data/lib/pacer.rb +4 -0
  4. data/lib/pacer/blueprints/payload_elements.rb +44 -0
  5. data/lib/pacer/core/graph.rb +1 -0
  6. data/lib/pacer/core/graph/element_route.rb +3 -2
  7. data/lib/pacer/core/graph/path_route.rb +138 -0
  8. data/lib/pacer/core/route.rb +145 -1
  9. data/lib/pacer/filter/empty_filter.rb +2 -0
  10. data/lib/pacer/filter/loop_filter.rb +85 -0
  11. data/lib/pacer/filter/property_filter/filters.rb +6 -1
  12. data/lib/pacer/graph/graph_transactions_mixin.rb +6 -1
  13. data/lib/pacer/graph/pacer_graph.rb +47 -30
  14. data/lib/pacer/graph/yaml_encoder.rb +2 -0
  15. data/lib/pacer/loader.rb +9 -0
  16. data/lib/pacer/pipe/block_filter_pipe.rb +3 -2
  17. data/lib/pacer/pipe/loop_pipe.rb +23 -17
  18. data/lib/pacer/pipe/multi_pipe.rb +40 -0
  19. data/lib/pacer/pipe/naked_pipe.rb +21 -0
  20. data/lib/pacer/pipe/unwrapping_pipe.rb +4 -0
  21. data/lib/pacer/pipe/wrapping_pipe.rb +4 -0
  22. data/lib/pacer/pipes.rb +2 -1
  23. data/lib/pacer/route/mixin/route_operations.rb +15 -0
  24. data/lib/pacer/route_builder.rb +8 -11
  25. data/lib/pacer/side_effect/counted.rb +1 -0
  26. data/lib/pacer/side_effect/group_count.rb +1 -1
  27. data/lib/pacer/support/array.rb +18 -0
  28. data/lib/pacer/support/enumerable.rb +4 -0
  29. data/lib/pacer/transform/cap.rb +31 -0
  30. data/lib/pacer/transform/flat_map.rb +9 -0
  31. data/lib/pacer/transform/make_pairs.rb +29 -0
  32. data/lib/pacer/transform/map.rb +50 -2
  33. data/lib/pacer/transform/path.rb +27 -42
  34. data/lib/pacer/transform/path_tree.rb +111 -0
  35. data/lib/pacer/transform/payload.rb +50 -0
  36. data/lib/pacer/transform/process.rb +15 -0
  37. data/lib/pacer/transform/wrapped_path.rb +23 -0
  38. data/lib/pacer/utils/graph_analysis.rb +21 -7
  39. data/lib/pacer/version.rb +1 -1
  40. data/lib/pacer/wrappers/edge_wrapper.rb +18 -7
  41. data/lib/pacer/wrappers/element_wrapper.rb +4 -0
  42. data/lib/pacer/wrappers/index_wrapper.rb +1 -1
  43. data/lib/pacer/wrappers/path_wrapping_pipe_function.rb +85 -0
  44. data/lib/pacer/wrappers/vertex_wrapper.rb +33 -3
  45. data/pom.xml +1 -1
  46. data/spec/pacer/filter/property_filter/edge_filters_spec.rb +63 -0
  47. data/spec/pacer/filter/property_filter/filters_spec.rb +17 -7
  48. data/spec/pacer/transform/join_spec.rb +0 -1
  49. data/spec/pacer/transform/path_tree_spec.rb +97 -0
  50. data/spec/spec_helper.rb +2 -2
  51. metadata +18 -3
@@ -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
- case element_type source, args
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)
@@ -8,6 +8,7 @@ module Pacer
8
8
  def count
9
9
  counted.count
10
10
  end
11
+ alias run! count
11
12
  end
12
13
  end
13
14
 
@@ -19,7 +19,7 @@ module Pacer
19
19
  def to_h
20
20
  h = {}
21
21
  min = @min || 0
22
- side_effect.each do |k,v|
22
+ cap.first.each do |k,v|
23
23
  h[k] = v if v >= min
24
24
  end
25
25
  h
@@ -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
  #
@@ -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,9 @@
1
+ module Pacer
2
+ module Routes
3
+ module RouteOperations
4
+ def flat_map(opts = {}, &block)
5
+ map(&block).scatter(opts)
6
+ end
7
+ end
8
+ end
9
+ 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
+
@@ -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
- pf = Pacer::Wrappers::WrappingPipeFunction.new back || source, block
20
- pf = Pacer::Wrappers::UnwrappingPipeFunction.new pf
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
@@ -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 => :object
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
- module Methods
21
- def transpose
22
- collect { |arraylist| arraylist.to_a }.transpose
23
- end
20
+ import com.tinkerpop.pipes.transform.PathPipe
24
21
 
25
- def subgraph(target_graph = nil, opts = {})
26
- raise "Can't create a subgraph within itself." if target_graph == graph
27
- target_graph ||= Pacer.tg
28
- target_graph.vertex_name ||= graph.vertex_name
29
- missing_edges = Set[]
30
- bulk_job(nil, target_graph) do |path|
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
- import com.tinkerpop.pipes.transform.PathPipe
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
- include Methods
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