pacer 0.9.1.1-java
Sign up to get free protection for your applications and to get access to all the features.
- data/.autotest +8 -0
- data/.document +5 -0
- data/.gitignore +26 -0
- data/.rspec +1 -0
- data/.rvmrc +0 -0
- data/CONTRIBUTORS +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +24 -0
- data/README.md +187 -0
- data/Rakefile +49 -0
- data/autotest/discover.rb +1 -0
- data/bin/autospec +16 -0
- data/bin/autotest +16 -0
- data/bin/rake +16 -0
- data/bin/rcov +16 -0
- data/bin/rspec +16 -0
- data/bin/yard +16 -0
- data/bin/yardoc +16 -0
- data/lib/pacer/blueprints/extensions.rb +77 -0
- data/lib/pacer/blueprints/multi_graph.rb +121 -0
- data/lib/pacer/blueprints/ruby_graph.rb +199 -0
- data/lib/pacer/blueprints/tg.rb +100 -0
- data/lib/pacer/blueprints.rb +4 -0
- data/lib/pacer/core/graph/edges_route.rb +92 -0
- data/lib/pacer/core/graph/element_route.rb +171 -0
- data/lib/pacer/core/graph/graph_index_route.rb +48 -0
- data/lib/pacer/core/graph/graph_route.rb +55 -0
- data/lib/pacer/core/graph/mixed_route.rb +96 -0
- data/lib/pacer/core/graph/vertices_route.rb +220 -0
- data/lib/pacer/core/graph.rb +13 -0
- data/lib/pacer/core/route.rb +502 -0
- data/lib/pacer/core/side_effect.rb +11 -0
- data/lib/pacer/core.rb +8 -0
- data/lib/pacer/exceptions.rb +11 -0
- data/lib/pacer/extensions/block_filter_element.rb +22 -0
- data/lib/pacer/extensions.rb +6 -0
- data/lib/pacer/filter/block_filter.rb +31 -0
- data/lib/pacer/filter/collection_filter.rb +109 -0
- data/lib/pacer/filter/empty_filter.rb +70 -0
- data/lib/pacer/filter/future_filter.rb +68 -0
- data/lib/pacer/filter/index_filter.rb +30 -0
- data/lib/pacer/filter/loop_filter.rb +95 -0
- data/lib/pacer/filter/object_filter.rb +55 -0
- data/lib/pacer/filter/property_filter/edge_filters.rb +93 -0
- data/lib/pacer/filter/property_filter/filters.rb +269 -0
- data/lib/pacer/filter/property_filter.rb +111 -0
- data/lib/pacer/filter/random_filter.rb +13 -0
- data/lib/pacer/filter/range_filter.rb +104 -0
- data/lib/pacer/filter/uniq_filter.rb +12 -0
- data/lib/pacer/filter/where_filter/node_visitor.rb +280 -0
- data/lib/pacer/filter/where_filter.rb +47 -0
- data/lib/pacer/filter.rb +17 -0
- data/lib/pacer/function_resolver.rb +43 -0
- data/lib/pacer/graph/edge_mixin.rb +127 -0
- data/lib/pacer/graph/element_mixin.rb +202 -0
- data/lib/pacer/graph/graph_indices_mixin.rb +93 -0
- data/lib/pacer/graph/graph_mixin.rb +361 -0
- data/lib/pacer/graph/graph_transactions_mixin.rb +207 -0
- data/lib/pacer/graph/index_mixin.rb +30 -0
- data/lib/pacer/graph/vertex_mixin.rb +119 -0
- data/lib/pacer/graph.rb +14 -0
- data/lib/pacer/pipe/blackbox_pipeline.rb +48 -0
- data/lib/pacer/pipe/block_filter_pipe.rb +38 -0
- data/lib/pacer/pipe/collection_filter_pipe.rb +10 -0
- data/lib/pacer/pipe/cross_product_transform_pipe.rb +48 -0
- data/lib/pacer/pipe/enumerable_pipe.rb +30 -0
- data/lib/pacer/pipe/expandable_pipe.rb +63 -0
- data/lib/pacer/pipe/id_collection_filter_pipe.rb +33 -0
- data/lib/pacer/pipe/is_empty_pipe.rb +30 -0
- data/lib/pacer/pipe/is_unique_pipe.rb +61 -0
- data/lib/pacer/pipe/label_collection_filter_pipe.rb +21 -0
- data/lib/pacer/pipe/label_prefix_pipe.rb +21 -0
- data/lib/pacer/pipe/loop_pipe.rb +86 -0
- data/lib/pacer/pipe/map_pipe.rb +36 -0
- data/lib/pacer/pipe/never_pipe.rb +9 -0
- data/lib/pacer/pipe/process_pipe.rb +37 -0
- data/lib/pacer/pipe/property_comparison_pipe.rb +40 -0
- data/lib/pacer/pipe/ruby_pipe.rb +25 -0
- data/lib/pacer/pipe/simple_visitor_pipe.rb +43 -0
- data/lib/pacer/pipe/stream_sort_pipe.rb +84 -0
- data/lib/pacer/pipe/stream_uniq_pipe.rb +33 -0
- data/lib/pacer/pipe/type_filter_pipe.rb +22 -0
- data/lib/pacer/pipe/unary_transform_pipe.rb +59 -0
- data/lib/pacer/pipe/variable_store_iterator_wrapper.rb +26 -0
- data/lib/pacer/pipe/visitor_pipe.rb +67 -0
- data/lib/pacer/pipes.rb +61 -0
- data/lib/pacer/route/mixin/bulk_operations.rb +52 -0
- data/lib/pacer/route/mixin/route_operations.rb +107 -0
- data/lib/pacer/route/mixin/variable_route_module.rb +26 -0
- data/lib/pacer/route/mixins.rb +3 -0
- data/lib/pacer/route.rb +228 -0
- data/lib/pacer/routes.rb +6 -0
- data/lib/pacer/side_effect/aggregate.rb +31 -0
- data/lib/pacer/side_effect/counted.rb +30 -0
- data/lib/pacer/side_effect/group_count.rb +44 -0
- data/lib/pacer/side_effect/is_unique.rb +32 -0
- data/lib/pacer/side_effect/section.rb +25 -0
- data/lib/pacer/side_effect/visitor.rb +37 -0
- data/lib/pacer/side_effect.rb +11 -0
- data/lib/pacer/support/array_list.rb +28 -0
- data/lib/pacer/support/enumerable.rb +100 -0
- data/lib/pacer/support/hash.rb +9 -0
- data/lib/pacer/support/iterator_mixins.rb +110 -0
- data/lib/pacer/support/native_exception.rb +22 -0
- data/lib/pacer/support/proc.rb +16 -0
- data/lib/pacer/support.rb +10 -0
- data/lib/pacer/transform/cap.rb +50 -0
- data/lib/pacer/transform/gather.rb +9 -0
- data/lib/pacer/transform/has_count_cap.rb +41 -0
- data/lib/pacer/transform/join.rb +181 -0
- data/lib/pacer/transform/map.rb +23 -0
- data/lib/pacer/transform/path.rb +50 -0
- data/lib/pacer/transform/process.rb +23 -0
- data/lib/pacer/transform/scatter.rb +23 -0
- data/lib/pacer/transform/sort_section.rb +103 -0
- data/lib/pacer/transform/stream_sort.rb +21 -0
- data/lib/pacer/transform/stream_uniq.rb +21 -0
- data/lib/pacer/transform.rb +16 -0
- data/lib/pacer/utils/graph_analysis.rb +112 -0
- data/lib/pacer/utils/trie.rb +93 -0
- data/lib/pacer/utils/tsort.rb +65 -0
- data/lib/pacer/utils/y_files.rb +127 -0
- data/lib/pacer/utils.rb +10 -0
- data/lib/pacer/version.rb +13 -0
- data/lib/pacer/wrappers/edge_wrapper.rb +51 -0
- data/lib/pacer/wrappers/element_wrapper.rb +78 -0
- data/lib/pacer/wrappers/new_element.rb +106 -0
- data/lib/pacer/wrappers/vertex_wrapper.rb +51 -0
- data/lib/pacer/wrappers.rb +19 -0
- data/lib/pacer-0.9.1.1-standalone.jar +0 -0
- data/lib/pacer.rb +290 -0
- data/pacer.gemspec +30 -0
- data/pom/standalone.xml +22 -0
- data/pom.xml +124 -0
- data/samples/grateful-dead.xml +26380 -0
- data/samples/grateful_dead.rb +63 -0
- data/samples/profile.rb +15 -0
- data/spec/data/grateful-dead.xml +26380 -0
- data/spec/data/pacer.graphml +319 -0
- data/spec/pacer/blueprints/dex_spec.rb +172 -0
- data/spec/pacer/blueprints/neo4j_spec.rb +177 -0
- data/spec/pacer/blueprints/tg_spec.rb +128 -0
- data/spec/pacer/core/graph/edges_route_spec.rb +52 -0
- data/spec/pacer/core/graph/element_route_spec.rb +46 -0
- data/spec/pacer/core/graph/graph_route_spec.rb +94 -0
- data/spec/pacer/core/graph/vertices_route_spec.rb +169 -0
- data/spec/pacer/core/route_spec.rb +197 -0
- data/spec/pacer/filter/collection_filter_spec.rb +19 -0
- data/spec/pacer/filter/empty_filter_spec.rb +29 -0
- data/spec/pacer/filter/future_filter_spec.rb +97 -0
- data/spec/pacer/filter/loop_filter_spec.rb +31 -0
- data/spec/pacer/filter/property_filter_spec.rb +111 -0
- data/spec/pacer/filter/random_filter_spec.rb +17 -0
- data/spec/pacer/filter/uniq_filter_spec.rb +18 -0
- data/spec/pacer/filter/where_filter_spec.rb +93 -0
- data/spec/pacer/graph/edge_mixin_spec.rb +116 -0
- data/spec/pacer/graph/element_mixin_spec.rb +297 -0
- data/spec/pacer/graph/graph_mixin_spec.rb +538 -0
- data/spec/pacer/graph/index_mixin_spec.rb +0 -0
- data/spec/pacer/graph/vertex_mixin_spec.rb +192 -0
- data/spec/pacer/pipe/block_filter_pipe_spec.rb +0 -0
- data/spec/pacer/pipe/labels_filter_pipe_spec.rb +0 -0
- data/spec/pacer/pipe/ruby_pipe_spec.rb +0 -0
- data/spec/pacer/pipe/type_filter_pipe_spec.rb +0 -0
- data/spec/pacer/route/mixin/base_spec.rb +419 -0
- data/spec/pacer/route/mixin/bulk_operations_spec.rb +30 -0
- data/spec/pacer/route/mixin/route_operations_spec.rb +127 -0
- data/spec/pacer/support/array_list_spec.rb +0 -0
- data/spec/pacer/support/enumerable_spec.rb +115 -0
- data/spec/pacer/transform/join_spec.rb +138 -0
- data/spec/pacer/transform/path_spec.rb +54 -0
- data/spec/pacer/utils/tsort_spec.rb +89 -0
- data/spec/pacer/wrapper/edge_wrapper_spec.rb +33 -0
- data/spec/pacer/wrapper/element_wrapper_spec.rb +169 -0
- data/spec/pacer/wrapper/vertex_wrapper_spec.rb +33 -0
- data/spec/pacer_spec.rb +0 -0
- data/spec/spec_helper.rb +91 -0
- data/spec/support/contexts.rb +14 -0
- data/spec/support/graph_runner.rb +142 -0
- data/spec/support/matchers.rb +19 -0
- data/spec/support/use_transactions.rb +31 -0
- data/spec/tackle/simple_mixin.rb +21 -0
- data/spec/tackle/tinkerpop_graph_mixins.rb +60 -0
- 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
|