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,61 +1,79 @@
1
- module Neo4j::Server
2
- class CypherTransaction
3
- include Neo4j::Transaction::Instance
4
- include Neo4j::Core::CypherTranslator
5
- include Resource
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 :exec_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_tx` or `_delete_tx`.
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
11
+ include Neo4j::Transaction::Instance
12
+ include Neo4j::Core::CypherTranslator
13
+ include Resource
6
14
 
7
- attr_reader :commit_url, :exec_url
15
+ attr_reader :commit_url, :exec_url, :base_url, :connection
8
16
 
9
- class CypherError < StandardError
10
- attr_reader :code, :status
11
- def initialize(code, status, message)
12
- super(message)
13
- @code = code
14
- @status = status
17
+ def initialize(url, session_connection)
18
+ @base_url = url
19
+ @connection = session_connection
20
+ register_instance
15
21
  end
16
- end
17
22
 
18
- def initialize(db, response, url, connection)
19
- @connection = connection
20
- @commit_url = response.body['commit']
21
- @exec_url = response.headers['Location']
22
- raise "NO ENDPOINT URL #{@connection} : HEAD: #{response.headers.inspect}" if !@exec_url || @exec_url.empty?
23
- init_resource_data(response.body, url)
24
- expect_response_code(response,201)
25
- register_instance
26
- end
23
+ ROW_REST = %w(row REST)
24
+ def _query(cypher_query, params = nil)
25
+ fail 'Transaction expired, unable to perform query' if expired?
26
+ statement = {statement: cypher_query, parameters: params, resultDataContents: ROW_REST}
27
+ body = {statements: [statement]}
27
28
 
28
- def _query(cypher_query, params=nil)
29
- statement = { statement: cypher_query, parameters: params, resultDataContents: ['row', 'REST'] }
30
- body = { statements: [statement] }
31
- response = @connection.post(@exec_url, body)
32
- _create_cypher_response(response)
33
- end
29
+ response = exec_url && commit_url ? connection.post(exec_url, body) : register_urls(body)
30
+ _create_cypher_response(response)
31
+ end
34
32
 
35
- def _create_cypher_response(response)
36
- first_result = response.body['results'][0]
33
+ def _delete_tx
34
+ _tx_query(:delete, exec_url, headers: resource_headers)
35
+ end
37
36
 
38
- cr = CypherResponse.new(response, true)
39
- if !response.body['errors'].empty?
40
- first_error = response.body['errors'].first
41
- cr.set_error(first_error['message'], first_error['code'], first_error['code'])
42
- else
43
- cr.set_data(first_result['data'], first_result['columns'])
37
+ def _commit_tx
38
+ _tx_query(:post, commit_url, nil)
44
39
  end
45
- cr
46
- end
47
40
 
48
- def _delete_tx
49
- response = @connection.delete(@exec_url, headers: resource_headers)
50
- expect_response_code(response,200)
51
- response
52
- end
41
+ private
53
42
 
54
- def _commit_tx
55
- response = @connection.post(@commit_url)
43
+ def _tx_query(action, endpoint, headers = {})
44
+ return empty_response if !commit_url || expired?
45
+ response = connection.send(action, endpoint, headers)
46
+ expect_response_code(response, 200)
47
+ response
48
+ end
49
+
50
+ def register_urls(body)
51
+ response = connection.post(base_url, body)
52
+ @commit_url = response.body['commit']
53
+ @exec_url = response.headers['Location']
54
+ fail "NO ENDPOINT URL #{connection} : HEAD: #{response.headers.inspect}" if !exec_url || exec_url.empty?
55
+ init_resource_data(response.body, base_url)
56
+ expect_response_code(response, 201)
57
+ response
58
+ end
59
+
60
+ def _create_cypher_response(response)
61
+ first_result = response.body['results'][0]
56
62
 
57
- expect_response_code(response,200)
58
- response
63
+ cr = CypherResponse.new(response, true)
64
+ if response.body['errors'].empty?
65
+ cr.set_data(first_result['data'], first_result['columns'])
66
+ else
67
+ first_error = response.body['errors'].first
68
+ expired if first_error['message'].match(/Unrecognized transaction id/)
69
+ cr.set_error(first_error['message'], first_error['code'], first_error['code'])
70
+ end
71
+ cr
72
+ end
73
+
74
+ def empty_response
75
+ OpenStruct.new(status: 200, body: '')
76
+ end
59
77
  end
60
78
  end
61
- end
79
+ end
@@ -1,44 +1,39 @@
1
1
  module Neo4j
2
2
  module Server
3
3
  module Resource
4
-
5
4
  class ServerException < Exception
6
5
  end
7
6
 
8
7
  attr_reader :resource_data, :resource_url
9
8
 
10
9
  def init_resource_data(resource_data, resource_url)
11
- raise "Exception #{resource_data['exception']}" if resource_data['exception']
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
+
12
13
  @resource_url = resource_url
13
14
  @resource_data = resource_data
14
- raise "expected @resource_data to be Hash got #{@resource_data.class}" unless @resource_data.respond_to?(:[])
15
+
15
16
  self
16
17
  end
17
18
 
18
-
19
- def wrap_resource(db, rel, resource_class, verb = :get, statement = {}, connection)
20
- url = resource_url(rel)
21
- payload = statement.empty? ? nil : statement
22
- response = case verb
23
- when :get then connection.get(url, payload)
24
- when :post then connection.post(url, payload)
25
- else raise "Illegal verb #{verb}"
26
- end
27
- response.status == 404 ? nil : resource_class.new(db, response, url, connection)
19
+ def wrap_resource(connection = Neo4j::Session.current)
20
+ url = resource_url('transaction')
21
+ CypherTransaction.new(url, connection)
28
22
  end
29
23
 
30
- def resource_url(rel = nil)
31
- return @resource_url if rel.nil?
32
- url = resource_data[rel.to_s]
33
- raise "No resource rel '#{rel}', available #{@resource_data.keys.inspect}" unless url
34
- url
24
+ def resource_url(key = nil)
25
+ return @resource_url if key.nil?
26
+
27
+ @resource_data.fetch key.to_s
28
+ rescue KeyError
29
+ raise "No resource key '#{key}', available #{@resource_data.keys.inspect}"
35
30
  end
36
31
 
37
- def handle_response_error(response, msg="Error for request" )
38
- raise ServerException.new("#{msg} #{response.env && response.env[:url].to_s}, #{response.status}, #{response.status}")
32
+ def handle_response_error(response, msg = 'Error for request')
33
+ fail ServerException, "#{msg} #{response.env && response.env[:url].to_s}, #{response.status}, #{response.status}"
39
34
  end
40
35
 
41
- def expect_response_code(response, expected_code, msg="Error for request" )
36
+ def expect_response_code(response, expected_code, msg = 'Error for request')
42
37
  handle_response_error(response, "Expected response code #{expected_code} #{msg}") unless response.status == expected_code
43
38
  response
44
39
  end
@@ -57,7 +52,7 @@ module Neo4j
57
52
  end
58
53
 
59
54
  def convert_from_json_value(value)
60
- JSON.parse(value, :quirks_mode => true)
55
+ JSON.parse(value, quirks_mode: true)
61
56
  end
62
57
  end
63
58
  end
@@ -1,9 +1,8 @@
1
1
  module Neo4j
2
2
  module EntityEquality
3
- def ==(o)
4
- o.class == self.class && o.neo_id == neo_id
3
+ def ==(other)
4
+ other.class == self.class && other.neo_id == neo_id
5
5
  end
6
6
  alias_method :eql?, :==
7
-
8
7
  end
9
- end
8
+ end
data/lib/neo4j/label.rb CHANGED
@@ -7,23 +7,23 @@ module Neo4j
7
7
 
8
8
  # @abstract
9
9
  def name
10
- raise 'not implemented'
10
+ fail 'not implemented'
11
11
  end
12
12
 
13
13
  # @abstract
14
14
  def create_index(*properties)
15
- raise 'not implemented'
15
+ fail 'not implemented'
16
16
  end
17
17
 
18
18
  # @abstract
19
19
  def drop_index(*properties)
20
- raise 'not implemented'
20
+ fail 'not implemented'
21
21
  end
22
22
 
23
23
  # List indices for a label
24
24
  # @abstract
25
25
  def indexes
26
- raise 'not implemented'
26
+ fail 'not implemented'
27
27
  end
28
28
 
29
29
  # Creates a neo4j constraint on a property
@@ -34,11 +34,11 @@ module Neo4j
34
34
  #
35
35
  def create_constraint(property, constraints, session = Neo4j::Session.current)
36
36
  cypher = case constraints[:type]
37
- when :unique
38
- "CREATE CONSTRAINT ON (n:`#{name}`) ASSERT n.`#{property}` IS UNIQUE"
39
- else
40
- raise "Not supported constrain #{constraints.inspect} for property #{property} (expected :type => :unique)"
41
- end
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
42
  session._query_or_fail(cypher)
43
43
  end
44
44
 
@@ -51,10 +51,10 @@ module Neo4j
51
51
  #
52
52
  def drop_constraint(property, constraint, session = Neo4j::Session.current)
53
53
  cypher = case constraint[:type]
54
- when :unique
55
- "DROP CONSTRAINT ON (n:`#{name}`) ASSERT n.`#{property}` IS UNIQUE"
56
- else
57
- raise "Not supported constrain #{constraint.inspect}"
54
+ when :unique
55
+ "DROP CONSTRAINT ON (n:`#{name}`) ASSERT n.`#{property}` IS UNIQUE"
56
+ else
57
+ fail "Not supported constrain #{constraint.inspect}"
58
58
  end
59
59
  session._query_or_fail(cypher)
60
60
  end
@@ -78,9 +78,6 @@ module Neo4j
78
78
  def find_nodes(label_name, key, value, session = Neo4j::Session.current)
79
79
  session.find_nodes(label_name, key, value)
80
80
  end
81
-
82
81
  end
83
-
84
82
  end
85
-
86
83
  end
data/lib/neo4j/node.rb CHANGED
@@ -1,9 +1,7 @@
1
1
  module Neo4j
2
-
3
2
  # The base class for both the Embedded and Server Neo4j Node
4
3
  # Notice this class is abstract and can't be instantiated
5
4
  class Node
6
-
7
5
  # A module that allows plugins to register wrappers around Neo4j::Node objects
8
6
  module Wrapper
9
7
  # Used by Neo4j::NodeMixin to wrap nodes
@@ -21,44 +19,44 @@ module Neo4j
21
19
  include PropertyContainer
22
20
 
23
21
  # @return [Hash<Symbol, Object>] all properties of the node
24
- def props()
25
- raise 'not implemented'
22
+ def props
23
+ fail 'not implemented'
26
24
  end
27
25
 
28
26
  # replace all properties with new properties
29
27
  # @param [Hash<Symbol, Object>] properties a hash of properties the node should have
30
28
  def props=(properties)
31
- raise 'not implemented'
29
+ fail 'not implemented'
32
30
  end
33
31
 
34
32
  # Refresh the properties by reading it from the database again next time an property value is requested.
35
33
  def refresh
36
- raise 'not implemented'
34
+ fail 'not implemented'
37
35
  end
38
36
 
39
37
  # Updates the properties, keeps old properties
40
38
  # @param [Hash<Symbol, Object>] properties hash of properties that should be updated on the node
41
39
  def update_props(properties)
42
- raise 'not implemented'
40
+ fail 'not implemented'
43
41
  end
44
42
 
45
43
  # Directly remove the property on the node (low level method, may need transaction)
46
44
  def remove_property(key)
47
- raise 'not implemented'
45
+ fail 'not implemented'
48
46
  end
49
47
 
50
48
  # Directly set the property on the node (low level method, may need transaction)
51
49
  # @param [Symbol, String] key
52
50
  # @param value see Neo4j::PropertyValidator::VALID_PROPERTY_VALUE_CLASSES for valid values
53
51
  def set_property(key, value)
54
- raise 'not implemented'
52
+ fail 'not implemented'
55
53
  end
56
54
 
57
55
  # Directly get the property on the node (low level method, may need transaction)
58
56
  # @param [Symbol, String] key
59
57
  # @return the value of the key
60
58
  def get_property(key, value)
61
- raise 'not implemented'
59
+ fail 'not implemented'
62
60
  end
63
61
 
64
62
  # Creates a relationship of given type to other_node with optionally properties
@@ -66,7 +64,7 @@ module Neo4j
66
64
  # @param [Neo4j::Node] other_node the other node
67
65
  # @param [Hash<Symbol, Object>] props optionally properties for the created relationship
68
66
  def create_rel(type, other_node, props = nil)
69
- raise 'not implemented'
67
+ fail 'not implemented'
70
68
  end
71
69
 
72
70
 
@@ -89,45 +87,45 @@ module Neo4j
89
87
  # node_a.rels(type: :friends, dir: :outgoing, between: node_b)
90
88
  #
91
89
  def rels(match = {dir: :both})
92
- raise 'not implemented'
90
+ fail 'not implemented'
93
91
  end
94
92
 
95
93
  # Adds one or more Neo4j labels on the node
96
94
  # @param [Array<Symbol>] labels one or more labels to add
97
95
  def add_label(*labels)
98
- raise 'not implemented'
96
+ fail 'not implemented'
99
97
  end
100
98
 
101
99
  # Sets label on the node. Any old labels will be removed
102
100
  # @param [Array<Symbol>] labels one or more labels to set
103
101
  def set_label(*labels)
104
- raise 'not implemented'
102
+ fail 'not implemented'
105
103
  end
106
104
 
107
105
  # Removes given labels
108
106
  def remove_label(*labels)
109
- raise 'not implemented'
107
+ fail 'not implemented'
110
108
  end
111
109
 
112
110
  #
113
111
  # @return [Array<Symbol>]all labels on the node
114
- def labels()
115
- raise 'not implemented'
112
+ def labels
113
+ fail 'not implemented'
116
114
  end
117
115
 
118
116
  # Deletes this node from the database
119
- def del()
120
- raise 'not implemented'
117
+ def del
118
+ fail 'not implemented'
121
119
  end
122
120
 
123
121
  # @return true if the node exists in the database
124
122
  def exist?
125
- raise 'not implemented'
123
+ fail 'not implemented'
126
124
  end
127
125
 
128
126
  # @return all the Neo4j labels for this node
129
127
  def labels
130
- raise 'not implemented'
128
+ fail 'not implemented'
131
129
  end
132
130
 
133
131
  # Returns the only node of a given type and direction that is attached to this node, or nil.
@@ -144,27 +142,27 @@ module Neo4j
144
142
  #
145
143
  # @param (see #rel)
146
144
  def node(specs = {})
147
- raise 'not implemented'
145
+ fail 'not implemented'
148
146
  end
149
147
 
150
148
  # Same as #node but returns the relationship. Notice it may raise an exception if there are more then one relationship matching.
151
149
  def rel(spec = {})
152
- raise 'not implemented'
150
+ fail 'not implemented'
153
151
  end
154
152
 
155
153
  def _rel(spec = {})
156
- raise 'not implemented'
154
+ fail 'not implemented'
157
155
  end
158
156
 
159
157
  # Returns true or false if there is one or more relationships
160
158
  # @return [Boolean]
161
159
  def rel?(spec = {})
162
- raise 'not implemented'
160
+ fail 'not implemented'
163
161
  end
164
162
 
165
163
  # @return [Boolean] true if the node exists
166
164
  def exist?
167
- raise 'not implemented'
165
+ fail 'not implemented'
168
166
  end
169
167
 
170
168
  # Works like #rels method but instead returns the nodes.
@@ -174,12 +172,12 @@ module Neo4j
174
172
  # @return [Enumerable<Neo4j::Node>] an Enumeration of either Neo4j::Node objects or wrapped Neo4j::Node objects
175
173
  # @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
176
174
  def nodes(specs = {})
177
- #rels(specs).map{|n| n.other_node(self)}
175
+ # rels(specs).map{|n| n.other_node(self)}
178
176
  end
179
177
 
180
178
  class << self
181
179
  # Creates a node
182
- def create(props=nil, *labels_or_db)
180
+ def create(props = nil, *labels_or_db)
183
181
  session = Neo4j::Core::ArgumentHelper.session(labels_or_db)
184
182
  session.create_node(props, labels_or_db)
185
183
  end
@@ -197,21 +195,19 @@ module Neo4j
197
195
  end
198
196
 
199
197
  # Find the node with given label and value
200
- def find_nodes(label, value=nil, session = Neo4j::Session.current!)
198
+ def find_nodes(label, value = nil, session = Neo4j::Session.current!)
201
199
  session.find_nodes(label, value)
202
200
  end
203
201
  end
204
202
 
205
203
  def initialize
206
- raise "Can't instantiate abstract class" if abstract_class?
204
+ fail "Can't instantiate abstract class" if abstract_class?
207
205
  end
208
206
 
209
207
  private
208
+
210
209
  def abstract_class?
211
210
  self.class == Node
212
211
  end
213
-
214
-
215
212
  end
216
-
217
- end
213
+ end