neo4j-core 3.0.0.alpha.1
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.
- checksums.yaml +7 -0
- data/Gemfile +22 -0
- data/README.md +332 -0
- data/lib/neo4j-core.rb +27 -0
- data/lib/neo4j-core/cypher_translator.rb +34 -0
- data/lib/neo4j-core/hash_with_indifferent_access.rb +165 -0
- data/lib/neo4j-core/helpers.rb +25 -0
- data/lib/neo4j-core/label.rb +8 -0
- data/lib/neo4j-core/version.rb +5 -0
- data/lib/neo4j-embedded.rb +18 -0
- data/lib/neo4j-embedded/embedded_database.rb +29 -0
- data/lib/neo4j-embedded/embedded_label.rb +80 -0
- data/lib/neo4j-embedded/embedded_node.rb +163 -0
- data/lib/neo4j-embedded/embedded_relationship.rb +44 -0
- data/lib/neo4j-embedded/embedded_session.rb +151 -0
- data/lib/neo4j-embedded/property.rb +43 -0
- data/lib/neo4j-embedded/to_java.rb +49 -0
- data/lib/neo4j-server.rb +10 -0
- data/lib/neo4j-server/cypher_label.rb +28 -0
- data/lib/neo4j-server/cypher_node.rb +140 -0
- data/lib/neo4j-server/cypher_node_uncommited.rb +12 -0
- data/lib/neo4j-server/cypher_relationship.rb +82 -0
- data/lib/neo4j-server/cypher_response.rb +113 -0
- data/lib/neo4j-server/cypher_session.rb +156 -0
- data/lib/neo4j-server/cypher_transaction.rb +81 -0
- data/lib/neo4j-server/resource.rb +73 -0
- data/lib/neo4j/entity_equality.rb +9 -0
- data/lib/neo4j/jars/concurrentlinkedhashmap-lru-1.3.1.jar +0 -0
- data/lib/neo4j/jars/geronimo-jta_1.1_spec-1.1.1.jar +0 -0
- data/lib/neo4j/jars/lucene-core-3.6.2.jar +0 -0
- data/lib/neo4j/jars/neo4j-cypher-2.0.0-M06.jar +0 -0
- data/lib/neo4j/jars/neo4j-kernel-2.0-SNAPSHOT-tests.jar +0 -0
- data/lib/neo4j/jars/neo4j-kernel-2.0.0-M06.jar +0 -0
- data/lib/neo4j/jars/neo4j-lucene-index-2.0.0-M06.jar +0 -0
- data/lib/neo4j/jars/neo4j-management-2.0.0-M06.jar +0 -0
- data/lib/neo4j/jars/org.apache.servicemix.bundles.jline-0.9.94_1.jar +0 -0
- data/lib/neo4j/jars/parboiled-core-1.1.6.jar +0 -0
- data/lib/neo4j/jars/parboiled-scala_2.10-1.1.6.jar +0 -0
- data/lib/neo4j/jars/scala-library-2.10.2.jar +0 -0
- data/lib/neo4j/label.rb +88 -0
- data/lib/neo4j/node.rb +185 -0
- data/lib/neo4j/property_container.rb +22 -0
- data/lib/neo4j/property_validator.rb +23 -0
- data/lib/neo4j/relationship.rb +84 -0
- data/lib/neo4j/session.rb +124 -0
- data/lib/neo4j/tasks/neo4j_server.rb +131 -0
- data/lib/neo4j/transaction.rb +52 -0
- data/neo4j-core.gemspec +35 -0
- metadata +144 -0
@@ -0,0 +1,156 @@
|
|
1
|
+
module Neo4j::Server
|
2
|
+
|
3
|
+
# Plugin
|
4
|
+
Neo4j::Session.register_db(:server_db) do |endpoint_url|
|
5
|
+
response = HTTParty.get(endpoint_url)
|
6
|
+
raise "Server not available on #{endpoint_url} (response code #{response.code})" unless response.code == 200
|
7
|
+
root_data = JSON.parse(response.body)
|
8
|
+
Neo4j::Server::CypherSession.new(root_data['data'])
|
9
|
+
end
|
10
|
+
|
11
|
+
class CypherSession < Neo4j::Session
|
12
|
+
include Resource
|
13
|
+
include Neo4j::Core::CypherTranslator
|
14
|
+
|
15
|
+
alias_method :super_query, :query
|
16
|
+
|
17
|
+
def initialize(data_url)
|
18
|
+
Neo4j::Session.register(self)
|
19
|
+
initialize_resource(data_url)
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
"CypherSession #{@resource_url}"
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize_resource(data_url)
|
27
|
+
response = HTTParty.get(data_url)
|
28
|
+
expect_response_code(response,200)
|
29
|
+
data_resource = JSON.parse(response.body)
|
30
|
+
raise "!!!!NO data_resource for #{response.body}" unless data_resource
|
31
|
+
# store the resource data
|
32
|
+
init_resource_data(data_resource, data_url)
|
33
|
+
end
|
34
|
+
|
35
|
+
def close
|
36
|
+
super
|
37
|
+
Neo4j::Transaction.unregister_current
|
38
|
+
end
|
39
|
+
|
40
|
+
def begin_tx
|
41
|
+
tx = wrap_resource(self, 'transaction', CypherTransaction, nil, :post)
|
42
|
+
Thread.current[:neo4j_curr_tx] = tx
|
43
|
+
tx
|
44
|
+
end
|
45
|
+
|
46
|
+
def create_node(props=nil, labels=[])
|
47
|
+
l = labels.empty? ? "" : ":" + labels.map{|k| "`#{k}`"}.join(':')
|
48
|
+
q = "CREATE (n#{l} #{cypher_prop_list(props)}) RETURN ID(n)"
|
49
|
+
cypher_response = _query_or_fail(q, true)
|
50
|
+
CypherNode.new(self, cypher_response)
|
51
|
+
end
|
52
|
+
|
53
|
+
def load_node(neo_id)
|
54
|
+
cypher_response = _query("START n=node(#{neo_id}) RETURN n")
|
55
|
+
if (!cypher_response.error?)
|
56
|
+
CypherNode.new(self, neo_id)
|
57
|
+
elsif (cypher_response.error_status == 'EntityNotFoundException')
|
58
|
+
return nil
|
59
|
+
else
|
60
|
+
cypher_response.raise_error
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def load_relationship(neo_id)
|
65
|
+
cypher_response = _query("START r=relationship(#{neo_id}) RETURN r")
|
66
|
+
if (!cypher_response.error?)
|
67
|
+
CypherRelationship.new(self, neo_id)
|
68
|
+
elsif (cypher_response.error_msg =~ /not found/) # Ugly that the Neo4j API gives us this error message
|
69
|
+
return nil
|
70
|
+
else
|
71
|
+
cypher_response.raise_error
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def create_label(name)
|
76
|
+
CypherLabel.new(self, name)
|
77
|
+
end
|
78
|
+
|
79
|
+
def indexes(label)
|
80
|
+
response = HTTParty.get("#{@resource_url}schema/index/#{label}")
|
81
|
+
expect_response_code(response, 200)
|
82
|
+
data_resource = JSON.parse(response.body)
|
83
|
+
|
84
|
+
property_keys = data_resource.map do |row|
|
85
|
+
row['property-keys'].map(&:to_sym)
|
86
|
+
end
|
87
|
+
|
88
|
+
{
|
89
|
+
property_keys: property_keys
|
90
|
+
}
|
91
|
+
end
|
92
|
+
|
93
|
+
def find_all_nodes(label_name)
|
94
|
+
response = _query_or_fail("MATCH (n:`#{label_name}`) RETURN ID(n)")
|
95
|
+
search_result_to_enumerable(response)
|
96
|
+
end
|
97
|
+
|
98
|
+
def find_nodes(label_name, key, value)
|
99
|
+
response = _query_or_fail <<-CYPHER
|
100
|
+
MATCH (n:`#{label_name}`)
|
101
|
+
WHERE n.#{key} = '#{value}'
|
102
|
+
RETURN ID(n)
|
103
|
+
CYPHER
|
104
|
+
search_result_to_enumerable(response)
|
105
|
+
end
|
106
|
+
|
107
|
+
def query(*params, &query_dsl)
|
108
|
+
result = super
|
109
|
+
if result.error?
|
110
|
+
raise Neo4j::Session::CypherError.new(result.error_msg, result.error_code, result.error_status)
|
111
|
+
end
|
112
|
+
result.to_hash_enumeration
|
113
|
+
end
|
114
|
+
|
115
|
+
# TODO remove this function and do not use cypher DSL internally
|
116
|
+
def _query_internal(*params, &query_dsl)
|
117
|
+
super_query(*params, &query_dsl)
|
118
|
+
end
|
119
|
+
|
120
|
+
def _query_or_fail(q, single_row = false, params=nil)
|
121
|
+
response = _query(q, params)
|
122
|
+
response.raise_error if response.error?
|
123
|
+
single_row ? response.first_data : response
|
124
|
+
end
|
125
|
+
|
126
|
+
def query_default_return
|
127
|
+
" RETURN ID(n)"
|
128
|
+
end
|
129
|
+
|
130
|
+
def _query(q, params=nil)
|
131
|
+
curr_tx = Neo4j::Transaction.current
|
132
|
+
if (curr_tx)
|
133
|
+
curr_tx._query(q, params)
|
134
|
+
else
|
135
|
+
url = resource_url('cypher')
|
136
|
+
q = params.nil? ? {query: q} : {query: q, params: params}
|
137
|
+
response = HTTParty.post(url, headers: resource_headers, body: q.to_json)
|
138
|
+
CypherResponse.create_with_no_tx(response)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def search_result_to_enumerable(response)
|
143
|
+
return [] unless response.data
|
144
|
+
|
145
|
+
Enumerator.new do |yielder|
|
146
|
+
response.data.each do |data|
|
147
|
+
yielder << CypherNode.new(self, data[0]).wrapper
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
|
153
|
+
|
154
|
+
|
155
|
+
end
|
156
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Neo4j::Server
|
2
|
+
class CypherTransaction
|
3
|
+
attr_reader :commit_url, :exec_url
|
4
|
+
|
5
|
+
include Resource
|
6
|
+
include Neo4j::Core::CypherTranslator
|
7
|
+
|
8
|
+
class CypherError < StandardError
|
9
|
+
attr_reader :code, :status
|
10
|
+
def initialize(code, status, message)
|
11
|
+
super(message)
|
12
|
+
@code = code
|
13
|
+
@status = status
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(db, response, url)
|
18
|
+
@commit_url = response['commit']
|
19
|
+
@exec_url = response.headers['location']
|
20
|
+
init_resource_data(response, url)
|
21
|
+
expect_response_code(response,201)
|
22
|
+
Neo4j::Transaction.register(self)
|
23
|
+
end
|
24
|
+
|
25
|
+
def _query(cypher_query, params=nil)
|
26
|
+
statement = {statement: cypher_query}
|
27
|
+
body = {statements: [statement]}
|
28
|
+
|
29
|
+
if params
|
30
|
+
# TODO can't get this working for some reason using parameters
|
31
|
+
#props = params.keys.inject({}) do|ack, k|
|
32
|
+
# ack[k] = {name: params[k]}
|
33
|
+
# ack
|
34
|
+
#end
|
35
|
+
#statement[:parameters] = props
|
36
|
+
|
37
|
+
# So we have to do this workaround
|
38
|
+
params.each_pair do |k,v|
|
39
|
+
statement[:statement].gsub!("{ #{k} }", escape_value(v))
|
40
|
+
end
|
41
|
+
end
|
42
|
+
response = HTTParty.post(@exec_url, headers: resource_headers, body: body.to_json)
|
43
|
+
|
44
|
+
first_result = response['results'][0]
|
45
|
+
cr = CypherResponse.new(response, true)
|
46
|
+
|
47
|
+
if (response['errors'].empty?)
|
48
|
+
cr.set_data(first_result['data'], first_result['columns'])
|
49
|
+
else
|
50
|
+
first_error = response['errors'].first
|
51
|
+
cr.set_error(first_error['message'], first_error['status'], first_error['code'])
|
52
|
+
end
|
53
|
+
cr
|
54
|
+
end
|
55
|
+
|
56
|
+
def success
|
57
|
+
# this is need in the Java API
|
58
|
+
end
|
59
|
+
|
60
|
+
def failure
|
61
|
+
@failure = true
|
62
|
+
end
|
63
|
+
|
64
|
+
def failure?
|
65
|
+
!!@failure
|
66
|
+
end
|
67
|
+
|
68
|
+
def finish
|
69
|
+
Neo4j::Transaction.unregister(self)
|
70
|
+
if failure?
|
71
|
+
response = HTTParty.delete(@exec_url, headers: resource_headers)
|
72
|
+
else
|
73
|
+
response = HTTParty.post(@commit_url, headers: resource_headers)
|
74
|
+
end
|
75
|
+
expect_response_code(response,200)
|
76
|
+
response
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Neo4j
|
2
|
+
module Server
|
3
|
+
module Resource
|
4
|
+
|
5
|
+
class ServerException < Exception
|
6
|
+
end
|
7
|
+
|
8
|
+
attr_reader :resource_data, :resource_url
|
9
|
+
|
10
|
+
def init_resource_data(resource_data, resource_url)
|
11
|
+
raise "Exception #{response['exception']}" if resource_data['exception']
|
12
|
+
@resource_url = resource_url
|
13
|
+
@resource_data = resource_data
|
14
|
+
raise "expected @resource_data to be Hash got #{@resource_data.class}" unless @resource_data.respond_to?(:[])
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
def wrap_resource(db, rel, resource_class, args=nil, verb=:get, payload=nil)
|
20
|
+
url = resource_url(rel, args)
|
21
|
+
response = HTTParty.send(verb, url, headers: {'Content-Type' => 'application/json'}, body: payload)
|
22
|
+
response.code == 404 ? nil : resource_class.new(db, response, url)
|
23
|
+
end
|
24
|
+
|
25
|
+
def resource_url(rel=nil, args=nil)
|
26
|
+
return @resource_url unless rel
|
27
|
+
url = @resource_data[rel.to_s]
|
28
|
+
raise "No resource rel '#{rel}', available #{@resource_data.keys.inspect}" unless url
|
29
|
+
return url unless args
|
30
|
+
if (args.is_a?(Hash))
|
31
|
+
args.keys.inject(url){|ack, key| ack.sub("{#{key}}",args[key].to_s)}
|
32
|
+
else
|
33
|
+
"#{url}/#{args.to_s}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def handle_response_error(response, msg="Error for request", url = response.request.path.to_s )
|
38
|
+
raise ServerException.new("#{msg} #{url}, #{response.code}, #{response.body}")
|
39
|
+
end
|
40
|
+
|
41
|
+
def expect_response_code(response, expected_code, msg="Error for request", url=response.request.path.to_s )
|
42
|
+
handle_response_error(response, "Expected response code #{expected_code} #{msg}",url) unless response.code == expected_code
|
43
|
+
response
|
44
|
+
end
|
45
|
+
|
46
|
+
def response_exception(response)
|
47
|
+
return nil if response.body.nil? || response.body.empty?
|
48
|
+
JSON.parse(response.body)['exception']
|
49
|
+
end
|
50
|
+
|
51
|
+
def resource_headers
|
52
|
+
{'Content-Type' => 'application/json', 'Accept' => 'application/json'}
|
53
|
+
end
|
54
|
+
|
55
|
+
def resource_url_id(url = @resource_url)
|
56
|
+
url.match(/\/(\d+)$/)[1].to_i
|
57
|
+
end
|
58
|
+
|
59
|
+
def convert_from_json_value(value)
|
60
|
+
JSON.parse(value, :quirks_mode => true)
|
61
|
+
end
|
62
|
+
|
63
|
+
def convert_to_json_value(value)
|
64
|
+
case value
|
65
|
+
when String
|
66
|
+
%Q["#{value}"]
|
67
|
+
else
|
68
|
+
value.to_s
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
data/lib/neo4j/label.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
module Neo4j
|
2
|
+
class Label
|
3
|
+
|
4
|
+
# @abstract
|
5
|
+
def name
|
6
|
+
raise 'not implemented'
|
7
|
+
end
|
8
|
+
|
9
|
+
# @abstract
|
10
|
+
def create_index(*properties)
|
11
|
+
raise 'not implemented'
|
12
|
+
end
|
13
|
+
|
14
|
+
# @abstract
|
15
|
+
def drop_index(*properties)
|
16
|
+
raise 'not implemented'
|
17
|
+
end
|
18
|
+
|
19
|
+
# List indices for a label
|
20
|
+
# @abstract
|
21
|
+
def indexes
|
22
|
+
raise 'not implemented'
|
23
|
+
end
|
24
|
+
|
25
|
+
class << self
|
26
|
+
include Neo4j::Core::CypherTranslator
|
27
|
+
|
28
|
+
def create(name, session = Neo4j::Session.current)
|
29
|
+
session.create_label(name)
|
30
|
+
end
|
31
|
+
|
32
|
+
def query(label_name, query, session = Neo4j::Session.current)
|
33
|
+
cypher = "MATCH (n:`#{label_name}`)"
|
34
|
+
cypher += condition_to_cypher(query) if query[:conditions] && !query[:conditions].empty?
|
35
|
+
cypher += session.query_default_return
|
36
|
+
cypher += order_to_cypher(query) if query[:order]
|
37
|
+
|
38
|
+
response = session._query_or_fail(cypher)
|
39
|
+
session.search_result_to_enumerable(response) # TODO make it work in Embedded and refactor
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
def find_all_nodes(label_name, session = Neo4j::Session.current)
|
44
|
+
session.find_all_nodes(label_name)
|
45
|
+
end
|
46
|
+
|
47
|
+
def find_nodes(label_name, key, value, session = Neo4j::Session.current)
|
48
|
+
session.find_nodes(label_name, key, value)
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def condition_to_cypher(query)
|
54
|
+
conditions = query[:conditions]
|
55
|
+
" WHERE " + conditions.keys.map do |k|
|
56
|
+
"n.#{k}=#{escape_value(conditions[k])}"
|
57
|
+
end.join(" AND ")
|
58
|
+
end
|
59
|
+
|
60
|
+
def order_to_cypher(query)
|
61
|
+
cypher = " ORDER BY "
|
62
|
+
order = query[:order]
|
63
|
+
|
64
|
+
handleHash = Proc.new do |hash|
|
65
|
+
if (hash.is_a?(Hash))
|
66
|
+
k, v = hash.first
|
67
|
+
raise "only :asc or :desc allowed in order, got #{query.inspect}" unless [:asc, :desc].include?(v)
|
68
|
+
v.to_sym == :asc ? "n.`#{k}`" : "n.`#{k}` DESC"
|
69
|
+
else
|
70
|
+
"n.`#{hash}`" unless hash.is_a?(Hash)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
case order
|
75
|
+
when Array
|
76
|
+
cypher += order.map(&handleHash).join(', ')
|
77
|
+
when Hash
|
78
|
+
cypher += handleHash.call(order)
|
79
|
+
else
|
80
|
+
cypher += "n.`#{order}`"
|
81
|
+
end
|
82
|
+
|
83
|
+
cypher
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
data/lib/neo4j/node.rb
ADDED
@@ -0,0 +1,185 @@
|
|
1
|
+
module Neo4j
|
2
|
+
|
3
|
+
# A module that allows plugins to register wrappers around Neo4j::Node objects
|
4
|
+
module Wrapper
|
5
|
+
# Used by Neo4j::NodeMixin to wrap nodes
|
6
|
+
def wrapper
|
7
|
+
self
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
# The base class for both the Embedded and Server Neo4j Node
|
12
|
+
# Notice this class is abstract and can't be instantiated
|
13
|
+
class Node
|
14
|
+
include EntityEquality
|
15
|
+
include Wrapper
|
16
|
+
include PropertyContainer
|
17
|
+
|
18
|
+
# @return [Hash] all properties of the node
|
19
|
+
def props()
|
20
|
+
raise 'not implemented'
|
21
|
+
end
|
22
|
+
|
23
|
+
# replace all properties with new properties
|
24
|
+
# @param hash a hash of properties the node should have
|
25
|
+
def props=(hash)
|
26
|
+
raise 'not implemented'
|
27
|
+
end
|
28
|
+
|
29
|
+
# Directly remove the property on the node (low level method, may need transaction)
|
30
|
+
def remove_property(key)
|
31
|
+
raise 'not implemented'
|
32
|
+
end
|
33
|
+
|
34
|
+
# Directly set the property on the node (low level method, may need transaction)
|
35
|
+
# @param [Hash, String] key
|
36
|
+
# @param value see Neo4j::PropertyValidator::VALID_PROPERTY_VALUE_CLASSES for valid values
|
37
|
+
def set_property(key, value)
|
38
|
+
raise 'not implemented'
|
39
|
+
end
|
40
|
+
|
41
|
+
# Directly get the property on the node (low level method, may need transaction)
|
42
|
+
# @param [Hash, String] key
|
43
|
+
# @return the value of the key
|
44
|
+
def get_property(key, value)
|
45
|
+
raise 'not implemented'
|
46
|
+
end
|
47
|
+
|
48
|
+
# Creates a relationship of given type to other_node with optionally properties
|
49
|
+
# @param [Symbol] type the type of the relation between the two nodes
|
50
|
+
# @param [Neo4j::Node] other_node the other node
|
51
|
+
# @param [Hash] props optionally properties for the created relationship
|
52
|
+
def create_rel(type, other_node, props = nil)
|
53
|
+
raise 'not implemented'
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
# Returns an enumeration of relationships.
|
58
|
+
# It always returns relationships of depth one.
|
59
|
+
#
|
60
|
+
# @param [Hash] opts the options to create a message with.
|
61
|
+
# @option opts [Symbol] :dir dir the direction of the relationship, allowed values: :both, :incoming, :outgoing.
|
62
|
+
# @option opts [Symbol] :type the type of relationship to navigate
|
63
|
+
# @option opts [Symbol] :between return all the relationships between this and given node
|
64
|
+
# @return [Enumerable] of Neo4j::Relationship objects
|
65
|
+
#
|
66
|
+
# @example Return both incoming and outgoing relationships of any type
|
67
|
+
# node_a.rels
|
68
|
+
#
|
69
|
+
# @example All outgoing or incoming relationship of type friends
|
70
|
+
# node_a.rels(type: :friends)
|
71
|
+
#
|
72
|
+
# @example All outgoing relationships between me and another node of type friends
|
73
|
+
# node_a.rels(type: :friends, dir: :outgoing, between: node_b)
|
74
|
+
#
|
75
|
+
def rels(match = {dir: :both})
|
76
|
+
raise 'not implemented'
|
77
|
+
end
|
78
|
+
|
79
|
+
# Adds one or more Neo4j labels on the node
|
80
|
+
def add_label(*labels)
|
81
|
+
raise 'not implemented'
|
82
|
+
end
|
83
|
+
|
84
|
+
# @return all labels on the node
|
85
|
+
def labels()
|
86
|
+
raise 'not implemented'
|
87
|
+
end
|
88
|
+
|
89
|
+
# Deletes this node from the database
|
90
|
+
def del()
|
91
|
+
raise 'not implemented'
|
92
|
+
end
|
93
|
+
|
94
|
+
# @return true if the node exists in the database
|
95
|
+
def exist?
|
96
|
+
raise 'not implemented'
|
97
|
+
end
|
98
|
+
|
99
|
+
# @returns all the Neo4j labels for this node
|
100
|
+
def labels
|
101
|
+
raise 'not implemented'
|
102
|
+
end
|
103
|
+
|
104
|
+
# Returns the only node of a given type and direction that is attached to this node, or nil.
|
105
|
+
# This is a convenience method that is used in the commonly occuring situation where a node has exactly zero or one relationships of a given type and direction to another node.
|
106
|
+
# Typically this invariant is maintained by the rest of the code: if at any time more than one such relationships exist, it is a fatal error that should generate an exception.
|
107
|
+
#
|
108
|
+
# This method reflects that semantics and returns either:
|
109
|
+
# * nil if there are zero relationships of the given type and direction,
|
110
|
+
# * the relationship if there's exactly one, or
|
111
|
+
# * throws an exception in all other cases.
|
112
|
+
#
|
113
|
+
# This method should be used only in situations with an invariant as described above. In those situations, a "state-checking" method (e.g. #rel?) is not required,
|
114
|
+
# because this method behaves correctly "out of the box."
|
115
|
+
#
|
116
|
+
# @param (see #rel)
|
117
|
+
def node(specs = {})
|
118
|
+
raise 'not implemented'
|
119
|
+
end
|
120
|
+
|
121
|
+
# Same as #node but returns the relationship. Notice it may raise an exception if there are more then one relationship matching.
|
122
|
+
def rel(spec = {})
|
123
|
+
raise 'not implemented'
|
124
|
+
end
|
125
|
+
|
126
|
+
# Returns true or false if there is one or more relationships
|
127
|
+
# Same as `!! #rel()`
|
128
|
+
def rel?(spec = {})
|
129
|
+
raise 'not implemented'
|
130
|
+
end
|
131
|
+
|
132
|
+
# Same as Neo4j::Node#exist?
|
133
|
+
def exist?
|
134
|
+
raise 'not implemented'
|
135
|
+
end
|
136
|
+
|
137
|
+
# Works like #rels method but instead returns the nodes.
|
138
|
+
# It does try to load a Ruby wrapper around each node
|
139
|
+
# @abstract
|
140
|
+
# @param (see #rels)
|
141
|
+
# @return [Enumerable] an Enumeration of either Neo4j::Node objects or wrapped Neo4j::Node objects
|
142
|
+
# @notice it's possible that the same node is returned more then once because of several relationship reaching to the same node, see #outgoing for alternative
|
143
|
+
def nodes(specs = {})
|
144
|
+
#rels(specs).map{|n| n.other_node(self)}
|
145
|
+
end
|
146
|
+
|
147
|
+
class << self
|
148
|
+
# Creates a node
|
149
|
+
def create(props=nil, *labels_or_db)
|
150
|
+
session = Neo4j::Core::ArgumentHelper.session(labels_or_db)
|
151
|
+
session.create_node(props, labels_or_db)
|
152
|
+
end
|
153
|
+
|
154
|
+
# Loads a node from the database with given id
|
155
|
+
def load(neo_id, session = Neo4j::Session.current)
|
156
|
+
node = session.load_node(neo_id)
|
157
|
+
node && node.wrapper
|
158
|
+
end
|
159
|
+
|
160
|
+
# Checks if the given entity node or entity id (Neo4j::Node#neo_id) exists in the database.
|
161
|
+
# @return [true, false] if exist
|
162
|
+
def exist?(entity_or_entity_id, session = Neo4j::Session.current)
|
163
|
+
session.node_exist?(neo_id)
|
164
|
+
end
|
165
|
+
|
166
|
+
# Find the node with given label and value
|
167
|
+
def find_nodes(label, value=nil, session = Neo4j::Session.current)
|
168
|
+
session.find_nodes(label, value)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def initialize
|
173
|
+
raise "Can't instantiate abstract class" if abstract_class?
|
174
|
+
puts "Instantiated!"
|
175
|
+
end
|
176
|
+
|
177
|
+
private
|
178
|
+
def abstract_class?
|
179
|
+
self.class == Node
|
180
|
+
end
|
181
|
+
|
182
|
+
|
183
|
+
end
|
184
|
+
|
185
|
+
end
|