neo4j-core 3.1.1 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +7 -12
  3. data/README.md +7 -7
  4. data/lib/neo4j-core.rb +3 -2
  5. data/lib/neo4j-core/active_entity.rb +8 -10
  6. data/lib/neo4j-core/cypher_translator.rb +61 -59
  7. data/lib/neo4j-core/hash_with_indifferent_access.rb +31 -22
  8. data/lib/neo4j-core/helpers.rb +15 -17
  9. data/lib/neo4j-core/label.rb +7 -6
  10. data/lib/neo4j-core/query.rb +271 -268
  11. data/lib/neo4j-core/query_clauses.rb +371 -355
  12. data/lib/neo4j-core/query_find_in_batches.rb +26 -26
  13. data/lib/neo4j-core/version.rb +1 -1
  14. data/lib/neo4j-embedded.rb +2 -2
  15. data/lib/neo4j-embedded/cypher_response.rb +40 -41
  16. data/lib/neo4j-embedded/embedded_database.rb +21 -22
  17. data/lib/neo4j-embedded/embedded_ha_session.rb +13 -11
  18. data/lib/neo4j-embedded/embedded_impermanent_session.rb +9 -8
  19. data/lib/neo4j-embedded/embedded_label.rb +64 -70
  20. data/lib/neo4j-embedded/embedded_node.rb +68 -73
  21. data/lib/neo4j-embedded/embedded_relationship.rb +6 -13
  22. data/lib/neo4j-embedded/embedded_session.rb +128 -132
  23. data/lib/neo4j-embedded/embedded_transaction.rb +34 -33
  24. data/lib/neo4j-embedded/property.rb +84 -77
  25. data/lib/neo4j-embedded/to_java.rb +24 -23
  26. data/lib/neo4j-server.rb +1 -1
  27. data/lib/neo4j-server/cypher_authentication.rb +105 -103
  28. data/lib/neo4j-server/cypher_label.rb +25 -23
  29. data/lib/neo4j-server/cypher_node.rb +180 -177
  30. data/lib/neo4j-server/cypher_node_uncommited.rb +11 -9
  31. data/lib/neo4j-server/cypher_relationship.rb +101 -102
  32. data/lib/neo4j-server/cypher_response.rb +171 -170
  33. data/lib/neo4j-server/cypher_session.rb +209 -205
  34. data/lib/neo4j-server/cypher_transaction.rb +66 -48
  35. data/lib/neo4j-server/resource.rb +17 -22
  36. data/lib/neo4j/entity_equality.rb +3 -4
  37. data/lib/neo4j/label.rb +13 -16
  38. data/lib/neo4j/node.rb +30 -34
  39. data/lib/neo4j/property_container.rb +3 -3
  40. data/lib/neo4j/property_validator.rb +4 -5
  41. data/lib/neo4j/relationship.rb +17 -22
  42. data/lib/neo4j/session.rb +19 -21
  43. data/lib/neo4j/tasks/config_server.rb +2 -3
  44. data/lib/neo4j/tasks/neo4j_server.rake +82 -74
  45. data/lib/neo4j/transaction.rb +23 -22
  46. data/neo4j-core.gemspec +21 -16
  47. metadata +72 -2
@@ -1,22 +1,23 @@
1
- module Neo4j::Embedded
2
- # A Utility class for translating Ruby object to Neo4j Java types
3
- # @private
4
- module ToJava
5
- def type_to_java(type)
6
- Java::OrgNeo4jGraphdb::DynamicRelationshipType.withName(type.to_s)
7
- end
1
+ module Neo4j
2
+ module Embedded
3
+ # A Utility class for translating Ruby object to Neo4j Java types
4
+ # @private
5
+ module ToJava
6
+ def type_to_java(type)
7
+ Java::OrgNeo4jGraphdb::DynamicRelationshipType.withName(type.to_s)
8
+ end
8
9
 
9
- module_function :type_to_java
10
+ module_function :type_to_java
10
11
 
11
- def types_to_java(types)
12
- types.inject([]) { |result, type| result << type_to_java(type) }.to_java(Java::OrgNeo4jGraphdb::RelationshipType)
13
- end
12
+ def types_to_java(types)
13
+ types.inject([]) { |result, type| result << type_to_java(type) }.to_java(Java::OrgNeo4jGraphdb::RelationshipType)
14
+ end
14
15
 
15
- module_function :types_to_java
16
+ module_function :types_to_java
16
17
 
17
18
 
18
- def dir_from_java(dir)
19
- case dir
19
+ def dir_from_java(dir)
20
+ case dir
20
21
  when Java::OrgNeo4jGraphdb::Direction::OUTGOING then
21
22
  :outgoing
22
23
  when Java::OrgNeo4jGraphdb::Direction::BOTH then
@@ -24,14 +25,14 @@ module Neo4j::Embedded
24
25
  when Java::OrgNeo4jGraphdb::Direction::INCOMING then
25
26
  :incoming
26
27
  else
27
- raise "unknown direction '#{dir} / #{dir.class}'"
28
+ fail "unknown direction '#{dir} / #{dir.class}'"
29
+ end
28
30
  end
29
- end
30
31
 
31
- module_function :dir_from_java
32
+ module_function :dir_from_java
32
33
 
33
- def dir_to_java(dir)
34
- case dir
34
+ def dir_to_java(dir)
35
+ case dir
35
36
  when :outgoing then
36
37
  Java::OrgNeo4jGraphdb::Direction::OUTGOING
37
38
  when :both then
@@ -39,11 +40,11 @@ module Neo4j::Embedded
39
40
  when :incoming then
40
41
  Java::OrgNeo4jGraphdb::Direction::INCOMING
41
42
  else
42
- raise "unknown direction '#{dir}', expects argument: outgoing, :incoming or :both"
43
+ fail "unknown direction '#{dir}', expects argument: outgoing, :incoming or :both"
44
+ end
43
45
  end
44
- end
45
-
46
- module_function :dir_to_java
47
46
 
47
+ module_function :dir_to_java
48
+ end
48
49
  end
49
50
  end
data/lib/neo4j-server.rb CHANGED
@@ -9,4 +9,4 @@ require 'neo4j-server/cypher_session'
9
9
  require 'neo4j-server/cypher_node_uncommited'
10
10
  require 'neo4j-server/cypher_relationship'
11
11
  require 'neo4j-server/cypher_response'
12
- require 'neo4j-server/cypher_transaction'
12
+ require 'neo4j-server/cypher_transaction'
@@ -1,123 +1,125 @@
1
- module Neo4j::Server
2
- # Neo4j 2.2 has an authentication layer. This class provides methods for interacting with it.
3
- class CypherAuthentication
4
- class InvalidPasswordError < RuntimeError; end
5
- class PasswordChangeRequiredError < RuntimeError; end
6
- class MissingCredentialsError < RuntimeError; end
1
+ module Neo4j
2
+ module Server
3
+ # Neo4j 2.2 has an authentication layer. This class provides methods for interacting with it.
4
+ class CypherAuthentication
5
+ class InvalidPasswordError < RuntimeError; end
6
+ class PasswordChangeRequiredError < RuntimeError; end
7
+ class MissingCredentialsError < RuntimeError; end
7
8
 
8
- attr_reader :connection, :url, :params, :token
9
+ attr_reader :connection, :url, :params, :token
9
10
 
10
- # @param [String] url_string The server address with protocol and port.
11
- # @param [Faraday::Connection] session_connection A Faraday::Connection object. This is either an existing object, likely the
12
- # same object used by the server for data, or a new one created specifically for auth tasks.
13
- # @param [Hash] params_hash Faraday connection options. In particularly, we're looking for basic_auth creds.
14
- def initialize(url_string, session_connection = new_connection, params_hash = {})
15
- @url = url_string
16
- @connection = session_connection
17
- @params = params_hash
18
- end
11
+ # @param [String] url_string The server address with protocol and port.
12
+ # @param [Faraday::Connection] session_connection A Faraday::Connection object. This is either an existing object, likely the
13
+ # same object used by the server for data, or a new one created specifically for auth tasks.
14
+ # @param [Hash] params_hash Faraday connection options. In particularly, we're looking for basic_auth creds.
15
+ def initialize(url_string, session_connection = new_connection, params_hash = {})
16
+ @url = url_string
17
+ @connection = session_connection
18
+ @params = params_hash
19
+ end
19
20
 
20
- # Set the username and password used to communicate with the server.
21
- def basic_auth(username, password)
22
- params[:basic_auth] ||= {}
23
- params[:basic_auth][:username] = username
24
- params[:basic_auth][:password] = password
25
- end
21
+ # Set the username and password used to communicate with the server.
22
+ def basic_auth(username, password)
23
+ params[:basic_auth] ||= {}
24
+ params[:basic_auth][:username] = username
25
+ params[:basic_auth][:password] = password
26
+ end
26
27
 
27
- # POSTs to the password change endpoint of the API. Does not invalidate tokens.
28
- # @param [String] old_password The current password.
29
- # @param [String] new_password The password you want to use.
30
- # @return [Hash] The response from the server.
31
- def change_password(old_password, new_password)
32
- connection.post("#{url}/user/neo4j/password", { 'password' => old_password, 'new_password' => new_password }).body
33
- end
28
+ # POSTs to the password change endpoint of the API. Does not invalidate tokens.
29
+ # @param [String] old_password The current password.
30
+ # @param [String] new_password The password you want to use.
31
+ # @return [Hash] The response from the server.
32
+ def change_password(old_password, new_password)
33
+ connection.post("#{url}/user/neo4j/password", 'password' => old_password, 'new_password' => new_password).body
34
+ end
34
35
 
35
- # Uses the given username and password to obtain a token, then adds the token to the connection's parameters.
36
- # @return [String] An access token provided by the server.
37
- def authenticate
38
- auth_response = auth_connection("#{url}/authentication")
39
- auth_hash = if auth_response.body.empty?
40
- nil
41
- elsif auth_response.body.is_a?(String)
42
- JSON.parse(auth_response.body)['errors'][0]['code'] == 'Neo.ClientError.Security.AuthorizationFailed' ? auth_attempt : nil
43
- else
44
- auth_response
45
- end
46
- return nil if auth_hash.nil?
47
- add_auth_headers(token_or_error(auth_hash))
48
- end
36
+ # Uses the given username and password to obtain a token, then adds the token to the connection's parameters.
37
+ # @return [String] An access token provided by the server.
38
+ def authenticate
39
+ auth_response = auth_connection("#{url}/authentication")
40
+ auth_hash = if auth_response.body.empty?
41
+ nil
42
+ elsif auth_response.body.is_a?(String)
43
+ JSON.parse(auth_response.body)['errors'][0]['code'] == 'Neo.ClientError.Security.AuthorizationFailed' ? auth_attempt : nil
44
+ else
45
+ auth_response
46
+ end
47
+ return nil if auth_hash.nil?
48
+ add_auth_headers(token_or_error(auth_hash))
49
+ end
49
50
 
50
- # Invalidates the existing token, which will invalidate all conncetions using this token, applies for a new token, adds this into
51
- # the connection headers.
52
- # @param [String] password The current server password.
53
- def reauthenticate(password)
54
- invalidate_token(password)
55
- add_auth_headers(token_or_error(auth_attempt))
56
- end
51
+ # Invalidates the existing token, which will invalidate all conncetions using this token, applies for a new token, adds this into
52
+ # the connection headers.
53
+ # @param [String] password The current server password.
54
+ def reauthenticate(password)
55
+ invalidate_token(password)
56
+ add_auth_headers(token_or_error(auth_attempt))
57
+ end
57
58
 
58
- # Requests a token from the authentication endpoint using the given username and password.
59
- # @return [Faraday::Response] The server's response, to be interpreted.
60
- def auth_attempt
61
- begin
62
- user = params[:basic_auth][:username]
63
- pass = params[:basic_auth][:password]
64
- rescue NoMethodError
65
- raise MissingCredentialsError, 'Neo4j authentication is enabled, username/password are required but missing'
59
+ # Requests a token from the authentication endpoint using the given username and password.
60
+ # @return [Faraday::Response] The server's response, to be interpreted.
61
+ def auth_attempt
62
+ begin
63
+ user = params[:basic_auth][:username]
64
+ pass = params[:basic_auth][:password]
65
+ rescue NoMethodError
66
+ raise MissingCredentialsError, 'Neo4j authentication is enabled, username/password are required but missing'
67
+ end
68
+ connection.post("#{url}/authentication", 'username' => user, 'password' => pass)
66
69
  end
67
- connection.post("#{url}/authentication", { 'username' => user, 'password' => pass })
68
- end
69
70
 
70
- # Takes a response object from the server and returns a token or fails with an error.
71
- # Todo: more error states!
72
- # @param [Farday::Response] auth_response The response after attempting authentication
73
- # @return [String] An authentication token.
74
- def token_or_error(auth_response)
75
- begin
76
- raise PasswordChangeRequiredError, "Server requires a password change, please visit #{url}" if auth_response.body['password_change_required']
77
- raise InvalidPasswordError, "Neo4j server responded with: #{auth_response.body['errors'][0]['message']}" if auth_response.status.to_i == 422
78
- rescue NoMethodError
79
- raise 'Unexpected auth response, please open an issue at https://github.com/neo4jrb/neo4j-core/issues'
71
+ # Takes a response object from the server and returns a token or fails with an error.
72
+ # TODO: more error states!
73
+ # @param [Farday::Response] auth_response The response after attempting authentication
74
+ # @return [String] An authentication token.
75
+ def token_or_error(auth_response)
76
+ begin
77
+ fail PasswordChangeRequiredError, "Server requires a password change, please visit #{url}" if auth_response.body['password_change_required']
78
+ fail InvalidPasswordError, "Neo4j server responded with: #{auth_response.body['errors'][0]['message']}" if auth_response.status.to_i == 422
79
+ rescue NoMethodError
80
+ raise 'Unexpected auth response, please open an issue at https://github.com/neo4jrb/neo4j-core/issues'
81
+ end
82
+ auth_response.body['authorization_token']
80
83
  end
81
- auth_response.body['authorization_token']
82
- end
83
84
 
84
- # Invalidates tokens as described at http://neo4j.com/docs/snapshot/rest-api-security.html#rest-api-invalidating-the-authorization-token
85
- # @param [String] current_password The current password used to connect to the database
86
- def invalidate_token(current_password)
87
- connection.post("#{url}/user/neo4j/authorization_token", { 'password' => current_password }).body
88
- end
85
+ # Invalidates tokens as described at http://neo4j.com/docs/snapshot/rest-api-security.html#rest-api-invalidating-the-authorization-token
86
+ # @param [String] current_password The current password used to connect to the database
87
+ def invalidate_token(current_password)
88
+ connection.post("#{url}/user/neo4j/authorization_token", 'password' => current_password).body
89
+ end
89
90
 
90
- # Stores an authentication token in the properly-formatted header.
91
- # This does not do any checking that what it has been given is a token. Whatever param is given will be base64 encoded and used as the header.
92
- # @param [String] token The authentication token provided by the database.
93
- def add_auth_headers(token)
94
- @token = token
95
- connection.headers['Authorization'] = "Basic realm=\"Neo4j\" #{token_hash(token)}"
96
- end
91
+ # Stores an authentication token in the properly-formatted header.
92
+ # This does not do any checking that what it has been given is a token. Whatever param is given will be base64 encoded and used as the header.
93
+ # @param [String] token The authentication token provided by the database.
94
+ def add_auth_headers(token)
95
+ @token = token
96
+ connection.headers['Authorization'] = "Basic realm=\"Neo4j\" #{token_hash(token)}"
97
+ end
97
98
 
98
- private
99
+ private
99
100
 
100
- # Makes testing easier, we can stub this method to simulate different responses
101
- def auth_connection(url)
102
- connection.get(url)
103
- end
101
+ # Makes testing easier, we can stub this method to simulate different responses
102
+ def auth_connection(url)
103
+ connection.get(url)
104
+ end
104
105
 
105
- def self.new_connection
106
- conn = Faraday.new do |b|
107
- b.request :json
108
- b.response :json, :content_type => "application/json"
109
- b.use Faraday::Adapter::NetHttpPersistent
106
+ def self.new_connection
107
+ conn = Faraday.new do |b|
108
+ b.request :json
109
+ b.response :json, content_type: 'application/json'
110
+ b.use Faraday::Adapter::NetHttpPersistent
111
+ end
112
+ conn.headers = {'Content-Type' => 'application/json'}
113
+ conn
110
114
  end
111
- conn.headers = { 'Content-Type' => 'application/json' }
112
- conn
113
- end
114
115
 
115
- def new_connection
116
- self.class.new_connection
117
- end
116
+ def new_connection
117
+ self.class.new_connection
118
+ end
118
119
 
119
- def token_hash(token)
120
- ::Base64.strict_encode64(":#{token}")
120
+ def token_hash(token)
121
+ ::Base64.strict_encode64(":#{token}")
122
+ end
121
123
  end
122
124
  end
123
- end
125
+ end
@@ -1,32 +1,34 @@
1
- module Neo4j::Server
2
- class CypherLabel < Neo4j::Label
3
- extend Forwardable
4
- def_delegator :@session, :query_cypher_for
5
- attr_reader :name
1
+ module Neo4j
2
+ module Server
3
+ class CypherLabel < Neo4j::Label
4
+ extend Forwardable
5
+ def_delegator :@session, :query_cypher_for
6
+ attr_reader :name
6
7
 
7
- def initialize(session, name)
8
- @name = name
9
- @session = session
10
- end
8
+ def initialize(session, name)
9
+ @name = name
10
+ @session = session
11
+ end
11
12
 
12
- def create_index(*properties)
13
- response = @session._query("CREATE INDEX ON :`#{@name}`(#{properties.join(',')})")
14
- response.raise_error if response.error?
15
- end
13
+ def create_index(*properties)
14
+ response = @session._query("CREATE INDEX ON :`#{@name}`(#{properties.join(',')})")
15
+ response.raise_error if response.error?
16
+ end
16
17
 
17
- def drop_index(*properties)
18
- properties.each do |property|
19
- response = @session._query("DROP INDEX ON :`#{@name}`(#{property})")
20
- response.raise_error if response.error? && !response.error_msg.match(/No such INDEX ON/)
18
+ def drop_index(*properties)
19
+ properties.each do |property|
20
+ response = @session._query("DROP INDEX ON :`#{@name}`(#{property})")
21
+ response.raise_error if response.error? && !response.error_msg.match(/No such INDEX ON/)
22
+ end
21
23
  end
22
- end
23
24
 
24
- def indexes
25
- @session.indexes(@name)
26
- end
25
+ def indexes
26
+ @session.indexes(@name)
27
+ end
27
28
 
28
- def uniqueness_constraints
29
- @session.uniqueness_constraints(@name)
29
+ def uniqueness_constraints
30
+ @session.uniqueness_constraints(@name)
31
+ end
30
32
  end
31
33
  end
32
34
  end
@@ -1,218 +1,221 @@
1
- module Neo4j::Server
2
- class CypherNode < Neo4j::Node
3
- include Neo4j::Server::Resource
4
- include Neo4j::Core::CypherTranslator
5
- include Neo4j::Core::ActiveEntity
6
-
7
- def initialize(session, value)
8
- @session = session
9
-
10
- @id = if value.is_a?(Hash)
11
- hash = value['data']
12
- @props = Hash[hash.map{ |k, v| [k.to_sym, v] }]
13
- @labels = value['metadata']['labels'].map(&:to_sym) if value['metadata']
14
- value['id'] # value['self'].match(/\d+$/)[0].to_i
15
- else
16
- value
1
+ module Neo4j
2
+ module Server
3
+ class CypherNode < Neo4j::Node
4
+ include Neo4j::Server::Resource
5
+ include Neo4j::Core::CypherTranslator
6
+ include Neo4j::Core::ActiveEntity
7
+
8
+ def initialize(session, value)
9
+ @session = session
10
+
11
+ @neo_id = if value.is_a?(Hash)
12
+ hash = value['data']
13
+ @props = Hash[hash.map { |k, v| [k.to_sym, v] }]
14
+ @labels = value['metadata']['labels'].map!(&:to_sym) if value['metadata']
15
+ value['id'] # value['self'].match(/\d+$/)[0].to_i
16
+ else
17
+ value
18
+ end
17
19
  end
18
- end
19
20
 
20
- def neo_id
21
- @id
22
- end
21
+ attr_reader :neo_id
23
22
 
24
- def inspect
25
- "CypherNode #{neo_id} (#{object_id})"
26
- end
23
+ def inspect
24
+ "CypherNode #{neo_id} (#{object_id})"
25
+ end
27
26
 
28
- # TODO, needed by neo4j-cypher
29
- def _java_node
30
- self
31
- end
27
+ # TODO, needed by neo4j-cypher
28
+ def _java_node
29
+ self
30
+ end
32
31
 
33
- # (see Neo4j::Node#create_rel)
34
- def create_rel(type, other_node, props = nil)
35
- id = @session._query_or_fail(rel_string(type, other_node, props), true, cypher_prop_list(props))
36
- data_hash = { 'type' => type, 'data' => props, 'start' => self.neo_id.to_s, 'end' => other_node.neo_id.to_s, 'id' => id }
37
- CypherRelationship.new(@session, data_hash)
38
- end
32
+ # (see Neo4j::Node#create_rel)
33
+ def create_rel(type, other_node, props = nil)
34
+ ids_hash = {start_neo_id: neo_id, end_neo_id: other_node.neo_id}
35
+ props_with_ids = props.nil? ? ids_hash : cypher_prop_list(props).merge(ids_hash)
36
+ id = @session._query_or_fail(rel_string(type, other_node, props), true, props_with_ids)
37
+ data_hash = {'type' => type, 'data' => props, 'start' => neo_id, 'end' => other_node.neo_id, 'id' => id}
38
+ CypherRelationship.new(@session, data_hash)
39
+ end
39
40
 
40
- def rel_string(type, other_node, props)
41
- "MATCH (a), (b) WHERE ID(a) = #{neo_id} AND ID(b) = #{other_node.neo_id} CREATE (a)-[r:`#{type}` #{prop_identifier(props)}]->(b) RETURN ID(r)"
42
- end
41
+ def rel_string(type, other_node, props)
42
+ "MATCH (a), (b) WHERE ID(a) = {start_neo_id} AND ID(b) = {end_neo_id} CREATE (a)-[r:`#{type}` #{prop_identifier(props)}]->(b) RETURN ID(r)"
43
+ end
43
44
 
44
- # (see Neo4j::Node#props)
45
- def props
46
- if @props
47
- @props
48
- else
49
- hash = @session._query_entity_data("#{match_start} RETURN n", nil, neo_id: neo_id)
50
- @props = Hash[hash['data'].map{ |k, v| [k.to_sym, v] }]
45
+ # (see Neo4j::Node#props)
46
+ def props
47
+ if @props
48
+ @props
49
+ else
50
+ hash = @session._query_entity_data("#{match_start} RETURN n", nil, neo_id: neo_id)
51
+ @props = Hash[hash['data'].map { |k, v| [k.to_sym, v] }]
52
+ end
51
53
  end
52
- end
53
54
 
54
- def refresh
55
- @props = nil
56
- end
55
+ def refresh
56
+ @props = nil
57
+ end
57
58
 
58
- # (see Neo4j::Node#remove_property)
59
- def remove_property(key)
60
- refresh
61
- @session._query_or_fail("#{match_start} REMOVE n.`#{key}`", false, { neo_id: neo_id })
62
- end
59
+ # (see Neo4j::Node#remove_property)
60
+ def remove_property(key)
61
+ refresh
62
+ @session._query_or_fail("#{match_start} REMOVE n.`#{key}`", false, neo_id: neo_id)
63
+ end
63
64
 
64
- # (see Neo4j::Node#set_property)
65
- def set_property(key,value)
66
- refresh
67
- @session._query_or_fail("#{match_start} SET n.`#{key}` = { value }", false, { value: value, neo_id: neo_id })
68
- value
69
- end
65
+ # (see Neo4j::Node#set_property)
66
+ def set_property(key, value)
67
+ refresh
68
+ @session._query_or_fail("#{match_start} SET n.`#{key}` = { value }", false, value: value, neo_id: neo_id)
69
+ value
70
+ end
70
71
 
71
- # (see Neo4j::Node#props=)
72
- def props=(properties)
73
- refresh
74
- @session._query_or_fail("#{match_start} SET n = { props }", false, { props: properties, neo_id: neo_id })
75
- properties
76
- end
72
+ # (see Neo4j::Node#props=)
73
+ def props=(properties)
74
+ refresh
75
+ @session._query_or_fail("#{match_start} SET n = { props }", false, props: properties, neo_id: neo_id)
76
+ properties
77
+ end
77
78
 
78
- def remove_properties(properties)
79
- refresh
80
- q = "#{match_start} REMOVE " + properties.map do |k|
81
- "n.`#{k}`"
82
- end.join(', ')
83
- @session._query_or_fail(q, false, neo_id: neo_id)
84
- end
79
+ def remove_properties(properties)
80
+ refresh
81
+ q = "#{match_start} REMOVE " + properties.map do |k|
82
+ "n.`#{k}`"
83
+ end.join(', ')
84
+ @session._query_or_fail(q, false, neo_id: neo_id)
85
+ end
85
86
 
86
- # (see Neo4j::Node#update_props)
87
- def update_props(properties)
88
- refresh
89
- return if properties.empty?
90
-
91
- removed_keys = properties.keys.select{|k| properties[k].nil?}
92
- remove_properties(removed_keys) unless removed_keys.empty?
93
- properties_to_set = properties.keys - removed_keys
94
- return if properties_to_set.empty?
95
- props_list = cypher_prop_list(properties)[:props].merge({ neo_id: neo_id })
96
- @session._query_or_fail("#{match_start} SET #{cypher_properties(properties_to_set)}", false, props_list)
97
- properties
98
- end
87
+ # (see Neo4j::Node#update_props)
88
+ def update_props(properties)
89
+ refresh
90
+ return if properties.empty?
91
+
92
+ removed_keys = properties.keys.select { |k| properties[k].nil? }
93
+ remove_properties(removed_keys) unless removed_keys.empty?
94
+ properties_to_set = properties.keys - removed_keys
95
+ return if properties_to_set.empty?
96
+ props_list = cypher_prop_list(properties)[:props].merge(neo_id: neo_id)
97
+ @session._query_or_fail("#{match_start} SET #{cypher_properties(properties_to_set)}", false, props_list)
98
+ properties
99
+ end
99
100
 
100
- # (see Neo4j::Node#get_property)
101
- def get_property(key)
102
- @props ? @props[key.to_sym] : @session._query_or_fail("#{match_start} RETURN n.`#{key}`", true, neo_id: neo_id)
103
- end
101
+ # (see Neo4j::Node#get_property)
102
+ def get_property(key)
103
+ @props ? @props[key.to_sym] : @session._query_or_fail("#{match_start} RETURN n.`#{key}`", true, neo_id: neo_id)
104
+ end
104
105
 
105
- # (see Neo4j::Node#labels)
106
- def labels
107
- @labels ||= @session._query_or_fail("#{match_start} RETURN labels(n) as labels", true, neo_id: neo_id)
108
- @labels.map(&:to_sym)
109
- end
106
+ # (see Neo4j::Node#labels)
107
+ def labels
108
+ @labels ||= @session._query_or_fail("#{match_start} RETURN labels(n) as labels", true, neo_id: neo_id).map!(&:to_sym)
109
+ end
110
110
 
111
- def _cypher_label_list(labels)
112
- ':' + labels.map{|label| "`#{label}`"}.join(':')
113
- end
111
+ def _cypher_label_list(labels_list)
112
+ ':' + labels_list.map { |label| "`#{label}`" }.join(':')
113
+ end
114
114
 
115
- def add_label(*labels)
116
- @session._query_or_fail("#{match_start} SET n #{_cypher_label_list(labels)}", false, neo_id: neo_id)
117
- end
115
+ def add_label(*new_labels)
116
+ @session._query_or_fail("#{match_start} SET n #{_cypher_label_list(new_labels)}", false, neo_id: neo_id)
117
+ new_labels.each { |label| labels << label }
118
+ end
118
119
 
119
- def remove_label(*labels)
120
- @session._query_or_fail("#{match_start} REMOVE n #{_cypher_label_list(labels)}", false, neo_id: neo_id)
121
- end
120
+ def remove_label(*target_labels)
121
+ @session._query_or_fail("#{match_start} REMOVE n #{_cypher_label_list(target_labels)}", false, neo_id: neo_id)
122
+ target_labels.each { |label| labels.delete(label) } unless labels.nil?
123
+ end
122
124
 
123
- def set_label(*label_names)
124
- q = "#{match_start} #{remove_labels_if_needed} #{set_labels_if_needed(label_names)}"
125
- @session._query_or_fail(q, false, neo_id: neo_id)
126
- end
125
+ def set_label(*label_names)
126
+ q = "#{match_start} #{remove_labels_if_needed} #{set_labels_if_needed(label_names)}"
127
+ @session._query_or_fail(q, false, neo_id: neo_id)
128
+ end
127
129
 
128
- # (see Neo4j::Node#del)
129
- def del
130
- @session._query_or_fail("#{match_start} OPTIONAL MATCH n-[r]-() DELETE n, r", false, neo_id: neo_id)
131
- end
130
+ # (see Neo4j::Node#del)
131
+ def del
132
+ @session._query_or_fail("#{match_start} OPTIONAL MATCH n-[r]-() DELETE n, r", false, neo_id: neo_id)
133
+ end
132
134
 
133
- alias_method :delete, :del
134
- alias_method :destroy, :del
135
+ alias_method :delete, :del
136
+ alias_method :destroy, :del
135
137
 
136
- # (see Neo4j::Node#exist?)
137
- def exist?
138
- @session._query("#{match_start} RETURN ID(n)", neo_id: neo_id).data.empty? ? false : true
139
- end
138
+ # (see Neo4j::Node#exist?)
139
+ def exist?
140
+ @session._query("#{match_start} RETURN ID(n)", neo_id: neo_id).data.empty? ? false : true
141
+ end
140
142
 
141
- # (see Neo4j::Node#node)
142
- def node(match={})
143
- ensure_single_relationship { match(CypherNode, "p as result LIMIT 2", match) }
144
- end
143
+ # (see Neo4j::Node#node)
144
+ def node(match = {})
145
+ ensure_single_relationship { match(CypherNode, 'p as result LIMIT 2', match) }
146
+ end
145
147
 
146
- # (see Neo4j::Node#rel)
147
- def rel(match={})
148
- ensure_single_relationship { match(CypherRelationship, "r as result LIMIT 2", match) }
149
- end
148
+ # (see Neo4j::Node#rel)
149
+ def rel(match = {})
150
+ ensure_single_relationship { match(CypherRelationship, 'r as result LIMIT 2', match) }
151
+ end
150
152
 
151
- # (see Neo4j::Node#rel?)
152
- def rel?(match={})
153
- result = match(CypherRelationship, "r as result", match)
154
- !!result.first
155
- end
153
+ # (see Neo4j::Node#rel?)
154
+ def rel?(match = {})
155
+ result = match(CypherRelationship, 'r as result', match)
156
+ !!result.first
157
+ end
156
158
 
157
- # (see Neo4j::Node#nodes)
158
- def nodes(match={})
159
- match(CypherNode, "p as result", match)
160
- end
159
+ # (see Neo4j::Node#nodes)
160
+ def nodes(match = {})
161
+ match(CypherNode, 'p as result', match)
162
+ end
161
163
 
162
- # (see Neo4j::Node#rels)
163
- def rels(match = {dir: :both})
164
- match(CypherRelationship, "r as result", match)
165
- end
164
+ # (see Neo4j::Node#rels)
165
+ def rels(match = {dir: :both})
166
+ match(CypherRelationship, 'r as result', match)
167
+ end
166
168
 
167
- # @private
168
- def match(clazz, returns, match={})
169
- to_dir = {outgoing: ->(rel) {"-#{rel}->"},
170
- incoming: ->(rel) {"<-#{rel}-"},
171
- both: ->(rel) {"-#{rel}-"} }
172
-
173
- cypher_rel = match[:type] ? "[r:`#{match[:type]}`]" : '[r]'
174
- between_id = match[:between] ? "MATCH (p) WHERE ID(p) = #{match[:between].neo_id}" : ""
175
- dir_func = to_dir[match[:dir] || :both]
176
- cypher = "#{match_start} #{between_id} MATCH (n)#{dir_func.call(cypher_rel)}(p) RETURN #{returns}"
177
- r = @session._query(cypher, neo_id: neo_id)
178
- r.raise_error if r.error?
179
- _map_result(r)
180
- end
169
+ # @private
170
+ def match(clazz, returns, match = {})
171
+ to_dir = {outgoing: ->(rel) { "-#{rel}->" },
172
+ incoming: ->(rel) { "<-#{rel}-" },
173
+ both: ->(rel) { "-#{rel}-" }}
174
+
175
+ cypher_rel = match[:type] ? "[r:`#{match[:type]}`]" : '[r]'
176
+ between_id = match[:between] ? "MATCH (p) WHERE ID(p) = #{match[:between].neo_id}" : ''
177
+ dir_func = to_dir[match[:dir] || :both]
178
+ cypher = "#{match_start} #{between_id} MATCH (n)#{dir_func.call(cypher_rel)}(p) RETURN #{returns}"
179
+ r = @session._query(cypher, neo_id: neo_id)
180
+ r.raise_error if r.error?
181
+ _map_result(r)
182
+ end
181
183
 
182
- def _map_result(r)
183
- r.to_node_enumeration.map { |rel| rel.result }
184
- end
184
+ def _map_result(r)
185
+ r.to_node_enumeration.map(&:result)
186
+ end
185
187
 
186
- private
188
+ private
187
189
 
188
- def cypher_properties(properties_to_set)
189
- properties_to_set.map! { |k| "n.`#{k}` = {`#{k}`}"}.join(',')
190
- end
190
+ def cypher_properties(properties_to_set)
191
+ properties_to_set.map! { |k| "n.`#{k}` = {`#{k}`}" }.join(',')
192
+ end
191
193
 
192
- def remove_labels_if_needed
193
- if labels.empty?
194
- ""
195
- else
196
- " REMOVE n #{_cypher_label_list(labels)}"
194
+ def remove_labels_if_needed
195
+ if labels.empty?
196
+ ''
197
+ else
198
+ " REMOVE n #{_cypher_label_list(labels)}"
199
+ end
197
200
  end
198
- end
199
201
 
200
- def set_labels_if_needed(label_names)
201
- if label_names.empty?
202
- ""
203
- else
204
- " SET n #{_cypher_label_list(label_names.map(&:to_sym).uniq)}"
202
+ def set_labels_if_needed(label_names)
203
+ if label_names.empty?
204
+ ''
205
+ else
206
+ " SET n #{_cypher_label_list(label_names.map(&:to_sym).uniq)}"
207
+ end
205
208
  end
206
- end
207
209
 
208
- def ensure_single_relationship(&block)
209
- result = yield
210
- raise "Expected to only find one relationship from node #{neo_id} matching #{match.inspect} but found #{result.count}" if result.count > 1
211
- result.first
212
- end
210
+ def ensure_single_relationship(&block)
211
+ result = yield
212
+ fail "Expected to only find one relationship from node #{neo_id} matching #{match.inspect} but found #{result.count}" if result.count > 1
213
+ result.first
214
+ end
213
215
 
214
- def match_start(identifier = 'n')
215
- "MATCH (#{identifier}) WHERE ID(#{identifier}) = {neo_id}"
216
+ def match_start(identifier = 'n')
217
+ "MATCH (#{identifier}) WHERE ID(#{identifier}) = {neo_id}"
218
+ end
216
219
  end
217
220
  end
218
221
  end