neo4j-core 8.1.4 → 9.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 +5 -5
- data/README.md +71 -8
- data/lib/neo4j-core.rb +3 -49
- data/lib/neo4j/core.rb +4 -0
- data/lib/neo4j/core/config.rb +13 -0
- data/lib/neo4j/core/cypher_session/adaptors.rb +15 -15
- data/lib/neo4j/core/cypher_session/adaptors/bolt.rb +39 -48
- data/lib/neo4j/core/cypher_session/adaptors/bolt/chunk_writer_io.rb +0 -4
- data/lib/neo4j/core/cypher_session/adaptors/bolt/pack_stream.rb +7 -3
- data/lib/neo4j/core/cypher_session/adaptors/embedded.rb +1 -2
- data/lib/neo4j/core/cypher_session/adaptors/has_uri.rb +4 -0
- data/lib/neo4j/core/cypher_session/adaptors/http.rb +1 -3
- data/lib/neo4j/core/cypher_session/responses.rb +1 -1
- data/lib/neo4j/core/cypher_session/responses/bolt.rb +0 -17
- data/lib/neo4j/core/cypher_session/responses/embedded.rb +9 -7
- data/lib/neo4j/core/cypher_session/responses/http.rb +3 -4
- data/lib/neo4j/core/cypher_session/transactions.rb +2 -0
- data/lib/{neo4j-core → neo4j/core}/helpers.rb +1 -14
- data/lib/neo4j/core/logging.rb +44 -0
- data/lib/{neo4j-core → neo4j/core}/query.rb +7 -6
- data/lib/{neo4j-core → neo4j/core}/query_clauses.rb +9 -16
- data/lib/{neo4j-core → neo4j/core}/query_find_in_batches.rb +3 -5
- data/lib/{neo4j-core → neo4j/core}/version.rb +1 -1
- data/lib/neo4j/transaction.rb +6 -8
- data/neo4j-core.gemspec +13 -11
- metadata +46 -50
- data/lib/ext/kernel.rb +0 -9
- data/lib/neo4j-core/active_entity.rb +0 -11
- data/lib/neo4j-core/label.rb +0 -9
- data/lib/neo4j-embedded.rb +0 -16
- data/lib/neo4j-embedded/cypher_response.rb +0 -71
- data/lib/neo4j-embedded/embedded_database.rb +0 -26
- data/lib/neo4j-embedded/embedded_ha_session.rb +0 -30
- data/lib/neo4j-embedded/embedded_impermanent_session.rb +0 -17
- data/lib/neo4j-embedded/embedded_label.rb +0 -88
- data/lib/neo4j-embedded/embedded_node.rb +0 -206
- data/lib/neo4j-embedded/embedded_relationship.rb +0 -77
- data/lib/neo4j-embedded/embedded_session.rb +0 -203
- data/lib/neo4j-embedded/embedded_transaction.rb +0 -30
- data/lib/neo4j-embedded/label.rb +0 -66
- data/lib/neo4j-embedded/property.rb +0 -106
- data/lib/neo4j-embedded/to_java.rb +0 -44
- data/lib/neo4j-server.rb +0 -12
- data/lib/neo4j-server/cypher_label.rb +0 -35
- data/lib/neo4j-server/cypher_node.rb +0 -221
- data/lib/neo4j-server/cypher_relationship.rb +0 -142
- data/lib/neo4j-server/cypher_response.rb +0 -248
- data/lib/neo4j-server/cypher_session.rb +0 -263
- data/lib/neo4j-server/cypher_transaction.rb +0 -100
- data/lib/neo4j-server/label.rb +0 -40
- data/lib/neo4j-server/resource.rb +0 -57
- data/lib/neo4j/entity_equality.rb +0 -8
- data/lib/neo4j/entity_marshal.rb +0 -20
- data/lib/neo4j/label.rb +0 -90
- data/lib/neo4j/node.rb +0 -216
- data/lib/neo4j/property_container.rb +0 -17
- data/lib/neo4j/property_validator.rb +0 -22
- data/lib/neo4j/relationship.rb +0 -161
- data/lib/neo4j/session.rb +0 -222
@@ -1,100 +0,0 @@
|
|
1
|
-
module Neo4j
|
2
|
-
module Server
|
3
|
-
# The CypherTransaction object lifecycle is as follows:
|
4
|
-
# * It is initialized with the transactional endpoint URL and the connection object to use for communication. It does not communicate with the server to create this.
|
5
|
-
# * The first query within the transaction sets the commit and execution addresses, :commit_url and :query_url.
|
6
|
-
# * At any time, `failure` can be called to mark a transaction failed and trigger a rollback upon closure.
|
7
|
-
# * `close` is called to end the transaction. It calls `commit` or `delete`.
|
8
|
-
#
|
9
|
-
# If a transaction is created and then closed without performing any queries, an OpenStruct is returned that behaves like a successfully closed query.
|
10
|
-
class CypherTransaction < Neo4j::Transaction::Base
|
11
|
-
include Resource
|
12
|
-
|
13
|
-
attr_reader :commit_url, :query_url
|
14
|
-
|
15
|
-
def connection
|
16
|
-
@session.connection
|
17
|
-
end
|
18
|
-
|
19
|
-
def base_url
|
20
|
-
require 'uri'
|
21
|
-
URI(@session.instance_variable_get('@resource_url')).tap do |uri|
|
22
|
-
uri.path = ''
|
23
|
-
end.to_s
|
24
|
-
end
|
25
|
-
|
26
|
-
ROW_REST = %w[row REST]
|
27
|
-
def _query(cypher_query, params = nil)
|
28
|
-
fail 'Transaction expired, unable to perform query' if expired?
|
29
|
-
statement = {statement: cypher_query, parameters: params, resultDataContents: ROW_REST}
|
30
|
-
body = {statements: [statement]}
|
31
|
-
|
32
|
-
response = @query_url ? query(body) : start(body)
|
33
|
-
|
34
|
-
create_cypher_response(response)
|
35
|
-
end
|
36
|
-
|
37
|
-
def start(body)
|
38
|
-
request(:post, start_url, 201, body).tap do |response|
|
39
|
-
@commit_url = response.body[:commit]
|
40
|
-
@query_url = response.headers[:Location]
|
41
|
-
|
42
|
-
fail "NO ENDPOINT URL #{connection} : HEAD: #{response.headers.inspect}" if !@query_url || @query_url.empty?
|
43
|
-
|
44
|
-
init_resource_data(response.body, base_url)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
def start_url
|
49
|
-
@session.resource_data.fetch(:transaction) || base_url
|
50
|
-
end
|
51
|
-
|
52
|
-
def query(body)
|
53
|
-
request(:post, @query_url, 200, body)
|
54
|
-
end
|
55
|
-
|
56
|
-
EMPTY_RESPONSE = OpenStruct.new(status: 200, body: '')
|
57
|
-
|
58
|
-
def delete
|
59
|
-
return EMPTY_RESPONSE if !@commit_url || expired?
|
60
|
-
|
61
|
-
request(:delete, @query_url, 200, nil, resource_headers)
|
62
|
-
end
|
63
|
-
|
64
|
-
def commit
|
65
|
-
return EMPTY_RESPONSE if !@commit_url || expired?
|
66
|
-
|
67
|
-
request(:post, @commit_url, 200, nil, resource_headers)
|
68
|
-
end
|
69
|
-
|
70
|
-
private
|
71
|
-
|
72
|
-
def request(action, endpoint_url, expected_code = 200, body = nil, headers = {})
|
73
|
-
connection.send(action, endpoint_url, body, headers).tap do |response|
|
74
|
-
expect_response_code!(response, expected_code)
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def create_cypher_response(response)
|
79
|
-
CypherResponse.new(response, true).tap do |cypher_response|
|
80
|
-
if response.body[:errors].empty?
|
81
|
-
cypher_response.set_data(response.body[:results][0])
|
82
|
-
else
|
83
|
-
first_error = response.body[:errors].first
|
84
|
-
tx_cleanup!(first_error)
|
85
|
-
cypher_response.set_error(first_error)
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
def tx_cleanup!(first_error)
|
91
|
-
autoclosed!
|
92
|
-
mark_expired if first_error[:message] =~ /Unrecognized transaction id/
|
93
|
-
end
|
94
|
-
|
95
|
-
def empty_response
|
96
|
-
OpenStruct.new(status: 200, body: '')
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
data/lib/neo4j-server/label.rb
DELETED
@@ -1,40 +0,0 @@
|
|
1
|
-
module Neo4j
|
2
|
-
class Label
|
3
|
-
class << self
|
4
|
-
def constraints(session = Neo4j::Session.current)
|
5
|
-
session.connection.get(CONSTRAINT_PATH).body
|
6
|
-
end
|
7
|
-
|
8
|
-
def constraint?(label_name, property, session = Neo4j::Session.current)
|
9
|
-
label_constraints = session.connection.get("#{CONSTRAINT_PATH}/#{label_name}").body
|
10
|
-
!label_constraints.select { |c| c[:label] == label_name.to_s && c[:property_keys].first == property.to_s }.empty?
|
11
|
-
end
|
12
|
-
|
13
|
-
def indexes(session = Neo4j::Session.current)
|
14
|
-
session.connection.get(INDEX_PATH).body
|
15
|
-
end
|
16
|
-
|
17
|
-
def index?(label_name, property, session = Neo4j::Session.current)
|
18
|
-
label_indexes = session.connection.get("#{INDEX_PATH}/#{label_name}").body
|
19
|
-
!label_indexes.select { |i| i[:label] == label_name.to_s && i[:property_keys].first == property.to_s }.empty?
|
20
|
-
end
|
21
|
-
|
22
|
-
def drop_all_indexes(session = Neo4j::Session.current)
|
23
|
-
indexes.each do |i|
|
24
|
-
begin
|
25
|
-
session._query_or_fail("DROP INDEX ON :`#{i[:label]}`(#{i[:property_keys].first})")
|
26
|
-
rescue Neo4j::Server::CypherResponse::ResponseError
|
27
|
-
# This will error on each constraint. Ignore and continue.
|
28
|
-
next
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def drop_all_constraints(session = Neo4j::Session.current)
|
34
|
-
constraints.each do |c|
|
35
|
-
session._query_or_fail("DROP CONSTRAINT ON (n:`#{c[:label]}`) ASSERT n.`#{c[:property_keys].first}` IS UNIQUE")
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
@@ -1,57 +0,0 @@
|
|
1
|
-
module Neo4j
|
2
|
-
module Server
|
3
|
-
module Resource
|
4
|
-
class ServerException < Exception
|
5
|
-
end
|
6
|
-
|
7
|
-
attr_reader :resource_data, :resource_url
|
8
|
-
|
9
|
-
def init_resource_data(resource_data, resource_url)
|
10
|
-
fail "Exception #{resource_data[:exception]}" if resource_data[:exception]
|
11
|
-
fail "Expected @resource_data to be Hash got #{resource_data.inspect}" unless resource_data.respond_to?(:[])
|
12
|
-
|
13
|
-
@resource_data = resource_data
|
14
|
-
@resource_url = resource_url
|
15
|
-
|
16
|
-
self
|
17
|
-
end
|
18
|
-
|
19
|
-
def wrap_resource(connection)
|
20
|
-
CypherTransaction.new(resource_url(:transaction), connection)
|
21
|
-
end
|
22
|
-
|
23
|
-
def resource_url(key = nil)
|
24
|
-
return @resource_url if key.nil?
|
25
|
-
resource_data.fetch key
|
26
|
-
rescue KeyError
|
27
|
-
raise "No resource key '#{key}', available #{@resource_data.keys.inspect}"
|
28
|
-
end
|
29
|
-
|
30
|
-
def expect_response_code!(response, expected_code, msg = 'Error for request')
|
31
|
-
handle_response_error!(response, "Expected response code #{expected_code} #{msg}") unless response.status == expected_code
|
32
|
-
response
|
33
|
-
end
|
34
|
-
|
35
|
-
def handle_response_error!(response, msg = 'Error for request')
|
36
|
-
fail ServerException, "#{msg} #{response.env && response.env[:url].to_s}, #{response.status}"
|
37
|
-
end
|
38
|
-
|
39
|
-
def response_exception(response)
|
40
|
-
return nil if response.body.nil? || response.body.empty?
|
41
|
-
JSON.parse(response.body)[:exception]
|
42
|
-
end
|
43
|
-
|
44
|
-
def resource_headers
|
45
|
-
{'Content-Type' => 'application/json', 'Accept' => 'application/json; charset=UTF-8', 'User-Agent' => ::Neo4j::Session.user_agent_string}
|
46
|
-
end
|
47
|
-
|
48
|
-
def resource_url_id(url = resource_url)
|
49
|
-
url.match(%r{/(\d+)$})[1].to_i
|
50
|
-
end
|
51
|
-
|
52
|
-
def convert_from_json_value(value)
|
53
|
-
JSON.parse(value, quirks_mode: true)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
data/lib/neo4j/entity_marshal.rb
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
module Neo4j
|
2
|
-
# To support Ruby marshaling
|
3
|
-
module EntityMarshal
|
4
|
-
def marshal_dump
|
5
|
-
marshal_instance_variables.map(&method(:instance_variable_get))
|
6
|
-
end
|
7
|
-
|
8
|
-
def marshal_load(array)
|
9
|
-
marshal_instance_variables.zip(array).each do |var, value|
|
10
|
-
instance_variable_set(var, value)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
private
|
15
|
-
|
16
|
-
def marshal_instance_variables
|
17
|
-
self.class::MARSHAL_INSTANCE_VARIABLES
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
data/lib/neo4j/label.rb
DELETED
@@ -1,90 +0,0 @@
|
|
1
|
-
module Neo4j
|
2
|
-
# A label is a named graph construct that is used to group nodes.
|
3
|
-
# See Neo4j::Node how to create and delete nodes
|
4
|
-
# @see http://docs.neo4j.org/chunked/milestone/graphdb-neo4j-labels.html
|
5
|
-
class Label
|
6
|
-
class InvalidQueryError < StandardError; end
|
7
|
-
|
8
|
-
# @abstract
|
9
|
-
def name
|
10
|
-
fail 'not implemented'
|
11
|
-
end
|
12
|
-
|
13
|
-
# @abstract
|
14
|
-
def create_index(*properties)
|
15
|
-
fail 'not implemented'
|
16
|
-
end
|
17
|
-
|
18
|
-
# @abstract
|
19
|
-
def drop_index(*properties)
|
20
|
-
fail 'not implemented'
|
21
|
-
end
|
22
|
-
|
23
|
-
# List indices for a label
|
24
|
-
# @abstract
|
25
|
-
def indexes
|
26
|
-
fail 'not implemented'
|
27
|
-
end
|
28
|
-
|
29
|
-
# Creates a neo4j constraint on a property
|
30
|
-
# See http://docs.neo4j.org/chunked/stable/query-constraints.html
|
31
|
-
# @example
|
32
|
-
# label = Neo4j::Label.create(:person, session)
|
33
|
-
# label.create_constraint(:name, {type: :unique}, session)
|
34
|
-
#
|
35
|
-
def create_constraint(property, constraints, session = Neo4j::Session.current)
|
36
|
-
cypher = case constraints[:type]
|
37
|
-
when :unique
|
38
|
-
"CREATE CONSTRAINT ON (n:`#{name}`) ASSERT n.`#{property}` IS UNIQUE"
|
39
|
-
else
|
40
|
-
fail "Not supported constrain #{constraints.inspect} for property #{property} (expected :type => :unique)"
|
41
|
-
end
|
42
|
-
session._query_or_fail(cypher)
|
43
|
-
end
|
44
|
-
|
45
|
-
# Drops a neo4j constraint on a property
|
46
|
-
# See http://docs.neo4j.org/chunked/stable/query-constraints.html
|
47
|
-
# @example
|
48
|
-
# label = Neo4j::Label.create(:person, session)
|
49
|
-
# label.create_constraint(:name, {type: :unique}, session)
|
50
|
-
# label.drop_constraint(:name, {type: :unique}, session)
|
51
|
-
#
|
52
|
-
def drop_constraint(property, constraint, session = Neo4j::Session.current)
|
53
|
-
cypher = case constraint[:type]
|
54
|
-
when :unique
|
55
|
-
"DROP CONSTRAINT ON (n:`#{name}`) ASSERT n.`#{property}` IS UNIQUE"
|
56
|
-
else
|
57
|
-
fail "Not supported constrain #{constraint.inspect}"
|
58
|
-
end
|
59
|
-
session._query_or_fail(cypher)
|
60
|
-
end
|
61
|
-
|
62
|
-
private
|
63
|
-
|
64
|
-
def validate_index_options!(options)
|
65
|
-
return unless options[:type] && options[:type] != :exact
|
66
|
-
fail "Type #{options[:type]} is not supported"
|
67
|
-
end
|
68
|
-
|
69
|
-
class << self
|
70
|
-
INDEX_PATH = '/db/data/schema/index/'
|
71
|
-
CONSTRAINT_PATH = '/db/data/schema/constraint/'
|
72
|
-
|
73
|
-
# Returns a label of given name that can be used to specifying constraints
|
74
|
-
# @param [Symbol,String] name the name of the label
|
75
|
-
def create(name, session = Neo4j::Session.current)
|
76
|
-
session.create_label(name)
|
77
|
-
end
|
78
|
-
|
79
|
-
# @return [Enumerable<Neo4j::Node>] all nodes having given label. Nodes can be wrapped in your own model ruby classes.
|
80
|
-
def find_all_nodes(label_name, session = Neo4j::Session.current)
|
81
|
-
session.find_all_nodes(label_name)
|
82
|
-
end
|
83
|
-
|
84
|
-
# @return [Enumerable<Neo4j::Node>] all nodes having given label and properties. Nodes can be wrapped in your own model ruby classes.
|
85
|
-
def find_nodes(label_name, key, value, session = Neo4j::Session.current)
|
86
|
-
session.find_nodes(label_name, key, value)
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
data/lib/neo4j/node.rb
DELETED
@@ -1,216 +0,0 @@
|
|
1
|
-
module Neo4j
|
2
|
-
# The base class for both the Embedded and Server Neo4j Node
|
3
|
-
# Notice this class is abstract and can't be instantiated
|
4
|
-
class Node
|
5
|
-
# A module that allows plugins to register wrappers around Neo4j::Node objects
|
6
|
-
module Wrapper
|
7
|
-
# Used by Neo4j::NodeMixin to wrap nodes
|
8
|
-
def wrapper
|
9
|
-
self
|
10
|
-
end
|
11
|
-
|
12
|
-
def neo4j_obj
|
13
|
-
self
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
include EntityEquality
|
18
|
-
include Wrapper
|
19
|
-
include PropertyContainer
|
20
|
-
include EntityMarshal
|
21
|
-
|
22
|
-
# @return [Hash<Symbol, Object>] all properties of the node
|
23
|
-
def props
|
24
|
-
fail 'not implemented'
|
25
|
-
end
|
26
|
-
|
27
|
-
# replace all properties with new properties
|
28
|
-
# @param [Hash<Symbol, Object>] properties a hash of properties the node should have
|
29
|
-
def props=(properties)
|
30
|
-
fail 'not implemented'
|
31
|
-
end
|
32
|
-
|
33
|
-
# Refresh the properties by reading it from the database again next time an property value is requested.
|
34
|
-
def refresh
|
35
|
-
fail 'not implemented'
|
36
|
-
end
|
37
|
-
|
38
|
-
# Updates the properties, keeps old properties
|
39
|
-
# @param [Hash<Symbol, Object>] properties hash of properties that should be updated on the node
|
40
|
-
def update_props(properties)
|
41
|
-
fail 'not implemented'
|
42
|
-
end
|
43
|
-
|
44
|
-
# Directly remove the property on the node (low level method, may need transaction)
|
45
|
-
def remove_property(key)
|
46
|
-
fail 'not implemented'
|
47
|
-
end
|
48
|
-
|
49
|
-
# Directly set the property on the node (low level method, may need transaction)
|
50
|
-
# @param [Symbol, String] key
|
51
|
-
# @param value see Neo4j::PropertyValidator::VALID_PROPERTY_VALUE_CLASSES for valid values
|
52
|
-
def set_property(key, value)
|
53
|
-
fail 'not implemented'
|
54
|
-
end
|
55
|
-
|
56
|
-
# Directly get the property on the node (low level method, may need transaction)
|
57
|
-
# @param [Symbol, String] key
|
58
|
-
# @return the value of the key
|
59
|
-
def get_property(key, value)
|
60
|
-
fail 'not implemented'
|
61
|
-
end
|
62
|
-
|
63
|
-
# Creates a relationship of given type to other_node with optionally properties
|
64
|
-
# @param [Symbol] type the type of the relation between the two nodes
|
65
|
-
# @param [Neo4j::Node] other_node the other node
|
66
|
-
# @param [Hash<Symbol, Object>] props optionally properties for the created relationship
|
67
|
-
def create_rel(type, other_node, props = nil)
|
68
|
-
fail 'not implemented'
|
69
|
-
end
|
70
|
-
|
71
|
-
|
72
|
-
# Returns an enumeration of relationships.
|
73
|
-
# It always returns relationships of depth one.
|
74
|
-
#
|
75
|
-
# @param [Hash] match the options to create a message with.
|
76
|
-
# @option match [Symbol] :dir dir the direction of the relationship, allowed values: :both, :incoming, :outgoing.
|
77
|
-
# @option match [Symbol] :type the type of relationship to navigate
|
78
|
-
# @option match [Symbol] :between return all the relationships between this and given node
|
79
|
-
# @return [Enumerable<Neo4j::Relationship>] of Neo4j::Relationship objects
|
80
|
-
#
|
81
|
-
# @example Return both incoming and outgoing relationships of any type
|
82
|
-
# node_a.rels
|
83
|
-
#
|
84
|
-
# @example All outgoing or incoming relationship of type friends
|
85
|
-
# node_a.rels(type: :friends)
|
86
|
-
#
|
87
|
-
# @example All outgoing relationships between me and another node of type friends
|
88
|
-
# node_a.rels(type: :friends, dir: :outgoing, between: node_b)
|
89
|
-
#
|
90
|
-
def rels(match = {dir: :both})
|
91
|
-
fail 'not implemented'
|
92
|
-
end
|
93
|
-
|
94
|
-
# Adds one or more Neo4j labels on the node
|
95
|
-
# @param [Array<Symbol>] labels one or more labels to add
|
96
|
-
def add_label(*labels)
|
97
|
-
fail 'not implemented'
|
98
|
-
end
|
99
|
-
|
100
|
-
# Sets label on the node. Any old labels will be removed
|
101
|
-
# @param [Array<Symbol>] labels one or more labels to set
|
102
|
-
def set_label(*labels)
|
103
|
-
fail 'not implemented'
|
104
|
-
end
|
105
|
-
|
106
|
-
# Removes given labels
|
107
|
-
def remove_label(*labels)
|
108
|
-
fail 'not implemented'
|
109
|
-
end
|
110
|
-
|
111
|
-
#
|
112
|
-
# @return [Array<Symbol>]all labels on the node
|
113
|
-
def labels
|
114
|
-
fail 'not implemented'
|
115
|
-
end
|
116
|
-
|
117
|
-
# Deletes this node from the database
|
118
|
-
def del
|
119
|
-
fail 'not implemented'
|
120
|
-
end
|
121
|
-
|
122
|
-
# @return [Boolean] true if the node exists in the database
|
123
|
-
def exist?
|
124
|
-
fail 'not implemented'
|
125
|
-
end
|
126
|
-
|
127
|
-
# Returns the only node of a given type and direction that is attached to this node, or nil.
|
128
|
-
# 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.
|
129
|
-
# 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.
|
130
|
-
#
|
131
|
-
# This method reflects that semantics and returns either:
|
132
|
-
# * nil if there are zero relationships of the given type and direction,
|
133
|
-
# * the relationship if there's exactly one, or
|
134
|
-
# * throws an exception in all other cases.
|
135
|
-
#
|
136
|
-
# 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,
|
137
|
-
# because this method behaves correctly "out of the box."
|
138
|
-
#
|
139
|
-
# @param (see #rel)
|
140
|
-
def node(specs = {})
|
141
|
-
fail 'not implemented'
|
142
|
-
end
|
143
|
-
|
144
|
-
# Same as #node but returns the relationship. Notice it may raise an exception if there are more then one relationship matching.
|
145
|
-
def rel(spec = {})
|
146
|
-
fail 'not implemented'
|
147
|
-
end
|
148
|
-
|
149
|
-
def _rel(spec = {})
|
150
|
-
fail 'not implemented'
|
151
|
-
end
|
152
|
-
|
153
|
-
# Returns true or false if there is one or more relationships
|
154
|
-
# @return [Boolean]
|
155
|
-
def rel?(spec = {})
|
156
|
-
fail 'not implemented'
|
157
|
-
end
|
158
|
-
|
159
|
-
# Works like #rels method but instead returns the nodes.
|
160
|
-
# It does try to load a Ruby wrapper around each node
|
161
|
-
# @abstract
|
162
|
-
# @param (see #rels)
|
163
|
-
# @return [Enumerable<Neo4j::Node>] an Enumeration of either Neo4j::Node objects or wrapped Neo4j::Node objects
|
164
|
-
# @note it's possible that the same node is returned more than once because of several relationship reaching to the same node, see #outgoing for alternative
|
165
|
-
def nodes(specs = {})
|
166
|
-
# rels(specs).map{|n| n.other_node(self)}
|
167
|
-
end
|
168
|
-
|
169
|
-
class << self
|
170
|
-
# Creates a node
|
171
|
-
def create(props = nil, *labels_or_db)
|
172
|
-
session = if labels_or_db.last.is_a?(Neo4j::Session)
|
173
|
-
labels_or_db.pop
|
174
|
-
else
|
175
|
-
Neo4j::Session.current!
|
176
|
-
end
|
177
|
-
|
178
|
-
session.create_node(props, labels_or_db)
|
179
|
-
end
|
180
|
-
|
181
|
-
# Loads a node from the database with given id
|
182
|
-
def load(neo_id, session = Neo4j::Session.current!)
|
183
|
-
node = _load(neo_id, session)
|
184
|
-
node && node.wrapper
|
185
|
-
end
|
186
|
-
|
187
|
-
# Same as #load but does not try to return a wrapped node
|
188
|
-
# @return [Neo4j::Node] an unwrapped node
|
189
|
-
def _load(neo_id, session = Neo4j::Session.current!)
|
190
|
-
session.load_node(neo_id)
|
191
|
-
end
|
192
|
-
|
193
|
-
# Find the node with given label and value
|
194
|
-
def find_nodes(label, value = nil, session = Neo4j::Session.current!)
|
195
|
-
session.find_nodes(label, value)
|
196
|
-
end
|
197
|
-
|
198
|
-
def validate_match!(match)
|
199
|
-
invalid_match_keys = match.keys - %i[type dir between]
|
200
|
-
fail "Invalid match keys: #{invalid_match_keys.inspect}" if !invalid_match_keys.empty?
|
201
|
-
|
202
|
-
fail "Invalid dir: #{match[:dir]}" if ![nil, :incoming, :outgoing, :both].include?(match[:dir])
|
203
|
-
end
|
204
|
-
end
|
205
|
-
|
206
|
-
def initialize
|
207
|
-
fail "Can't instantiate abstract class" if abstract_class?
|
208
|
-
end
|
209
|
-
|
210
|
-
private
|
211
|
-
|
212
|
-
def abstract_class?
|
213
|
-
self.class == Node
|
214
|
-
end
|
215
|
-
end
|
216
|
-
end
|