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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -1
- data/Gemfile.lock +2 -36
- data/README.md +105 -30
- data/arcade.yml +4 -4
- data/bin/console +16 -16
- 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 +26 -65
- data/lib/arcade/api/primitives.rb +57 -29
- data/lib/arcade/base.rb +72 -39
- data/lib/{config.rb → arcade/config.rb} +5 -6
- data/lib/arcade/database.rb +104 -154
- data/lib/arcade/errors.rb +8 -0
- data/lib/arcade/match.rb +162 -0
- data/lib/{query.rb → arcade/query.rb} +34 -18
- data/lib/{support → arcade/support}/conversions.rb +1 -0
- data/lib/{support → arcade/support}/model.rb +20 -15
- data/lib/arcade/version.rb +1 -1
- data/lib/arcade.rb +11 -11
- data/lib/model/document.rb +22 -0
- data/lib/model/edge.rb +12 -0
- data/lib/model/vertex.rb +67 -48
- metadata +15 -12
- 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/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,28 +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
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
end
|
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
|
122
120
|
end
|
123
121
|
|
124
122
|
alias create_class create_type
|
125
123
|
|
126
124
|
# ------------ drop type -----------
|
127
125
|
# delete any record prior to the attempt to drop a type.
|
128
|
-
# The `unsafe` option is
|
126
|
+
# The `unsafe` option is not implemented.
|
129
127
|
def drop_type type
|
130
128
|
Api.execute database, "drop type #{type} if exists"
|
131
129
|
end
|
132
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
|
+
|
133
160
|
# ------------ create -----------
|
134
161
|
# returns an rid of the successfully created vertex or document
|
135
162
|
#
|
@@ -141,21 +168,34 @@ module Arcade
|
|
141
168
|
#
|
142
169
|
def create type, **params
|
143
170
|
# uses API
|
144
|
-
Api.create_document database, type, **params
|
171
|
+
Api.create_document database, type, session_id: session, **params
|
145
172
|
end
|
146
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
|
147
185
|
def insert **params
|
148
186
|
|
149
|
-
content_params = params.except( :type, :bucket, :index, :from, :return )
|
187
|
+
content_params = params.except( :type, :bucket, :index, :from, :return, :session_id )
|
150
188
|
target_params = params.slice( :type, :bucket, :index )
|
189
|
+
# session_id = params[:session_id] # extraxt session_id --> future-use?
|
151
190
|
if target_params.empty?
|
152
|
-
|
191
|
+
raise "Could not insert: target missing (type:, bucket:, index:)"
|
153
192
|
elsif content_params.empty?
|
154
193
|
logger.error "Nothing to Insert"
|
155
194
|
else
|
156
195
|
content = "CONTENT #{ content_params.to_json }"
|
157
196
|
target = target_params.map{|y,z| y==:type ? z : "#{y.to_s} #{ z } "}.join
|
158
|
-
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)
|
159
199
|
end
|
160
200
|
end
|
161
201
|
|
@@ -178,103 +218,61 @@ module Arcade
|
|
178
218
|
rid = rid.join(':')
|
179
219
|
rid = rid[1..-1] if rid[0]=="#"
|
180
220
|
if rid.rid?
|
181
|
-
Api.query( database, "select from #{rid}" ).first &.allocate_model(autocomplete)
|
221
|
+
Api.query( database, "select from #{rid}", session_id: session ).first &.allocate_model(autocomplete)
|
182
222
|
else
|
183
223
|
raise Arcade::QueryError "Get requires a rid input", caller
|
184
224
|
end
|
185
225
|
end
|
186
226
|
|
187
|
-
# ------------------------------
|
188
|
-
# Adds properties to the type
|
227
|
+
# ------------------------------ get ------------------------------------------------------ #
|
189
228
|
#
|
190
|
-
#
|
191
|
-
# Api.property <database>, <type>, name1: a_format , name2: a_format
|
229
|
+
# Delete the specified rid
|
192
230
|
#
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
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
|
197
238
|
#
|
198
|
-
#
|
239
|
+
# Uses the given session_id for transaction-based operations
|
199
240
|
#
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
false
|
207
|
-
else
|
208
|
-
r.keys == [ :propertyName, :typeName, :operation ] && r[:operation] == 'create property'
|
209
|
-
end
|
210
|
-
end.uniq
|
211
|
-
if success == [true]
|
212
|
-
commit database
|
213
|
-
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
|
214
247
|
else
|
215
|
-
|
248
|
+
response
|
216
249
|
end
|
217
|
-
|
218
|
-
|
219
|
-
end
|
220
|
-
|
221
|
-
# ------------------------------ index ------------------------------------------------- #
|
222
|
-
def self.index database, type, name , *properties
|
223
|
-
properties = properties.map( &:to_s )
|
224
|
-
unique_requested = "unique" if properties.delete("unique")
|
225
|
-
unique_requested = "notunique" if properties.delete("notunique" )
|
226
|
-
automatic = true if
|
227
|
-
properties << name if properties.empty?
|
228
|
-
end
|
229
|
-
|
230
|
-
def delete rid
|
231
|
-
r = Api.execute( database ){ "delete from #{rid}" }
|
232
|
-
success = r == [{ :count => 1 }]
|
233
250
|
end
|
234
|
-
|
251
|
+
# ------------------------------ execute ------------------------------------------------------ #
|
235
252
|
# execute a command which modifies the database
|
236
253
|
#
|
237
254
|
# The operation is performed via Transaction/Commit
|
238
255
|
# If an Error occurs, its rolled back
|
239
256
|
#
|
257
|
+
# If a transaction is already active, a nested transation is initiated
|
258
|
+
#
|
240
259
|
def execute &block
|
241
|
-
|
242
|
-
|
243
|
-
response = Api.execute database,
|
244
|
-
# rescue HTTPX::HTTPError => e
|
245
|
-
# raise e.message
|
246
|
-
# puts e.methods
|
247
|
-
# puts e.status
|
248
|
-
# puts e.response
|
249
|
-
# puts e.message
|
250
|
-
# puts e.exception
|
251
|
-
# puts e.cause
|
252
|
-
# end
|
253
|
-
# puts response.inspect # debugging
|
260
|
+
# initiate a new transaction
|
261
|
+
s= Api.begin_transaction database
|
262
|
+
response = Api.execute database, session_id: s, &block
|
254
263
|
r= if response.is_a? Hash
|
255
|
-
_allocate_model
|
256
|
-
# elsif response.is_a? Array
|
257
|
-
# remove empty results
|
258
|
-
# response.delete_if{|y| y.empty?}
|
259
|
-
# response.map do | res |
|
260
|
-
# if res.key? :"@rid"
|
261
|
-
# allocate_model res
|
262
|
-
# else
|
263
|
-
# res
|
264
|
-
# end
|
265
|
-
# end
|
264
|
+
_allocate_model response
|
266
265
|
else
|
267
266
|
response
|
268
267
|
end
|
269
|
-
if Api.commit( database, s) == 204
|
268
|
+
if Api.commit( database, session_id: s) == 204
|
270
269
|
r # return associated array of Arcade::Base-objects
|
271
270
|
else
|
272
271
|
[]
|
273
272
|
end
|
274
|
-
rescue Dry::Struct::Error,
|
275
|
-
Api.rollback database, s
|
276
|
-
logger.
|
277
|
-
# logger.error "Execution FAILED --> #{e.exception.message}"
|
273
|
+
rescue Dry::Struct::Error, Arcade::QueryError => e
|
274
|
+
Api.rollback database, session_id: s, log: false
|
275
|
+
logger.info "Execution FAILED --> Status #{e.status}"
|
278
276
|
[] # return empty result
|
279
277
|
end
|
280
278
|
|
@@ -283,7 +281,7 @@ module Arcade
|
|
283
281
|
# detects database-records and allocates them as model-objects
|
284
282
|
#
|
285
283
|
def query query_object
|
286
|
-
Api.query database, query_object.to_s
|
284
|
+
Api.query database, query_object.to_s, session_id: session
|
287
285
|
end
|
288
286
|
|
289
287
|
# returns an array of rid's (same logic as create)
|
@@ -292,20 +290,12 @@ module Arcade
|
|
292
290
|
content = attributes.empty? ? "" : "CONTENT #{attributes.to_json}"
|
293
291
|
cr = ->( f, t ) do
|
294
292
|
begin
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
# puts e.status
|
299
|
-
# puts e.message
|
300
|
-
# puts e.message.class
|
301
|
-
# end
|
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
|
302
296
|
raise unless e.message =~ /Found duplicate key/
|
303
|
-
puts "#"+e.
|
297
|
+
puts "#"+e.detail.split("#").last[0..-3]
|
304
298
|
end
|
305
|
-
#else
|
306
|
-
# logger.error "Could not create Edge #{edge_class} from #{f} to #{t}"
|
307
|
-
## logger.error edges.to_s
|
308
|
-
#end
|
309
299
|
end
|
310
300
|
from = [from] unless from.is_a? Array
|
311
301
|
to = [to] unless to.is_a? Array
|
@@ -316,52 +306,12 @@ module Arcade
|
|
316
306
|
|
317
307
|
end
|
318
308
|
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
# not used
|
323
|
-
# def get_schema
|
324
|
-
# query( "select from schema:types" ).map do |a|
|
325
|
-
# puts "a: #{a}"
|
326
|
-
# class_name = a["name"]
|
327
|
-
# inherent_class = a["parentTypes"].empty? ? [Object,nil] : a["parentTypes"].map(&:camelcase_and_namespace)
|
328
|
-
# namespace, type_name = a["type"].camelcase_and_namespace
|
329
|
-
# namespace= Arcade if namespace.nil?
|
330
|
-
# klass= Dry::Core::ClassBuilder.new( name: type_name,
|
331
|
-
# parent: nil,
|
332
|
-
# namespace: namespace).call
|
333
|
-
# end
|
334
|
-
# rescue NameError
|
335
|
-
# logger.error "Dataset type #{e} not defined."
|
336
|
-
# raise
|
337
|
-
# end
|
338
|
-
# Postgres is not implemented
|
339
|
-
# connects to the database and initialises @connection
|
340
|
-
def connection
|
341
|
-
@connection
|
309
|
+
def session
|
310
|
+
@session_id
|
342
311
|
end
|
343
312
|
|
344
|
-
def
|
345
|
-
|
346
|
-
|
347
|
-
# connect through the ruby postgres driver
|
348
|
-
# c= PG::Connection.new dbname: Config.database[environment],
|
349
|
-
# user: Config.username[environment],
|
350
|
-
# password: Config.password[environment],
|
351
|
-
# host: Config.pg[:host],
|
352
|
-
# port: Config.pg[:port]
|
353
|
-
#
|
354
|
-
end
|
355
|
-
rescue PG::ConnectionBad => e
|
356
|
-
if e.to_s =~ /Credentials/
|
357
|
-
logger.error "NOT CONNECTED ! Either Database is not present or credentials (#{ Config.username[environment]} / #{Config.password[environment]}) are wrong"
|
358
|
-
nil
|
359
|
-
else
|
360
|
-
raise
|
361
|
-
end
|
362
|
-
end # def
|
363
|
-
|
364
|
-
|
365
|
-
|
313
|
+
def session?
|
314
|
+
!session.nil?
|
315
|
+
end
|
366
316
|
end # class
|
367
317
|
end # module
|
data/lib/arcade/errors.rb
CHANGED
@@ -22,6 +22,14 @@ module Arcade
|
|
22
22
|
end
|
23
23
|
|
24
24
|
class QueryError < RuntimeError
|
25
|
+
attr_reader :error, :args
|
26
|
+
def initialize error: "", detail: "", exception: "", **args
|
27
|
+
@error = error
|
28
|
+
# @detail = detail
|
29
|
+
@args = args
|
30
|
+
@exception = exception
|
31
|
+
super detail
|
32
|
+
end
|
25
33
|
end
|
26
34
|
|
27
35
|
# used by Dry::Validation, not covered by "error"
|
data/lib/arcade/match.rb
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
module Arcade
|
2
|
+
class Match
|
3
|
+
|
4
|
+
include Arcade::Support::Sql
|
5
|
+
|
6
|
+
=begin
|
7
|
+
This is a very simple wrapper for the match statement
|
8
|
+
|
9
|
+
Initialize: a= Arcade::Match.new type: Arcade::DatabaseType, where: { property: 'value' }, as: :alias
|
10
|
+
Complete: b = a.out( Arcade::EdgeType ).node( while: true, as: item )[ .in.node ... ]
|
11
|
+
Inspect b.to_s
|
12
|
+
Query DB b.execute [.allocate_model]
|
13
|
+
[.analyse_result]
|
14
|
+
|
15
|
+
=end
|
16
|
+
|
17
|
+
def initialize type: , **args
|
18
|
+
|
19
|
+
@args = args
|
20
|
+
@as = []
|
21
|
+
|
22
|
+
@stack = [ "MATCH { type: #{type.database_name}, #{ assigned_parameters } }" ]
|
23
|
+
|
24
|
+
return self
|
25
|
+
end
|
26
|
+
|
27
|
+
# Inspect the generated match statement
|
28
|
+
def to_s &b
|
29
|
+
r = ""
|
30
|
+
r = "DISTINCT " if @distinct
|
31
|
+
r << @as.join(",")
|
32
|
+
r= yield(r) if block_given?
|
33
|
+
@stack.join("") + " RETURN #{r} "
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
# Execute the @stack
|
38
|
+
# generally followed by `select_result` to convert json-hashes to arcade objects
|
39
|
+
#
|
40
|
+
# The optional block modifies the result-statement
|
41
|
+
# i.e
|
42
|
+
# TG::TimeGraph.grid( 2023, 2..9 ).out(HasPosition)
|
43
|
+
# .node( as: :contract )
|
44
|
+
# .execute { "contract.symbol" }
|
45
|
+
# .select_result
|
46
|
+
# gets all associated contracts connected to the month-grid
|
47
|
+
def execute &b
|
48
|
+
Arcade::Init.db.query( to_s( &b ) )
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
# todo : metaprogramming!
|
53
|
+
def out edge=""
|
54
|
+
raise "edge must be a Database-class" unless edge.is_a?(Class) || edge.empty?
|
55
|
+
@stack << ".out(#{edge.is_a?(Class) ? edge.database_name.to_or : ''})"
|
56
|
+
return self
|
57
|
+
end
|
58
|
+
def in edge=""
|
59
|
+
raise "edge must be a Database-class" unless edge.is_a?(Class) || edge.empty?
|
60
|
+
@stack << ".in(#{edge.is_a?(Class) ? edge.database_name.to_or : ''})"
|
61
|
+
return self
|
62
|
+
end
|
63
|
+
def both edge=""
|
64
|
+
raise "edge must be a Database-class" unless edge.is_a?(Class) || edge.empty?
|
65
|
+
@stack << ".both(#{edge.is_a?(Class) ? edge.database_name.to_or : ''})"
|
66
|
+
return self
|
67
|
+
end
|
68
|
+
|
69
|
+
# add conditions on edges to the match statement
|
70
|
+
def inE edge="", **args
|
71
|
+
raise "edge must be a Database-class" unless edge.is_a?(Class) || edge.empty?
|
72
|
+
@stack << ".inE(#{edge.is_a?(Class) ? edge.database_name.to_or : ''}) #{assigned_parameters}.outV()"
|
73
|
+
return self
|
74
|
+
end
|
75
|
+
def outE edge="", **args
|
76
|
+
raise "edge must be a Database-class" unless edge.is_a?(Class) || edge.empty?
|
77
|
+
@stack << ".outE(#{edge.is_a?(Class) ? edge.database_name.to_or : ''}) #{assigned_parameters}.inV()"
|
78
|
+
return self
|
79
|
+
end
|
80
|
+
def bothE edge="", **args
|
81
|
+
raise "edge must be a Database-class" unless edge.is_a?(Class) || edge.empty?
|
82
|
+
@stack << ".bothE(#{edge.is_a?(Class) ? edge.database_name : ''}) #{assigned_parameters}.bothV()"
|
83
|
+
return self
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
# general declation of a node (ie. vertex)
|
88
|
+
def node **args
|
89
|
+
@args = args
|
90
|
+
@stack << if args.empty?
|
91
|
+
"{}"
|
92
|
+
else
|
93
|
+
"{ #{ assigned_parameters } }"
|
94
|
+
end
|
95
|
+
return self
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
|
100
|
+
### ---------------- end of public api ---------------------------------------------------------------- ###
|
101
|
+
private
|
102
|
+
|
103
|
+
|
104
|
+
def assigned_parameters
|
105
|
+
@args.map do | k, v |
|
106
|
+
# unless k == :while # mask ruby keyword
|
107
|
+
send k, v
|
108
|
+
# else
|
109
|
+
# the_while v
|
110
|
+
# end
|
111
|
+
end.compact.join(', ')
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
## Metastatement ---------- last --------------------
|
116
|
+
##
|
117
|
+
## generates a node declaration for fetching the last element of a traversal on the previous edge
|
118
|
+
##
|
119
|
+
## ie: Arcade::Match.new( type: self.class, where: { symbol: symbol } ).out( Arcade::HasContract )
|
120
|
+
# .node( as: :c, where: where )
|
121
|
+
# .in( Arcade::IsOrder )
|
122
|
+
# .node( last: true, as: :o )
|
123
|
+
#
|
124
|
+
# --> ... }.in('is_order'){ while: (in('is_order').size() > 0), where: (in('is_order').size() == 0), as: o }
|
125
|
+
def last arg
|
126
|
+
in_or_out = @stack[-1][1..-1] # use the last statement
|
127
|
+
"while: ( #{in_or_out}.size() > 0 ), where: (#{in_or_out}.size() == 0)"
|
128
|
+
end
|
129
|
+
|
130
|
+
def distinct arg
|
131
|
+
@distinct = true
|
132
|
+
end
|
133
|
+
|
134
|
+
|
135
|
+
def where arg
|
136
|
+
"where: ( #{ generate_sql_list( arg ) } )" unless arg.empty?
|
137
|
+
end
|
138
|
+
|
139
|
+
def while arg
|
140
|
+
if arg.is_a? TrueClass
|
141
|
+
"while: ( true )"
|
142
|
+
else
|
143
|
+
"while: ( #{ generate_sql_list( arg ) } )"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def maxdepth arg
|
148
|
+
"maxDepth: #{arg}"
|
149
|
+
end
|
150
|
+
|
151
|
+
def as arg
|
152
|
+
@as << arg
|
153
|
+
"as: " + arg.to_s
|
154
|
+
end
|
155
|
+
|
156
|
+
def type klassname
|
157
|
+
raise "type must be a Database-class" unless klassname.is_a? Class
|
158
|
+
"type: #{klassname.database_name}"
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
end
|
@@ -110,12 +110,16 @@ module Arcade
|
|
110
110
|
' ( '+ the_argument.compose + ' ) '
|
111
111
|
when Class
|
112
112
|
the_argument.database_name
|
113
|
-
|
113
|
+
when Arcade::Match
|
114
|
+
'(' + the_argument.to_s + ')'
|
115
|
+
when String
|
114
116
|
if the_argument.to_s.rid? # a string with "#ab:cd"
|
115
117
|
the_argument
|
116
|
-
|
118
|
+
else
|
119
|
+
'(' + the_argument + ')'
|
120
|
+
end
|
121
|
+
else # a database-class-name
|
117
122
|
the_argument.to_s
|
118
|
-
end
|
119
123
|
end
|
120
124
|
else
|
121
125
|
raise "cannot complete until a target is specified"
|
@@ -311,27 +315,39 @@ end # class << self
|
|
311
315
|
self
|
312
316
|
end
|
313
317
|
|
314
|
-
# connects by adding {in_or_out}('edgeClass')
|
315
|
-
def connect_with in_or_out, via: nil
|
316
|
-
argument = " #{in_or_out}(#{via.to_or if via.present?})"
|
317
|
-
end
|
318
|
+
# # connects by adding {in_or_out}('edgeClass')
|
319
|
+
# def connect_with in_or_out, via: nil
|
320
|
+
# argument = " #{in_or_out}(#{via.to_or if via.present?})"
|
321
|
+
# end
|
322
|
+
|
318
323
|
# adds a connection
|
319
|
-
# in_or_out: :out
|
320
|
-
# :in
|
324
|
+
# in_or_out: :out ---> out('edgeClass')[where-condition]
|
325
|
+
# :in ---> in('edgeClass')[where-condition]
|
326
|
+
# :inE ---> inE('edgeClass')[where-condition].outV()
|
327
|
+
# :outE ---> outE('edgeClass')[where-condition].inV()
|
328
|
+
#
|
329
|
+
# via: Edge-Class
|
330
|
+
# where: Condition to be applied on the targed vertex (in_or_out = :in, :out, :both)
|
331
|
+
# or on the intermitted edge (in_or_out = :inE, :outE, :bothE)
|
332
|
+
# Condition is inserted as "in_or_out[ condition ]"
|
333
|
+
# Attention: ranges have to be included as array, ie [ 2..4 ]
|
334
|
+
#
|
321
335
|
|
322
336
|
def nodes in_or_out = :out, via: nil, where: nil, expand: false
|
323
337
|
|
324
338
|
condition = where.present? ? "[ #{generate_sql_list(where)} ]" : ""
|
325
339
|
via = resolve_edge_name(via) unless via.nil?
|
326
340
|
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
341
|
+
argument = if in_or_out.to_s[-1] == 'E'
|
342
|
+
case in_or_out.to_s[0..-2]
|
343
|
+
when 'in'
|
344
|
+
"inE(#{via})#{condition}.outV()"
|
345
|
+
when 'out'
|
346
|
+
"outE(#{via})#{condition}.inV()"
|
347
|
+
end
|
348
|
+
else
|
349
|
+
"#{in_or_out.to_s}(#{via})#{condition}"
|
350
|
+
end
|
335
351
|
|
336
352
|
if expand.present?
|
337
353
|
send :expand, argument
|
@@ -349,7 +365,7 @@ end # class << self
|
|
349
365
|
# returns nil if the query was not sucessfully executed
|
350
366
|
def execute(reduce: false, autoload: true )
|
351
367
|
# unless projection.nil? || projection.empty?
|
352
|
-
result = db.
|
368
|
+
result = db.transmit { compose }
|
353
369
|
return nil unless result.is_a?(Array)
|
354
370
|
block_given? ? result.map{|x| yield x } : result
|
355
371
|
# return result.first if reduce && result.size == 1
|