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.
@@ -27,7 +27,8 @@ module Arcade
27
27
  [], # aliases
28
28
  '', # database
29
29
  [], #set,
30
- [] # remove
30
+ [], # remove
31
+ [] # group by
31
32
  args.each{|k,v| send k, v}
32
33
  @fill = block_given? ? yield : 'and'
33
34
  end
@@ -62,12 +63,10 @@ module Arcade
62
63
  @q[:kind]
63
64
  end
64
65
  end
65
- =begin
66
- Output the compiled query
67
- Parameter: destination (rest, batch )
68
- If the query is submitted via the REST-Interface (as get-command), the limit parameter is extracted.
69
- =end
70
-
66
+ # ------------------ compose---------------------------------------
67
+ # Output the compiled query
68
+ # Parameter: destination (rest, batch )
69
+ # If the query is submitted via the REST-Interface (as get-command), the limit parameter is extracted.
71
70
  def compose(destination: :batch)
72
71
  if kind.to_sym == :update
73
72
  return_statement = "return after " + ( @q[:aliases].empty? ? "$current" : @q[:aliases].first.to_s)
@@ -110,21 +109,24 @@ module Arcade
110
109
  ' ( '+ the_argument.compose + ' ) '
111
110
  when Class
112
111
  the_argument.database_name
113
- else
112
+ when Arcade::Match
113
+ '(' + the_argument.to_s + ')'
114
+ when String
114
115
  if the_argument.to_s.rid? # a string with "#ab:cd"
115
116
  the_argument
116
- else # a database-class-name
117
+ else
118
+ '(' + the_argument + ')'
119
+ end
120
+ else # a database-class-name
117
121
  the_argument.to_s
118
- end
119
122
  end
120
123
  else
121
124
  raise "cannot complete until a target is specified"
122
125
  end
123
126
  end
124
127
 
125
- =begin
126
- from can either be a Databaseclass to operate on or a Subquery providing data to query further
127
- =end
128
+ # -------------------- from ---------------------------------------
129
+ # arg can either be a Database class to operate on or a Subquery providing data to query further
128
130
  def from arg = nil
129
131
  if arg.present?
130
132
  @q[:database] = arg
@@ -311,27 +313,36 @@ end # class << self
311
313
  self
312
314
  end
313
315
 
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
316
+
317
+ # ------------------ nodes ---------------------------------------
318
318
  # adds a connection
319
- # in_or_out: :out ---> outE('edgeClass').in[where-condition]
320
- # :in ---> inE('edgeClass').out[where-condition]
319
+ # in_or_out: :out ---> out('edgeClass')[where-condition]
320
+ # :in ---> in('edgeClass')[where-condition]
321
+ # :inE ---> inE('edgeClass')[where-condition].outV()
322
+ # :outE ---> outE('edgeClass')[where-condition].inV()
323
+ #
324
+ # via: Edge-Class
325
+ # where: Condition to be applied on the targed vertex (in_or_out = :in, :out, :both)
326
+ # or on the intermitted edge (in_or_out = :inE, :outE, :bothE)
327
+ # Condition is inserted as "in_or_out[ condition ]"
328
+ # Attention: ranges have to be included as array, ie [ 2..4 ]
329
+ #
321
330
 
322
331
  def nodes in_or_out = :out, via: nil, where: nil, expand: false
323
332
 
324
- condition = where.present? ? "[ #{generate_sql_list(where)} ]" : ""
333
+ condition = where.present? ? "[ #{generate_sql_list(where)} ]" : ""
325
334
  via = resolve_edge_name(via) unless via.nil?
326
335
 
327
- start = if in_or_out.is_a? Symbol
328
- in_or_out.to_s
329
- elsif in_or_out.is_a? String
330
- in_or_out
331
- else
332
- "both"
333
- end
334
- argument = " #{start}(#{via})#{condition} "
336
+ argument = if in_or_out.to_s[-1] == 'E'
337
+ case in_or_out.to_s[0..-2]
338
+ when 'in'
339
+ "inE(#{via})#{condition}.outV()"
340
+ when 'out'
341
+ "outE(#{via})#{condition}.inV()"
342
+ end
343
+ else
344
+ "#{in_or_out.to_s}(#{via})#{condition}"
345
+ end
335
346
 
336
347
  if expand.present?
337
348
  send :expand, argument
@@ -349,7 +360,7 @@ end # class << self
349
360
  # returns nil if the query was not sucessfully executed
350
361
  def execute(reduce: false, autoload: true )
351
362
  # unless projection.nil? || projection.empty?
352
- result = db.execute { compose }
363
+ result = db.transmit { compose }
353
364
  return nil unless result.is_a?(Array)
354
365
  block_given? ? result.map{|x| yield x } : result
355
366
  # return result.first if reduce && result.size == 1
@@ -63,6 +63,7 @@ module Arcade
63
63
  #
64
64
 
65
65
  def select_result condition=nil
66
+ return [] if self.empty?
66
67
  condition = first.keys.first if condition.nil?
67
68
  map{|x| x[condition.to_sym]}.flatten.allocate_model
68
69
  end
@@ -15,42 +15,42 @@ module Arcade
15
15
  end.join(',')
16
16
  end
17
17
 
18
-
19
18
  # used by array#allocate_model
20
19
  def _allocate_model response=nil, auto = Config.autoload
21
20
 
22
-
23
21
  if response.is_a? Hash
24
22
  # save rid to a safe place
25
- temp_rid = response.delete :"@rid"
26
- type = response.delete :"@type"
27
- cat = response.delete :"@cat"
23
+ temp_rid = response.delete( :"@rid" ) || response.delete( :rid ) || "#0:0"
24
+ type = response.delete( :"@type" ) || response.delete( :type ) || nil
25
+ cat = response.delete( :"@cat" ) || response.delete( :cat ) || "d"
28
26
 
29
27
  return response if type.nil?
30
- temp_rid = "#0:0" if temp_rid.nil?
31
- type = "d" if type.nil?
32
28
  # extract type infos and convert to database-name
33
- n, type_name = type.camelcase_and_namespace
34
- n = self.namespace if n.nil?
29
+ namespace, type_name = type.camelcase_and_namespace
30
+ namespace = self.namespace if namespace.nil?
35
31
  # autoconvert rid's in attributes to model-records (exclude edges!)
36
32
  if auto && !(cat.to_s =='e')
37
- response.transform_values! do |x|
33
+ response.transform_values do |x|
38
34
  case x
39
35
  when String
40
- x.rid? ? x.load_rid : x
36
+ x.rid? ? x.load_rid : x # follow links
41
37
  when Array
42
- x.map do| y |
43
- if y.is_a?(Hash) && y.include?(:@type) # if embedded documents are present, load them
44
- y.allocate_model(false)
45
- elsif y.rid? # if links are present, load the object
46
- y.load_rid(false) # do not autoload further records, prevents from recursive locking
47
- else
48
- y
49
- end
38
+ a =[]
39
+ x.map do | y |
40
+ # Thread.new do ## using thread or fiber decreases the performance significantly.
41
+ if y.is_a?(Hash) && y.include?(:@type) # if embedded documents are present, load them
42
+ _allocate_model(y,false)
43
+ elsif y.rid? # if links are present, load the object
44
+ y.load_rid(false) # do not autoload further records, prevents from recursive locking
45
+ else
46
+ y
47
+ end
48
+ # end
50
49
  end
51
50
  when Hash
52
51
  if x.include?(:@type)
53
- x.allocate_model(false)
52
+ #x.allocate_model(false)
53
+ _allocate_model(x,false)
54
54
  else
55
55
  x.transform_values!{|z| z.rid? ? z.load_rid(false) : z }
56
56
  end
@@ -60,7 +60,7 @@ module Arcade
60
60
  end
61
61
  end
62
62
  # choose the appropriate class
63
- klass= Dry::Core::ClassBuilder.new( name: type_name, parent: nil, namespace: n).call
63
+ klass= Dry::Core::ClassBuilder.new( name: type_name, parent: nil, namespace: namespace ).call
64
64
  #
65
65
  begin
66
66
  # create a new object of that class with the appropriate attributes
@@ -74,10 +74,11 @@ module Arcade
74
74
  elsif response.is_a? Array
75
75
  puts "Allocate_model..detected array"
76
76
  ## recursive behavior, just in case
77
- response.map{ | y | _allocate_model y }
77
+ response.map{ | y | _allocate_model y, auto }
78
78
  elsif response.rid?
79
79
  # Autoload rid's
80
80
  response.load_rid
81
+ # auto ? response.load_rid : response
81
82
  else
82
83
  response
83
84
  end
@@ -44,7 +44,7 @@ If »NULL« should be addressed, { key: nil } is translated to "key = NULL" (us
44
44
  attributes.map do |key, value|
45
45
  case value
46
46
  when nil
47
- "#{key}=NULL"
47
+ "#{key} is NULL"
48
48
  when ::Array
49
49
  if value == [nil]
50
50
  "#{key} is NULL"
@@ -11,9 +11,12 @@ module Arcade
11
11
  delimiters = Regexp.union(['-', '_'])
12
12
  n,c= self.split(delimiters).then { |first, *rest| [first.tap {|s| s[0] = s[0].upcase}, rest.map(&:capitalize).join] }
13
13
  ## if base is the first part of the type-name, probably Arcade::Base is choosen a namespace. thats wrong
14
- namespace_present = unless n == 'Base'
14
+ # Arcade::Base descendants are prefetched in Init.models
15
+ # They have to be fetched to load both `Arcade::Ge` and `Arcade::GeSomething`
16
+ # (otherwise: Arcade::Ge and Arcade::Ge::Something)
17
+ namespace_present = unless n == 'Base' || Arcade::Init.models.include?(n)
15
18
  Object.const_get(n) rescue false # Database.namespace
16
- else
19
+ else
17
20
  false
18
21
  end
19
22
  # if no namespace is found, leave it empty and return the capitalized string as class name
@@ -22,6 +25,10 @@ module Arcade
22
25
  [ Database.namespace, self.capitalize ]
23
26
  end
24
27
  end
28
+ def camelcase
29
+ # Split the string into words, capitalize each word, and join them together
30
+ self.split('_').map(&:capitalize).join
31
+ end
25
32
 
26
33
  def snake_case
27
34
  n= if split('::').first == Database.namespace.to_s
@@ -51,7 +58,7 @@ module Arcade
51
58
  end
52
59
  # return a valid rid (format: "nn:mm") or nil
53
60
  def rid
54
- self["#"].nil? ? "#"+ self : self if rid?
61
+ self["#"].nil? ? "#"+ self : self if rid?
55
62
  end
56
63
 
57
64
  def where **args
@@ -80,7 +87,7 @@ module Arcade
80
87
  ## Load the database object if the string is a rid
81
88
  # (Default: No Autoloading of rid-links)
82
89
  def load_rid autocomplete = false
83
- db.get( self ){ autocomplete } if rid? rescue nil
90
+ db.get( self.strip ){ autocomplete } if rid? rescue nil
84
91
  end
85
92
 
86
93
  # updates the record ### retired in favour of Arcade::Base.update
@@ -104,13 +111,3 @@ module Arcade
104
111
  end
105
112
 
106
113
  String.include Arcade::Support::String
107
-
108
- module Types
109
- include Dry.Types()
110
-
111
- # include in attribute definitions
112
- Rid = String.constrained( format: /\A[#]{1}[0-9]{1,}:[0-9]{1,}\z/ )
113
- Blockchain = String.constrained( format: /^(algo|eth|btc)$/ ) # add other blockchain symbols here
114
- Email = String.constrained( format: /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i )
115
-
116
- end
@@ -1,3 +1,3 @@
1
1
  module Arcade
2
- VERSION = "0.4"
2
+ VERSION = "0.5.2"
3
3
  end
data/lib/arcade.rb CHANGED
@@ -9,28 +9,29 @@ require "dry/core/class_builder"
9
9
  require "dry/core/class_attributes"
10
10
  require 'json'
11
11
 
12
- module Types
13
- include Dry.Types()
14
- end
12
+ require_relative '../lib/types'
13
+ #module Types
14
+ # include Dry.Types()
15
+ #end
15
16
  require 'yaml'
16
17
  require 'securerandom'
17
18
  require 'httpx'
18
- require 'arcade/errors'
19
- require_relative '../lib/support/object'
20
- require_relative '../lib/support/string'
21
- require_relative '../lib/support/class'
22
- require_relative '../lib/support/sql'
23
- require_relative '../lib/support/model'
24
- require_relative '../lib/arcade/logging'
25
- require_relative '../lib/config'
26
- require_relative '../lib/support/conversions'
19
+ require_relative '../lib/arcade/support/object'
20
+ require_relative '../lib/arcade/support/string'
21
+ require_relative '../lib/arcade/support/class'
22
+ require_relative '../lib/arcade/support/sql'
23
+ require_relative '../lib/arcade/support/model'
24
+ require_relative '../lib/arcade/support/conversions'
27
25
  require_relative '../lib/arcade/api/primitives'
28
26
  require_relative '../lib/arcade/api/operations'
27
+ require_relative '../lib/arcade/errors'
29
28
  require_relative '../lib/arcade/base'
29
+ require_relative '../lib/arcade/logging'
30
+ require_relative '../lib/arcade/config'
30
31
  require_relative '../lib/arcade/database'
31
- require_relative '../lib/init'
32
+ require_relative '../lib/arcade/match'
33
+ require_relative '../lib/arcade/query'
34
+ require_relative '../lib/arcade/init'
32
35
  require_relative "../lib/models"
33
- require_relative '../lib/query'
34
- require_relative '../lib/match'
35
36
  require_relative '../lib/railtie' if defined? Rails::Railtie
36
37
 
@@ -6,5 +6,27 @@ module Arcade
6
6
  # def accepted_methods
7
7
  #
8
8
  # end
9
+ def self.create **attributes
10
+ Api.create_document db.database, database_name, session_id: db.session, **attributes
11
+ end
12
+
13
+ =begin
14
+ Document.delete fires a "delete" command to the database.
15
+ To remove all records use »all: true« as argument
16
+
17
+ The "where" parameter is optional
18
+ =end
19
+ def self.delete where: {} , **args
20
+ if args[:all] == true
21
+ where = {}
22
+ else
23
+ where.merge!(args) if where.is_a?(Hash)
24
+ return 0 if where.empty?
25
+ end
26
+ # query returns [{count => n }]
27
+ # puts "delete from #{database_name} #{compose_where(where)}"
28
+ db.transmit { "delete from `#{database_name}` #{compose_where(where)}" } &.first[:count] rescue 0
29
+ end
30
+
9
31
  end
10
32
  end
data/lib/model/edge.rb CHANGED
@@ -13,8 +13,37 @@ module Arcade
13
13
  #
14
14
  def self.create from:, to:, **attr
15
15
  db.create_edge database_name, from: from, to: to, **attr
16
+ rescue Arcade::QueryError => e
17
+ if e.message =~ /Duplicated key\s+.+\sfound on index/
18
+ if e.args.keys.first == :exceptionArgs
19
+ # return the previously assigned edge
20
+ e.args[:exceptionArgs].split('|').last.load_rid
21
+ else
22
+ raise
23
+ end
24
+ else
25
+ raise
26
+ end
27
+
16
28
  end
17
29
 
30
+
31
+ ## class-method: Delete Edge between two vertices
32
+ ##
33
+ ## returns the count of deleted edges
34
+ ##
35
+ ## todo: add conditions
36
+ def self.delete from:, to:
37
+ return 0 if from.nil? || to.nil?
38
+ raise "parameter ( from: ) must be a String or a Vertex" unless from.is_a?(String) || from.is_a?( Arcade::Vertex)
39
+ raise "parameter ( to: ) must be a String or a Vertex" unless to.is_a?(String) || to.is_a?( Arcade::Vertex)
40
+ raise "parameters (from: + to:) must respond to `.rid`" unless from.respond_to?( :rid) && to.respond_to?(:rid)
41
+
42
+ db.execute{ "delete edge from #{from.rid} to #{to.rid} " } &.select_result &.first
43
+ end
44
+
45
+
46
+ ## instance method: Delete specified edge
18
47
  def delete
19
48
  db.execute{ "delete edge #{ rid }" }.select_result
20
49
  end
@@ -0,0 +1,41 @@
1
+ module Arcade
2
+ class Revision < Arcade::Vertex
3
+ attribute :protocol?, Types::Nominal::Array
4
+
5
+ @@u='root' # default user
6
+ @@i = 'Record initiated'
7
+
8
+ def self.set_user u
9
+ @@u = u
10
+ end
11
+
12
+ def self.set_initial_message m
13
+ @@i = m
14
+ end
15
+
16
+ def self.insert **attr
17
+
18
+ action = block_given? ? yield : @@i
19
+ i= super **attr
20
+ i.update_list :protocol,
21
+ Arcade::RevisionRecord.create( user: @@u, action: action ),
22
+ modus: :first
23
+ i.refresh
24
+
25
+ end
26
+
27
+
28
+ def update **attr
29
+ hist_state = attr.keys.map do |k|
30
+ [ k, send( k ) ]
31
+ end.to_h
32
+ super **attr
33
+ revision = if block_given?
34
+ Arcade::RevisionRecord.create( user: @@u, action: yield, fields: hist_state )
35
+ else
36
+ Arcade::RevisionRecord.create( fields: hist_state )
37
+ end
38
+ update_list :protocol, revision, modus: :append
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,29 @@
1
+ module Arcade
2
+ class RevisionRecord < Document
3
+ attribute :user, Types::Nominal::String.default( 'root'.freeze )
4
+ attribute :action, Types::Nominal::String.default( "Property changed".freeze )
5
+ attribute :date, Types::Nominal::Date.default( Date.today.freeze )
6
+ attribute :fields?, Types::Nominal::Hash
7
+
8
+ # Revision Records are always embedded.
9
+ # create and insert methods return an new ruby record
10
+ def self.create **attr
11
+ new **( { rid: '#0:0', date: Date.today}.merge attr )
12
+ end
13
+ def self.insert **attr
14
+ new **( { rid: '#0:0', date: Date.today}.merge **attr )
15
+ end
16
+
17
+ =begin
18
+ Its not allowed to delete records.
19
+ =end
20
+ def self.delete where: {} , **args
21
+ raise ArcadeQueryError, "Its not possible to delete revision records"
22
+ end
23
+
24
+ def delete
25
+ raise ArcadeQueryError, "Its not possible to delete revision records"
26
+ end
27
+
28
+ end
29
+ end