arcadedb 0.4 → 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 +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
|