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.
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