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.
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
- Vertex.delete fires a "delete vertex" command to the database.
18
+ Creates a Vertex-Instance.
19
+ Similar to `Vertex#insert`.
18
20
 
19
- To remove all records use »all: true« as argument
21
+ Difference is the presence of a `created` property, a timestamp set to the time and date of creation.
22
+ =end
20
23
 
21
- To remove a specific rid, use rid: "#nn:mmm" as argument
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
- ExtraNode.delete where: { item: 67 } == ExtraNode.delete item: 67
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.execute { "delete vertex #{args[:rid]}" }.first["count"]
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
- db.execute { "delete vertex #{database_name} #{compose_where(where)}" } &.first[:count] rescue 0
39
- end
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
- # Supports where: --> Strategie.first nodes where: {size: 10}
75
- # "select both()[ size = 10 ] from #113:8 "
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
- s = Query.new from: rid
78
- s.nodes in_or_out, via: via, **args
79
- if execute
80
- s.query &.select_result
81
- else
82
- s # just return the query
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, **args
88
- in_or_out = in_or_out.to_s + "E"
89
- nodes in_or_out, depth, via: via , execute: execute, **args
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
- nodes :inE, count, via: via
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
- nodes :outE, count, via: via
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
- nodes :bothE, count, via: via
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
- if execute
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 in: self, out: a_vertex, some: attributes. on: the, edge: type }
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 , vertex: VertexClass.create()).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 # return the assigned vertex
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 = -> { "{#{self.in.count}->}{->#{self.out.count }}, " }
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: '0.4'
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: 2023-10-25 00:00:00.000000000 Z
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/query.rb
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.4.6
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