arcadedb 0.4 → 0.5.2
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 +32 -1
- data/Gemfile.lock +2 -36
- data/README.md +191 -41
- data/arcade.yml +1 -1
- data/bin/console +17 -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 +48 -78
- data/lib/arcade/api/primitives.rb +64 -29
- data/lib/arcade/base.rb +105 -51
- data/lib/{config.rb → arcade/config.rb} +5 -6
- data/lib/arcade/database.rb +107 -156
- data/lib/arcade/errors.rb +8 -0
- data/lib/{init.rb → arcade/init.rb} +5 -2
- data/lib/arcade/match.rb +210 -0
- data/lib/{query.rb → arcade/query.rb} +40 -29
- data/lib/{support → arcade/support}/conversions.rb +1 -0
- data/lib/{support → arcade/support}/model.rb +23 -22
- data/lib/{support → arcade/support}/sql.rb +1 -1
- data/lib/{support → arcade/support}/string.rb +11 -14
- data/lib/arcade/version.rb +1 -1
- data/lib/arcade.rb +16 -15
- data/lib/model/document.rb +22 -0
- data/lib/model/edge.rb +29 -0
- data/lib/model/revision.rb +41 -0
- data/lib/model/revision_record.rb +29 -0
- data/lib/model/vertex.rb +118 -48
- data/lib/models.rb +2 -0
- data/lib/types.rb +35 -0
- metadata +22 -16
- data/lib/match.rb +0 -216
- /data/lib/{support → arcade/support}/class.rb +0 -0
- /data/lib/{support → arcade/support}/object.rb +0 -0
data/lib/model/vertex.rb
CHANGED
@@ -13,45 +13,46 @@ module Arcade
|
|
13
13
|
end
|
14
14
|
## ----------------------------------------- Class Methods------------------------------------ ##
|
15
15
|
# #
|
16
|
+
|
16
17
|
=begin
|
17
|
-
|
18
|
+
Creates a Vertex-Instance.
|
19
|
+
Similar to `Vertex#insert`.
|
18
20
|
|
19
|
-
|
21
|
+
Difference is the presence of a `created` property, a timestamp set to the time and date of creation.
|
22
|
+
=end
|
20
23
|
|
21
|
-
|
24
|
+
def self.create timestamp: true, **args
|
25
|
+
#t= timestamp ? ", created = Date(#{DateTime.now.to_i}) " : ""
|
26
|
+
t= timestamp ? ", created = sysdate() " : ""
|
27
|
+
# db.transmit { "create VERTEX #{database_name} set #{args.map{|x,y| [x,y.to_or].join("=")}.join(', ')+t}" } &.first.allocate_model(false)
|
28
|
+
Api.create_document db.database, database_name, session_id: db.session, **args
|
29
|
+
end
|
22
30
|
|
23
|
-
"where" parameter is optional
|
24
31
|
|
25
|
-
|
32
|
+
=begin
|
33
|
+
Vertex.delete fires a "delete vertex" command to the database.
|
34
|
+
To remove all records use »all: true« as argument
|
35
|
+
To remove a specific rid, use rid: "#nn:mmm" as argument
|
36
|
+
|
37
|
+
The "where" parameter is optional
|
26
38
|
|
39
|
+
Example:
|
40
|
+
ExtraNode.delete where: { item: 67 } == ExtraNode.delete item: 67
|
27
41
|
=end
|
28
42
|
def self.delete where: {} , **args
|
29
43
|
if args[:all] == true
|
30
44
|
where = {}
|
31
45
|
elsif args[:rid].present?
|
32
|
-
return db.
|
46
|
+
return db.transmit { "delete from #{args[:rid]}" }.first["count"]
|
33
47
|
else
|
34
48
|
where.merge!(args) if where.is_a?(Hash)
|
35
49
|
return 0 if where.empty?
|
36
50
|
end
|
37
51
|
# query returns [{count => n }]
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
=begin
|
42
|
-
Creates a Vertex-Instance.
|
43
|
-
Similar to `Vertex#insert`.
|
44
|
-
|
45
|
-
Difference is the presence of a `created` property, a timestamp set to the time and date of creation.
|
46
|
-
=end
|
47
|
-
|
48
|
-
def self.create timestamp: true, **args
|
49
|
-
#t= timestamp ? ", created = Date(#{DateTime.now.to_i}) " : ""
|
50
|
-
t= timestamp ? ", created = sysdate() " : ""
|
51
|
-
db.execute { "create VERTEX #{database_name} set #{args.map{|x,y| [x,y.to_or].join("=")}.join(', ')+t}" } &.first.allocate_model(false)
|
52
|
+
# puts "delete from #{database_name} #{compose_where(where)}"
|
53
|
+
db.transmit { "delete from `#{database_name}` #{compose_where(where)}" } &.first[:count] rescue 0
|
52
54
|
end
|
53
55
|
|
54
|
-
|
55
56
|
## get adjacent nodes based on a query on the actual model
|
56
57
|
|
57
58
|
|
@@ -71,22 +72,41 @@ module Arcade
|
|
71
72
|
def expand
|
72
73
|
self
|
73
74
|
end
|
74
|
-
#
|
75
|
-
|
75
|
+
# --------------------------------- Nodes ----------------------------------------------- #
|
76
|
+
# fetches adjacet nodes
|
77
|
+
# supported
|
78
|
+
# nodes in_or_out = :in, :out, :both, :inE, :outE
|
79
|
+
# depth = fetch the n'th node through travese
|
80
|
+
# via: = Arcade Database Type (the ruby class)
|
81
|
+
# where: = a condition
|
82
|
+
# inE, outE --> matches attributes on the edge
|
83
|
+
# in, out, both -> matches attributes on the adjacent vertex
|
84
|
+
# Example Strategie.first nodes where: {size: 10}
|
85
|
+
# "select both()[ size = 10 ] from #113:8 "
|
86
|
+
#
|
76
87
|
def nodes in_or_out=:both, depth= 1, via: nil , execute: true, **args
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
end
|
88
|
+
if depth <= 1
|
89
|
+
s= query.nodes in_or_out, via: via, **args
|
90
|
+
execute ? s.query &.select_result : s
|
91
|
+
else
|
92
|
+
travese in_or_out, depth: depth, start_at: depth-1, via: via, execute: execute, where: args[:where]
|
93
|
+
end
|
84
94
|
end
|
85
95
|
|
86
96
|
# Supports where: { where condition for edges }
|
87
|
-
def edges in_or_out = :both, depth= 1, via: nil , execute: true
|
88
|
-
|
89
|
-
|
97
|
+
def edges in_or_out = :both, depth= 1, via: nil , execute: true
|
98
|
+
|
99
|
+
v = in_or_out.to_s.delete_suffix 'E'
|
100
|
+
e = v + 'E'
|
101
|
+
edge_name = via.nil? ? "" : resolve_edge_name( via )
|
102
|
+
argument = "#{e}(#{edge_name})"
|
103
|
+
q= if depth > 1
|
104
|
+
repeated_argument = Array.new(depth -1 , "#{v}(#{edge_name})").join(".")
|
105
|
+
query.projection repeated_argument + "." + argument
|
106
|
+
else
|
107
|
+
query.projection argument
|
108
|
+
end
|
109
|
+
execute ? q.execute &.allocate_model : q
|
90
110
|
end
|
91
111
|
|
92
112
|
|
@@ -120,17 +140,17 @@ module Arcade
|
|
120
140
|
|
121
141
|
# get via-type-edges through in
|
122
142
|
def inE count=1, via:nil
|
123
|
-
|
143
|
+
edges :in, count, via: via
|
124
144
|
end
|
125
145
|
#
|
126
146
|
# get via-type-edges through out
|
127
147
|
def outE count=1, via:nil
|
128
|
-
|
148
|
+
edges :out, count, via: via
|
129
149
|
end
|
130
150
|
|
131
151
|
# get all via-type-edges
|
132
152
|
def bothE count=1, via:nil
|
133
|
-
|
153
|
+
edges :both, count, via: via
|
134
154
|
end
|
135
155
|
|
136
156
|
|
@@ -158,34 +178,33 @@ module Arcade
|
|
158
178
|
the_query.where where if where.present?
|
159
179
|
the_query.while "$depth < #{depth} " unless depth <=0
|
160
180
|
outer_query = Query.new from: the_query, where: "$depth >= #{start_at}"
|
161
|
-
|
162
|
-
outer_query.execute.allocate_model
|
163
|
-
else
|
164
|
-
# the_query.from self # complete the query by assigning self
|
165
|
-
the_query # returns the Query -traverse object
|
166
|
-
end
|
181
|
+
execute ? outer_query.execute.allocate_model : the_query # return only the traverse part
|
167
182
|
end
|
168
183
|
|
169
184
|
=begin
|
170
185
|
Assigns another Vertex via an EdgeClass. If specified, puts attributes on the edge.
|
171
186
|
|
187
|
+
`Vertex.assign via: Edge to: Vertex`
|
188
|
+
|
172
189
|
Returns the reloaded assigned vertex
|
173
190
|
|
174
191
|
Wrapper for
|
175
|
-
Edge.create
|
192
|
+
Edge.create from: self, to: a_vertex, some: attributes. on: the, edge: type }
|
176
193
|
|
177
194
|
returns the assigned vertex, thus enabling to chain vertices through
|
178
195
|
|
179
|
-
Vertex.assign() via: E ,
|
196
|
+
Vertex.assign() via: E , to: VertexClass.create()).assign( via: E, ... )
|
180
197
|
or
|
181
198
|
(1..100).each{|n| vertex = vertex.assign(via: E2, vertex: V2.create(item: n))}
|
182
199
|
=end
|
183
200
|
|
184
|
-
def assign vertex: , via: , **attributes
|
201
|
+
def assign vertex: nil , via: Arcade::Edge , **attributes
|
202
|
+
vertex = attributes[:to] if attributes.has_key? :to
|
203
|
+
raise "vertex not provided" if vertex.nil?
|
185
204
|
|
186
205
|
via.create from: self, to: vertex, **attributes
|
187
206
|
|
188
|
-
db.get vertex.rid
|
207
|
+
db.get vertex.rid unless vertex.is_a? Array # return the assigned vertex
|
189
208
|
rescue IndexError => e
|
190
209
|
db.logger.error "Edge not created, already present."
|
191
210
|
vertex # return the vertex (for chaining)
|
@@ -194,6 +213,50 @@ or
|
|
194
213
|
nil
|
195
214
|
end
|
196
215
|
|
216
|
+
=begin
|
217
|
+
----------------------------------- Match ------------------------------------------
|
218
|
+
(ClassMethod)
|
219
|
+
|
220
|
+
Creates a Match-Statement based on the vertex type
|
221
|
+
|
222
|
+
```
|
223
|
+
m = Watchlist.match( symbol: 'iBit', as: :ibit )
|
224
|
+
.out( HasUnderlying)
|
225
|
+
.node( as: :u )
|
226
|
+
|
227
|
+
m.to_s
|
228
|
+
=> "MATCH { type: watchlist, where: ( symbol='iBit' ), as: ibit }
|
229
|
+
.out('has_underlying'){ as: u }
|
230
|
+
RETURN ibit,u "
|
231
|
+
```
|
232
|
+
=end
|
233
|
+
def self.match **args
|
234
|
+
as = args.delete( :as) || :a
|
235
|
+
args = args.delete( :where ) if args.key?( :where )
|
236
|
+
Arcade::Match.new type: self, where: args, as: as
|
237
|
+
end
|
238
|
+
|
239
|
+
=begin
|
240
|
+
----------------------------------- Match ------------------------------------------
|
241
|
+
(InstanceMethod)
|
242
|
+
|
243
|
+
Creates a Match-Statement based on the current vertex ( i.e as response to a website-request )
|
244
|
+
|
245
|
+
```
|
246
|
+
params[:rid] => "#49:0"
|
247
|
+
|
248
|
+
m= params[:rid].load_rid.match.out( HasUnterlying ).node( as: :u )
|
249
|
+
m.to_s
|
250
|
+
=> "MATCH { type: strategie, rid: #49:0 }.out('has_underlying'){ as: u } RETURN u "
|
251
|
+
```
|
252
|
+
=end
|
253
|
+
def match as: nil
|
254
|
+
if as.nil?
|
255
|
+
Arcade::Match.new vertex: self
|
256
|
+
else
|
257
|
+
Arcade::Match.new vertex: self, as: as
|
258
|
+
end
|
259
|
+
end
|
197
260
|
|
198
261
|
def remove
|
199
262
|
db.execute{ "delete vertex #{rid}" }
|
@@ -205,12 +268,12 @@ Format: < Classname: Edges, Attributes >
|
|
205
268
|
=end
|
206
269
|
def to_human
|
207
270
|
|
208
|
-
in_and_out =
|
271
|
+
in_and_out = coe.then{| i,o| "{#{i}->}{->#{o}}, "}
|
209
272
|
|
210
273
|
#Default presentation of Arcade::Base::Model-Objects
|
211
274
|
|
212
275
|
"<#{self.class.to_s.snake_case}[#{rid}]:" +
|
213
|
-
in_and_out
|
276
|
+
in_and_out +
|
214
277
|
invariant_attributes.map do |attr, value|
|
215
278
|
v= case value
|
216
279
|
when Class
|
@@ -225,6 +288,13 @@ Format: < Classname: Edges, Attributes >
|
|
225
288
|
end
|
226
289
|
|
227
290
|
|
291
|
+
# Count Of Edges (coe)
|
292
|
+
# returns an Array: [ count-of-in-edges, count-of-out-edges ]
|
293
|
+
def coe
|
294
|
+
db.query( "select in().size() as ie, out().size() as oe from #{rid}" ) &.first.values
|
295
|
+
end
|
296
|
+
|
297
|
+
|
228
298
|
def refresh
|
229
299
|
# force reloading of edges and nodes
|
230
300
|
# edges are not cached (now)
|
data/lib/models.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require_relative "../lib/model/document.rb"
|
2
|
+
require_relative "../lib/model/revision_record.rb"
|
2
3
|
require_relative "../lib/model/vertex.rb"
|
3
4
|
require_relative "../lib/model/edge.rb"
|
5
|
+
require_relative "../lib/model/revision.rb"
|
4
6
|
require_relative "../lib/model/basicvertex.rb"
|
5
7
|
require_relative "../lib/model/basicdocument.rb"
|
6
8
|
require_relative "../lib/model/basicedge.rb"
|
data/lib/types.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
module Types
|
2
|
+
include Dry.Types()
|
3
|
+
|
4
|
+
# include in attribute definitions
|
5
|
+
Rid = String.constrained( format: /\A[#]{1}[0-9]{1,}:[0-9]{1,}\z/ )
|
6
|
+
Blockchain = String.constrained( format: /^(algo|eth|btc)$/ ) # add other blockchain symbols here
|
7
|
+
Email = String.constrained( format: /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i )
|
8
|
+
|
9
|
+
|
10
|
+
# Define a type for your DateTime with timezone. Crucially, use
|
11
|
+
# the `.constructor` to handle the string parsing and potential errors. (source: Gemini)
|
12
|
+
Types::DateTime = Types::Any.constructor do |value|
|
13
|
+
begin
|
14
|
+
case value
|
15
|
+
when String
|
16
|
+
::DateTime.parse(value)
|
17
|
+
when Integer
|
18
|
+
::Time.at(value).to_datetime
|
19
|
+
else
|
20
|
+
::DateTime.now
|
21
|
+
end
|
22
|
+
rescue ArgumentError
|
23
|
+
# Handle invalid date strings. Options:
|
24
|
+
# 1. Raise an exception (recommended for API responses)
|
25
|
+
raise Dry::Types::CoercionError.new(
|
26
|
+
value, DateTime, "Invalid DateTime format: #{value}"
|
27
|
+
)
|
28
|
+
# 2. Return nil (less strict, but might lead to unexpected behavior)
|
29
|
+
# nil
|
30
|
+
# 3. Use a default value (be careful with this, as it might mask errors)
|
31
|
+
# DateTime.now
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: arcadedb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 0.5.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Hartmut Bischoff
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-04-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -144,31 +144,37 @@ files:
|
|
144
144
|
- examples/relation_1__1.rb
|
145
145
|
- examples/relation_1__n.rb
|
146
146
|
- examples/relation_n_n.rb
|
147
|
+
- iruby/.ipynb_checkpoints/01-start-and-end-of-datarows-checkpoint.ipynb
|
148
|
+
- iruby/01-start-and-end-of-datarows.ipynb
|
149
|
+
- iruby/db-console.rb
|
147
150
|
- lib/arcade.rb
|
148
151
|
- lib/arcade/api/operations.rb
|
149
152
|
- lib/arcade/api/primitives.rb
|
150
153
|
- lib/arcade/base.rb
|
154
|
+
- lib/arcade/config.rb
|
151
155
|
- lib/arcade/database.rb
|
152
156
|
- lib/arcade/errors.rb
|
157
|
+
- lib/arcade/init.rb
|
153
158
|
- lib/arcade/logging.rb
|
159
|
+
- lib/arcade/match.rb
|
160
|
+
- lib/arcade/query.rb
|
161
|
+
- lib/arcade/support/class.rb
|
162
|
+
- lib/arcade/support/conversions.rb
|
163
|
+
- lib/arcade/support/model.rb
|
164
|
+
- lib/arcade/support/object.rb
|
165
|
+
- lib/arcade/support/sql.rb
|
166
|
+
- lib/arcade/support/string.rb
|
154
167
|
- lib/arcade/version.rb
|
155
|
-
- lib/config.rb
|
156
|
-
- lib/init.rb
|
157
|
-
- lib/match.rb
|
158
168
|
- lib/model/basicdocument.rb
|
159
169
|
- lib/model/basicedge.rb
|
160
170
|
- lib/model/basicvertex.rb
|
161
171
|
- lib/model/document.rb
|
162
172
|
- lib/model/edge.rb
|
173
|
+
- lib/model/revision.rb
|
174
|
+
- lib/model/revision_record.rb
|
163
175
|
- lib/model/vertex.rb
|
164
176
|
- lib/models.rb
|
165
|
-
- lib/
|
166
|
-
- lib/support/class.rb
|
167
|
-
- lib/support/conversions.rb
|
168
|
-
- lib/support/model.rb
|
169
|
-
- lib/support/object.rb
|
170
|
-
- lib/support/sql.rb
|
171
|
-
- lib/support/string.rb
|
177
|
+
- lib/types.rb
|
172
178
|
- rails.md
|
173
179
|
- rails/arcade.rb
|
174
180
|
- rails/config.yml
|
@@ -177,7 +183,7 @@ homepage: https://github.com/topofocus/arcadedb
|
|
177
183
|
licenses:
|
178
184
|
- MIT
|
179
185
|
metadata: {}
|
180
|
-
post_install_message:
|
186
|
+
post_install_message:
|
181
187
|
rdoc_options: []
|
182
188
|
require_paths:
|
183
189
|
- lib
|
@@ -192,8 +198,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
192
198
|
- !ruby/object:Gem::Version
|
193
199
|
version: '0'
|
194
200
|
requirements: []
|
195
|
-
rubygems_version: 3.
|
196
|
-
signing_key:
|
201
|
+
rubygems_version: 3.5.23
|
202
|
+
signing_key:
|
197
203
|
specification_version: 4
|
198
204
|
summary: Ruby Interface to ArcadeDB
|
199
205
|
test_files: []
|
data/lib/match.rb
DELETED
@@ -1,216 +0,0 @@
|
|
1
|
-
#require 'active_support/inflector'
|
2
|
-
|
3
|
-
module Arcade
|
4
|
-
######################## MatchConnection ###############################
|
5
|
-
|
6
|
-
MatchAttributes = Struct.new(:edge, :direction, :as, :count, :where, :while, :max_depth , :depth_alias, :path_alias, :optional )
|
7
|
-
|
8
|
-
# where and while can be composed incremental
|
9
|
-
# direction, as, connect and edge cannot be changed after initialisation
|
10
|
-
class MatchConnection
|
11
|
-
include Support::Sql
|
12
|
-
|
13
|
-
def initialize edge= nil, direction: :both, as: nil, count: 1, **args
|
14
|
-
|
15
|
-
the_edge = edge.is_a?( Class ) ? edge.ref_name : edge.to_s unless edge.nil? || edge == E
|
16
|
-
@q = MatchAttributes.new the_edge , # class
|
17
|
-
direction, # may be :both, :in, :out
|
18
|
-
as, # a string
|
19
|
-
count, # a number
|
20
|
-
args[:where],
|
21
|
-
args[:while],
|
22
|
-
args[:max_depth],
|
23
|
-
args[:depth_alias], # not implemented
|
24
|
-
args[:path_alias], # not implemented
|
25
|
-
args[:optional] # not implemented
|
26
|
-
end
|
27
|
-
|
28
|
-
def direction= dir
|
29
|
-
@q[:direction] = dir
|
30
|
-
end
|
31
|
-
|
32
|
-
|
33
|
-
def direction
|
34
|
-
fillup = @q[:edge].present? ? @q[:edge] : ''
|
35
|
-
case @q[:direction]
|
36
|
-
when :both
|
37
|
-
".both(#{fillup})"
|
38
|
-
when :in
|
39
|
-
".in(#{fillup})"
|
40
|
-
when :out
|
41
|
-
".out(#{fillup})"
|
42
|
-
when :both_vertex, :bothV
|
43
|
-
".bothV()"
|
44
|
-
when :out_vertex, :outV
|
45
|
-
".outV()"
|
46
|
-
when :in_vertex, :inV
|
47
|
-
".inV()"
|
48
|
-
when :both_edge, :bothE
|
49
|
-
".bothE(#{fillup})"
|
50
|
-
when :out_edge, :outE
|
51
|
-
".outE(#{fillup})"
|
52
|
-
when :in_edge, :outE
|
53
|
-
".inE(#{fillup})"
|
54
|
-
end
|
55
|
-
|
56
|
-
end
|
57
|
-
|
58
|
-
def count c=nil
|
59
|
-
if c
|
60
|
-
@q[:count] = c
|
61
|
-
else
|
62
|
-
@q[:count]
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
def max_depth d=nil
|
67
|
-
if d.nil?
|
68
|
-
@q[:max_depth].present? ? "maxDepth: #{@q[:max_depth] }" : nil
|
69
|
-
else
|
70
|
-
@q[:max_depth] = d
|
71
|
-
end
|
72
|
-
end
|
73
|
-
def edge
|
74
|
-
@q[:edge]
|
75
|
-
end
|
76
|
-
|
77
|
-
def compose
|
78
|
-
where_statement =( where.nil? || where.size <5 ) ? nil : "where: ( #{ generate_sql_list( @q[:where] ) })"
|
79
|
-
while_statement =( while_s.nil? || while_s.size <5) ? nil : "while: ( #{ generate_sql_list( @q[:while] )})"
|
80
|
-
|
81
|
-
ministatement = "{"+ [ as, where_statement, while_statement, max_depth].compact.join(', ') + "}"
|
82
|
-
ministatement = "" if ministatement=="{}"
|
83
|
-
|
84
|
-
(1 .. count).map{|x| direction }.join("") + ministatement
|
85
|
-
|
86
|
-
end
|
87
|
-
alias :to_s :compose
|
88
|
-
|
89
|
-
end # class
|
90
|
-
|
91
|
-
|
92
|
-
######################## MatchStatement ################################
|
93
|
-
|
94
|
-
MatchSAttributes = Struct.new(:match_class, :as, :where )
|
95
|
-
class MatchStatement
|
96
|
-
include Support
|
97
|
-
def initialize match_class, as: 0, **args
|
98
|
-
reduce_class = ->(c){ c.is_a?(Class) ? c.ref_name : c.to_s }
|
99
|
-
|
100
|
-
@q = MatchSAttributes.new( reduce_class[match_class], # class
|
101
|
-
as.respond_to?(:zero?) && as.zero? ? reduce_class[match_class].pluralize : as ,
|
102
|
-
args[ :where ])
|
103
|
-
|
104
|
-
@query_stack = [ self ]
|
105
|
-
end
|
106
|
-
|
107
|
-
def match_alias
|
108
|
-
"as: #{@q[:as]}"
|
109
|
-
end
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
# used for the first compose-statement of a compose-query
|
114
|
-
def compose_simple
|
115
|
-
where_statement = where.is_a?(String) && where.size <3 ? nil : "where: ( #{ generate_sql_list( @q[:where] ) })"
|
116
|
-
'{'+ [ "class: #{@q[:match_class]}", as , where_statement].compact.join(', ') + '}'
|
117
|
-
end
|
118
|
-
|
119
|
-
|
120
|
-
def << connection
|
121
|
-
@query_stack << connection
|
122
|
-
self # return MatchStatement
|
123
|
-
end
|
124
|
-
#
|
125
|
-
def compile &b
|
126
|
-
"match " + @query_stack.map( &:to_s ).join + return_statement( &b )
|
127
|
-
end
|
128
|
-
|
129
|
-
|
130
|
-
# executes the standard-case.
|
131
|
-
# returns
|
132
|
-
# * as: :hash : an array of hashes
|
133
|
-
# * as: :array : an array of hash-values
|
134
|
-
# * as :flatten: a simple array of hash-values
|
135
|
-
#
|
136
|
-
# The optional block is used to customize the output.
|
137
|
-
# All previously defiend »as«-Statements are provided though the control variable.
|
138
|
-
#
|
139
|
-
# Background
|
140
|
-
# A match query "Match {class aaa, as: 'aa'} return aa "
|
141
|
-
#
|
142
|
-
# returns [ aa: { result of the query, a Vertex or a value-item }, aa: {}...}, ...] ]
|
143
|
-
# (The standard case)
|
144
|
-
#
|
145
|
-
# A match query "Match {class aaa, as: 'aa'} return aa.name "
|
146
|
-
# returns [ aa.name: { name }, aa.name: { name }., ...] ]
|
147
|
-
#
|
148
|
-
# Now, execute( as: :flatten){ "aa.name" } returns
|
149
|
-
# [name1, name2 ,. ...]
|
150
|
-
#
|
151
|
-
#
|
152
|
-
# Return statements (examples from https://orientdb.org/docs/3.0.x/sql/SQL-Match.html)
|
153
|
-
# "person.name as name, friendship.since as since, friend.name as friend"
|
154
|
-
#
|
155
|
-
# " person.name + \" is a friend of \" + friend.name as friends"
|
156
|
-
#
|
157
|
-
# "$matches"
|
158
|
-
# "$elements"
|
159
|
-
# "$paths"
|
160
|
-
# "$pathElements"
|
161
|
-
#
|
162
|
-
#
|
163
|
-
def execute as: :hash, &b
|
164
|
-
r = V.db.execute{ compile &b }
|
165
|
-
case as
|
166
|
-
when :hash
|
167
|
-
r
|
168
|
-
when :array
|
169
|
-
r.map{|y| y.values}
|
170
|
-
when :flatten
|
171
|
-
r.map{|y| y.values}.orient_flatten
|
172
|
-
else
|
173
|
-
raise ArgumentError, "Specify parameter «as:» with :hash, :array, :flatten"
|
174
|
-
end
|
175
|
-
end
|
176
|
-
# def compose
|
177
|
-
#
|
178
|
-
# '{'+ [ "class: #{@q[:match_class]}",
|
179
|
-
# "as: #{@as}" , where, while_s,
|
180
|
-
# @maxdepth >0 ? "maxdepth: #{maxdepth}": nil ].compact.join(', ')+'}'
|
181
|
-
# end
|
182
|
-
|
183
|
-
alias :to_s :compose_simple
|
184
|
-
|
185
|
-
|
186
|
-
## return_statement
|
187
|
-
#
|
188
|
-
# summarizes defined as-statements ready to be included as last parameter
|
189
|
-
# in the match-statement-stack
|
190
|
-
#
|
191
|
-
# They can be modified through a block.
|
192
|
-
#
|
193
|
-
# i.e
|
194
|
-
#
|
195
|
-
# t= TestQuery.match( where: {a: 9, b: 's'}, as: nil ) << E.connect("<-", as: :test)
|
196
|
-
# t.return_statement{|y| "#{y.last}.name"}
|
197
|
-
#
|
198
|
-
# =>> " return test.name"
|
199
|
-
#
|
200
|
-
#return_statement is always called through compile
|
201
|
-
#
|
202
|
-
# t.compile{|y| "#{y.last}.name"}
|
203
|
-
|
204
|
-
private
|
205
|
-
def return_statement
|
206
|
-
resolve_as = ->{ @query_stack.map{|s| s.as.split(':').last unless s.as.nil? }.compact }
|
207
|
-
" return " + statement = if block_given?
|
208
|
-
a= yield resolve_as[]
|
209
|
-
a.is_a?(Array) ? a.join(', ') : a
|
210
|
-
else
|
211
|
-
resolve_as[].join(', ')
|
212
|
-
end
|
213
|
-
end
|
214
|
-
end # class
|
215
|
-
|
216
|
-
end # module
|
File without changes
|
File without changes
|