pacer 0.9.1.1-java

Sign up to get free protection for your applications and to get access to all the features.
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