neo4j-core 0.0.15-java → 2.0.0.alpha.1-java
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +2 -2
- data/README.rdoc +12 -192
- data/lib/neo4j-core.rb +3 -19
- data/lib/neo4j-core/database.rb +5 -4
- data/lib/neo4j-core/event_handler.rb +1 -1
- data/lib/neo4j-core/index/class_methods.rb +41 -27
- data/lib/neo4j-core/index/index.rb +4 -3
- data/lib/neo4j-core/index/index_config.rb +23 -30
- data/lib/neo4j-core/index/indexer.rb +53 -65
- data/lib/neo4j-core/index/indexer_registry.rb +2 -2
- data/lib/neo4j-core/index/lucene_query.rb +42 -53
- data/lib/neo4j-core/node/class_methods.rb +4 -4
- data/lib/neo4j-core/node/node.rb +8 -1
- data/lib/neo4j-core/property/property.rb +3 -1
- data/lib/neo4j-core/relationship/relationship.rb +10 -8
- data/lib/neo4j-core/rels/rels.rb +4 -9
- data/lib/neo4j-core/rels/traverser.rb +27 -13
- data/lib/neo4j-core/traversal/prune_evaluator.rb +2 -2
- data/lib/neo4j-core/traversal/traverser.rb +27 -122
- data/lib/neo4j-core/type_converters/type_converters.rb +287 -0
- data/lib/neo4j-core/version.rb +1 -1
- data/lib/neo4j-core/version.rb~ +3 -0
- data/lib/neo4j/config.rb +6 -3
- data/lib/neo4j/neo4j.rb +22 -51
- data/lib/neo4j/node.rb +0 -27
- data/lib/neo4j/relationship.rb +0 -25
- data/lib/test.rb +27 -0
- data/neo4j-core.gemspec +2 -2
- metadata +11 -17
- data/lib/neo4j-core/cypher/cypher.rb +0 -969
- data/lib/neo4j-core/cypher/result_wrapper.rb +0 -48
- data/lib/neo4j-core/hash_with_indifferent_access.rb +0 -165
- data/lib/neo4j-core/index/unique_factory.rb +0 -54
- data/lib/neo4j-core/property/java.rb +0 -59
- data/lib/neo4j-core/wrapper/class_methods.rb +0 -22
- data/lib/neo4j-core/wrapper/wrapper.rb +0 -20
- data/lib/neo4j/algo.rb +0 -300
- data/lib/neo4j/cypher.rb +0 -180
@@ -1,48 +0,0 @@
|
|
1
|
-
module Neo4j
|
2
|
-
module Core
|
3
|
-
module Cypher
|
4
|
-
# Wraps the Cypher query result.
|
5
|
-
# Loads the node and relationships wrapper if possible and use symbol as column keys.
|
6
|
-
# @notice The result is a once forward read only Enumerable, work if you need to read the result twice - use #to_a
|
7
|
-
#
|
8
|
-
# @example
|
9
|
-
# result = Neo4j.query(@a, @b){|a,b| node(a,b).as(:n)}
|
10
|
-
# r = @query_result.to_a # can only loop once
|
11
|
-
# r.size.should == 2
|
12
|
-
# r.first.should include(:n)
|
13
|
-
# r[0][:n].neo_id.should == @a.neo_id
|
14
|
-
# r[1][:n].neo_id.should == @b.neo_id
|
15
|
-
class ResultWrapper
|
16
|
-
include Enumerable
|
17
|
-
|
18
|
-
# @return the original result from the Neo4j Cypher Engine, once forward read only !
|
19
|
-
attr_reader :source
|
20
|
-
|
21
|
-
def initialize(source)
|
22
|
-
@source = source
|
23
|
-
end
|
24
|
-
|
25
|
-
# @return [Array<Symbol>] the columns in the query result
|
26
|
-
def columns
|
27
|
-
@source.columns.map{|x| x.to_sym}
|
28
|
-
end
|
29
|
-
|
30
|
-
# for the Enumerable contract
|
31
|
-
def each
|
32
|
-
@source.each { |row| yield map(row) }
|
33
|
-
end
|
34
|
-
|
35
|
-
# Maps each row so that we can use symbols for column names.
|
36
|
-
# @private
|
37
|
-
def map(row)
|
38
|
-
out = {} # move to a real hash!
|
39
|
-
row.each do |key, value|
|
40
|
-
out[key.to_sym] = value.respond_to?(:wrapper) ? value.wrapper : value
|
41
|
-
end
|
42
|
-
out
|
43
|
-
end
|
44
|
-
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
@@ -1,165 +0,0 @@
|
|
1
|
-
module Neo4j
|
2
|
-
module Core
|
3
|
-
# Stolen from http://as.rubyonrails.org/classes/HashWithIndifferentAccess.html
|
4
|
-
# We don't want to depend on active support
|
5
|
-
class HashWithIndifferentAccess < Hash
|
6
|
-
|
7
|
-
# Always returns true, so that <tt>Array#extract_options!</tt> finds members of this class.
|
8
|
-
def extractable_options?
|
9
|
-
true
|
10
|
-
end
|
11
|
-
|
12
|
-
def with_indifferent_access
|
13
|
-
dup
|
14
|
-
end
|
15
|
-
|
16
|
-
def nested_under_indifferent_access
|
17
|
-
self
|
18
|
-
end
|
19
|
-
|
20
|
-
def initialize(constructor = {})
|
21
|
-
if constructor.is_a?(Hash)
|
22
|
-
super()
|
23
|
-
update(constructor)
|
24
|
-
else
|
25
|
-
super(constructor)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
def default(key = nil)
|
30
|
-
if key.is_a?(Symbol) && include?(key = key.to_s)
|
31
|
-
self[key]
|
32
|
-
else
|
33
|
-
super
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def self.new_from_hash_copying_default(hash)
|
38
|
-
new(hash).tap do |new_hash|
|
39
|
-
new_hash.default = hash.default
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
|
44
|
-
alias_method :regular_update, :update unless method_defined?(:regular_update)
|
45
|
-
|
46
|
-
# Assigns a new value to the hash:
|
47
|
-
#
|
48
|
-
# hash = HashWithIndifferentAccess.new
|
49
|
-
# hash[:key] = "value"
|
50
|
-
#
|
51
|
-
def []=(key, value)
|
52
|
-
regular_writer(convert_key(key), convert_value(value))
|
53
|
-
end
|
54
|
-
|
55
|
-
alias_method :store, :[]=
|
56
|
-
|
57
|
-
# Updates the instantized hash with values from the second:
|
58
|
-
#
|
59
|
-
# hash_1 = HashWithIndifferentAccess.new
|
60
|
-
# hash_1[:key] = "value"
|
61
|
-
#
|
62
|
-
# hash_2 = HashWithIndifferentAccess.new
|
63
|
-
# hash_2[:key] = "New Value!"
|
64
|
-
#
|
65
|
-
# hash_1.update(hash_2) # => {"key"=>"New Value!"}
|
66
|
-
#
|
67
|
-
def update(other_hash)
|
68
|
-
if other_hash.is_a? HashWithIndifferentAccess
|
69
|
-
super(other_hash)
|
70
|
-
else
|
71
|
-
other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
|
72
|
-
self
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
alias_method :merge!, :update
|
77
|
-
|
78
|
-
# Checks the hash for a key matching the argument passed in:
|
79
|
-
#
|
80
|
-
# hash = HashWithIndifferentAccess.new
|
81
|
-
# hash["key"] = "value"
|
82
|
-
# hash.key? :key # => true
|
83
|
-
# hash.key? "key" # => true
|
84
|
-
#
|
85
|
-
def key?(key)
|
86
|
-
super(convert_key(key))
|
87
|
-
end
|
88
|
-
|
89
|
-
alias_method :include?, :key?
|
90
|
-
alias_method :has_key?, :key?
|
91
|
-
alias_method :member?, :key?
|
92
|
-
|
93
|
-
# Fetches the value for the specified key, same as doing hash[key]
|
94
|
-
def fetch(key, *extras)
|
95
|
-
super(convert_key(key), *extras)
|
96
|
-
end
|
97
|
-
|
98
|
-
# Returns an array of the values at the specified indices:
|
99
|
-
#
|
100
|
-
# hash = HashWithIndifferentAccess.new
|
101
|
-
# hash[:a] = "x"
|
102
|
-
# hash[:b] = "y"
|
103
|
-
# hash.values_at("a", "b") # => ["x", "y"]
|
104
|
-
#
|
105
|
-
def values_at(*indices)
|
106
|
-
indices.collect {|key| self[convert_key(key)]}
|
107
|
-
end
|
108
|
-
|
109
|
-
# Returns an exact copy of the hash.
|
110
|
-
def dup
|
111
|
-
self.class.new(self).tap do |new_hash|
|
112
|
-
new_hash.default = default
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
# Merges the instantized and the specified hashes together, giving precedence to the values from the second hash.
|
117
|
-
# Does not overwrite the existing hash.
|
118
|
-
def merge(hash)
|
119
|
-
self.dup.update(hash)
|
120
|
-
end
|
121
|
-
|
122
|
-
# Performs the opposite of merge, with the keys and values from the first hash taking precedence over the second.
|
123
|
-
# This overloaded definition prevents returning a regular hash, if reverse_merge is called on a <tt>HashWithDifferentAccess</tt>.
|
124
|
-
def reverse_merge(other_hash)
|
125
|
-
super self.class.new_from_hash_copying_default(other_hash)
|
126
|
-
end
|
127
|
-
|
128
|
-
def reverse_merge!(other_hash)
|
129
|
-
replace(reverse_merge( other_hash ))
|
130
|
-
end
|
131
|
-
|
132
|
-
# Removes a specified key from the hash.
|
133
|
-
def delete(key)
|
134
|
-
super(convert_key(key))
|
135
|
-
end
|
136
|
-
|
137
|
-
def stringify_keys!; self end
|
138
|
-
def stringify_keys; dup end
|
139
|
-
# undef :symbolize_keys!
|
140
|
-
def symbolize_keys; to_hash.symbolize_keys end
|
141
|
-
def to_options!; self end
|
142
|
-
|
143
|
-
# Convert to a Hash with String keys.
|
144
|
-
def to_hash
|
145
|
-
Hash.new(default).merge!(self)
|
146
|
-
end
|
147
|
-
|
148
|
-
protected
|
149
|
-
def convert_key(key)
|
150
|
-
key.kind_of?(Symbol) ? key.to_s : key
|
151
|
-
end
|
152
|
-
|
153
|
-
def convert_value(value)
|
154
|
-
if value.is_a? Hash
|
155
|
-
value #.nested_under_indifferent_access
|
156
|
-
elsif value.is_a?(Array)
|
157
|
-
value.dup.replace(value.map { |e| convert_value(e) })
|
158
|
-
else
|
159
|
-
value
|
160
|
-
end
|
161
|
-
end
|
162
|
-
end
|
163
|
-
|
164
|
-
end
|
165
|
-
end
|
@@ -1,54 +0,0 @@
|
|
1
|
-
module Neo4j
|
2
|
-
module Core
|
3
|
-
|
4
|
-
module Index
|
5
|
-
|
6
|
-
# A Utility class that can be used to make it easier to create unique entities. It uses {Neo4j::Core::Index::Indexer#put_if_absent}.
|
7
|
-
#
|
8
|
-
# @see Indexer#put_if_absent
|
9
|
-
#
|
10
|
-
# @example
|
11
|
-
# index = index_for_type(:exact)
|
12
|
-
# Neo4j::Core::Index::UniqueFactory.new(:email, index) { |k,v| Neo4j::Node.new(k => v) }.get_or_create(:email, 'foo@gmail.com')
|
13
|
-
#
|
14
|
-
class UniqueFactory
|
15
|
-
# @param [Symbol] key only one key is possible
|
16
|
-
# @param [Java::Neo4j] index the lucene index (see #index_for_type)
|
17
|
-
# @yield a proc for initialize each created entity
|
18
|
-
def initialize(key, index, &entity_creator_block)
|
19
|
-
@key = key
|
20
|
-
@index = index
|
21
|
-
@entity_creator_block = entity_creator_block || Proc.new{|k,v| Neo4j::Node.new(key.to_s => v)}
|
22
|
-
end
|
23
|
-
|
24
|
-
# Get the indexed entity, creating it (exactly once) if no indexed entity exists.
|
25
|
-
# There must be an index on the key
|
26
|
-
# @param [Symbol] key the key to find the entity under in the index.
|
27
|
-
# @param [String, Fixnum, Float] value the value the key is mapped to for the entity in the index.
|
28
|
-
# @param [Hash] props optional properties that the entity will have if created
|
29
|
-
# @yield optional, make it possible to initialize the created node in a block
|
30
|
-
def get_or_create(key, value, props=nil, &init_block)
|
31
|
-
tx = Neo4j::Transaction.new
|
32
|
-
result = @index.get(key.to_s, value).get_single
|
33
|
-
return result if result
|
34
|
-
|
35
|
-
created = @entity_creator_block.call(key,value)
|
36
|
-
result = @index.put_if_absent(created._java_entity, key.to_s, value)
|
37
|
-
if result.nil?
|
38
|
-
props.each_pair{|k,v| created[k.to_s] = v} if props
|
39
|
-
init_block.call(result) if init_block
|
40
|
-
result = created
|
41
|
-
else
|
42
|
-
created.del
|
43
|
-
end
|
44
|
-
tx.success
|
45
|
-
result
|
46
|
-
ensure
|
47
|
-
tx.finish
|
48
|
-
end
|
49
|
-
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
end
|
54
|
-
end
|
@@ -1,59 +0,0 @@
|
|
1
|
-
module Neo4j
|
2
|
-
module Core
|
3
|
-
module Property
|
4
|
-
# This module is only used for documentation purpose
|
5
|
-
# It simplify declares the java methods which are available Java org.neo4j.graphdb.PropertyContainer
|
6
|
-
# @see http://api.neo4j.org/1.6.1/org/neo4j/graphdb/PropertyContainer.html
|
7
|
-
module Java
|
8
|
-
|
9
|
-
|
10
|
-
# Get the GraphDatabaseService that this Node or Relationship belongs to.
|
11
|
-
# @return [Java::Neo4jGraphdbGraphDatabaseService]
|
12
|
-
def graph_database
|
13
|
-
end
|
14
|
-
|
15
|
-
# Returns the property value associated with the given key, or a default value.
|
16
|
-
# The value is of one of the valid property types, i.e. a Java primitive, a String or an array of any of the valid types.
|
17
|
-
# If there's no property associated with key an unchecked exception is raised.
|
18
|
-
# The idiomatic way to avoid an exception for an unknown key and instead get null back is to use a default value: Object valueOrNull = nodeOrRel.getProperty( key, null )
|
19
|
-
# @param [String] key the property key
|
20
|
-
# @param [String] default_value the default value that will be returned if no property value was associated with the given key
|
21
|
-
# @return [String,Fixnum,Boolean,Float,Array] ]the property value associated with the given key.
|
22
|
-
# @raise an exception if not given a default value and there is no property for the given key
|
23
|
-
# @see Neo4j::Core:Property#[]
|
24
|
-
def get_property(key, default_value = nil)
|
25
|
-
end
|
26
|
-
|
27
|
-
# @return all existing property keys, or an empty iterable if this property container has no properties.
|
28
|
-
def property_keys
|
29
|
-
end
|
30
|
-
|
31
|
-
|
32
|
-
# Removes the property associated with the given key and returns the old value.
|
33
|
-
# @param [String] key the name of the property
|
34
|
-
# @return [String,Fixnum,Boolean,Float,Array, nil] The old value or <tt>nil</tt> if there's no property associated with the key.
|
35
|
-
def remove_property(key)
|
36
|
-
end
|
37
|
-
|
38
|
-
# Sets the property value for the given key to value.
|
39
|
-
# The property value must be one of the valid property types, i.e:
|
40
|
-
# * boolean or boolean[]
|
41
|
-
# * byte or byte[]
|
42
|
-
# * short or short[]
|
43
|
-
# * int or int[]
|
44
|
-
# * long or long[]
|
45
|
-
# * float or float[]
|
46
|
-
# * double or double[]
|
47
|
-
# * char or char[]
|
48
|
-
# * java.lang.String or String[]
|
49
|
-
# Notice that JRuby does map Ruby primitive object (e.g. Fixnum) to java primitives automatically.
|
50
|
-
# Also, nil is not an accepted property value.
|
51
|
-
# @param [String] key the property key
|
52
|
-
# @param [String,Fixnum,Boolean,Float,Array] value
|
53
|
-
# @see Neo4j::Core::Property#[]=
|
54
|
-
def set_property(key, value)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
@@ -1,22 +0,0 @@
|
|
1
|
-
module Neo4j
|
2
|
-
module Core
|
3
|
-
module Wrapper
|
4
|
-
module ClassMethods
|
5
|
-
|
6
|
-
# Tries to load a wrapper for this node if possible
|
7
|
-
# @see #wrapper_proc=
|
8
|
-
def wrapper(entity)
|
9
|
-
@_wrapper_proc ? @_wrapper_proc.call(entity) : entity
|
10
|
-
end
|
11
|
-
|
12
|
-
# Sets the procs to be used to load wrappers
|
13
|
-
# @see #wrapper
|
14
|
-
def wrapper_proc=(proc)
|
15
|
-
@_wrapper_proc = proc
|
16
|
-
end
|
17
|
-
|
18
|
-
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
@@ -1,20 +0,0 @@
|
|
1
|
-
module Neo4j
|
2
|
-
module Core
|
3
|
-
# Can be used to define your own wrapper class around nodes and relationships
|
4
|
-
module Wrapper
|
5
|
-
|
6
|
-
# @return [self, Object] return self or a wrapper Ruby object
|
7
|
-
# @see Neo4j::Node::ClassMethods#wrapper
|
8
|
-
def wrapper
|
9
|
-
self.class.wrapper(self)
|
10
|
-
end
|
11
|
-
|
12
|
-
# This can be implemented by a wrapper to returned the underlying java node or relationship.
|
13
|
-
# You can override this method in your own wrapper class.
|
14
|
-
# @return self
|
15
|
-
def _java_entity
|
16
|
-
self
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
data/lib/neo4j/algo.rb
DELETED
@@ -1,300 +0,0 @@
|
|
1
|
-
module Neo4j
|
2
|
-
|
3
|
-
|
4
|
-
# A wrapper for the Neo4j Algo <tt>org.neo4j.graphalgo.GraphAlgoFactory</tt> class.
|
5
|
-
#
|
6
|
-
# @example the length of the first path found between two nodes
|
7
|
-
# Neo4j::Algo.all_paths(a,b).outgoing(:friends).depth(1).first.length
|
8
|
-
#
|
9
|
-
# @example the nodes in the shortest path between two nodes
|
10
|
-
# Neo4j::Algo.shortest_path(a,b).outgoing(:friends).outgoing(:knows).to_a #=> [node1, node2, node3 ...]
|
11
|
-
#
|
12
|
-
# @example the relationships in the shortest path between two nodes
|
13
|
-
# Neo4j::Algo.shortest_path(a,b).outgoing(:friends).outgoing(:knows).rels.to_a #=> [rel1, rel2, rel3 ...]
|
14
|
-
#
|
15
|
-
# @example The Dijkstra algorithm using a cost evaluator
|
16
|
-
# Neo4j::Algo.dijkstra_path(a,b).cost_evaluator{|rel,direction| rel[:weight]}
|
17
|
-
class Algo
|
18
|
-
include Enumerable
|
19
|
-
include Neo4j::Core::ToJava
|
20
|
-
|
21
|
-
class EstimateEvaluator #:nodoc
|
22
|
-
include org.neo4j.graphalgo.EstimateEvaluator
|
23
|
-
include Neo4j::Core::ToJava
|
24
|
-
|
25
|
-
def initialize(&evaluator)
|
26
|
-
@evaluator = evaluator
|
27
|
-
end
|
28
|
-
|
29
|
-
# Implements T getCost(Node node, Node goal)
|
30
|
-
# Estimate the weight of the remaining path from one node to another.
|
31
|
-
def get_cost(node, goal)
|
32
|
-
@evaluator.call(node, goal)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
class CostEvaluator #:nodoc
|
37
|
-
include org.neo4j.graphalgo.CostEvaluator
|
38
|
-
include Neo4j::Core::ToJava
|
39
|
-
|
40
|
-
def initialize(&evaluator)
|
41
|
-
@evaluator = evaluator
|
42
|
-
end
|
43
|
-
|
44
|
-
# Implements the Java Method: T getCost(Relationship relationship, Direction direction)
|
45
|
-
# From the JavaDoc: <pre>
|
46
|
-
# This is the general method for looking up costs for relationships.
|
47
|
-
# This can do anything, like looking up a property or running some small calculation.
|
48
|
-
# Parameters:
|
49
|
-
# relationship -
|
50
|
-
# direction - The direction in which the relationship is being evaluated, either Direction.INCOMING or Direction.OUTGOING.
|
51
|
-
# Returns:
|
52
|
-
# The cost for this edge/relationship
|
53
|
-
# </pre>
|
54
|
-
def get_cost(relationship, direction)
|
55
|
-
@evaluator.call(relationship, dir_from_java(direction))
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def initialize(from, to, &factory_proc) #:nodoc:
|
60
|
-
@from = from
|
61
|
-
@to = to
|
62
|
-
@factory_proc = factory_proc
|
63
|
-
@type_and_dirs = []
|
64
|
-
|
65
|
-
end
|
66
|
-
|
67
|
-
def _depth #:nodoc:
|
68
|
-
@depth || java.lang.Integer::MAX_VALUE
|
69
|
-
end
|
70
|
-
|
71
|
-
def _expander #:nodoc:
|
72
|
-
expander = @expander
|
73
|
-
expander ||= @type_and_dirs.empty? ? org.neo4j.kernel.Traversal.expanderForAllTypes() : org.neo4j.kernel.Traversal.expanderForTypes(*@type_and_dirs)
|
74
|
-
expander
|
75
|
-
end
|
76
|
-
|
77
|
-
def _cost_evaluator #:nodoc:
|
78
|
-
raise "Algorithm requeries a cost evalulator, use the cost_evaluator to provide one" unless @cost_evaluator
|
79
|
-
@cost_evaluator
|
80
|
-
end
|
81
|
-
|
82
|
-
def _estimate_evaluator #:nodoc:
|
83
|
-
raise "Algorithm requeries a estimate evaluator, use the estimate_evaluator to provide one" unless @estimate_evaluator
|
84
|
-
@estimate_evaluator
|
85
|
-
end
|
86
|
-
|
87
|
-
# Specifies which outgoing relationship should be traversed for the graph algorithm
|
88
|
-
#
|
89
|
-
# @param [String, Symbol] rel the relationship type
|
90
|
-
# @return self
|
91
|
-
def outgoing(rel)
|
92
|
-
@type_and_dirs << type_to_java(rel)
|
93
|
-
@type_and_dirs << dir_to_java(:outgoing)
|
94
|
-
self
|
95
|
-
end
|
96
|
-
|
97
|
-
# Specifies which incoming relationship should be traversed for the graph algorithm
|
98
|
-
#
|
99
|
-
# @param [String, Symbol] rel the relationship type
|
100
|
-
# @return self
|
101
|
-
def incoming(rel)
|
102
|
-
@type_and_dirs << type_to_java(rel)
|
103
|
-
@type_and_dirs << dir_to_java(:incoming)
|
104
|
-
self
|
105
|
-
end
|
106
|
-
|
107
|
-
# Specifies which relationship should be traversed for the graph algorithm
|
108
|
-
#
|
109
|
-
# @example
|
110
|
-
# Neo4j::Algo.shortest_path(@x,@y).expand{|node| node._rels(:outgoing, :friends)}
|
111
|
-
#
|
112
|
-
# @example the above is same as:
|
113
|
-
# Neo4j::Algo.shortest_path(@x,@y).outgoing(:friends)
|
114
|
-
#
|
115
|
-
# @param [Proc] expander_proc a proc relationship type (symbol)
|
116
|
-
# @return self
|
117
|
-
def expand(&expander_proc)
|
118
|
-
@expander = (Neo4j::Core::Traversal::RelExpander.create_pair(&expander_proc))
|
119
|
-
self
|
120
|
-
end
|
121
|
-
|
122
|
-
# If only a single path should be returned,
|
123
|
-
# default for some algorithms, like shortest_path
|
124
|
-
def single
|
125
|
-
@single = true
|
126
|
-
self
|
127
|
-
end
|
128
|
-
|
129
|
-
# See #single
|
130
|
-
# Not sure if this method is useful
|
131
|
-
def many
|
132
|
-
@single = false
|
133
|
-
end
|
134
|
-
|
135
|
-
# The depth of the traversal
|
136
|
-
# Notice not all algorithm uses this argument (aStar and dijkstra)
|
137
|
-
def depth(depth)
|
138
|
-
@depth = depth
|
139
|
-
self
|
140
|
-
end
|
141
|
-
|
142
|
-
# Specifies a cost evaluator for the algorithm.
|
143
|
-
# Only available for the aStar and dijkstra algorithms.
|
144
|
-
#
|
145
|
-
# @example
|
146
|
-
# Neo4j::Algo.dijkstra(@x,@y).cost_evaluator{|rel,*| rel[:weight]}
|
147
|
-
#
|
148
|
-
def cost_evaluator(&cost_evaluator_proc)
|
149
|
-
@cost_evaluator = CostEvaluator.new(&cost_evaluator_proc)
|
150
|
-
self
|
151
|
-
end
|
152
|
-
|
153
|
-
# Specifies an evaluator that returns an (optimistic) estimation of the cost to get from the current node (in the traversal) to the end node.
|
154
|
-
# Only available for the aStar algorithm.
|
155
|
-
#
|
156
|
-
# The provided proc estimate the weight of the remaining path from one node to another.
|
157
|
-
# The proc takes two parameters:
|
158
|
-
# * node :: the node to estimate the weight from.
|
159
|
-
# * goal :: the node to estimate the weight to.
|
160
|
-
#
|
161
|
-
# The proc should return an estimation of the weight of the path from the first node to the second.
|
162
|
-
#
|
163
|
-
# @example
|
164
|
-
# Neo4j::Algo.a_star(@x,@y).cost_evaluator{...}.estimate_evaluator{|node,goal| some calculation returning a Float}
|
165
|
-
#
|
166
|
-
def estimate_evaluator(&estimate_evaluator_proc)
|
167
|
-
@estimate_evaluator = EstimateEvaluator.new(&estimate_evaluator_proc)
|
168
|
-
self
|
169
|
-
end
|
170
|
-
|
171
|
-
# Specifies that nodes should be returned from as result
|
172
|
-
# @see #rels
|
173
|
-
def nodes
|
174
|
-
@path_finder_method = :nodes
|
175
|
-
self
|
176
|
-
end
|
177
|
-
|
178
|
-
# Specifies that relationships should be returned from as result
|
179
|
-
# @see #nodes
|
180
|
-
def rels
|
181
|
-
@path_finder_method = :relationships
|
182
|
-
self
|
183
|
-
end
|
184
|
-
|
185
|
-
|
186
|
-
# So that one can call directly method on the PathFinder result from an executed algorithm.
|
187
|
-
def method_missing(m, *args, &block)
|
188
|
-
execute_algo.send(m, *args)
|
189
|
-
end
|
190
|
-
|
191
|
-
def each(&block) #:nodoc:
|
192
|
-
if @single && @path_finder_method
|
193
|
-
execute_algo.send(@path_finder_method).each &block
|
194
|
-
else
|
195
|
-
traversal = execute_algo
|
196
|
-
traversal.each &block if traversal
|
197
|
-
end
|
198
|
-
end
|
199
|
-
|
200
|
-
def execute_algo #:nodoc:
|
201
|
-
instance = self.instance_eval(&@factory_proc)
|
202
|
-
if @single
|
203
|
-
instance.find_single_path(@from._java_node, @to._java_node)
|
204
|
-
else
|
205
|
-
instance.find_all_paths(@from._java_node, @to._java_node)
|
206
|
-
end
|
207
|
-
end
|
208
|
-
|
209
|
-
# Returns an instance of Neo4j::Algo which can find all available paths between two nodes.
|
210
|
-
# These returned paths can contain loops (i.e. a node can occur more than once in any returned path).
|
211
|
-
def self.all_paths(from, to)
|
212
|
-
Algo.new(from, to) { org.neo4j.graphalgo.GraphAlgoFactory.all_paths(_expander, _depth) }
|
213
|
-
end
|
214
|
-
|
215
|
-
# See #all_paths, returns the first path found
|
216
|
-
def self.all_path(from, to)
|
217
|
-
Algo.new(from, to) { org.neo4j.graphalgo.GraphAlgoFactory.all_paths(_expander, _depth) }.single
|
218
|
-
end
|
219
|
-
|
220
|
-
# Returns an instance of Neo4j::Algo which can find all simple paths between two nodes.
|
221
|
-
# These returned paths cannot contain loops (i.e. a node cannot occur more than once in any returned path).
|
222
|
-
def self.all_simple_paths(from, to)
|
223
|
-
Algo.new(from, to) { org.neo4j.graphalgo.GraphAlgoFactory.all_simple_paths(_expander, _depth) }
|
224
|
-
end
|
225
|
-
|
226
|
-
# See #all_simple_paths, returns the first path found
|
227
|
-
def self.all_simple_path(from, to)
|
228
|
-
Algo.new(from, to) { org.neo4j.graphalgo.GraphAlgoFactory.all_simple_paths(_expander, _depth) }.single
|
229
|
-
end
|
230
|
-
|
231
|
-
# Returns an instance of Neo4j::Algo which can find all shortest paths (that is paths with as short Path.length() as possible) between two nodes.
|
232
|
-
# These returned paths cannot contain loops (i.e. a node cannot occur more than once in any returned path).
|
233
|
-
def self.shortest_paths(from, to)
|
234
|
-
Algo.new(from, to) { org.neo4j.graphalgo.GraphAlgoFactory.shortest_path(_expander, _depth) }
|
235
|
-
end
|
236
|
-
|
237
|
-
# See #shortest_paths, returns the first path found
|
238
|
-
def self.shortest_path(from, to)
|
239
|
-
Algo.new(from, to) { org.neo4j.graphalgo.GraphAlgoFactory.shortest_path(_expander, _depth) }.single
|
240
|
-
end
|
241
|
-
|
242
|
-
# Returns an instance of Neo4j::Algo which uses the Dijkstra algorithm to find the cheapest path between two nodes.
|
243
|
-
# The definition of "cheap" is the lowest possible cost to get from the start node to the end node, where the cost is returned from costEvaluator.
|
244
|
-
# These returned paths cannot contain loops (i.e. a node cannot occur more than once in any returned path).
|
245
|
-
# See http://en.wikipedia.org/wiki/Dijkstra%27s_algorithm for more information.
|
246
|
-
#
|
247
|
-
# Example
|
248
|
-
#
|
249
|
-
# Neo4j::Algo.dijkstra_path(node_a,node_b).cost_evaluator{|rel,*| rel[:weight]}.rels
|
250
|
-
#
|
251
|
-
def self.dijkstra_paths(from, to)
|
252
|
-
Algo.new(from, to) { org.neo4j.graphalgo.GraphAlgoFactory.dijkstra(_expander, _cost_evaluator) }
|
253
|
-
end
|
254
|
-
|
255
|
-
# See #dijkstra_paths, returns the first path found
|
256
|
-
#
|
257
|
-
def self.dijkstra_path(from, to)
|
258
|
-
Algo.new(from, to) { org.neo4j.graphalgo.GraphAlgoFactory.dijkstra(_expander, _cost_evaluator) }.single
|
259
|
-
end
|
260
|
-
|
261
|
-
# Returns an instance of Neo4j::Algo which uses the A* algorithm to find the cheapest path between two nodes.
|
262
|
-
# The definition of "cheap" is the lowest possible cost to get from the start node to the end node, where the cost is returned from lengthEvaluator and estimateEvaluator. These returned paths cannot contain loops (i.e. a node cannot occur more than once in any returned path).
|
263
|
-
# See http://en.wikipedia.org/wiki/A*_search_algorithm for more information.
|
264
|
-
#
|
265
|
-
# Expacts an cost evaluator and estimate evaluator, see Algo#cost_evaluator and Algo#estimate_evaluator
|
266
|
-
#
|
267
|
-
# Example:
|
268
|
-
#
|
269
|
-
# Neo4j::Algo.a_star_path(@x,@y).cost_evaluator{|rel,*| rel[:weight]}.estimate_evaluator{|node,goal| returns a float value}
|
270
|
-
#
|
271
|
-
def self.a_star_paths(from, to)
|
272
|
-
Algo.new(from, to) { org.neo4j.graphalgo.GraphAlgoFactory.a_star(_expander, _cost_evaluator, _estimate_evaluator) }
|
273
|
-
end
|
274
|
-
|
275
|
-
# See #a_star_paths, returns the first path found
|
276
|
-
#
|
277
|
-
def self.a_star_path(from, to)
|
278
|
-
Algo.new(from, to) { org.neo4j.graphalgo.GraphAlgoFactory.a_star(_expander, _cost_evaluator, _estimate_evaluator) }.single
|
279
|
-
end
|
280
|
-
|
281
|
-
# Returns an instance of Neo4j::Algo can find all paths of a certain length(depth) between two nodes.
|
282
|
-
# These returned paths cannot contain loops (i.e. a node cannot occur more than once in any returned path).
|
283
|
-
# Expects setting the depth parameter (the lenghto of the path) by the Algo#depth method.
|
284
|
-
#
|
285
|
-
# Example:
|
286
|
-
#
|
287
|
-
# Neo4j::Algo.with_length_paths(node_a,node_b).depth(2).each {|x| puts "Node #{x}"}
|
288
|
-
#
|
289
|
-
def self.with_length_paths(from,to)
|
290
|
-
Algo.new(from, to) { org.neo4j.graphalgo.GraphAlgoFactory.paths_with_length(_expander, _depth) }
|
291
|
-
end
|
292
|
-
|
293
|
-
# See #with_length_paths, returns the first path found
|
294
|
-
#
|
295
|
-
def self.with_length_path(from,to)
|
296
|
-
Algo.new(from, to) { org.neo4j.graphalgo.GraphAlgoFactory.paths_with_length(_expander, _depth) }.single
|
297
|
-
end
|
298
|
-
|
299
|
-
end
|
300
|
-
end
|