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
data/Gemfile CHANGED
@@ -13,12 +13,7 @@ group :development do
13
13
  # pacer-* gems are required for testing pacer.
14
14
  # If you have the gem repos cloned locally, we'll use them.
15
15
  #
16
- libs = [
17
- ['pacer-neo4j', '2.0.0.pre'],
18
- ['pacer-orient', '2.0.0.pre'],
19
- ['pacer-dex', '2.0.0.pre']
20
- ]
21
- libs.each do |lib, version|
16
+ [ 'pacer-neo4j', 'pacer-orient', 'pacer-dex'].each do |lib|
22
17
  if File.directory? "../#{lib}"
23
18
  gem lib, :path => "../#{lib}"
24
19
  end
data/lib/pacer.rb CHANGED
@@ -42,6 +42,10 @@ module Pacer
42
42
  require 'pacer/loader'
43
43
 
44
44
  class << self
45
+ def help(section = nil)
46
+ Pacer.tg.help section
47
+ end
48
+
45
49
  # A global place for pacer to put debug info if it's tucked deep in
46
50
  # its internals. Should typically not be used unless a mysterious
47
51
  # bug needs to be analyzed but that never really happens ;)
@@ -0,0 +1,44 @@
1
+ module Pacer
2
+ module Payload
3
+ class Element
4
+ include com.tinkerpop.blueprints.Element
5
+ extend Forwardable
6
+
7
+ def initialize(element, payload = nil)
8
+ @element = element
9
+ self.payload = payload
10
+ end
11
+
12
+ def inspect
13
+ "#<Payload #{ element.inspect } -- #{ payload.inspect }>"
14
+ end
15
+
16
+ attr_reader :element
17
+ attr_accessor :payload
18
+ end
19
+
20
+ class Edge < Element
21
+ include com.tinkerpop.blueprints.Edge
22
+
23
+ def_delegators :@element,
24
+ # Object
25
+ :equals, :toString, :hashCode,
26
+ # Element
27
+ :getId, :getPropertyKeys, :getProperty, :setProperty, :removeProperty, :getRawElement,
28
+ # Edge
29
+ :getLabel, :getVertex, :getRawEdge
30
+ end
31
+
32
+ class Vertex < Element
33
+ include com.tinkerpop.blueprints.Vertex
34
+
35
+ def_delegators :@element,
36
+ # Object
37
+ :equals, :toString, :hashCode,
38
+ # Element
39
+ :getId, :getPropertyKeys, :getProperty, :setProperty, :removeProperty, :getRawElement,
40
+ # Vertex
41
+ :getEdges, :getVertices, :query, :getRawVertex
42
+ end
43
+ end
44
+ end
@@ -11,3 +11,4 @@ require 'pacer/core/graph/element_route'
11
11
  require 'pacer/core/graph/edges_route'
12
12
  require 'pacer/core/graph/vertices_route'
13
13
  require 'pacer/core/graph/mixed_route'
14
+ require 'pacer/core/graph/path_route'
@@ -40,8 +40,9 @@ module Pacer::Core::Graph
40
40
  # Create a new TinkerGraph based on the paths of all matching elements.
41
41
  #
42
42
  # @return [TinkerGraph] the subgraph
43
- def subgraph opts = {}
44
- paths.subgraph nil, opts
43
+ def subgraph(graph = nil, opts = {})
44
+ graph, opts = [nil, graph] if graph.is_a? Hash
45
+ paths.subgraph graph, opts
45
46
  end
46
47
 
47
48
  # Delete all matching elements.
@@ -0,0 +1,138 @@
1
+ module Pacer::Core::Graph
2
+ module PathRoute
3
+ def help(section = nil)
4
+ case section
5
+ when :paths
6
+ puts <<HELP
7
+ The following path helper methods are available:
8
+
9
+ #transpose Works the same as Array transpase
10
+
11
+ #subgraph(target_graph, opts) Add each element in the path to the graph
12
+ target_graph: PacerGraph (optional) if not specified creates a new TG.
13
+ opts:
14
+ create_vertices: Boolean Create vertices for edges if needed
15
+ Edges can not be created without both vertices being present. If
16
+ this option is not set and a vertex is missing, raises a
17
+ Pacer::ElementNotFound exception.
18
+ ignore_missing_vertices: Boolean Squelches the above mentioned exception
19
+ show_missing_vertices: Boolean Complain about missing vertices
20
+
21
+ #compact_paths Removes nils from paths
22
+
23
+ #heads Route to only the first element from each path
24
+
25
+ #tails Route to only the last element from each path
26
+
27
+ #pairs(head, tail) Route to a mini path of only the first and last elements
28
+ head: Number Array index of the : first : element in the pair
29
+ tail: Number : second :
30
+
31
+ #len(n) Filter paths by length
32
+ n: Number | Range
33
+
34
+ #hashify Make a hash of the properties and relationships of the path
35
+ This is just a simple view on the data to facilitate analysis
36
+
37
+ HELP
38
+ else
39
+ super
40
+ end
41
+ description
42
+ end
43
+
44
+ def transpose
45
+ collect { |arraylist| arraylist.to_a }.transpose
46
+ end
47
+
48
+ def subgraph(target_graph = nil, opts = {})
49
+ raise "Can't create a subgraph within itself." if target_graph == graph
50
+ target_graph ||= Pacer.tg
51
+ target_graph.vertex_name ||= graph.vertex_name
52
+ missing_edges = Set[]
53
+ bulk_job(nil, target_graph) do |path|
54
+ path.select { |e| e.is_a? Pacer::Vertex }.each do |vertex|
55
+ vertex.clone_into target_graph
56
+ end
57
+ path.select { |e| e.is_a? Pacer::Edge }.each do |edge|
58
+ unless edge.clone_into target_graph, ignore_missing_vertices: true
59
+ missing_edges << edge
60
+ end
61
+ end
62
+ end
63
+ if missing_edges.any?
64
+ missing_edges.to_route(graph: graph, element_type: :edge).bulk_job nil, target_graph do |edge|
65
+ edge.clone_into target_graph,
66
+ ignore_missing_vertices: opts[:ignore_missing_vertices],
67
+ show_missing_vertices: opts[:show_missing_vertices]
68
+ end
69
+ end
70
+ target_graph
71
+ end
72
+
73
+ def payloads
74
+ map element_type: :path, route_name: 'payloads' do |path|
75
+ path.map do |e|
76
+ if e.is_a? Pacer::Payload::Element
77
+ e.payload
78
+ elsif e.is_a? Pacer::Wrappers::ElementWrapper
79
+ e.element_payload
80
+ end
81
+ end
82
+ end
83
+ end
84
+
85
+ def compact_paths
86
+ map element_type: :path, route_name: 'compact' do |path|
87
+ path.compact
88
+ end
89
+ end
90
+
91
+ def heads(et = :vertex)
92
+ map element_type: et, route_name: 'heads' do |path|
93
+ path.first
94
+ end
95
+ end
96
+
97
+ def tails(et = :vertex)
98
+ map element_type: et, route_name: 'tails' do |path|
99
+ path.last
100
+ end
101
+ end
102
+
103
+ def pairs(head = 0, tail = -1)
104
+ map element_type: :path, route_name: "pairs[#{ head },#{ tail }]" do |path|
105
+ [path[head], path[tail]]
106
+ end
107
+ end
108
+
109
+ def len(n)
110
+ select do |path|
111
+ n === path.length
112
+ end
113
+ end
114
+
115
+ def hashify
116
+ map(element_type: :hash, route_name: 'trees') do |path|
117
+ path.to_a.reverse.reduce({}) do |tree, element|
118
+ if element.element_type == :vertex
119
+ tree.merge element.properties
120
+ else
121
+ { element.label => [tree] }
122
+ end
123
+ end
124
+ end
125
+ end
126
+ protected
127
+
128
+ def configure_iterator(iter)
129
+ if respond_to? :graph
130
+ pipe = Pacer::Pipes::PathWrappingPipe.new(graph)
131
+ pipe.setStarts iter
132
+ pipe
133
+ else
134
+ iter
135
+ end
136
+ end
137
+ end
138
+ end
@@ -156,6 +156,148 @@ module Pacer
156
156
  nil
157
157
  end
158
158
 
159
+ def help(section = nil)
160
+ general_topics = <<HELP
161
+ Some general help sections:
162
+ :routes What are Pacer's routes?
163
+ :basics Simple usage examples
164
+ :creation How to create records
165
+ :tools Some things you can do
166
+ :graphs Available graphs
167
+ :help How to contribute to Pacer's inline help
168
+ HELP
169
+ if section == :help
170
+ puts <<HELP
171
+ Contributions of help topics will be greatly apreciated.
172
+
173
+ Help topics should be added to the modules that they describe. General topics
174
+ help can be added here for now but will likely be moved eventually.
175
+
176
+ See lib/pacer/transform/map.rb for an example of how to define contextual help
177
+ topics.
178
+
179
+ If you add a general topic, remember to add it to the list above.
180
+
181
+ Remember to call super for unrecognized sections! :)
182
+
183
+ HELP
184
+ elsif section == :routes
185
+ puts <<HELP
186
+ Pacer's routes are a very efficient and fast way to deal with data.
187
+
188
+ The fundamental thing about them is that they are lazily evaluated, which
189
+ allows very expensive traversals to be defined, yet nearly always produces
190
+ results immediately with very low memory requirements, too.
191
+
192
+
193
+
194
+ HELP
195
+ elsif section == :basics
196
+ puts <<HELP
197
+ Pacer basics:
198
+
199
+ g = Pacer.tg # create an in-memory graph
200
+ # - help(:graphs) for other types
201
+ g.v # create a route to all vertices in the graph
202
+ # vertices are your basic documents.
203
+ g.e # ... or all edges
204
+ # edges connect documents. They can have properties, too!
205
+ g.v(name: 'Sue', gender: 'male') # find all boys named Sue
206
+ # all elements are schemaless, so you
207
+ # can specify any properties
208
+ g.v(name: 'Sue', gender: 'male').count # how many are there?
209
+ # see how we can chain calls? Powerful
210
+ # traversals can be defined this way.
211
+ g.v.out_e(:friend) # see Sue's 'friend' relationships
212
+ g.v.out_e(:friend).in_v # continue on to his friends
213
+ g.v.out_e(:friend).in_v.out(:friends) # continue on to his friends-of-friends
214
+ g.v.in(:friend) # see who has friended Sue.
215
+ # all edges are directional to follow an
216
+ # edge, use #out or #out_e; to go
217
+ # backwards along an edge, use #in or
218
+ # #in_e
219
+
220
+ #{general_topics}
221
+
222
+ HELP
223
+ elsif section == :creation
224
+ puts <<HELP
225
+ How we can create records in a Pacer graph:
226
+
227
+ g = Pacer.tg # create a new in-memory graph to play with
228
+ sue = g.create_vertex name: 'Sue', gender: 'male'
229
+ # create a record with properties
230
+ ghost = g.create_vertex # get lazy and create an empty one
231
+ sue.add_edges_to :friend, ghost # Sue has friended the ghost.
232
+ # This relationship can be traversed in
233
+ # both directions so a reverse
234
+ # relationship is *not* required, but can
235
+ # be created:
236
+ ghost.add_edges_to :friend, sue, type: 'spooky'
237
+ # We can also create edges with properties
238
+ sue[:age] = 27 # It's that easy to add or change a property
239
+ sue['fav foods'] = [pie, donuts] # Pacer can shoehorn any data into the
240
+ # graph as long as it's serializable.
241
+
242
+ #{general_topics}
243
+
244
+ HELP
245
+ elsif section == :tools
246
+ puts <<HELP
247
+
248
+ HELP
249
+ elsif section == :graphs or section == :plugins
250
+ puts <<HELP
251
+ Various graphs are supported in their own Rubygems. Check out pacer-neo4j,
252
+ pacer-orient, pacer-dex for now. New graphs emerge frequently and I hope to
253
+ support many of them.
254
+
255
+ Search rubygems.org or github for projects that start with "pacer-" to see what
256
+ other plugins exist as well.
257
+
258
+ #{general_topics}
259
+
260
+ HELP
261
+ else
262
+ if section
263
+ puts "Unrecognized help section specified"
264
+ puts
265
+ elsif not is_a? Graph
266
+ puts "No specialized help has been defined for this step yet."
267
+ puts
268
+ end
269
+ puts <<HELP
270
+ How to use Pacer's inline help:
271
+
272
+ You can use Pacer.help(:section) to print help on general topics, or get
273
+ context-specific help by calling help on any route. For example:
274
+
275
+ graph.v.out_e.map.help # will give you help about the map command.
276
+
277
+ #{general_topics}
278
+
279
+ General options (may not be available for all methods)
280
+
281
+ element_type: Symbol Set what type of element is emitted from this step.
282
+ registered types: #{ Pacer::RouteBuilder.current.element_types.keys.map(&:inspect).join ', ' }
283
+
284
+ graph: PacerGraph If the route contains graph elements, specify that they
285
+ are from this graph
286
+
287
+ route_name: String Name for this route when inspecting it in IRB.
288
+
289
+ info: String Put what you want here. Appears when the route is inspected.
290
+
291
+ extensions: [Module] Extra extensions to add to the route.
292
+
293
+ wrapper: Class Wrap elements in this class.
294
+ For each element, wrapper.new(graph, element) happens
295
+
296
+ HELP
297
+ end
298
+ description
299
+ end
300
+
159
301
  def description(join = ' -> ')
160
302
  "#<#{inspect_strings.join(join)}>"
161
303
  end
@@ -308,7 +450,9 @@ module Pacer
308
450
  # @return [java.util.Iterator]
309
451
  def source_iterator
310
452
  if @source
311
- iterator_from_source(@source)
453
+ iter = iterator_from_source(@source)
454
+ iter.enablePath(true) if iter.respond_to? :enablePath
455
+ iter
312
456
  elsif @back
313
457
  @back.send(:source_iterator)
314
458
  end
@@ -70,6 +70,8 @@ module Pacer
70
70
  'Obj'
71
71
  when :mixed
72
72
  'Elem'
73
+ else
74
+ element_type.to_s.capitalize
73
75
  end
74
76
  s = "#{s} #{ @info }" if @info
75
77
  s
@@ -47,6 +47,91 @@ module Pacer
47
47
  attr_reader :looping_route
48
48
  attr_accessor :while_description
49
49
 
50
+ def help(section = nil)
51
+ case section
52
+ when nil
53
+ puts <<HELP
54
+ Loops allow you to define repetative paths and paths of indeterminate length.
55
+ As a path is expanded within a loop, a control block that you define can
56
+ determine whether the given vertex should be emitted or not and whether it
57
+ should be used to continue looping or not.
58
+
59
+ There are two minor variations of the loop pipe. This is much more efficient
60
+ because it does not need to keep track of every element's path.
61
+
62
+ g.v.
63
+ loop { |route| route.out_e.in_v }.
64
+ while { |element, depth| :loop_and_emit }
65
+
66
+ This is less efficient but having the path available to you can be very
67
+ powerful:
68
+
69
+ g.v.
70
+ loop { |route| route.out_e.in_v }.
71
+ while { |element, depth, path| :loop_and_emit }
72
+
73
+ Control block arguments:
74
+ element - the element that could be emitted or looped on
75
+ depth - each initial element is depth 0 and has not had the loop route applied to it yet.
76
+ path - (optional) the full array of elements that got us to where we are
77
+
78
+ From the while block, you can give back one of three symbols:
79
+ :loop - loop on this element; don't include it in results
80
+ :emit - don't loop on this element; include it in the results
81
+ :discard - don't loop on this element; don't include it in the results
82
+ :loop_and_emit - loop on this element and include it in the results
83
+
84
+ In addition:
85
+ false | nil - maps to :discard
86
+ anything else - maps to :loop_and_emit
87
+
88
+ See the :examples section for some interesting ways to use the loop pipe.
89
+
90
+ HELP
91
+ when :examples
92
+ puts <<HELP
93
+ Range from 1 to 9:
94
+
95
+ [1].to_route.loop { |r| r.map { |n| n + 1 } }.while { |n, depth| n < 10 }
96
+ #==> 1 2 3 4 5 6 7 8 9
97
+
98
+ Why didn't this give me even numbers?
99
+
100
+ [1].to_route.loop { |r| r.map { |n| n + 1 } }.while { |n, depth| n.even? ? :emit : :emit_and_loop }
101
+ #==> 1 2
102
+
103
+ Odd numbers:
104
+
105
+ [1].to_route.loop { |r| r.map { |n| n + 1 } }.while { |n, depth| n.even? ? :loop : :emit_and_loop }.limit(10)
106
+ #==> 1 3 5 7 9 11 13 15 17 19
107
+
108
+ Fibonacci sequence:
109
+
110
+ [[0, 1]].to_route(element_type: :path).
111
+ loop { |r| r.map { |a, b| [b, a+b] } }.
112
+ while { true }.limit(40).
113
+ tails(element_type: :number)
114
+ #==> 1 1 2 3 5 8 13 21
115
+ #==> 34 55 89 144 233 377 610 987
116
+ #==> 1597 2584 4181 6765 10946 17711 28657 46368
117
+ #==> 75025 121393 196418 317811 514229 832040 1346269 2178309
118
+ #==> 3524578 5702887 9227465 14930352 24157817 39088169 63245986 102334155
119
+
120
+ You usually won't mean to do these, but you can:
121
+
122
+ [1].to_route.loop { |r| 123 }.while { true }.limit(10)
123
+ #==> 123 123 123 123 123 123 123 123 123 123
124
+
125
+ [1].to_route.loop { |r| r }.while { true }.limit(10)
126
+ #==> 1 1 1 1 1 1 1 1 1 1
127
+
128
+ HELP
129
+ else
130
+ super
131
+ end
132
+ description
133
+ end
134
+
50
135
  def looping_route=(route)
51
136
  if route.is_a? Proc
52
137
  @looping_route = Pacer::Route.block_branch(self, route)