arcadedb 0.3.3 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|