arcadedb 0.3.3 → 0.5.0
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 +4 -4
- data/CHANGELOG.md +19 -1
- data/Gemfile +2 -7
- data/Gemfile.lock +59 -75
- data/README.md +105 -30
- data/arcade.yml +4 -8
- data/arcadedb.gemspec +3 -6
- data/bin/console +19 -19
- data/iruby/.ipynb_checkpoints/01-start-and-end-of-datarows-checkpoint.ipynb +258 -0
- data/iruby/01-start-and-end-of-datarows.ipynb +203 -0
- data/iruby/db-console.rb +73 -0
- data/lib/arcade/api/operations.rb +56 -144
- data/lib/arcade/api/primitives.rb +126 -0
- data/lib/arcade/base.rb +88 -56
- data/lib/{config.rb → arcade/config.rb} +14 -13
- data/lib/arcade/database.rb +110 -131
- data/lib/arcade/errors.rb +8 -0
- data/lib/arcade/match.rb +162 -0
- data/lib/{query.rb → arcade/query.rb} +36 -20
- data/lib/{support → arcade/support}/conversions.rb +2 -1
- data/lib/{support → arcade/support}/model.rb +25 -17
- data/lib/arcade/version.rb +1 -1
- data/lib/arcade.rb +15 -18
- data/lib/model/document.rb +22 -0
- data/lib/model/edge.rb +12 -0
- data/lib/model/vertex.rb +70 -51
- metadata +31 -14
- data/lib/arcade/api/version.rb +0 -5
- data/lib/match.rb +0 -216
- /data/lib/{init.rb → arcade/init.rb} +0 -0
- /data/lib/{support → arcade/support}/class.rb +0 -0
- /data/lib/{support → arcade/support}/object.rb +0 -0
- /data/lib/{support → arcade/support}/sql.rb +0 -0
- /data/lib/{support → arcade/support}/string.rb +0 -0
@@ -1,5 +1,6 @@
|
|
1
1
|
module Arcade
|
2
2
|
module Api
|
3
|
+
extend Primitives
|
3
4
|
=begin
|
4
5
|
This is a simple admin interface
|
5
6
|
|
@@ -8,7 +9,7 @@ module Arcade
|
|
8
9
|
$ Arcade::Api.drop_database <a string> # returns true if successfull
|
9
10
|
|
10
11
|
$ Arcade::Api.create_document <database>, <type>, attributes
|
11
|
-
$ Arcade::Api.execute( <database> ) { <query> }
|
12
|
+
$ Arcade::Api.execute( <database> [, session_id: some_session_id ]) { <query> }
|
12
13
|
$ Arcade::Api.query( <database> ) { <query> }
|
13
14
|
$ Arcade::Api.get_record <database>, rid # returns a hash
|
14
15
|
|
@@ -22,46 +23,53 @@ module Arcade
|
|
22
23
|
|
23
24
|
=end
|
24
25
|
|
26
|
+
# ------------------------------ Service methods ------------------------------------------------- #
|
27
|
+
# ------------------------------ ------------------------------------------------- #
|
28
|
+
# ------------------------------ databases ------------------------------------------------- #
|
29
|
+
# returns an array of databases present on the database-server #
|
25
30
|
|
26
31
|
def self.databases
|
27
32
|
get_data 'databases'
|
28
33
|
end
|
29
34
|
|
35
|
+
# ------------------------------ create database ------------------------------------------------- #
|
36
|
+
# creates a database if not present #
|
30
37
|
def self.create_database name
|
31
|
-
|
32
|
-
payload = { "command" => "create database #{name}" }
|
33
|
-
post_data "server",
|
34
|
-
|
35
|
-
rescue QueryError => e
|
38
|
+
return if databases.include?( name.to_s )
|
39
|
+
payload = { "command" => "create database #{name}" }
|
40
|
+
post_data "server", payload
|
41
|
+
rescue Arcade::QueryError => e
|
36
42
|
logger.fatal "Create database #{name} through \"POST create/#{name}\" failed"
|
37
43
|
logger.fatal e
|
38
44
|
raise
|
39
45
|
end
|
40
46
|
|
47
|
+
# ------------------------------ drop database ------------------------------------------------- #
|
48
|
+
# deletes the given database #
|
41
49
|
def self.drop_database name
|
42
|
-
|
43
|
-
payload = {
|
44
|
-
|
45
|
-
|
50
|
+
return unless databases.include?( name.to_s )
|
51
|
+
payload = {"command" => "drop database #{name}" }
|
52
|
+
post_data "server", payload
|
53
|
+
rescue Arcade::QueryError => e
|
54
|
+
logger.fatal "Drop database #{name} through \"POST drop database/#{name}\" failed"
|
55
|
+
raise
|
46
56
|
end
|
47
57
|
# ------------------------------ create document ------------------------------------------------- #
|
48
|
-
# adds a document to the database
|
58
|
+
# adds a document to the specified database table
|
49
59
|
#
|
50
60
|
# specify database-fields as hash-type parameters
|
51
61
|
#
|
52
62
|
# i.e Arcade::Api.create_document 'devel', 'documents', name: 'herta meyer', age: 56, sex: 'f'
|
53
63
|
#
|
54
64
|
# returns the rid of the inserted dataset
|
55
|
-
#
|
56
|
-
def self.create_document database, type, **attributes
|
57
|
-
payload = { "@type" => type }.merge( attributes )
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
end
|
64
|
-
post_data "document/#{database}", options
|
65
|
+
#
|
66
|
+
def self.create_document database, type, session_id: nil, **attributes
|
67
|
+
payload = { "@type" => type }.merge( attributes )
|
68
|
+
if session_id.nil?
|
69
|
+
post_data "document/#{database}", payload
|
70
|
+
else
|
71
|
+
post_transaction "document/#{database}", payload, session_id: session_id
|
72
|
+
end
|
65
73
|
end
|
66
74
|
|
67
75
|
# ------------------------------ execute ------------------------------------------------- #
|
@@ -71,32 +79,26 @@ module Arcade
|
|
71
79
|
#
|
72
80
|
# returns an Array of results (if propriate)
|
73
81
|
# i.e
|
74
|
-
# Arcade::Api.
|
82
|
+
# Arcade::Api.execute( "devel" ) { 'select from test ' }
|
75
83
|
# =y [{"@rid"=>"#57:0", "@type"=>"test", "name"=>"Hugo"}, {"@rid"=>"#60:0", "@type"=>"test", "name"=>"Hubert"}]
|
76
84
|
#
|
77
|
-
def self.execute database,
|
78
|
-
pl =
|
79
|
-
|
80
|
-
|
81
|
-
|
85
|
+
def self.execute database, session_id: nil
|
86
|
+
pl = provide_payload(yield)
|
87
|
+
if session_id.nil?
|
88
|
+
post_data "command/#{database}" , pl
|
89
|
+
else
|
90
|
+
post_transaction "command/#{database}" , pl, session_id: session_id
|
82
91
|
end
|
83
|
-
post_data "command/#{database}" , options
|
84
|
-
rescue Arcade::QueryError => e
|
85
|
-
# puts e.methods
|
86
|
-
#puts e.exception
|
87
|
-
# puts e.full_message
|
88
|
-
if e.message =~ /retry/
|
89
|
-
retry
|
90
|
-
else
|
91
|
-
raise e.message
|
92
|
-
end
|
93
92
|
end
|
94
93
|
|
95
94
|
# ------------------------------ query ------------------------------------------------- #
|
96
95
|
# same for idempotent queries
|
97
|
-
def self.query database, query
|
98
|
-
|
99
|
-
|
96
|
+
def self.query database, query, session_id: nil
|
97
|
+
if session_id.nil?
|
98
|
+
post_data "query/#{database}" , provide_payload(query)
|
99
|
+
else
|
100
|
+
post_transaction "query/#{database}" , provide_payload(query), session_id: session_id
|
101
|
+
end
|
100
102
|
end
|
101
103
|
|
102
104
|
# ------------------------------ get_record ------------------------------------------------- #
|
@@ -114,7 +116,7 @@ module Arcade
|
|
114
116
|
if rid.rid?
|
115
117
|
get_data "document/#{database}/#{rid}"
|
116
118
|
else
|
117
|
-
raise
|
119
|
+
raise Error "Get requires a rid input"
|
118
120
|
end
|
119
121
|
end
|
120
122
|
|
@@ -133,20 +135,17 @@ module Arcade
|
|
133
135
|
#
|
134
136
|
def self.property database, type, **args
|
135
137
|
|
136
|
-
begin_transaction database
|
138
|
+
s= begin_transaction database
|
137
139
|
success = args.map do | name, format |
|
138
|
-
r= execute(database) {" create property #{type.to_s}.#{name.to_s} #{format.to_s} " } &.first
|
139
|
-
|
140
|
-
false
|
141
|
-
else
|
142
|
-
r.keys == [ :propertyName, :typeName, :operation ] && r[:operation] == 'create property'
|
143
|
-
end
|
140
|
+
r= execute(database, session_id: s) {" create property #{type.to_s}.#{name.to_s} #{format.to_s} " } &.first
|
141
|
+
r.nil? ? false : r[:operation] == 'create property'
|
144
142
|
end.uniq
|
145
143
|
if success == [true]
|
146
|
-
commit database
|
144
|
+
commit database, session_id: s
|
147
145
|
true
|
148
146
|
else
|
149
|
-
rollback database
|
147
|
+
rollback database log: false, session_id: s
|
148
|
+
false
|
150
149
|
end
|
151
150
|
|
152
151
|
|
@@ -155,54 +154,23 @@ module Arcade
|
|
155
154
|
# ------------------------------ index ------------------------------------------------- #
|
156
155
|
def self.index database, type, name , *properties
|
157
156
|
properties = properties.map( &:to_s )
|
158
|
-
unique_requested = "unique" if properties.delete("unique")
|
157
|
+
unique_requested = "unique" if properties.delete("unique")
|
159
158
|
unique_requested = "notunique" if properties.delete("notunique" )
|
160
159
|
automatic = true if
|
161
160
|
properties << name if properties.empty?
|
162
|
-
|
163
|
-
# VV 22.10: providing an index-name raises an Error ( Encountered " "(" "( "" at line 1, column 44. Was expecting one of: <EOF> <SCHEMA> ... <NULL_STRATEGY> ... ";" ... "," ... )) )
|
164
|
-
# named indices droped for now
|
165
|
-
success = execute(database) {" create index on #{type} ( #{properties.join(',')} ) #{unique_requested}" } &.first
|
161
|
+
success = execute(database) {" create index IF NOT EXISTS on #{type} (#{properties.join(', ')}) #{unique_requested}" } &.first
|
166
162
|
# puts "success: #{success}"
|
167
|
-
|
168
|
-
|
169
|
-
end
|
170
|
-
|
163
|
+
success[:operation] == 'create index'
|
171
164
|
|
172
|
-
# ------------------------------ transaction ------------------------------------------------- #
|
173
|
-
#
|
174
|
-
def self.begin_transaction database
|
175
|
-
result = Typhoeus.post Arcade::Config.base_uri + "begin/#{database}", auth
|
176
|
-
@session_id = result.headers["arcadedb-session-id"]
|
177
|
-
|
178
|
-
# returns the session-id
|
179
165
|
end
|
180
166
|
|
181
167
|
|
182
|
-
# ------------------------------ commit ------------------------------------------------- #
|
183
|
-
def self.commit database
|
184
|
-
options = auth.merge( headers: { "arcadedb-session-id" => session })
|
185
|
-
post_data "commit/#{database}", options
|
186
|
-
@session_id = nil
|
187
|
-
end
|
188
|
-
|
189
|
-
# ------------------------------ rollback ------------------------------------------------- #
|
190
|
-
def self.rollback database, publish_error=true
|
191
|
-
options = auth.merge( headers: { "arcadedb-session-id" => session })
|
192
|
-
post_data "rollback/#{database}", options
|
193
|
-
@session_id = nil
|
194
|
-
raise Arcade::RollbackError "A Transaction has been rolled back" if publish_error
|
195
|
-
end
|
196
|
-
|
197
168
|
private
|
198
169
|
|
199
170
|
def self.logger
|
200
171
|
Database.logger
|
201
172
|
end
|
202
173
|
|
203
|
-
def self.session
|
204
|
-
@session_id
|
205
|
-
end
|
206
174
|
|
207
175
|
def self. provide_payload( the_yield, action: :post )
|
208
176
|
unless the_yield.is_a? Hash
|
@@ -231,76 +199,20 @@ module Arcade
|
|
231
199
|
[ :serializer, value.to_sym ]
|
232
200
|
end
|
233
201
|
when :language
|
234
|
-
if [:sql, :cypher, :gremlin, :neo4j ].include? value.to_sym
|
202
|
+
if [:sql, :cypher, :gremlin, :neo4j, :sqlscript, :graphql, :mongo ].include? value.to_sym
|
235
203
|
[ :language, value.to_sym ]
|
236
204
|
end
|
237
205
|
end # case
|
238
|
-
end .to_h )
|
239
|
-
end
|
240
|
-
|
241
|
-
def self.get_data command, options = auth
|
242
|
-
result = Typhoeus.get Arcade::Config.base_uri + command, options
|
243
|
-
analyse_result(result, command)
|
206
|
+
end .to_h ) # map
|
244
207
|
end
|
245
208
|
|
246
209
|
|
247
|
-
def self.post_data command, options = auth
|
248
|
-
# puts "Post DATA #{command} #{options}" # debug
|
249
|
-
i = 0; a=""
|
250
|
-
loop do
|
251
|
-
result = Typhoeus.post Arcade::Config.base_uri + command, options
|
252
|
-
# Code: 503 – Service Unavailable
|
253
|
-
if result.response_code.to_i == 503 # retry two times
|
254
|
-
i += 1
|
255
|
-
raise Arcade::QueryError, JSON.parse( result.response_body, symbolize_names: true )[:result] if i > 3
|
256
|
-
sleep 0.1
|
257
|
-
else
|
258
|
-
a= analyse_result(result, command )
|
259
|
-
break
|
260
|
-
end
|
261
|
-
end
|
262
|
-
a
|
263
|
-
end
|
264
210
|
|
265
|
-
# returns the json-response
|
266
|
-
def self.analyse_result r, command
|
267
|
-
if r.success?
|
268
|
-
return nil if r.response_code == 204 # no content
|
269
|
-
result = JSON.parse( r.response_body, symbolize_names: true )[:result]
|
270
|
-
if result == [{}]
|
271
|
-
[]
|
272
|
-
else
|
273
|
-
result
|
274
|
-
end
|
275
|
-
elsif r.timed_out?
|
276
|
-
raise Arcade::Error "Timeout Error", caller
|
277
|
-
[]
|
278
|
-
elsif r.response_code > 0
|
279
|
-
logger.error "Execution Failure – Code: #{ r.response_code } – #{r.status_message} "
|
280
|
-
error_message = JSON.parse( r.response_body, symbolize_names: true )
|
281
|
-
logger.error "ErrorMessage: #{ error_message[:detail]} "
|
282
|
-
if error_message[:detail] =~ /Duplicated key/
|
283
|
-
raise Arcade::IndexError, error_message[:detail]
|
284
|
-
else
|
285
|
-
# available fields: :detail, :exception, error
|
286
|
-
puts error_message[:detail]
|
287
|
-
#raise error_message[:detail], caller
|
288
|
-
end
|
289
|
-
end
|
290
|
-
end
|
291
211
|
def self.auth
|
292
212
|
@a ||= { httpauth: :basic,
|
293
|
-
username:
|
294
|
-
password:
|
213
|
+
username: Config.admin[:user],
|
214
|
+
password: Config.admin[:pass] }
|
295
215
|
end
|
296
216
|
|
297
|
-
def self.json
|
298
|
-
{ headers: { "Content-Type" => "application/json"} }
|
299
|
-
end
|
300
|
-
# not tested
|
301
|
-
def self.delete_data command
|
302
|
-
result = Typhoeus.delete Arcade::Config.base_uri + command, auth
|
303
|
-
analyse_result(result, command)
|
304
|
-
end
|
305
217
|
end
|
306
218
|
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
module Arcade
|
2
|
+
module Api
|
3
|
+
module Primitives
|
4
|
+
|
5
|
+
# This module handles the interaction with the database through HTTPX
|
6
|
+
#
|
7
|
+
# ------------------------------ http ------------------------------------------------------------ #
|
8
|
+
# persistent http handle to the database
|
9
|
+
|
10
|
+
def http
|
11
|
+
# break_on = -> (response) { response.status == 500 }
|
12
|
+
@http ||= HTTPX.plugin(:basic_auth).basic_auth(auth[:username], auth[:password])
|
13
|
+
.plugin(:persistent)
|
14
|
+
.plugin(:circuit_breaker)
|
15
|
+
# .plugin(:circuit_breaker, circuit_breaker_break_on: break_on)
|
16
|
+
end
|
17
|
+
|
18
|
+
# ------------------------------ get data -------------------------------------------------------- #
|
19
|
+
def get_data command
|
20
|
+
case response = http.get( Config.base_uri + command )
|
21
|
+
in {status: 200..299}
|
22
|
+
# success
|
23
|
+
JSON.parse( response.body, symbolize_names: true )[:result]
|
24
|
+
in {status: 400..}
|
25
|
+
raise Arcade::QueryError.new **response.json( symbolize_names: true )
|
26
|
+
else
|
27
|
+
# # http error
|
28
|
+
raise response
|
29
|
+
end
|
30
|
+
|
31
|
+
JSON.parse( response.body, symbolize_names: true )[:result]
|
32
|
+
# alternative to `raise for status `
|
33
|
+
# case response = http.basic_auth(auth[:username], auth[:password]).get( Config.base_uri + command )
|
34
|
+
# in { status: 200..203, body: }
|
35
|
+
# puts "success: #{JSON.parse(body, symbolize_names: true)[:result]}"
|
36
|
+
# in { status: 400..499, body: }
|
37
|
+
# puts "client error: #{body.json}"
|
38
|
+
# in { status: 500.., body: }
|
39
|
+
# puts "server error: #{body.to_s}"
|
40
|
+
# in { error: error }
|
41
|
+
# puts "error: #{error.message}"
|
42
|
+
# else
|
43
|
+
# raise "unexpected: #{response}"
|
44
|
+
# end
|
45
|
+
# puts "result : #{response}"
|
46
|
+
# puts "code: #{response.status}"
|
47
|
+
# analyse_result(response, command)
|
48
|
+
end
|
49
|
+
|
50
|
+
# ------------------------------ post data -------------------------------------------------------- #
|
51
|
+
def post_data command, payload
|
52
|
+
case response = http.post( Config.base_uri + command, json: payload )
|
53
|
+
in {status: 200..299}
|
54
|
+
# success
|
55
|
+
JSON.parse( response.body, symbolize_names: true )[:result]
|
56
|
+
in {status: 400..}
|
57
|
+
detail = response.json( symbolize_names: true )[:detail]
|
58
|
+
if detail =~ /Please retry the operation/
|
59
|
+
logger.error "--------------------------------"
|
60
|
+
logger.error " ----> Operation repeated <---- "
|
61
|
+
logger.error detail
|
62
|
+
logger.error "The query --> #{payload.inspect}"
|
63
|
+
logger.error "--------------------------------"
|
64
|
+
sleep 1
|
65
|
+
post_data command, payload
|
66
|
+
else
|
67
|
+
raise Arcade::QueryError.new **response.json( symbolize_names: true )
|
68
|
+
end
|
69
|
+
else
|
70
|
+
# # http error
|
71
|
+
raise response
|
72
|
+
end
|
73
|
+
# response.raise_for_status
|
74
|
+
end
|
75
|
+
|
76
|
+
# ------------------------------ transaction ------------------------------------------------- #
|
77
|
+
#
|
78
|
+
# The payload, optional as a JSON, accepts the following parameters:
|
79
|
+
# isolationLevel: READ_COMMITTED (default) or REPEATABLE_READ. (not implemented)
|
80
|
+
#
|
81
|
+
def begin_transaction database
|
82
|
+
result = http.post Config.base_uri + "begin/#{database}"
|
83
|
+
# returns the session-id
|
84
|
+
result.headers["arcadedb-session-id"]
|
85
|
+
end
|
86
|
+
|
87
|
+
# ------------------------------ post transaction ------------------------------------------------- #
|
88
|
+
def post_transaction command, params, session_id:
|
89
|
+
http_a = http.with( headers: { "arcadedb-session-id" => session_id } , debug_level: 1)
|
90
|
+
case response = http_a.post( Config.base_uri + command, json: params )
|
91
|
+
in {status: 200..299}
|
92
|
+
# success
|
93
|
+
JSON.parse( response.body, symbolize_names: true )[:result]
|
94
|
+
in {status: 400..}
|
95
|
+
## debug
|
96
|
+
# puts "Command: #{command}"
|
97
|
+
# puts "params: #{params}"
|
98
|
+
# puts response.json( symbolize_names: true )
|
99
|
+
raise Arcade::QueryError.new **response.json( symbolize_names: true )
|
100
|
+
else
|
101
|
+
# # http error
|
102
|
+
raise response
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# ------------------------------ commit ------------------------------------------------- #
|
107
|
+
def commit database, session_id:
|
108
|
+
http_a = http.with( headers: { "arcadedb-session-id" => session_id } , debug_level: 1)
|
109
|
+
response = http_a.post( Config.base_uri + "commit/#{database}" )
|
110
|
+
response.status # returns 204 --> success
|
111
|
+
# 403 --> invalid credentials
|
112
|
+
# 500 --> Transaction not begun
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
# ------------------------------ rollback ------------------------------------------------- #
|
117
|
+
def rollback database, session_id: , log: true
|
118
|
+
http_a = http.with( headers: { "arcadedb-session-id" => session_id } , debug_level: 1)
|
119
|
+
response = http_a.post( Config.base_uri + "rollback/#{database}" )
|
120
|
+
logger.info "A Transaction has been rolled back" if log
|
121
|
+
response.status # returns 500 !
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|