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