arcadedb 0.4 → 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.
@@ -9,7 +9,7 @@ module Arcade
9
9
  $ Arcade::Api.drop_database <a string> # returns true if successfull
10
10
 
11
11
  $ Arcade::Api.create_document <database>, <type>, attributes
12
- $ Arcade::Api.execute( <database> ) { <query> }
12
+ $ Arcade::Api.execute( <database> [, session_id: some_session_id ]) { <query> }
13
13
  $ Arcade::Api.query( <database> ) { <query> }
14
14
  $ Arcade::Api.get_record <database>, rid # returns a hash
15
15
 
@@ -38,7 +38,7 @@ module Arcade
38
38
  return if databases.include?( name.to_s )
39
39
  payload = { "command" => "create database #{name}" }
40
40
  post_data "server", payload
41
- rescue HTTPX::HTTPError => e
41
+ rescue Arcade::QueryError => e
42
42
  logger.fatal "Create database #{name} through \"POST create/#{name}\" failed"
43
43
  logger.fatal e
44
44
  raise
@@ -50,7 +50,7 @@ module Arcade
50
50
  return unless databases.include?( name.to_s )
51
51
  payload = {"command" => "drop database #{name}" }
52
52
  post_data "server", payload
53
- rescue HTTPX::HTTPError => e
53
+ rescue Arcade::QueryError => e
54
54
  logger.fatal "Drop database #{name} through \"POST drop database/#{name}\" failed"
55
55
  raise
56
56
  end
@@ -63,15 +63,13 @@ module Arcade
63
63
  #
64
64
  # returns the rid of the inserted dataset
65
65
  #
66
- def self.create_document database, type, **attributes
66
+ def self.create_document database, type, session_id: nil, **attributes
67
67
  payload = { "@type" => type }.merge( attributes )
68
- logger.debug "C: #{payload}"
69
- options = if session.nil?
70
- payload
71
- else
72
- payload.merge headers: { "arcadedb-session-id" => session }
73
- end
74
- post_data "document/#{database}", options
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
75
73
  end
76
74
 
77
75
  # ------------------------------ execute ------------------------------------------------- #
@@ -84,19 +82,23 @@ module Arcade
84
82
  # Arcade::Api.execute( "devel" ) { 'select from test ' }
85
83
  # =y [{"@rid"=>"#57:0", "@type"=>"test", "name"=>"Hugo"}, {"@rid"=>"#60:0", "@type"=>"test", "name"=>"Hubert"}]
86
84
  #
87
- def self.execute database, query=nil, session_id= nil
88
- pl = query.nil? ? provide_payload(yield) : provide_payload(query)
89
- if session_id.nil? && session.nil?
85
+ def self.execute database, session_id: nil
86
+ pl = provide_payload(yield)
87
+ if session_id.nil?
90
88
  post_data "command/#{database}" , pl
91
89
  else
92
- post_transaction "command/#{database}" , pl, session_id || session
90
+ post_transaction "command/#{database}" , pl, session_id: session_id
93
91
  end
94
92
  end
95
93
 
96
94
  # ------------------------------ query ------------------------------------------------- #
97
95
  # same for idempotent queries
98
- def self.query database, query
99
- post_data "query/#{database}" , provide_payload(query)
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 ------------------------------------------------- #
@@ -133,21 +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
- puts "R: #{r.inspect}"
140
- if r.nil?
141
- false
142
- else
143
- r[:operation] == 'create property'
144
- 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'
145
142
  end.uniq
146
143
  if success == [true]
147
- commit database
144
+ commit database, session_id: s
148
145
  true
149
146
  else
150
- rollback database
147
+ rollback database log: false, session_id: s
148
+ false
151
149
  end
152
150
 
153
151
 
@@ -160,9 +158,6 @@ module Arcade
160
158
  unique_requested = "notunique" if properties.delete("notunique" )
161
159
  automatic = true if
162
160
  properties << name if properties.empty?
163
- # puts " create index #{type.to_s}[#{name.to_s}] on #{type} ( #{properties.join(',')} ) #{unique_requested}"
164
- # VV 22.10: providing an index-name raises an Error ( Encountered " "(" "( "" at line 1, column 44. Was expecting one of: <EOF> <SCHEMA> ... <NULL_STRATEGY> ... ";" ... "," ... )) )
165
- # named indices droped for now
166
161
  success = execute(database) {" create index IF NOT EXISTS on #{type} (#{properties.join(', ')}) #{unique_requested}" } &.first
167
162
  # puts "success: #{success}"
168
163
  success[:operation] == 'create index'
@@ -176,9 +171,6 @@ module Arcade
176
171
  Database.logger
177
172
  end
178
173
 
179
- def self.session
180
- @session_id
181
- end
182
174
 
183
175
  def self. provide_payload( the_yield, action: :post )
184
176
  unless the_yield.is_a? Hash
@@ -207,7 +199,7 @@ module Arcade
207
199
  [ :serializer, value.to_sym ]
208
200
  end
209
201
  when :language
210
- if [:sql, :cypher, :gremlin, :neo4j ].include? value.to_sym
202
+ if [:sql, :cypher, :gremlin, :neo4j, :sqlscript, :graphql, :mongo ].include? value.to_sym
211
203
  [ :language, value.to_sym ]
212
204
  end
213
205
  end # case
@@ -216,42 +208,11 @@ module Arcade
216
208
 
217
209
 
218
210
 
219
- # returns the json-response ## retiered
220
- def self.analyse_result r, command
221
- if r.success?
222
- return nil if r.status == 204 # no content
223
- result = JSON.parse( r.response_body, symbolize_names: true )[:result]
224
- if result == [{}]
225
- []
226
- else
227
- result
228
- end
229
- elsif r.timed_out?
230
- raise Error "Timeout Error", caller
231
- []
232
- elsif r.response_code > 0
233
- logger.error "Execution Failure – Code: #{ r.response_code } – #{r.status_message} "
234
- error_message = JSON.parse( r.response_body, symbolize_names: true )
235
- logger.error "ErrorMessage: #{ error_message[:detail]} "
236
- if error_message[:detail] =~ /Duplicated key/
237
- raise IndexError, error_message[:detail]
238
- else
239
- # available fields: :detail, :exception, error
240
- puts error_message[:detail]
241
- #raise error_message[:detail], caller
242
- end
243
- end
244
- end
245
211
  def self.auth
246
212
  @a ||= { httpauth: :basic,
247
213
  username: Config.admin[:user],
248
214
  password: Config.admin[:pass] }
249
215
  end
250
216
 
251
- # not tested
252
- def self.delete_data command
253
- result = HTTPX.delete Config.base_uri + command, auth
254
- analyse_result(result, command)
255
- end
256
217
  end
257
218
  end
@@ -8,7 +8,7 @@ module Arcade
8
8
  # persistent http handle to the database
9
9
 
10
10
  def http
11
- break_on = -> (response) { response.status == 500 }
11
+ # break_on = -> (response) { response.status == 500 }
12
12
  @http ||= HTTPX.plugin(:basic_auth).basic_auth(auth[:username], auth[:password])
13
13
  .plugin(:persistent)
14
14
  .plugin(:circuit_breaker)
@@ -17,8 +17,16 @@ module Arcade
17
17
 
18
18
  # ------------------------------ get data -------------------------------------------------------- #
19
19
  def get_data command
20
- response = http.get( Config.base_uri + command )
21
- response.raise_for_status
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
22
30
 
23
31
  JSON.parse( response.body, symbolize_names: true )[:result]
24
32
  # alternative to `raise for status `
@@ -41,56 +49,76 @@ module Arcade
41
49
 
42
50
  # ------------------------------ post data -------------------------------------------------------- #
43
51
  def post_data command, payload
44
- # http = HTTPX.plugin(:basic_auth)
45
- # .basic_auth(auth[:username], auth[:password])
46
- response = http.post( Config.base_uri + command, json: payload )
47
- response.raise_for_status
48
- JSON.parse( response.body, symbolize_names: true )[:result]
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
49
74
  end
50
75
 
51
76
  # ------------------------------ transaction ------------------------------------------------- #
52
77
  #
78
+ # The payload, optional as a JSON, accepts the following parameters:
79
+ # isolationLevel: READ_COMMITTED (default) or REPEATABLE_READ. (not implemented)
80
+ #
53
81
  def begin_transaction database
54
82
  result = http.post Config.base_uri + "begin/#{database}"
55
- @session_id = result.headers["arcadedb-session-id"]
56
83
  # returns the session-id
84
+ result.headers["arcadedb-session-id"]
57
85
  end
58
86
 
59
87
  # ------------------------------ post transaction ------------------------------------------------- #
60
- def post_transaction command, params, session_id= @session_id
61
- # http = HTTPX.plugin(:basic_auth)
62
- # .basic_auth(auth[:username], auth[:password])
63
- # .with( headers: { "arcadedb-session-id"=>session }, debug_level: 1)
88
+ def post_transaction command, params, session_id:
64
89
  http_a = http.with( headers: { "arcadedb-session-id" => session_id } , debug_level: 1)
65
- response = http_a.post( Config.base_uri + command, json: params )
66
- response.raise_for_status
67
- JSON.parse( response.body, symbolize_names: true )[:result]
68
-
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
69
104
  end
70
105
 
71
106
  # ------------------------------ commit ------------------------------------------------- #
72
- def commit database, session_id = @session_id
107
+ def commit database, session_id:
73
108
  http_a = http.with( headers: { "arcadedb-session-id" => session_id } , debug_level: 1)
74
109
  response = http_a.post( Config.base_uri + "commit/#{database}" )
75
- response.raise_for_status
76
- @session_id = nil
77
110
  response.status # returns 204 --> success
78
- # 403 --> incalid credentials
111
+ # 403 --> invalid credentials
79
112
  # 500 --> Transaction not begun
80
113
 
81
114
  end
82
115
 
83
116
  # ------------------------------ rollback ------------------------------------------------- #
84
- def rollback database, session_id = @session_id, publish_error=true
85
- # http = HTTPX.plugin(:basic_auth)
86
- # .basic_auth(auth[:username], auth[:password])
87
- # .with( headers: { "arcadedb-session-id"=>session_id }, debug_level: 1)
117
+ def rollback database, session_id: , log: true
88
118
  http_a = http.with( headers: { "arcadedb-session-id" => session_id } , debug_level: 1)
89
119
  response = http_a.post( Config.base_uri + "rollback/#{database}" )
90
- response.raise_for_status
91
- @session_id = nil
92
- logger.error "A Transaction has been rolled back" # if publish_error
93
- response.status
120
+ logger.info "A Transaction has been rolled back" if log
121
+ response.status # returns 500 !
94
122
  end
95
123
  end
96
124
  end
data/lib/arcade/base.rb CHANGED
@@ -27,6 +27,16 @@ module Arcade
27
27
  self.name.snake_case
28
28
  end
29
29
 
30
+ def begin_transaction
31
+ db.begin_transaction
32
+ end
33
+ def commit
34
+ db.commit
35
+ end
36
+ def rollback
37
+ db.rollback
38
+ end
39
+
30
40
  def create_type
31
41
  the_class = nil # declare as local var
32
42
  parent_present = ->(cl){ db.hierarchy.flatten.include? cl }
@@ -67,7 +77,7 @@ module Arcade
67
77
  the_command = command[0 .. -2] # remove '\n'
68
78
  next if the_command == ''
69
79
  # db.logger.info "Custom Setup:: #{the_command}"
70
- db.execute { the_command }
80
+ db.transmit { the_command }
71
81
  end unless custom_setup.nil?
72
82
 
73
83
  rescue RollbackError => e
@@ -118,9 +128,11 @@ module Arcade
118
128
  # (not supported (jet): [RETURN <expression>] [FROM <query>] )
119
129
 
120
130
  def insert **attributes
121
- db.insert type: database_name, **attributes
131
+ db.insert type: database_name, session_id: attributes.delete(:session_id), **attributes
122
132
  end
123
133
 
134
+ alias create insert
135
+
124
136
  ## ----------------------------------------- create ---------------------------------- ##
125
137
  #
126
138
  # Adds a record to the database
@@ -128,21 +140,21 @@ module Arcade
128
140
  # returns the model dataset
129
141
  # ( depreciated )
130
142
 
131
- def create **attributes
132
- s = Api.begin_transaction db.database
133
- attributes.merge!( created: DateTime.now ) if timestamps
134
- record = insert **attributes
135
- Api.commit db.database, s
136
- record
137
- rescue QueryError => e
138
- db.logger.error "Dataset NOT created"
139
- db.logger.error "Provided Attributes: #{ attributes.inspect }"
140
- # Api.rollback db.database ---> raises "transactgion not begun"
141
- rescue Dry::Struct::Error => e
142
- Api.rollback db.database
143
- db.logger.error "#{ rid } :: Validation failed, record deleted."
144
- db.logger.error e.message
145
- end
143
+ # def create **attributes
144
+ # s = Api.begin_transaction db.database
145
+ # attributes.merge!( created: DateTime.now ) if timestamps
146
+ # record = insert **attributes
147
+ # Api.commit db.database, s
148
+ # record
149
+ # rescue HTTPX::HTTPError => e
150
+ # db.logger.error "Dataset NOT created"
151
+ # db.logger.error "Provided Attributes: #{ attributes.inspect }"
152
+ # Api.rollback db.database # ---> raises "transactgion not begun"
153
+ # rescue Dry::Struct::Error => e
154
+ # Api.rollback db.database
155
+ # db.logger.error "#{ rid } :: Validation failed, record deleted."
156
+ # db.logger.error e.message
157
+ # end
146
158
 
147
159
  def count **args
148
160
  command = "count(*)"
@@ -219,9 +231,9 @@ module Arcade
219
231
  # is equivalent to
220
232
  # Strategie.all.find{|y| y.symbol == 'Still' }
221
233
  def find **args
222
- f= where(**args).first
223
- f= where( "#{ args.keys.first } like #{ args.values.first.to_or }" ).first if f.nil? || f.empty?
224
- f
234
+ where(**args).first
235
+ # f= where( "#{ args.keys.first } like #{ args.values.first.to_or }" ).first if f.nil? || f.empty?
236
+ # f
225
237
  end
226
238
  # update returns a list of updated records
227
239
  #
@@ -367,6 +379,9 @@ module Arcade
367
379
  end
368
380
  "%s : %s" % [ attr, v] unless v.nil?
369
381
  end.compact.sort.join(', ') + ">".gsub('"' , ' ')
382
+
383
+ rescue TypeError => e
384
+ attributes
370
385
  end
371
386
 
372
387
 
@@ -376,23 +391,41 @@ module Arcade
376
391
  to_human
377
392
  end
378
393
 
394
+
395
+ def html_attributes
396
+ invariant_attributes
397
+ end
398
+
399
+ def in_and_out_attributes
400
+ _modul, _class = self.class.to_s.split "::"
401
+ the_class = _modul == 'Arcade' ? _class : self.class.to_s
402
+ the_attributes = { :"CLASS" => the_class, :"IN" => self.in.count, :"OUT" => self.out.count, :"RID" => rid }
403
+ end
404
+
379
405
  def to_html # iruby
406
+ in_and_out = ->(r) { "[#{r}] : {#{self.in.count}->}{->#{self.out.count }}" }
407
+ the_rid = rid? && rid != "0:0" ? in_and_out[rid] : ""
380
408
  _modul, _class = self.class.to_s.split "::"
381
409
  the_class = _modul == 'Arcade' ? _class : self.class.to_s
382
- IRuby.display IRuby.html "<b style=\"color: #50953DFF\"><#{ the_class}</b>"
383
- + rid? ? "[#{ rid }]: " : " " + invariant_attributes.map do |attr, value|
384
- v= case value
385
- when Base
386
- "< #{ self.class.to_s.snake_case }: #{ value.rid } >"
387
- when Array
388
- value.map{|x| x.to_s}
389
- else
390
- value.from_db
391
- end
392
- "%s : %s" % [ attr, v] unless v.nil?
393
- end.compact.sort.join(', ') + ">".gsub('"' , ' ')
410
+ # the_attribute = ->(v) do
411
+ # case v
412
+ # when Base
413
+ # "< #{ self.class.to_s.snake_case }: #{ v.rid } >"
414
+ # when Array
415
+ # v.map{|x| x.to_s}
416
+ # else
417
+ # v.to_s
418
+ # end
419
+ # end
420
+ # last_part = invariant_attributes.map do |attr, value|
421
+ # [ attr, the_attribute[value] ].join(": ")
422
+ # end.join(', ')
423
+
424
+ # IRuby.display( [IRuby.html("<span style=\"color: #50953DFF\"><b>#{the_class}</b><#{ the_class }</b>#{the_rid}</span><br/> ") , IRuby.table(html_attributes) ] )
425
+ IRuby.display IRuby.html("<span style=\"color: #50953DFF\"><b>#{the_class}</b><#{ the_class }</b>#{the_rid}</span>< #{ html_attributes.map{|_,v| v }.join(', ') } >")
394
426
  end
395
427
 
428
+
396
429
  def update **args
397
430
  Query.new( from: rid , kind: :update, set: args).execute
398
431
  refresh
@@ -406,13 +439,13 @@ module Arcade
406
439
  obj.to_or
407
440
  end
408
441
  # if send( name ).nil? || send( name ).empty?
409
- db.execute { "update #{ rid } set #{ name } = #{ value }" }.first[:count]
442
+ db.transmit { "update #{ rid } set #{ name } = #{ value }" }.first[:count]
410
443
  # end
411
444
  end
412
445
 
413
446
  # updates a single property in an embedded document
414
447
  def update_embedded embedded, embedded_property, value
415
- db.execute{ " update #{rid} set `#{embedded}`.`#{embedded_property}` = #{value.to_or}" }
448
+ db.transmit { " update #{rid} set `#{embedded}`.`#{embedded_property}` = #{value.to_or}" }
416
449
  end
417
450
 
418
451
  def update_list list, value
@@ -422,9 +455,9 @@ module Arcade
422
455
  value.to_or
423
456
  end
424
457
  if send( list ).nil? || send( list ).empty?
425
- db.execute { "update #{ rid } set #{ list } = [#{ value }]" }
458
+ db.transmit { "update #{ rid } set #{ list } = [#{ value }]" }
426
459
  else
427
- db.execute { "update #{ rid } set #{ list } += #{ value }" }
460
+ db.transmit { "update #{ rid } set #{ list } += #{ value }" }
428
461
  end
429
462
  refresh
430
463
  end
@@ -432,14 +465,14 @@ module Arcade
432
465
  # updates a map property , actually adds the key-value pair to the property
433
466
  def update_map m, key, value
434
467
  if send( m ).nil?
435
- db.execute { "update #{ rid } set #{ m } = MAP ( #{ key.to_s.to_or } , #{ value.to_or } ) " }
468
+ db.transmit { "update #{ rid } set #{ m } = MAP ( #{ key.to_s.to_or } , #{ value.to_or } ) " }
436
469
  else
437
- db.execute { "update #{ rid } set #{ m }.`#{ key.to_s }` = #{ value.to_or }" }
470
+ db.transmit { "update #{ rid } set #{ m }.`#{ key.to_s }` = #{ value.to_or }" }
438
471
  end
439
472
  refresh
440
473
  end
441
474
  def delete
442
- response = db.execute { "delete from #{ rid }" }
475
+ response = db.transmit { "delete from #{ rid }" }
443
476
  true if response == [{ count: 1 }]
444
477
  end
445
478
  def == arg
@@ -3,11 +3,11 @@ module Arcade
3
3
  extend Dry::Configurable
4
4
  # central place to initialize constants
5
5
  #
6
- # ProjectRoot has to be a Pathname-Object
6
+ # ProjectRoot should to be a Pathname-Object and has to be defined before `arcadedb` is required
7
7
  #
8
8
  #puts "expand: #{File.expand_path(__dir__)}"
9
- unless Arcade.const_defined?( :ProjectRoot )
10
- Arcade::ProjectRoot = if defined?( Rails.env )
9
+ unless Object.const_defined?( :ProjectRoot )
10
+ ::ProjectRoot = if defined?( Rails.env )
11
11
  Rails.root
12
12
  else
13
13
  STDERR.puts "Using default (arcadedb gem) database credentials and settings"
@@ -15,10 +15,9 @@ module Arcade
15
15
  Pathname.new( File.expand_path( "../../", __FILE__ ))
16
16
  end
17
17
  else
18
- STDERR.puts "Using provided database credentials and settings fron #{Arcade::ProjectRoot}"
18
+ STDERR.puts "Using provided database credentials and settings from #{::ProjectRoot}"
19
19
  end
20
20
 
21
-
22
21
  # initialised a hash { environment => property }
23
22
  setting :username, default: :user, reader: true,
24
23
  constructor: ->(v) { yml(:environment).map{|x,y| [x , y[v.to_s]] }.to_h }
@@ -53,7 +52,7 @@ module Arcade
53
52
  def self.config_file
54
53
 
55
54
  configdir = -> do
56
- pr = ProjectRoot.is_a?(Pathname)? ProjectRoot : Pathname.new( ProjectRoot )
55
+ pr = ::ProjectRoot.is_a?(Pathname)? ::ProjectRoot : Pathname.new( ::ProjectRoot )
57
56
  ( cd = pr + 'arcade.yml' ).exist? || ( cd = pr + 'config' + 'arcade.yml' ).exist? || ( cd = pr + 'config.yml' )
58
57
  cd
59
58
  end