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
data/lib/arcade/base.rb
CHANGED
@@ -5,7 +5,7 @@ module Arcade
|
|
5
5
|
# schema schema.strict # -- throws an error if specified keys are missing
|
6
6
|
transform_keys{ |x| x[0] == '@' ? x[1..-1].to_sym : x.to_sym }
|
7
7
|
# Types::Rid --> only accept #000:000, raises an Error, if rid is not present
|
8
|
-
attribute :rid
|
8
|
+
attribute :rid?, Types::Rid
|
9
9
|
# maybe there are edges ## removed in favour of instance methods
|
10
10
|
# attribute :in?, Types::Nominal::Any
|
11
11
|
# attribute :out?, Types::Nominal::Any
|
@@ -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 }
|
@@ -41,12 +51,7 @@ module Arcade
|
|
41
51
|
if the_class.respond_to?(:demodulize)
|
42
52
|
if [ 'Document','Vertex', 'Edge'].include?(the_class.demodulize)
|
43
53
|
if the_class == superclass # no inheritance
|
44
|
-
## we have to use demodulize as the_class actually is Arcade::Vertex, ...
|
45
|
-
unless parent_present[ to_s.snake_case ]
|
46
54
|
db.create_type the_class.demodulize, to_s.snake_case
|
47
|
-
else
|
48
|
-
db.logger.warn "Type #{ to_s.snake_case } is present, process skipped"
|
49
|
-
end
|
50
55
|
else
|
51
56
|
if superclass.is_a? Class # maybe its a module.
|
52
57
|
extended = superclass.to_s.snake_case
|
@@ -72,10 +77,10 @@ module Arcade
|
|
72
77
|
the_command = command[0 .. -2] # remove '\n'
|
73
78
|
next if the_command == ''
|
74
79
|
# db.logger.info "Custom Setup:: #{the_command}"
|
75
|
-
db.
|
80
|
+
db.transmit { the_command }
|
76
81
|
end unless custom_setup.nil?
|
77
82
|
|
78
|
-
rescue
|
83
|
+
rescue RollbackError => e
|
79
84
|
db.logger.warn e
|
80
85
|
rescue RuntimeError => e
|
81
86
|
db.logger.warn e
|
@@ -123,9 +128,11 @@ module Arcade
|
|
123
128
|
# (not supported (jet): [RETURN <expression>] [FROM <query>] )
|
124
129
|
|
125
130
|
def insert **attributes
|
126
|
-
db.insert type: database_name, **attributes
|
131
|
+
db.insert type: database_name, session_id: attributes.delete(:session_id), **attributes
|
127
132
|
end
|
128
133
|
|
134
|
+
alias create insert
|
135
|
+
|
129
136
|
## ----------------------------------------- create ---------------------------------- ##
|
130
137
|
#
|
131
138
|
# Adds a record to the database
|
@@ -133,21 +140,21 @@ module Arcade
|
|
133
140
|
# returns the model dataset
|
134
141
|
# ( depreciated )
|
135
142
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
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
|
151
158
|
|
152
159
|
def count **args
|
153
160
|
command = "count(*)"
|
@@ -222,12 +229,11 @@ module Arcade
|
|
222
229
|
# Finds the first matching record providing the parameters of a `where` query
|
223
230
|
# Strategie.find symbol: 'Still'
|
224
231
|
# is equivalent to
|
225
|
-
# Strategie.all.find{|y| y.symbol == 'Still'
|
226
|
-
# }
|
232
|
+
# Strategie.all.find{|y| y.symbol == 'Still' }
|
227
233
|
def find **args
|
228
|
-
|
229
|
-
f= where( "#{ args.keys.first } like #{ args.values.first.to_or }" ).first if f.nil? || f.empty?
|
230
|
-
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
|
231
237
|
end
|
232
238
|
# update returns a list of updated records
|
233
239
|
#
|
@@ -278,14 +284,14 @@ module Arcade
|
|
278
284
|
end
|
279
285
|
result= query( **( { kind: :upsert }.merge statement ) ).execute do | answer|
|
280
286
|
z= answer[:"$current"] &.allocate_model(false) # do not autoload modelfiles
|
281
|
-
raise
|
287
|
+
raise LoadError "Upsert failed" unless z.is_a? Base
|
282
288
|
z # return record
|
283
289
|
end
|
284
290
|
end
|
285
291
|
|
286
292
|
|
287
293
|
def query **args
|
288
|
-
|
294
|
+
Query.new( **{ from: self }.merge(args) )
|
289
295
|
end
|
290
296
|
|
291
297
|
# immutable support
|
@@ -332,7 +338,7 @@ module Arcade
|
|
332
338
|
end
|
333
339
|
|
334
340
|
def query **args
|
335
|
-
|
341
|
+
Query.new( **{ from: rid }.merge(args) )
|
336
342
|
end
|
337
343
|
|
338
344
|
# to JSON controlls the serialisation of Arcade::Base Objects for the HTTP-JSON API
|
@@ -364,7 +370,7 @@ module Arcade
|
|
364
370
|
|
365
371
|
"<#{ self.class.to_s.snake_case }" + rid? ? "[#{ rid }]: " : " " + invariant_attributes.map do |attr, value|
|
366
372
|
v= case value
|
367
|
-
when
|
373
|
+
when Base
|
368
374
|
"< #{ self.class.to_s.snake_case }: #{ value.rid } >"
|
369
375
|
when Array
|
370
376
|
value.map{|x| x.to_s}
|
@@ -373,59 +379,85 @@ module Arcade
|
|
373
379
|
end
|
374
380
|
"%s : %s" % [ attr, v] unless v.nil?
|
375
381
|
end.compact.sort.join(', ') + ">".gsub('"' , ' ')
|
382
|
+
|
383
|
+
rescue TypeError => e
|
384
|
+
attributes
|
376
385
|
end
|
377
386
|
|
378
|
-
|
387
|
+
|
388
|
+
# configure irb-output to to_human for all Arcade::Base-Objects
|
389
|
+
#
|
390
|
+
def inspect
|
391
|
+
to_human
|
392
|
+
end
|
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
|
379
404
|
|
380
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] : ""
|
381
408
|
_modul, _class = self.class.to_s.split "::"
|
382
409
|
the_class = _modul == 'Arcade' ? _class : self.class.to_s
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
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(', ') } >")
|
395
426
|
end
|
396
427
|
|
428
|
+
|
397
429
|
def update **args
|
398
|
-
|
430
|
+
Query.new( from: rid , kind: :update, set: args).execute
|
399
431
|
refresh
|
400
432
|
end
|
401
433
|
|
402
434
|
# inserts or updates a embedded document
|
403
435
|
def insert_document name, obj
|
404
|
-
value = if obj.is_a?
|
436
|
+
value = if obj.is_a? Document
|
405
437
|
obj.to_json
|
406
438
|
else
|
407
439
|
obj.to_or
|
408
440
|
end
|
409
441
|
# if send( name ).nil? || send( name ).empty?
|
410
|
-
db.
|
442
|
+
db.transmit { "update #{ rid } set #{ name } = #{ value }" }.first[:count]
|
411
443
|
# end
|
412
444
|
end
|
413
445
|
|
414
446
|
# updates a single property in an embedded document
|
415
447
|
def update_embedded embedded, embedded_property, value
|
416
|
-
db.
|
448
|
+
db.transmit { " update #{rid} set `#{embedded}`.`#{embedded_property}` = #{value.to_or}" }
|
417
449
|
end
|
418
450
|
|
419
451
|
def update_list list, value
|
420
|
-
value = if value.is_a?
|
452
|
+
value = if value.is_a? Document
|
421
453
|
value.to_json
|
422
454
|
else
|
423
455
|
value.to_or
|
424
456
|
end
|
425
457
|
if send( list ).nil? || send( list ).empty?
|
426
|
-
db.
|
458
|
+
db.transmit { "update #{ rid } set #{ list } = [#{ value }]" }
|
427
459
|
else
|
428
|
-
db.
|
460
|
+
db.transmit { "update #{ rid } set #{ list } += #{ value }" }
|
429
461
|
end
|
430
462
|
refresh
|
431
463
|
end
|
@@ -433,14 +465,14 @@ module Arcade
|
|
433
465
|
# updates a map property , actually adds the key-value pair to the property
|
434
466
|
def update_map m, key, value
|
435
467
|
if send( m ).nil?
|
436
|
-
db.
|
468
|
+
db.transmit { "update #{ rid } set #{ m } = MAP ( #{ key.to_s.to_or } , #{ value.to_or } ) " }
|
437
469
|
else
|
438
|
-
db.
|
470
|
+
db.transmit { "update #{ rid } set #{ m }.`#{ key.to_s }` = #{ value.to_or }" }
|
439
471
|
end
|
440
472
|
refresh
|
441
473
|
end
|
442
474
|
def delete
|
443
|
-
response = db.
|
475
|
+
response = db.transmit { "delete from #{ rid }" }
|
444
476
|
true if response == [{ count: 1 }]
|
445
477
|
end
|
446
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
|
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
|
10
|
-
|
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
|
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 }
|
@@ -48,17 +47,19 @@ module Arcade
|
|
48
47
|
setting :namespace, default: :namespace, reader: true , constructor: ->(v) { yml(v) }
|
49
48
|
setting :secret, reader: true, default: 12, constructor: ->(v) { seed(v) }
|
50
49
|
private
|
51
|
-
|
50
|
+
# if a config dir exists, use it.
|
51
|
+
# Standard: ProjectRoot/config.yml
|
52
52
|
def self.config_file
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
( cd =
|
57
|
-
|
58
|
-
else
|
59
|
-
@cd
|
53
|
+
|
54
|
+
configdir = -> do
|
55
|
+
pr = ::ProjectRoot.is_a?(Pathname)? ::ProjectRoot : Pathname.new( ::ProjectRoot )
|
56
|
+
( cd = pr + 'arcade.yml' ).exist? || ( cd = pr + 'config' + 'arcade.yml' ).exist? || ( cd = pr + 'config.yml' )
|
57
|
+
cd
|
60
58
|
end
|
59
|
+
|
60
|
+
@cd ||= configdir[]
|
61
61
|
end
|
62
|
+
|
62
63
|
def self.yml key=nil
|
63
64
|
y= YAML::load_file( config_file )
|
64
65
|
key.nil? ? y : y[key]
|
data/lib/arcade/database.rb
CHANGED
@@ -17,12 +17,12 @@ module Arcade
|
|
17
17
|
defines :environment
|
18
18
|
|
19
19
|
def initialize environment=:development
|
20
|
-
self.class.configure_logger( Config.logger )
|
21
|
-
@connection = connect environment
|
20
|
+
self.class.configure_logger( Config.logger )
|
22
21
|
if self.class.environment.nil? # class attribute is set on the first call
|
23
22
|
# further instances of Database share the same environment
|
24
23
|
self.class.environment environment
|
25
24
|
end
|
25
|
+
@session_id = nil # declare session_id
|
26
26
|
self.class.namespace Object.const_get( Config.namespace )
|
27
27
|
end
|
28
28
|
|
@@ -39,20 +39,19 @@ module Arcade
|
|
39
39
|
#
|
40
40
|
def types refresh=false
|
41
41
|
# uses API
|
42
|
-
if
|
43
|
-
|
44
|
-
.map{ |
|
45
|
-
.map{ |y| y.delete_if{|_,b,| b.empty? } } # eliminate empty entries
|
42
|
+
if @types.nil? || refresh
|
43
|
+
@types = Api.query(database, "select from schema:types" )
|
44
|
+
.map{ |y| y.delete_if{|_,b,| b.blank? } } # eliminate empty entries
|
46
45
|
end
|
47
|
-
|
48
|
-
##
|
46
|
+
@types
|
47
|
+
## upon startup, this is the first access to the database-server
|
49
48
|
rescue NoMethodError => e
|
50
49
|
logger.fatal "Could not read Database Types. \n Is the database running?"
|
51
50
|
Kernel.exit
|
52
51
|
end
|
53
52
|
|
54
|
-
def indexes
|
55
|
-
|
53
|
+
def indexes refresh=false
|
54
|
+
types(refresh).find_all{|x| x.key? :indexes }.map{|y| y[:indexes]}.flatten
|
56
55
|
end
|
57
56
|
|
58
57
|
# ------------ hierarchy -------------
|
@@ -108,23 +107,56 @@ module Arcade
|
|
108
107
|
"create edge type #{type} "
|
109
108
|
end.concat( args.map{|x,y| "#{x} #{y} "}.join)
|
110
109
|
end
|
111
|
-
|
110
|
+
dbe= Api.execute database, &exe
|
112
111
|
types( true ) # update cached schema
|
113
|
-
|
114
|
-
|
115
|
-
rescue Arcade::QueryError
|
116
|
-
|
112
|
+
dbe
|
113
|
+
|
114
|
+
rescue Arcade::QueryError => e
|
115
|
+
if e.message =~/Type\s+.+\salready\s+exists/
|
116
|
+
Arcade::Database.logger.debug "Database type #{type} already present"
|
117
|
+
else
|
118
|
+
raise
|
119
|
+
end
|
117
120
|
end
|
118
121
|
|
119
122
|
alias create_class create_type
|
120
123
|
|
121
124
|
# ------------ drop type -----------
|
122
125
|
# delete any record prior to the attempt to drop a type.
|
123
|
-
# The `unsafe` option is
|
126
|
+
# The `unsafe` option is not implemented.
|
124
127
|
def drop_type type
|
125
128
|
Api.execute database, "drop type #{type} if exists"
|
126
129
|
end
|
127
130
|
|
131
|
+
# ------------------------------ transaction ----------------------------------------------------- #
|
132
|
+
# Encapsulates simple transactions
|
133
|
+
#
|
134
|
+
# nested transactions are not supported.
|
135
|
+
# * use the low-leve api.begin_tranaction for that purpose
|
136
|
+
# * reuses an existing transaction
|
137
|
+
#
|
138
|
+
def begin_transaction
|
139
|
+
@session_id ||= Api.begin_transaction database
|
140
|
+
end
|
141
|
+
# ------------------------------ commit ----------------------------------------------------- #
|
142
|
+
def commit
|
143
|
+
r = Api.commit( database, session_id: session)
|
144
|
+
@session_id = nil
|
145
|
+
true if r == 204
|
146
|
+
end
|
147
|
+
|
148
|
+
# ------------------------------ rollback ----------------------------------------------------- #
|
149
|
+
#
|
150
|
+
def rollback
|
151
|
+
r = Api.rollback( database, session_id: session)
|
152
|
+
@session_id = nil
|
153
|
+
true if r == 500
|
154
|
+
rescue HTTPX::HTTPError => e
|
155
|
+
raise
|
156
|
+
end
|
157
|
+
|
158
|
+
|
159
|
+
|
128
160
|
# ------------ create -----------
|
129
161
|
# returns an rid of the successfully created vertex or document
|
130
162
|
#
|
@@ -136,21 +168,34 @@ module Arcade
|
|
136
168
|
#
|
137
169
|
def create type, **params
|
138
170
|
# uses API
|
139
|
-
Api.create_document database, type, **params
|
171
|
+
Api.create_document database, type, session_id: session, **params
|
140
172
|
end
|
141
173
|
|
174
|
+
# ------------------------------ insert ------------------------------------------------------ #
|
175
|
+
#
|
176
|
+
# translates the given parameters to
|
177
|
+
# INSERT INTO [TYPE:]<type>|BUCKET:<bucket>|INDEX:<index>
|
178
|
+
# [(<field>[,]*) VALUES (<expression>[,]*)[,]*]|
|
179
|
+
# [CONTENT {<JSON>}|[{<JSON>}[,]*]]
|
180
|
+
#
|
181
|
+
# :from and :return are not supported
|
182
|
+
#
|
183
|
+
# If a transaction is active, the insert is executed in that context.
|
184
|
+
# Nested transactions are not supported
|
142
185
|
def insert **params
|
143
186
|
|
144
|
-
content_params = params.except( :type, :bucket, :index, :from, :return )
|
187
|
+
content_params = params.except( :type, :bucket, :index, :from, :return, :session_id )
|
145
188
|
target_params = params.slice( :type, :bucket, :index )
|
189
|
+
# session_id = params[:session_id] # extraxt session_id --> future-use?
|
146
190
|
if target_params.empty?
|
147
|
-
|
191
|
+
raise "Could not insert: target missing (type:, bucket:, index:)"
|
148
192
|
elsif content_params.empty?
|
149
193
|
logger.error "Nothing to Insert"
|
150
194
|
else
|
151
195
|
content = "CONTENT #{ content_params.to_json }"
|
152
196
|
target = target_params.map{|y,z| y==:type ? z : "#{y.to_s} #{ z } "}.join
|
153
|
-
Api.execute( database, "INSERT INTO #{target} #{content} "
|
197
|
+
result = Api.execute( database, session_id: session ){ "INSERT INTO #{target} #{content} "}
|
198
|
+
result &.first.allocate_model(false)
|
154
199
|
end
|
155
200
|
end
|
156
201
|
|
@@ -173,89 +218,61 @@ module Arcade
|
|
173
218
|
rid = rid.join(':')
|
174
219
|
rid = rid[1..-1] if rid[0]=="#"
|
175
220
|
if rid.rid?
|
176
|
-
Api.query( database, "select from #{rid}" ).first &.allocate_model(autocomplete)
|
221
|
+
Api.query( database, "select from #{rid}", session_id: session ).first &.allocate_model(autocomplete)
|
177
222
|
else
|
178
223
|
raise Arcade::QueryError "Get requires a rid input", caller
|
179
224
|
end
|
180
225
|
end
|
181
226
|
|
182
|
-
# ------------------------------
|
183
|
-
# Adds properties to the type
|
227
|
+
# ------------------------------ get ------------------------------------------------------ #
|
184
228
|
#
|
185
|
-
#
|
186
|
-
# Api.property <database>, <type>, name1: a_format , name2: a_format
|
229
|
+
# Delete the specified rid
|
187
230
|
#
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
231
|
+
def delete rid
|
232
|
+
r = Api.execute( database, session_id: session ){ "delete from #{rid}" }
|
233
|
+
success = r == [{ :count => 1 }]
|
234
|
+
end
|
235
|
+
|
236
|
+
# ------------------------------ transmit ------------------------------------------------------ #
|
237
|
+
# transmits a command which potentially modifies the database
|
192
238
|
#
|
193
|
-
#
|
239
|
+
# Uses the given session_id for transaction-based operations
|
194
240
|
#
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
false
|
202
|
-
else
|
203
|
-
r.keys == [ :propertyName, :typeName, :operation ] && r[:operation] == 'create property'
|
204
|
-
end
|
205
|
-
end.uniq
|
206
|
-
if success == [true]
|
207
|
-
commit database
|
208
|
-
true
|
241
|
+
# Otherwise just performs the operation
|
242
|
+
|
243
|
+
def transmit &block
|
244
|
+
response = Api.execute database, session_id: session, &block
|
245
|
+
if response.is_a? Hash
|
246
|
+
_allocate_model res
|
209
247
|
else
|
210
|
-
|
248
|
+
response
|
211
249
|
end
|
212
|
-
|
213
|
-
|
214
|
-
end
|
215
|
-
|
216
|
-
# ------------------------------ index ------------------------------------------------- #
|
217
|
-
def self.index database, type, name , *properties
|
218
|
-
properties = properties.map( &:to_s )
|
219
|
-
unique_requested = "unique" if properties.delete("unique")
|
220
|
-
unique_requested = "notunique" if properties.delete("notunique" )
|
221
|
-
automatic = true if
|
222
|
-
properties << name if properties.empty?
|
223
250
|
end
|
224
|
-
|
225
|
-
def delete rid
|
226
|
-
r = Api.execute( database ){ "delete from #{rid}" }
|
227
|
-
success = r == [{ :count => 1 }]
|
228
|
-
end
|
229
|
-
|
251
|
+
# ------------------------------ execute ------------------------------------------------------ #
|
230
252
|
# execute a command which modifies the database
|
231
253
|
#
|
232
254
|
# The operation is performed via Transaction/Commit
|
233
255
|
# If an Error occurs, its rolled back
|
234
256
|
#
|
257
|
+
# If a transaction is already active, a nested transation is initiated
|
258
|
+
#
|
235
259
|
def execute &block
|
236
|
-
|
237
|
-
|
238
|
-
|
260
|
+
# initiate a new transaction
|
261
|
+
s= Api.begin_transaction database
|
262
|
+
response = Api.execute database, session_id: s, &block
|
239
263
|
r= if response.is_a? Hash
|
240
|
-
_allocate_model
|
241
|
-
# elsif response.is_a? Array
|
242
|
-
# remove empty results
|
243
|
-
# response.delete_if{|y| y.empty?}
|
244
|
-
# response.map do | res |
|
245
|
-
# if res.key? :"@rid"
|
246
|
-
# allocate_model res
|
247
|
-
# else
|
248
|
-
# res
|
249
|
-
# end
|
250
|
-
# end
|
264
|
+
_allocate_model response
|
251
265
|
else
|
252
266
|
response
|
253
267
|
end
|
254
|
-
Api.commit database
|
255
|
-
|
268
|
+
if Api.commit( database, session_id: s) == 204
|
269
|
+
r # return associated array of Arcade::Base-objects
|
270
|
+
else
|
271
|
+
[]
|
272
|
+
end
|
256
273
|
rescue Dry::Struct::Error, Arcade::QueryError => e
|
257
|
-
Api.rollback database
|
258
|
-
logger.
|
274
|
+
Api.rollback database, session_id: s, log: false
|
275
|
+
logger.info "Execution FAILED --> Status #{e.status}"
|
259
276
|
[] # return empty result
|
260
277
|
end
|
261
278
|
|
@@ -264,7 +281,7 @@ module Arcade
|
|
264
281
|
# detects database-records and allocates them as model-objects
|
265
282
|
#
|
266
283
|
def query query_object
|
267
|
-
Api.query database, query_object.to_s
|
284
|
+
Api.query database, query_object.to_s, session_id: session
|
268
285
|
end
|
269
286
|
|
270
287
|
# returns an array of rid's (same logic as create)
|
@@ -272,11 +289,13 @@ module Arcade
|
|
272
289
|
|
273
290
|
content = attributes.empty? ? "" : "CONTENT #{attributes.to_json}"
|
274
291
|
cr = ->( f, t ) do
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
292
|
+
begin
|
293
|
+
cmd = -> (){ "create edge #{edge_class} from #{f.rid} to #{t.rid} #{content}" }
|
294
|
+
edges = transmit( &cmd ).allocate_model(false)
|
295
|
+
rescue Arcade::QueryError => e
|
296
|
+
raise unless e.message =~ /Found duplicate key/
|
297
|
+
puts "#"+e.detail.split("#").last[0..-3]
|
298
|
+
end
|
280
299
|
end
|
281
300
|
from = [from] unless from.is_a? Array
|
282
301
|
to = [to] unless to.is_a? Array
|
@@ -287,52 +306,12 @@ module Arcade
|
|
287
306
|
|
288
307
|
end
|
289
308
|
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
# not used
|
294
|
-
# def get_schema
|
295
|
-
# query( "select from schema:types" ).map do |a|
|
296
|
-
# puts "a: #{a}"
|
297
|
-
# class_name = a["name"]
|
298
|
-
# inherent_class = a["parentTypes"].empty? ? [Object,nil] : a["parentTypes"].map(&:camelcase_and_namespace)
|
299
|
-
# namespace, type_name = a["type"].camelcase_and_namespace
|
300
|
-
# namespace= Arcade if namespace.nil?
|
301
|
-
# klass= Dry::Core::ClassBuilder.new( name: type_name,
|
302
|
-
# parent: nil,
|
303
|
-
# namespace: namespace).call
|
304
|
-
# end
|
305
|
-
# rescue NameError
|
306
|
-
# logger.error "Dataset type #{e} not defined."
|
307
|
-
# raise
|
308
|
-
# end
|
309
|
-
# Postgres is not implemented
|
310
|
-
# connects to the database and initialises @connection
|
311
|
-
def connection
|
312
|
-
@connection
|
309
|
+
def session
|
310
|
+
@session_id
|
313
311
|
end
|
314
312
|
|
315
|
-
def
|
316
|
-
|
317
|
-
|
318
|
-
# connect through the ruby postgres driver
|
319
|
-
# c= PG::Connection.new dbname: Config.database[environment],
|
320
|
-
# user: Config.username[environment],
|
321
|
-
# password: Config.password[environment],
|
322
|
-
# host: Config.pg[:host],
|
323
|
-
# port: Config.pg[:port]
|
324
|
-
#
|
325
|
-
end
|
326
|
-
rescue PG::ConnectionBad => e
|
327
|
-
if e.to_s =~ /Credentials/
|
328
|
-
logger.error "NOT CONNECTED ! Either Database is not present or credentials (#{ Config.username[environment]} / #{Config.password[environment]}) are wrong"
|
329
|
-
nil
|
330
|
-
else
|
331
|
-
raise
|
332
|
-
end
|
333
|
-
end # def
|
334
|
-
|
335
|
-
|
336
|
-
|
313
|
+
def session?
|
314
|
+
!session.nil?
|
315
|
+
end
|
337
316
|
end # class
|
338
317
|
end # module
|