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,142 +0,0 @@
|
|
1
|
-
module Neo4j
|
2
|
-
module Server
|
3
|
-
class CypherRelationship < Neo4j::Relationship
|
4
|
-
include Neo4j::Server::Resource
|
5
|
-
include Neo4j::Core::ActiveEntity
|
6
|
-
|
7
|
-
MARSHAL_INSTANCE_VARIABLES = %i[@rel_type @props @start_node_neo_id @end_node_neo_id @id]
|
8
|
-
|
9
|
-
def initialize(session, value)
|
10
|
-
@session = session
|
11
|
-
@response_hash = value
|
12
|
-
@rel_type = @response_hash[:type]
|
13
|
-
@props = @response_hash[:data]
|
14
|
-
@start_node_neo_id = neo_id_integer(@response_hash[:start])
|
15
|
-
@end_node_neo_id = neo_id_integer(@response_hash[:end])
|
16
|
-
@id = @response_hash[:id]
|
17
|
-
end
|
18
|
-
|
19
|
-
def ==(other)
|
20
|
-
other.class == self.class && other.neo_id == neo_id
|
21
|
-
end
|
22
|
-
alias eql? ==
|
23
|
-
|
24
|
-
attr_reader :id
|
25
|
-
|
26
|
-
def neo_id
|
27
|
-
id
|
28
|
-
end
|
29
|
-
|
30
|
-
def inspect
|
31
|
-
"CypherRelationship #{neo_id}"
|
32
|
-
end
|
33
|
-
|
34
|
-
def load_resource
|
35
|
-
return if resource_data_present?
|
36
|
-
|
37
|
-
@resource_data = @session._query_or_fail("#{match_start} RETURN n", true, neo_id: neo_id) # r.first_data
|
38
|
-
end
|
39
|
-
|
40
|
-
attr_reader :start_node_neo_id
|
41
|
-
|
42
|
-
attr_reader :end_node_neo_id
|
43
|
-
|
44
|
-
def _start_node_id
|
45
|
-
@start_node_neo_id ||= get_node_id(:start)
|
46
|
-
end
|
47
|
-
|
48
|
-
def _end_node_id
|
49
|
-
@end_node_neo_id ||= get_node_id(:end)
|
50
|
-
end
|
51
|
-
|
52
|
-
def _start_node
|
53
|
-
@_start_node ||= Neo4j::Node._load(start_node_neo_id)
|
54
|
-
end
|
55
|
-
|
56
|
-
def _end_node
|
57
|
-
load_resource
|
58
|
-
@_end_node ||= Neo4j::Node._load(end_node_neo_id)
|
59
|
-
end
|
60
|
-
|
61
|
-
def get_node_id(direction)
|
62
|
-
load_resource
|
63
|
-
resource_url_id(resource_url(direction))
|
64
|
-
end
|
65
|
-
|
66
|
-
def get_property(key)
|
67
|
-
@session._query_or_fail("#{match_start} RETURN n.`#{key}`", true, neo_id: neo_id)
|
68
|
-
end
|
69
|
-
|
70
|
-
def set_property(key, value)
|
71
|
-
@session._query_or_fail("#{match_start} SET n.`#{key}` = {value}", false, value: value, neo_id: neo_id)
|
72
|
-
end
|
73
|
-
|
74
|
-
def remove_property(key)
|
75
|
-
@session._query_or_fail("#{match_start} REMOVE n.`#{key}`", false, neo_id: neo_id)
|
76
|
-
end
|
77
|
-
|
78
|
-
# (see Neo4j::Relationship#props)
|
79
|
-
def props
|
80
|
-
if @props
|
81
|
-
@props
|
82
|
-
else
|
83
|
-
hash = @session._query_entity_data("#{match_start} RETURN n", nil, neo_id: neo_id)
|
84
|
-
@props = Hash[hash[:data].map { |k, v| [k, v] }]
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
# (see Neo4j::Relationship#props=)
|
89
|
-
def props=(properties)
|
90
|
-
@session._query_or_fail("#{match_start} SET n = { props }", false, props: properties, neo_id: neo_id)
|
91
|
-
properties
|
92
|
-
end
|
93
|
-
|
94
|
-
# (see Neo4j::Relationship#update_props)
|
95
|
-
def update_props(properties)
|
96
|
-
return if properties.empty?
|
97
|
-
|
98
|
-
params = {}
|
99
|
-
q = "#{match_start} SET " + properties.keys.each_with_index.map do |k, _i|
|
100
|
-
param = k.to_s.tr_s('^a-zA-Z0-9', '_').gsub(/^_+|_+$/, '')
|
101
|
-
params[param] = properties[k]
|
102
|
-
|
103
|
-
"n.`#{k}`= {#{param}}"
|
104
|
-
end.join(',')
|
105
|
-
|
106
|
-
@session._query_or_fail(q, false, params.merge(neo_id: neo_id))
|
107
|
-
|
108
|
-
properties
|
109
|
-
end
|
110
|
-
|
111
|
-
def rel_type
|
112
|
-
@rel_type.to_sym
|
113
|
-
end
|
114
|
-
|
115
|
-
def del
|
116
|
-
@session._query("#{match_start} DELETE n", neo_id: neo_id)
|
117
|
-
end
|
118
|
-
alias delete del
|
119
|
-
alias destroy del
|
120
|
-
|
121
|
-
def exist?
|
122
|
-
response = @session._query("#{match_start} RETURN n", neo_id: neo_id)
|
123
|
-
# binding.pry
|
124
|
-
(response.data.nil? || response.data.empty?) ? false : true
|
125
|
-
end
|
126
|
-
|
127
|
-
private
|
128
|
-
|
129
|
-
def match_start(identifier = 'n')
|
130
|
-
"MATCH (node)-[#{identifier}]-() WHERE ID(#{identifier}) = {neo_id}"
|
131
|
-
end
|
132
|
-
|
133
|
-
def resource_data_present?
|
134
|
-
!resource_data.nil? && !resource_data.empty?
|
135
|
-
end
|
136
|
-
|
137
|
-
def neo_id_integer(id_or_url)
|
138
|
-
id_or_url.is_a?(Integer) ? id_or_url : id_or_url.split('/').last.to_i
|
139
|
-
end
|
140
|
-
end
|
141
|
-
end
|
142
|
-
end
|
@@ -1,248 +0,0 @@
|
|
1
|
-
module Neo4j
|
2
|
-
module Server
|
3
|
-
class CypherResponse
|
4
|
-
attr_reader :data, :columns, :error_msg, :error_status, :error_code, :response
|
5
|
-
|
6
|
-
class ResponseError < StandardError
|
7
|
-
attr_reader :status, :code
|
8
|
-
|
9
|
-
def initialize(msg, status, code)
|
10
|
-
super(msg)
|
11
|
-
@status = status
|
12
|
-
@code = code
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
class ConstraintViolationError < ResponseError; end
|
17
|
-
|
18
|
-
class HashEnumeration
|
19
|
-
include Enumerable
|
20
|
-
extend Forwardable
|
21
|
-
def_delegator :@response, :error_msg
|
22
|
-
def_delegator :@response, :error_status
|
23
|
-
def_delegator :@response, :error_code
|
24
|
-
def_delegator :@response, :columns
|
25
|
-
def_delegator :@response, :struct
|
26
|
-
|
27
|
-
def initialize(response, query)
|
28
|
-
@response = response
|
29
|
-
@query = query
|
30
|
-
end
|
31
|
-
|
32
|
-
def to_s
|
33
|
-
@query
|
34
|
-
end
|
35
|
-
|
36
|
-
def inspect
|
37
|
-
"Enumerable query: '#{@query}'"
|
38
|
-
end
|
39
|
-
|
40
|
-
def each
|
41
|
-
@response.each_data_row { |row| yield struct_rows(row) }
|
42
|
-
end
|
43
|
-
|
44
|
-
def struct_rows(row)
|
45
|
-
struct.new.tap do |result|
|
46
|
-
row.each_with_index { |value, i| result[columns[i]] = value }
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
EMPTY_STRING = ''
|
52
|
-
def to_struct_enumeration(cypher = EMPTY_STRING)
|
53
|
-
HashEnumeration.new(self, cypher)
|
54
|
-
end
|
55
|
-
|
56
|
-
def to_node_enumeration(cypher = EMPTY_STRING, session = Neo4j::Session.current)
|
57
|
-
Enumerator.new do |yielder|
|
58
|
-
to_struct_enumeration(cypher).each do |row|
|
59
|
-
yielder << row_pair_in_struct(row, session)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def row_pair_in_struct(row, session)
|
65
|
-
@struct.new.tap do |result|
|
66
|
-
row.each_pair do |column, value|
|
67
|
-
result[column] = map_row_value(value, session)
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
def map_row_value(value, session)
|
73
|
-
if value.is_a?(Hash) && looks_like_an_object?(value)
|
74
|
-
hash_value_as_object(value, session)
|
75
|
-
elsif value.is_a?(Array)
|
76
|
-
value.map! { |v| map_row_value(v, session) }
|
77
|
-
else
|
78
|
-
value
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
def hash_value_as_object(value, session)
|
83
|
-
return value unless %i[node relationship].include?(identify_entity(value))
|
84
|
-
add_entity_id(value)
|
85
|
-
|
86
|
-
basic_obj = (node?(value) ? CypherNode : CypherRelationship).new(session, value)
|
87
|
-
unwrapped? ? basic_obj : basic_obj.wrapper
|
88
|
-
end
|
89
|
-
|
90
|
-
def identify_entity(data)
|
91
|
-
self_string = data[:self]
|
92
|
-
if self_string
|
93
|
-
if self_string.include?('node')
|
94
|
-
:node
|
95
|
-
elsif self_string.include?('relationship')
|
96
|
-
:relationship
|
97
|
-
end
|
98
|
-
elsif %i[nodes relationships start end length].all? { |k| data.key?(k) }
|
99
|
-
:path
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
def looks_like_an_object?(value)
|
104
|
-
value[:labels] || value[:type]
|
105
|
-
end
|
106
|
-
|
107
|
-
def unwrapped!
|
108
|
-
@_unwrapped_obj = true
|
109
|
-
end
|
110
|
-
|
111
|
-
def unwrapped?
|
112
|
-
!!@_unwrapped_obj
|
113
|
-
end
|
114
|
-
|
115
|
-
def node?(value)
|
116
|
-
value[:labels]
|
117
|
-
end
|
118
|
-
|
119
|
-
attr_reader :struct
|
120
|
-
|
121
|
-
def initialize(response, uncommited = false)
|
122
|
-
@response = response
|
123
|
-
@uncommited = uncommited
|
124
|
-
end
|
125
|
-
|
126
|
-
def entity_data(id = nil)
|
127
|
-
if @uncommited
|
128
|
-
data = @data.first[:row].first
|
129
|
-
data.is_a?(Hash) ? {data: data, id: id} : data
|
130
|
-
else
|
131
|
-
data = @data[0][0]
|
132
|
-
data.is_a?(Hash) ? add_entity_id(data) : data
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
def first_data
|
137
|
-
if @uncommited
|
138
|
-
@data.first[:row].first
|
139
|
-
else
|
140
|
-
data = @data[0][0]
|
141
|
-
data.is_a?(Hash) ? add_entity_id(data) : data
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
def add_entity_id(data)
|
146
|
-
data[:id] = if data[:metadata] && data[:metadata][:id]
|
147
|
-
data[:metadata][:id]
|
148
|
-
else
|
149
|
-
data[:self].split('/')[-1].to_i
|
150
|
-
end
|
151
|
-
data
|
152
|
-
end
|
153
|
-
|
154
|
-
def error?
|
155
|
-
!!@error
|
156
|
-
end
|
157
|
-
|
158
|
-
def raise_if_cypher_error!
|
159
|
-
raise_cypher_error if error?
|
160
|
-
end
|
161
|
-
|
162
|
-
RETRYABLE_ERROR_STATUSES = %w[DeadlockDetectedException AcquireLockTimeoutException ExternalResourceFailureException UnknownFailureException]
|
163
|
-
def retryable_error?
|
164
|
-
return unless error?
|
165
|
-
RETRYABLE_ERROR_STATUSES.include?(@error_status)
|
166
|
-
end
|
167
|
-
|
168
|
-
def data?
|
169
|
-
!response.body[:data].nil?
|
170
|
-
end
|
171
|
-
|
172
|
-
def raise_unless_response_code(code)
|
173
|
-
fail "Response code #{response.status}, expected #{code} for #{response.headers[:location]}, #{response.body}" unless response.status == code
|
174
|
-
end
|
175
|
-
|
176
|
-
def each_data_row
|
177
|
-
data.each do |r|
|
178
|
-
yieldable = if @uncommitted
|
179
|
-
r[:row]
|
180
|
-
else
|
181
|
-
transaction_row?(r) ? r[:rest] : r
|
182
|
-
end
|
183
|
-
yield yieldable
|
184
|
-
end
|
185
|
-
end
|
186
|
-
|
187
|
-
def transaction_response?
|
188
|
-
response.respond_to?(:body) && !response.body[:commit].nil?
|
189
|
-
end
|
190
|
-
|
191
|
-
def set_data(response)
|
192
|
-
@data = response[:data]
|
193
|
-
@columns = response[:columns]
|
194
|
-
@struct = @columns.empty? ? Object.new : Struct.new(*@columns.map(&:to_sym))
|
195
|
-
self
|
196
|
-
end
|
197
|
-
|
198
|
-
def set_error(error)
|
199
|
-
@error = true
|
200
|
-
@error_msg = error[:message]
|
201
|
-
@error_status = error[:status] || error[:exception] || error[:code]
|
202
|
-
@error_code = error[:code] || error[:fullname]
|
203
|
-
self
|
204
|
-
end
|
205
|
-
|
206
|
-
CONSTRAINT_ERROR = 'Neo.ClientError.Schema.ConstraintViolation'
|
207
|
-
def raise_error
|
208
|
-
fail 'Tried to raise error without an error' unless @error
|
209
|
-
error_class = constraint_error? ? ConstraintViolationError : ResponseError
|
210
|
-
fail error_class.new(@error_msg, @error_status, @error_code)
|
211
|
-
end
|
212
|
-
|
213
|
-
def constraint_error?
|
214
|
-
@error_code == CONSTRAINT_ERROR || (@error_msg || '').include?('already exists with')
|
215
|
-
end
|
216
|
-
|
217
|
-
def raise_cypher_error
|
218
|
-
fail 'Tried to raise error without an error' unless @error
|
219
|
-
fail Neo4j::Session::CypherError.new(@error_msg, @error_code, @error_status)
|
220
|
-
end
|
221
|
-
|
222
|
-
|
223
|
-
def self.create_with_no_tx(response)
|
224
|
-
case response.status
|
225
|
-
when 200 then new(response).set_data(response.body)
|
226
|
-
when 400 then new(response).set_error(response.body)
|
227
|
-
else
|
228
|
-
fail "Unknown response code #{response.status} for #{response.env[:url]}"
|
229
|
-
end
|
230
|
-
end
|
231
|
-
|
232
|
-
def self.create_with_tx(response)
|
233
|
-
fail "Unknown response code #{response.status} for #{response.request_uri}" unless response.status == 200
|
234
|
-
|
235
|
-
new(response, true).tap do |cr|
|
236
|
-
body = response.body
|
237
|
-
body[:errors].empty? ? cr.set_data(body[:results].first) : cr.set_error(body[:errors].first)
|
238
|
-
end
|
239
|
-
end
|
240
|
-
|
241
|
-
private
|
242
|
-
|
243
|
-
def transaction_row?(row)
|
244
|
-
row.is_a?(Hash) && row[:rest] && row[:row]
|
245
|
-
end
|
246
|
-
end
|
247
|
-
end
|
248
|
-
end
|
@@ -1,263 +0,0 @@
|
|
1
|
-
require 'uri'
|
2
|
-
|
3
|
-
module Neo4j
|
4
|
-
module Server
|
5
|
-
Neo4j::Session.register_db(:server_db) do |endpoint_url, url_opts|
|
6
|
-
Neo4j::Server::CypherSession.open(endpoint_url, url_opts)
|
7
|
-
end
|
8
|
-
|
9
|
-
class CypherSession < Neo4j::Session
|
10
|
-
include Resource
|
11
|
-
|
12
|
-
alias super_query query
|
13
|
-
attr_reader :connection
|
14
|
-
|
15
|
-
def initialize(data_url, connection)
|
16
|
-
@connection = connection
|
17
|
-
Neo4j::Session.register(self)
|
18
|
-
initialize_resource(data_url)
|
19
|
-
Neo4j::Session._notify_listeners(:session_available, self)
|
20
|
-
end
|
21
|
-
|
22
|
-
# @param [Hash] params could be empty or contain basic authentication user and password
|
23
|
-
# @return [Faraday]
|
24
|
-
# @see https://github.com/lostisland/faraday
|
25
|
-
def self.create_connection(params, url = nil)
|
26
|
-
init_params = params[:initialize] && params.delete(:initialize)
|
27
|
-
conn = Faraday.new(url, init_params) do |b|
|
28
|
-
b.request :basic_auth, params[:basic_auth][:username], params[:basic_auth][:password] if params[:basic_auth]
|
29
|
-
b.request :multi_json
|
30
|
-
# b.response :logger, ::Logger.new(STDOUT), bodies: true
|
31
|
-
|
32
|
-
b.response :multi_json, symbolize_keys: true, content_type: 'application/json'
|
33
|
-
# b.use Faraday::Response::RaiseError
|
34
|
-
require 'typhoeus'
|
35
|
-
require 'typhoeus/adapters/faraday'
|
36
|
-
b.adapter :typhoeus
|
37
|
-
# b.adapter Faraday.default_adapter
|
38
|
-
end
|
39
|
-
conn.headers = {'Content-Type' => 'application/json', 'User-Agent' => ::Neo4j::Session.user_agent_string}
|
40
|
-
conn
|
41
|
-
end
|
42
|
-
|
43
|
-
# Opens a session to the database
|
44
|
-
# @see Neo4j::Session#open
|
45
|
-
#
|
46
|
-
# @param [String] endpoint_url - the url to the neo4j server, defaults to 'http://localhost:7474'
|
47
|
-
# @param [Hash] params faraday params, see #create_connection or an already created faraday connection
|
48
|
-
def self.open(endpoint_url = nil, params = {})
|
49
|
-
extract_basic_auth(endpoint_url, params)
|
50
|
-
url = endpoint_url || 'http://localhost:7474'
|
51
|
-
connection = params[:connection] || create_connection(params, url)
|
52
|
-
response = connection.get(url)
|
53
|
-
fail "Server not available on #{url} (response code #{response.status})" unless response.status == 200
|
54
|
-
establish_session(response.body, connection)
|
55
|
-
end
|
56
|
-
|
57
|
-
def self.establish_session(root_data, connection)
|
58
|
-
data_url = root_data[:data]
|
59
|
-
data_url << '/' unless data_url.nil? || data_url.end_with?('/')
|
60
|
-
CypherSession.new(data_url, connection)
|
61
|
-
end
|
62
|
-
|
63
|
-
def self.extract_basic_auth(url, params)
|
64
|
-
return unless url && URI(url).userinfo
|
65
|
-
params[:basic_auth] = {username: URI(url).user, password: URI(url).password}
|
66
|
-
end
|
67
|
-
|
68
|
-
private_class_method :extract_basic_auth
|
69
|
-
|
70
|
-
def db_type
|
71
|
-
:server_db
|
72
|
-
end
|
73
|
-
|
74
|
-
def to_s
|
75
|
-
"#{self.class} url: '#{@resource_url}'"
|
76
|
-
end
|
77
|
-
|
78
|
-
def inspect
|
79
|
-
"#{self} version: '#{version}'"
|
80
|
-
end
|
81
|
-
|
82
|
-
def version
|
83
|
-
resource_data ? resource_data[:neo4j_version] : ''
|
84
|
-
end
|
85
|
-
|
86
|
-
def initialize_resource(data_url)
|
87
|
-
response = @connection.get(data_url)
|
88
|
-
expect_response_code!(response, 200)
|
89
|
-
data_resource = response.body
|
90
|
-
fail "No data_resource for #{response.body}" unless data_resource
|
91
|
-
# store the resource data
|
92
|
-
init_resource_data(data_resource, data_url)
|
93
|
-
end
|
94
|
-
|
95
|
-
def self.transaction_class
|
96
|
-
Neo4j::Server::CypherTransaction
|
97
|
-
end
|
98
|
-
|
99
|
-
# Duplicate of CypherSession::Adaptor::Base#transaction
|
100
|
-
def transaction
|
101
|
-
return self.class.transaction_class.new(self) if !block_given?
|
102
|
-
|
103
|
-
begin
|
104
|
-
tx = transaction
|
105
|
-
|
106
|
-
yield tx
|
107
|
-
rescue Exception => e # rubocop:disable Lint/RescueException
|
108
|
-
tx.mark_failed
|
109
|
-
|
110
|
-
raise e
|
111
|
-
ensure
|
112
|
-
tx.close
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
def create_node(props = nil, labels = [])
|
117
|
-
label_string = labels.empty? ? '' : (':' + labels.map { |k| "`#{k}`" }.join(':'))
|
118
|
-
if !props.nil?
|
119
|
-
prop = '{props}'
|
120
|
-
props.each_key { |k| props.delete(k) if props[k].nil? }
|
121
|
-
end
|
122
|
-
|
123
|
-
id = _query_or_fail("CREATE (n#{label_string} #{prop}) RETURN ID(n)", true, props: props)
|
124
|
-
CypherNode.new(self, props.nil? ? id : {id: id, metadata: {labels: labels}, data: props})
|
125
|
-
end
|
126
|
-
|
127
|
-
def load_node(neo_id)
|
128
|
-
query.unwrapped.match(:n).where(n: {neo_id: neo_id}).pluck(:n).first
|
129
|
-
end
|
130
|
-
|
131
|
-
def load_relationship(neo_id)
|
132
|
-
query.unwrapped.optional_match('(n)-[r]-()').where(r: {neo_id: neo_id}).pluck(:r).first
|
133
|
-
rescue Neo4j::Session::CypherError => cypher_error
|
134
|
-
return nil if cypher_error.message =~ /not found$/
|
135
|
-
|
136
|
-
raise cypher_error
|
137
|
-
end
|
138
|
-
|
139
|
-
def create_label(name)
|
140
|
-
CypherLabel.new(self, name)
|
141
|
-
end
|
142
|
-
|
143
|
-
def uniqueness_constraints(label)
|
144
|
-
schema_properties("/db/data/schema/constraint/#{label}/uniqueness")
|
145
|
-
end
|
146
|
-
|
147
|
-
def indexes(label)
|
148
|
-
schema_properties("/db/data/schema/index/#{label}")
|
149
|
-
end
|
150
|
-
|
151
|
-
def schema_properties(query_string)
|
152
|
-
response = @connection.get(query_string)
|
153
|
-
expect_response_code!(response, 200)
|
154
|
-
{property_keys: response.body.map! { |row| row[:property_keys].map(&:to_sym) }}
|
155
|
-
end
|
156
|
-
|
157
|
-
def find_all_nodes(label_name)
|
158
|
-
search_result_to_enumerable_first_column(_query_or_fail("MATCH (n:`#{label_name}`) RETURN ID(n)"))
|
159
|
-
end
|
160
|
-
|
161
|
-
def find_nodes(label_name, key, value)
|
162
|
-
value = "'#{value}'" if value.is_a? String
|
163
|
-
|
164
|
-
response = _query_or_fail("MATCH (n:`#{label_name}`) WHERE n.#{key} = #{value} RETURN ID(n)")
|
165
|
-
search_result_to_enumerable_first_column(response)
|
166
|
-
end
|
167
|
-
|
168
|
-
def query(*args)
|
169
|
-
if [[String], [String, Hash]].include?(args.map(&:class))
|
170
|
-
response = _query(*args)
|
171
|
-
response.raise_error if response.error?
|
172
|
-
response.to_node_enumeration(args[0])
|
173
|
-
else
|
174
|
-
options = args[0] || {}
|
175
|
-
Neo4j::Core::Query.new(options.merge(session: self))
|
176
|
-
end
|
177
|
-
end
|
178
|
-
|
179
|
-
def _query_data(query)
|
180
|
-
r = _query_or_fail(query, true)
|
181
|
-
Neo4j::Transaction.current ? r : r[:data]
|
182
|
-
end
|
183
|
-
|
184
|
-
DEFAULT_RETRY_COUNT = ENV['NEO4J_RETRY_COUNT'].nil? ? 10 : ENV['NEO4J_RETRY_COUNT'].to_i
|
185
|
-
|
186
|
-
def _query_or_fail(query, single_row = false, params = {}, retry_count = DEFAULT_RETRY_COUNT)
|
187
|
-
query, params = query_and_params(query, params)
|
188
|
-
|
189
|
-
response = _query(query, params)
|
190
|
-
if response.error?
|
191
|
-
_retry_or_raise(query, params, single_row, retry_count, response)
|
192
|
-
else
|
193
|
-
single_row ? response.first_data : response
|
194
|
-
end
|
195
|
-
end
|
196
|
-
|
197
|
-
def query_and_params(query_or_query_string, params)
|
198
|
-
if query_or_query_string.is_a?(::Neo4j::Core::Query)
|
199
|
-
cypher = query_or_query_string.to_cypher
|
200
|
-
[cypher, query_or_query_string.send(:merge_params).merge(params)]
|
201
|
-
else
|
202
|
-
[query_or_query_string, params]
|
203
|
-
end
|
204
|
-
end
|
205
|
-
|
206
|
-
def _retry_or_raise(query, params, single_row, retry_count, response)
|
207
|
-
response.raise_error unless response.retryable_error?
|
208
|
-
retry_count > 0 ? _query_or_fail(query, single_row, params, retry_count - 1) : response.raise_error
|
209
|
-
end
|
210
|
-
|
211
|
-
def _query_entity_data(query, id = nil, params = {})
|
212
|
-
_query(query, params).tap(&:raise_if_cypher_error!).entity_data(id)
|
213
|
-
end
|
214
|
-
|
215
|
-
def _query(query, params = {}, options = {})
|
216
|
-
query, params = query_and_params(query, params)
|
217
|
-
|
218
|
-
ActiveSupport::Notifications.instrument('neo4j.cypher_query', params: params, context: options[:context],
|
219
|
-
cypher: query, pretty_cypher: options[:pretty_cypher]) do
|
220
|
-
if current_transaction
|
221
|
-
current_transaction._query(query, params)
|
222
|
-
else
|
223
|
-
query = params.nil? ? {'query' => query} : {'query' => query, 'params' => params}
|
224
|
-
response = @connection.post(resource_url(:cypher), query)
|
225
|
-
CypherResponse.create_with_no_tx(response)
|
226
|
-
end
|
227
|
-
end
|
228
|
-
end
|
229
|
-
|
230
|
-
def search_result_to_enumerable_first_column(response)
|
231
|
-
return [] unless response.data
|
232
|
-
|
233
|
-
Enumerator.new do |yielder|
|
234
|
-
response.data.each do |data|
|
235
|
-
if current_transaction
|
236
|
-
data[:row].each do |id|
|
237
|
-
yielder << CypherNode.new(self, id).wrapper
|
238
|
-
end
|
239
|
-
else
|
240
|
-
yielder << CypherNode.new(self, data[0]).wrapper
|
241
|
-
end
|
242
|
-
end
|
243
|
-
end
|
244
|
-
end
|
245
|
-
|
246
|
-
def current_transaction
|
247
|
-
Neo4j::Transaction.current_for(self)
|
248
|
-
end
|
249
|
-
|
250
|
-
EMPTY = ''
|
251
|
-
NEWLINE_W_SPACES = "\n "
|
252
|
-
def self.log_with
|
253
|
-
ActiveSupport::Notifications.subscribe('neo4j.cypher_query') do |_, start, finish, _id, payload|
|
254
|
-
ms = (finish - start) * 1000
|
255
|
-
params_string = (payload[:params] && !payload[:params].empty? ? "| #{payload[:params].inspect}" : EMPTY)
|
256
|
-
cypher = payload[:pretty_cypher] ? NEWLINE_W_SPACES + payload[:pretty_cypher].gsub(/\n/, NEWLINE_W_SPACES) : payload[:cypher]
|
257
|
-
|
258
|
-
yield(" #{ANSI::CYAN}#{payload[:context] || 'CYPHER'}#{ANSI::CLEAR} #{ANSI::YELLOW}#{ms.round}ms#{ANSI::CLEAR} #{cypher} #{params_string}")
|
259
|
-
end
|
260
|
-
end
|
261
|
-
end
|
262
|
-
end
|
263
|
-
end
|