pacer 0.9.1.1-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 (184) hide show
  1. data/.autotest +8 -0
  2. data/.document +5 -0
  3. data/.gitignore +26 -0
  4. data/.rspec +1 -0
  5. data/.rvmrc +0 -0
  6. data/CONTRIBUTORS +5 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE.txt +24 -0
  9. data/README.md +187 -0
  10. data/Rakefile +49 -0
  11. data/autotest/discover.rb +1 -0
  12. data/bin/autospec +16 -0
  13. data/bin/autotest +16 -0
  14. data/bin/rake +16 -0
  15. data/bin/rcov +16 -0
  16. data/bin/rspec +16 -0
  17. data/bin/yard +16 -0
  18. data/bin/yardoc +16 -0
  19. data/lib/pacer/blueprints/extensions.rb +77 -0
  20. data/lib/pacer/blueprints/multi_graph.rb +121 -0
  21. data/lib/pacer/blueprints/ruby_graph.rb +199 -0
  22. data/lib/pacer/blueprints/tg.rb +100 -0
  23. data/lib/pacer/blueprints.rb +4 -0
  24. data/lib/pacer/core/graph/edges_route.rb +92 -0
  25. data/lib/pacer/core/graph/element_route.rb +171 -0
  26. data/lib/pacer/core/graph/graph_index_route.rb +48 -0
  27. data/lib/pacer/core/graph/graph_route.rb +55 -0
  28. data/lib/pacer/core/graph/mixed_route.rb +96 -0
  29. data/lib/pacer/core/graph/vertices_route.rb +220 -0
  30. data/lib/pacer/core/graph.rb +13 -0
  31. data/lib/pacer/core/route.rb +502 -0
  32. data/lib/pacer/core/side_effect.rb +11 -0
  33. data/lib/pacer/core.rb +8 -0
  34. data/lib/pacer/exceptions.rb +11 -0
  35. data/lib/pacer/extensions/block_filter_element.rb +22 -0
  36. data/lib/pacer/extensions.rb +6 -0
  37. data/lib/pacer/filter/block_filter.rb +31 -0
  38. data/lib/pacer/filter/collection_filter.rb +109 -0
  39. data/lib/pacer/filter/empty_filter.rb +70 -0
  40. data/lib/pacer/filter/future_filter.rb +68 -0
  41. data/lib/pacer/filter/index_filter.rb +30 -0
  42. data/lib/pacer/filter/loop_filter.rb +95 -0
  43. data/lib/pacer/filter/object_filter.rb +55 -0
  44. data/lib/pacer/filter/property_filter/edge_filters.rb +93 -0
  45. data/lib/pacer/filter/property_filter/filters.rb +269 -0
  46. data/lib/pacer/filter/property_filter.rb +111 -0
  47. data/lib/pacer/filter/random_filter.rb +13 -0
  48. data/lib/pacer/filter/range_filter.rb +104 -0
  49. data/lib/pacer/filter/uniq_filter.rb +12 -0
  50. data/lib/pacer/filter/where_filter/node_visitor.rb +280 -0
  51. data/lib/pacer/filter/where_filter.rb +47 -0
  52. data/lib/pacer/filter.rb +17 -0
  53. data/lib/pacer/function_resolver.rb +43 -0
  54. data/lib/pacer/graph/edge_mixin.rb +127 -0
  55. data/lib/pacer/graph/element_mixin.rb +202 -0
  56. data/lib/pacer/graph/graph_indices_mixin.rb +93 -0
  57. data/lib/pacer/graph/graph_mixin.rb +361 -0
  58. data/lib/pacer/graph/graph_transactions_mixin.rb +207 -0
  59. data/lib/pacer/graph/index_mixin.rb +30 -0
  60. data/lib/pacer/graph/vertex_mixin.rb +119 -0
  61. data/lib/pacer/graph.rb +14 -0
  62. data/lib/pacer/pipe/blackbox_pipeline.rb +48 -0
  63. data/lib/pacer/pipe/block_filter_pipe.rb +38 -0
  64. data/lib/pacer/pipe/collection_filter_pipe.rb +10 -0
  65. data/lib/pacer/pipe/cross_product_transform_pipe.rb +48 -0
  66. data/lib/pacer/pipe/enumerable_pipe.rb +30 -0
  67. data/lib/pacer/pipe/expandable_pipe.rb +63 -0
  68. data/lib/pacer/pipe/id_collection_filter_pipe.rb +33 -0
  69. data/lib/pacer/pipe/is_empty_pipe.rb +30 -0
  70. data/lib/pacer/pipe/is_unique_pipe.rb +61 -0
  71. data/lib/pacer/pipe/label_collection_filter_pipe.rb +21 -0
  72. data/lib/pacer/pipe/label_prefix_pipe.rb +21 -0
  73. data/lib/pacer/pipe/loop_pipe.rb +86 -0
  74. data/lib/pacer/pipe/map_pipe.rb +36 -0
  75. data/lib/pacer/pipe/never_pipe.rb +9 -0
  76. data/lib/pacer/pipe/process_pipe.rb +37 -0
  77. data/lib/pacer/pipe/property_comparison_pipe.rb +40 -0
  78. data/lib/pacer/pipe/ruby_pipe.rb +25 -0
  79. data/lib/pacer/pipe/simple_visitor_pipe.rb +43 -0
  80. data/lib/pacer/pipe/stream_sort_pipe.rb +84 -0
  81. data/lib/pacer/pipe/stream_uniq_pipe.rb +33 -0
  82. data/lib/pacer/pipe/type_filter_pipe.rb +22 -0
  83. data/lib/pacer/pipe/unary_transform_pipe.rb +59 -0
  84. data/lib/pacer/pipe/variable_store_iterator_wrapper.rb +26 -0
  85. data/lib/pacer/pipe/visitor_pipe.rb +67 -0
  86. data/lib/pacer/pipes.rb +61 -0
  87. data/lib/pacer/route/mixin/bulk_operations.rb +52 -0
  88. data/lib/pacer/route/mixin/route_operations.rb +107 -0
  89. data/lib/pacer/route/mixin/variable_route_module.rb +26 -0
  90. data/lib/pacer/route/mixins.rb +3 -0
  91. data/lib/pacer/route.rb +228 -0
  92. data/lib/pacer/routes.rb +6 -0
  93. data/lib/pacer/side_effect/aggregate.rb +31 -0
  94. data/lib/pacer/side_effect/counted.rb +30 -0
  95. data/lib/pacer/side_effect/group_count.rb +44 -0
  96. data/lib/pacer/side_effect/is_unique.rb +32 -0
  97. data/lib/pacer/side_effect/section.rb +25 -0
  98. data/lib/pacer/side_effect/visitor.rb +37 -0
  99. data/lib/pacer/side_effect.rb +11 -0
  100. data/lib/pacer/support/array_list.rb +28 -0
  101. data/lib/pacer/support/enumerable.rb +100 -0
  102. data/lib/pacer/support/hash.rb +9 -0
  103. data/lib/pacer/support/iterator_mixins.rb +110 -0
  104. data/lib/pacer/support/native_exception.rb +22 -0
  105. data/lib/pacer/support/proc.rb +16 -0
  106. data/lib/pacer/support.rb +10 -0
  107. data/lib/pacer/transform/cap.rb +50 -0
  108. data/lib/pacer/transform/gather.rb +9 -0
  109. data/lib/pacer/transform/has_count_cap.rb +41 -0
  110. data/lib/pacer/transform/join.rb +181 -0
  111. data/lib/pacer/transform/map.rb +23 -0
  112. data/lib/pacer/transform/path.rb +50 -0
  113. data/lib/pacer/transform/process.rb +23 -0
  114. data/lib/pacer/transform/scatter.rb +23 -0
  115. data/lib/pacer/transform/sort_section.rb +103 -0
  116. data/lib/pacer/transform/stream_sort.rb +21 -0
  117. data/lib/pacer/transform/stream_uniq.rb +21 -0
  118. data/lib/pacer/transform.rb +16 -0
  119. data/lib/pacer/utils/graph_analysis.rb +112 -0
  120. data/lib/pacer/utils/trie.rb +93 -0
  121. data/lib/pacer/utils/tsort.rb +65 -0
  122. data/lib/pacer/utils/y_files.rb +127 -0
  123. data/lib/pacer/utils.rb +10 -0
  124. data/lib/pacer/version.rb +13 -0
  125. data/lib/pacer/wrappers/edge_wrapper.rb +51 -0
  126. data/lib/pacer/wrappers/element_wrapper.rb +78 -0
  127. data/lib/pacer/wrappers/new_element.rb +106 -0
  128. data/lib/pacer/wrappers/vertex_wrapper.rb +51 -0
  129. data/lib/pacer/wrappers.rb +19 -0
  130. data/lib/pacer-0.9.1.1-standalone.jar +0 -0
  131. data/lib/pacer.rb +290 -0
  132. data/pacer.gemspec +30 -0
  133. data/pom/standalone.xml +22 -0
  134. data/pom.xml +124 -0
  135. data/samples/grateful-dead.xml +26380 -0
  136. data/samples/grateful_dead.rb +63 -0
  137. data/samples/profile.rb +15 -0
  138. data/spec/data/grateful-dead.xml +26380 -0
  139. data/spec/data/pacer.graphml +319 -0
  140. data/spec/pacer/blueprints/dex_spec.rb +172 -0
  141. data/spec/pacer/blueprints/neo4j_spec.rb +177 -0
  142. data/spec/pacer/blueprints/tg_spec.rb +128 -0
  143. data/spec/pacer/core/graph/edges_route_spec.rb +52 -0
  144. data/spec/pacer/core/graph/element_route_spec.rb +46 -0
  145. data/spec/pacer/core/graph/graph_route_spec.rb +94 -0
  146. data/spec/pacer/core/graph/vertices_route_spec.rb +169 -0
  147. data/spec/pacer/core/route_spec.rb +197 -0
  148. data/spec/pacer/filter/collection_filter_spec.rb +19 -0
  149. data/spec/pacer/filter/empty_filter_spec.rb +29 -0
  150. data/spec/pacer/filter/future_filter_spec.rb +97 -0
  151. data/spec/pacer/filter/loop_filter_spec.rb +31 -0
  152. data/spec/pacer/filter/property_filter_spec.rb +111 -0
  153. data/spec/pacer/filter/random_filter_spec.rb +17 -0
  154. data/spec/pacer/filter/uniq_filter_spec.rb +18 -0
  155. data/spec/pacer/filter/where_filter_spec.rb +93 -0
  156. data/spec/pacer/graph/edge_mixin_spec.rb +116 -0
  157. data/spec/pacer/graph/element_mixin_spec.rb +297 -0
  158. data/spec/pacer/graph/graph_mixin_spec.rb +538 -0
  159. data/spec/pacer/graph/index_mixin_spec.rb +0 -0
  160. data/spec/pacer/graph/vertex_mixin_spec.rb +192 -0
  161. data/spec/pacer/pipe/block_filter_pipe_spec.rb +0 -0
  162. data/spec/pacer/pipe/labels_filter_pipe_spec.rb +0 -0
  163. data/spec/pacer/pipe/ruby_pipe_spec.rb +0 -0
  164. data/spec/pacer/pipe/type_filter_pipe_spec.rb +0 -0
  165. data/spec/pacer/route/mixin/base_spec.rb +419 -0
  166. data/spec/pacer/route/mixin/bulk_operations_spec.rb +30 -0
  167. data/spec/pacer/route/mixin/route_operations_spec.rb +127 -0
  168. data/spec/pacer/support/array_list_spec.rb +0 -0
  169. data/spec/pacer/support/enumerable_spec.rb +115 -0
  170. data/spec/pacer/transform/join_spec.rb +138 -0
  171. data/spec/pacer/transform/path_spec.rb +54 -0
  172. data/spec/pacer/utils/tsort_spec.rb +89 -0
  173. data/spec/pacer/wrapper/edge_wrapper_spec.rb +33 -0
  174. data/spec/pacer/wrapper/element_wrapper_spec.rb +169 -0
  175. data/spec/pacer/wrapper/vertex_wrapper_spec.rb +33 -0
  176. data/spec/pacer_spec.rb +0 -0
  177. data/spec/spec_helper.rb +91 -0
  178. data/spec/support/contexts.rb +14 -0
  179. data/spec/support/graph_runner.rb +142 -0
  180. data/spec/support/matchers.rb +19 -0
  181. data/spec/support/use_transactions.rb +31 -0
  182. data/spec/tackle/simple_mixin.rb +21 -0
  183. data/spec/tackle/tinkerpop_graph_mixins.rb +60 -0
  184. metadata +364 -0
@@ -0,0 +1,70 @@
1
+ module Pacer
2
+ class Route
3
+ class << self
4
+ # This method is useful for creating sideline routes that branch
5
+ # off of the current route.
6
+ #
7
+ # It creates a new route without any source based on the type,
8
+ # filters, function and extensions of the given route. The main
9
+ # thing about the returned route is that the pipeline that is
10
+ # built from it will not include any of the pipes that make up
11
+ # the route it's based on.
12
+ #
13
+ # @param [Route] back the route the new route is based on.
14
+ # @return [Route]
15
+ def empty(back)
16
+ Pacer::Route.new :filter => :empty, :back => back
17
+ end
18
+
19
+ def block_branch(back, block, branch_start = nil)
20
+ if block.arity == 0
21
+ route = block.call rescue nil
22
+ else
23
+ unless branch_start
24
+ if back.is_a? Pacer::Graph
25
+ branch_start = back
26
+ else
27
+ branch_start = Pacer::Route.empty(back)
28
+ end
29
+ end
30
+ route = block.call(branch_start) rescue nil
31
+ end
32
+ if route == branch_start
33
+ identity_branch(back).route
34
+ elsif route.is_a? Pacer::Route
35
+ route.route
36
+ else
37
+ empty(back).map(&block).route
38
+ end
39
+ end
40
+
41
+ def identity_branch(back)
42
+ Pacer::Route.empty(back).chain_route(:pipe_class => Pacer::Pipes::IdentityPipe,
43
+ :route_name => '@').route
44
+ end
45
+
46
+ end
47
+ end
48
+
49
+
50
+ module Filter
51
+ module EmptyFilter
52
+ protected
53
+
54
+ def after_initialize
55
+ @back = @source = nil
56
+ super
57
+ end
58
+
59
+ def build_pipeline
60
+ nil
61
+ end
62
+
63
+ def inspect_class_name
64
+ s = "#{element_type.to_s.scan(/Elem|Obj|V|E/).last}"
65
+ s = "#{s} #{ @info }" if @info
66
+ s
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,68 @@
1
+ module Pacer
2
+ module Routes
3
+ module RouteOperations
4
+ def lookahead(opts = {}, &block)
5
+ chain_route({ :filter => :future, :block => block }.merge(opts))
6
+ end
7
+
8
+ def neg_lookahead(opts = {}, &block)
9
+ chain_route({ :filter => :future, :neg_block => block }.merge(opts))
10
+ end
11
+ end
12
+ end
13
+
14
+ module Filter
15
+ module FutureFilter
16
+ import com.tinkerpop.pipes.filter.FutureFilterPipe
17
+
18
+ attr_accessor :min, :max
19
+
20
+ def block=(block)
21
+ @future_filter = [block, true]
22
+ end
23
+
24
+ def neg_block=(block)
25
+ @future_filter = [block, false]
26
+ end
27
+
28
+ protected
29
+
30
+ def after_initialize
31
+ @future_filter = nil unless defined? @future_filter
32
+ @route = nil unless defined? @route
33
+ super
34
+ end
35
+
36
+ def attach_pipe(end_pipe)
37
+ pipe = FutureFilterPipe.new(lookahead_pipe)
38
+ pipe.set_starts(end_pipe) if end_pipe
39
+ pipe
40
+ end
41
+
42
+ def lookahead_route
43
+ if @future_filter
44
+ block, has_elements = @future_filter
45
+ @future_filter = nil
46
+ route = block.call(Pacer::Route.empty(self))
47
+ if min or max
48
+ route = route.has_count_route(:min => min, :max => max).is(true)
49
+ end
50
+ unless has_elements
51
+ route = route.chain_route(:element_type => :object, :pipe_class => Pacer::Pipes::IsEmptyPipe, :route_name => 'negate')
52
+ end
53
+ @route = route
54
+ elsif @route
55
+ @route
56
+ end
57
+ end
58
+
59
+ def lookahead_pipe
60
+ Pacer::Route.pipeline(lookahead_route)
61
+ end
62
+
63
+ def inspect_string
64
+ "#{ inspect_class_name }(#{ lookahead_route.inspect })"
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,30 @@
1
+ module Pacer
2
+ module Filter
3
+ module IndexFilter
4
+ attr_accessor :index, :key, :value
5
+
6
+ def count
7
+ if @index and @key and @value
8
+ if @index.respond_to? :count
9
+ @index.count(@key, graph.encode_property(@value))
10
+ else
11
+ super
12
+ end
13
+ else
14
+ super
15
+ end
16
+ end
17
+
18
+ protected
19
+
20
+ def source_iterator
21
+ src = index.get(key, graph.encode_property(value)) || java.util.ArrayList.new
22
+ src.iterator
23
+ end
24
+
25
+ def inspect_string
26
+ "#{ inspect_class_name }(#{ key }: #{value.inspect})"
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,95 @@
1
+ module Pacer
2
+ module Core
3
+ module Route
4
+ def loop(&block)
5
+ chain_route :filter => :loop, :looping_route => block
6
+ end
7
+
8
+ # Apply the given path fragment multiple times in succession. If a Range
9
+ # or Array of numbers is given, the results are a combination of the
10
+ # results from all of the specified repetition levels. That is useful if
11
+ # a pattern may be nested to varying depths.
12
+ def repeat(arg, &block)
13
+ case arg
14
+ when Fixnum
15
+ range = arg..arg
16
+ arg.to_enum(:times).inject(self) do |route_end, count|
17
+ yield route_end
18
+ end
19
+ when Range
20
+ if arg.exclude_end?
21
+ range = arg.begin..(arg.end - 1)
22
+ else
23
+ range = arg
24
+ end
25
+ r = self.loop(&block).while do |e, depth, p|
26
+ if depth < range.begin
27
+ :loop
28
+ elsif depth < range.end
29
+ :loop_and_emit
30
+ elsif depth == range.end
31
+ :emit
32
+ else
33
+ false
34
+ end
35
+ end
36
+ r.while_description = "repeat #{ arg.inspect }"
37
+ r
38
+ else
39
+ raise "Invalid repeat range"
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ module ElementMixin
46
+ def loop(&block)
47
+ chain_route :filter => :loop, :looping_route => block
48
+ end
49
+ end
50
+
51
+ module Filter
52
+ module LoopFilter
53
+ attr_reader :looping_route
54
+ attr_accessor :while_description
55
+
56
+ def looping_route=(route)
57
+ if route.is_a? Proc
58
+ @looping_route = Pacer::Route.block_branch(self, route)
59
+ else
60
+ @looping_route = route
61
+ end
62
+ end
63
+
64
+ def while(yield_paths = false, &block)
65
+ @yield_paths = yield_paths
66
+ @control_block = block
67
+ self
68
+ end
69
+
70
+ protected
71
+
72
+ def attach_pipe(end_pipe)
73
+ unless @control_block
74
+ raise 'No loop control block specified. Use either #while or #until after #loop.'
75
+ end
76
+ pipe = Pacer::Pipes::LoopPipe.new(graph, looping_pipe, @control_block)
77
+ pipe.setStarts(end_pipe) if end_pipe
78
+ pipe
79
+ end
80
+
81
+ def looping_pipe
82
+ Pacer::Route.pipeline(looping_route)
83
+ end
84
+
85
+ def inspect_string
86
+ s = "#{ inspect_class_name }(#{ @looping_route.inspect })"
87
+ if while_description
88
+ "#{ s }(#{ while_description })"
89
+ else
90
+ s
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,55 @@
1
+ module Pacer
2
+ module Routes
3
+ module RouteOperations
4
+ def is(value)
5
+ if value.nil? and graph and defined? Pacer::DexGraph and graph.is_a? Pacer::DexGraph
6
+ # NOTE: This is a workaround for https://github.com/tinkerpop/blueprints/issues/178
7
+ only(value)
8
+ else
9
+ if value.is_a? Symbol
10
+ chain_route :filter => :property, :block => proc { |v| v.vars[value] == v }
11
+ else
12
+ chain_route({ :filter => :object, :value => value })
13
+ end
14
+ end
15
+ end
16
+
17
+ def is_not(value)
18
+ if value.nil? and graph and defined? Pacer::DexGraph and graph.is_a? Pacer::DexGraph
19
+ # NOTE: This is a workaround for https://github.com/tinkerpop/blueprints/issues/178
20
+ except(value)
21
+ else
22
+ if value.is_a? Symbol
23
+ chain_route :filter => :property, :block => proc { |v| v.vars[value] != v }
24
+ else
25
+ chain_route({ :filter => :object, :value => value, :negate => true })
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ module Filter
33
+ module ObjectFilter
34
+ import com.tinkerpop.pipes.filter.ObjectFilterPipe
35
+
36
+ attr_accessor :value, :negate
37
+
38
+ protected
39
+
40
+ def attach_pipe(end_pipe)
41
+ pipe = ObjectFilterPipe.new(value, negate ? Pacer::Pipes::NOT_EQUAL : Pacer::Pipes::EQUAL)
42
+ pipe.set_starts end_pipe if end_pipe
43
+ pipe
44
+ end
45
+
46
+ def inspect_string
47
+ if negate
48
+ "is_not(#{ value.inspect })"
49
+ else
50
+ "is(#{ value.inspect })"
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,93 @@
1
+ module Pacer
2
+ module Filter
3
+ module PropertyFilter
4
+ class EdgeFilters < Filters
5
+ attr_accessor :labels
6
+
7
+ protected
8
+
9
+ attr_accessor :non_ext_labels
10
+
11
+ public
12
+
13
+ def initialize(filters)
14
+ @best_index = nil
15
+ self.labels = []
16
+ self.non_ext_labels = []
17
+ super
18
+ end
19
+
20
+ def add_filter(filter, extension)
21
+ case filter
22
+ when String, Symbol
23
+ reset_properties
24
+ self.non_ext_labels << filter
25
+ self.labels << filter.to_s
26
+ else
27
+ super
28
+ end
29
+ end
30
+
31
+ def build_pipeline(route, start_pipe = nil, pipe = nil)
32
+ pipe ||= start_pipe
33
+ if labels.any?
34
+ label_pipe = Pacer::Pipes::LabelCollectionFilterPipe.new labels
35
+ label_pipe.set_starts pipe if pipe
36
+ Pacer.debug_pipes << { :name => labels.inspect, :start => pipe, :end => block_pipe } if Pacer.debug_pipes
37
+ pipe = label_pipe
38
+ start_pipe ||= pipe
39
+ end
40
+ super(route, start_pipe, pipe)
41
+ end
42
+
43
+ def any?
44
+ labels.any? or super
45
+ end
46
+
47
+ def to_s
48
+ if labels.any?
49
+ [labels.map { |l| l.to_sym.inspect }.join(', '), super].reject { |s| s == '' }.join ', '
50
+ else
51
+ super
52
+ end
53
+ end
54
+
55
+ def best_index(route)
56
+ index, key, value = find_best_index(route)
57
+ if key == 'label'
58
+ labels.delete value
59
+ end
60
+ super
61
+ end
62
+
63
+ protected
64
+
65
+ def reset_properties
66
+ @encoded_properties = nil
67
+ if @best_index
68
+ # put removed index label back...
69
+ i, k, v = @best_index
70
+ labels << v if k == 'label'
71
+ end
72
+ super
73
+ end
74
+
75
+ def find_best_index(route)
76
+ super do |avail, index_options|
77
+ labels.each do |label|
78
+ if idxs = avail["key:label"]
79
+ if choose_best_index
80
+ idxs.each do |idx|
81
+ index_options << [idx.count('label', label), idx, k, v]
82
+ end
83
+ else
84
+ return @best_index = [idxs.first, 'label', label]
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,269 @@
1
+ module Pacer
2
+ module Filter
3
+ module PropertyFilter
4
+ class Filters
5
+ attr_reader :properties, :extensions, :route_modules
6
+ attr_accessor :wrapper, :blocks
7
+
8
+ # Allow Pacer to use index counts to determine which index has
9
+ # the least number elements for the available keys in the query.
10
+ #
11
+ # @attr [Boolean] choose_best_index
12
+ attr_accessor :choose_best_index
13
+
14
+ # Allow Pacer to use manual indices without explicitly
15
+ # referencing them by name.
16
+ #
17
+ # @example Explicit manual index:
18
+ #
19
+ # graph.v(:index_name => { :index_key => value })
20
+ #
21
+ # @example Non-explicit index lookup:
22
+ #
23
+ # graph.v(:index_key => value)
24
+ #
25
+ # @attr [Boolean] search_manual_indices
26
+ attr_accessor :search_manual_indices
27
+
28
+ protected
29
+
30
+ attr_accessor :non_ext_props
31
+
32
+ public
33
+
34
+ def initialize(filters)
35
+ @properties = []
36
+ @blocks = []
37
+ @extensions = []
38
+ @wrapper = nil
39
+ @route_modules = []
40
+ @non_ext_props = []
41
+ @best_index_value = nil
42
+ add_filters filters, nil
43
+ end
44
+
45
+ # Set which graph this filter is currently operating on
46
+ #
47
+ # @note this is not threadsafe if you are reusing predefined
48
+ # routes on multiple graphs.
49
+ #
50
+ # @attr [GraphMixin] g a graph
51
+ attr_reader :graph
52
+
53
+ def graph=(g)
54
+ if graph != g
55
+ reset_properties
56
+ @graph = g
57
+ end
58
+ end
59
+
60
+ # Set which indices are available to be used to determine the
61
+ # best_index.
62
+ #
63
+ # @attr [Array<Pacer::IndexMixin>] i an array of indices
64
+ attr_reader :indices
65
+
66
+ def indices=(i)
67
+ if indices != i
68
+ reset_properties
69
+ @indices = i
70
+ end
71
+ end
72
+
73
+ def add_filter(filter, extension)
74
+ case filter
75
+ when Hash
76
+ reset_properties
77
+ filter.each do |k, v|
78
+ self.non_ext_props << [k.to_s, v] unless extension
79
+ self.properties << [k.to_s, v]
80
+ end
81
+ when Module, Class
82
+ if filter.is_a? Class and filter.ancestors.include? Pacer::Wrappers::ElementWrapper
83
+ self.wrapper = filter
84
+ else
85
+ self.extensions << filter
86
+ end
87
+ if filter.respond_to? :route_conditions
88
+ add_filters filter.route_conditions, filter
89
+ end
90
+ if filter.respond_to? :route
91
+ self.route_modules << filter
92
+ end
93
+ when Array
94
+ add_filters(filter, extension)
95
+ when nil
96
+ else
97
+ raise "Unknown filter: #{ filter.class }: #{ filter.inspect }"
98
+ end
99
+ end
100
+
101
+ def add_filters(filters, extension)
102
+ if filters.is_a? Array
103
+ filters.each do |filter|
104
+ add_filter filter, extension
105
+ end
106
+ else
107
+ add_filter filters, extension
108
+ end
109
+ end
110
+
111
+ def encode_value(value)
112
+ value = graph.encode_property(value)
113
+ if value.respond_to? :to_java
114
+ jvalue = value.to_java
115
+ elsif value.respond_to? :to_java_string
116
+ jvalue = value.to_java_string
117
+ else
118
+ jvalue = value
119
+ end
120
+ end
121
+
122
+ def build_pipeline(route, start_pipe, pipe = nil)
123
+ self.graph = route.graph
124
+ pipe ||= start_pipe
125
+ route_modules.each do |mod|
126
+ extension_route = mod.route(Pacer::Route.empty(route))
127
+ s, e = extension_route.send :build_pipeline
128
+ s.setStarts(pipe) if pipe
129
+ start_pipe ||= s
130
+ pipe = e
131
+ end
132
+ encoded_properties.each do |key, value|
133
+ new_pipe = PropertyFilterPipe.new(key, value, Pacer::Pipes::EQUAL)
134
+ new_pipe.set_starts pipe if pipe
135
+ Pacer.debug_pipes << { :name => key, :start => pipe, :end => new_pipe } if Pacer.debug_pipes
136
+ pipe = new_pipe
137
+ start_pipe ||= pipe
138
+ end
139
+ blocks.each do |block|
140
+ block_pipe = Pacer::Pipes::BlockFilterPipe.new(route, block)
141
+ block_pipe.set_starts pipe if pipe
142
+ Pacer.debug_pipes << { :name => 'block', :start => pipe, :end => block_pipe } if Pacer.debug_pipes
143
+ pipe = block_pipe
144
+ start_pipe ||= pipe
145
+ end
146
+ [start_pipe, pipe]
147
+ end
148
+
149
+ def any?
150
+ properties.any? or blocks.any? or route_modules.any? or extensions.any?
151
+ end
152
+
153
+ def extensions_only?
154
+ properties.none? and blocks.none? and route_modules.none? and (extensions.any? or wrapper)
155
+ end
156
+
157
+ def to_s
158
+ strings = []
159
+ strings << [wrapper.name] if wrapper
160
+ strings.concat extensions.map { |e| e.name }
161
+ strings.concat((non_ext_props - [@best_index_value]).map { |k, v| "#{ k }==#{ v.inspect }" })
162
+ strings.concat blocks.map { '&block' }
163
+ strings.concat route_modules.map { |mod| mod.name }
164
+ strings.join ', '
165
+ end
166
+
167
+ def best_index(element_type)
168
+ result = find_best_index(element_type)
169
+ # the call to find_best_index produces @best_index_value:
170
+ if properties.delete @best_index_value
171
+ @encoded_properties = nil
172
+ end
173
+ result
174
+ end
175
+
176
+ protected
177
+
178
+ def find_best_index(element_type)
179
+ return @best_index if @best_index
180
+ avail = available_indices(element_type)
181
+ return nil if avail.empty?
182
+ index_options = []
183
+ yield avail, index_options if block_given?
184
+ properties.each do |k, v|
185
+ if v.is_a? Hash
186
+ v.each do |k2, v2|
187
+ if (idxs = avail["name:#{k}"]).any?
188
+ if choose_best_index
189
+ idxs.each do |idx|
190
+ index_options << [idx.count(k2, encode_value(v2)), [idx, k2, v2], [k, v]]
191
+ end
192
+ else
193
+ @best_index_value = [k, v]
194
+ return @best_index = [idxs.first, k2, v2]
195
+ end
196
+ end
197
+ end
198
+ elsif (idxs = (avail["key:#{k}"] + avail[:all])).any?
199
+ if choose_best_index
200
+ idxs.each do |idx|
201
+ index_options << [idx.count(k, encode_value(v)), [idx, k, v], [k, v]]
202
+ end
203
+ else
204
+ @best_index_value = [k, v]
205
+ return @best_index = [idxs.first, k, v]
206
+ end
207
+ else
208
+ nil
209
+ end
210
+ end
211
+ index_options = index_options.sort_by do |a|
212
+ count = a.first
213
+ if count == 0 and search_manual_indices
214
+ # Only use 0 result indices if there is no alternative
215
+ # because most manual indices won't be populating the
216
+ # data in question.
217
+ java.lang.Integer::MAX_VALUE
218
+ else
219
+ count
220
+ end
221
+ end
222
+ _, @best_index, @best_index_value = index_options.first || [nil, [], []]
223
+ @best_index
224
+ end
225
+
226
+ def reset_properties
227
+ @encoded_properties = nil
228
+ if @best_index_value
229
+ # put removed index property back...
230
+ properties << @best_index_value
231
+ end
232
+ @best_index = nil
233
+ @best_index_value = nil
234
+ @available_indices = nil
235
+ end
236
+
237
+ def encoded_properties
238
+ @encoded_properties ||= properties.map do |k, v|
239
+ [k, encode_value(v)]
240
+ end
241
+ end
242
+
243
+ def available_indices(element_type)
244
+ return @available_indices if @available_indices
245
+ @available_indices = Hash.new { |h, k| h[k] = [] }
246
+ return @available_indices unless indices
247
+ index_class = graph.index_class(element_type)
248
+ indices.each do |index|
249
+ next unless index.index_class == index_class
250
+ if index.index_type == Pacer.automatic_index
251
+ if keys = index.getAutoIndexKeys
252
+ keys.each do |key|
253
+ @available_indices["key:#{key}"] << index
254
+ end
255
+ else
256
+ @available_indices[:all] << index
257
+ end
258
+ else
259
+ @available_indices["name:#{index.index_name}"] = [index]
260
+ @available_indices[:all] << index if search_manual_indices
261
+ end
262
+ end
263
+ @available_indices
264
+ end
265
+
266
+ end
267
+ end
268
+ end
269
+ end