Class: ActiveOrient::OrientDB

Inherits:
Object
  • Object
show all
Includes:
OrientSupport::Support
Defined in:
lib/rest.rb

Overview

OrientDB performs queries to a OrientDB-Database

The communication is based on the ActiveOrient-API.

The OrientDB-Server is specified in config/connect.yml

A Sample:

:orientdb:
  :server: localhost
    :port: 2480
  :database: working-database
  :admin:
    :user: admin-user 
    :pass: admin-password

Instance Method Summary collapse

Methods included from OrientSupport::Support

#compose_where, #generate_sql_list

Constructor Details

#initialize(database: nil, connect: true) ⇒ OrientDB

Returns a new instance of OrientDB.



49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/rest.rb', line 49

def initialize database: nil,  connect: true
  self.default_server = { :server => 'localhost', :port => 2480, :protocol => 'http', 
      :user => 'root', :password => 'root', :database => 'temp' }.merge default_server
  @res = get_ressource
  @database = database.presence || default_server[:database]
  self.logger = Logger.new('/dev/stdout') unless logger.present?
#    @database=@database#.camelcase
  connect() if connect
  # save existing classes 
  @classes = []
  ActiveOrient::Model.orientdb =  self
end

Instance Method Details

#call_function(*args) ⇒ Object

Execute a predefined Function



906
907
908
909
910
911
# File 'lib/rest.rb', line 906

def call_function    *args
#puts "uri:#{function_uri { args.join('/') } }"
  @res[ function_uri { args.join('/') } ].post ''
rescue RestClient::InternalServerError => e
  puts  JSON.parse(e.http_body)
end

#change_database(name) ⇒ Object

changes the working-database to name



146
147
148
149
150
# File 'lib/rest.rb', line 146

def change_database name 
  @classes = []
  @database =  name

end

#class_hierachie(base_class: '', requery: false) ⇒ Object

returns the class_hierachie

to fetch all Vertices

class_hiearchie( base_class: 'V').flatten

to fetch all Edges

class_hierachie( base_class: 'E').flatten

Notice: base_class has to be noted as String! There is no implicit conversion from Symbol or Class



238
239
240
241
242
243
244
245
246
247
248
249
# File 'lib/rest.rb', line 238

def class_hierachie base_class: '', requery: false
  @all_classes =  get_classes( 'name', 'superClass') if requery || @all_classes.blank?
  def fv s   # :nodoc:
   @all_classes.find_all{ |x| x[ 'superClass' ]== s }.map{| v| v[ 'name' ]} #.camelize } 
  end

  def fx v # :nodoc:
  fv(v).map{ |x| ar =  fx( x ) ; ar.empty? ? x :  [ x , ar  ] }
  end
  
  fx base_class
end

#class_name(name_or_class) ⇒ Object

Converts a given name to the camelized database-classname

Converts a given class-constant to the corresponding database-classname

returns a valid database-class name, nil if the class not exists



975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
# File 'lib/rest.rb', line 975

def class_name  name_or_class
  name= if name_or_class.is_a? Class
  name_or_class.to_s.split('::').last
  elsif name_or_class.is_a? ActiveOrient::Model
  name_or_class.classname
  else
  name_or_class.to_s.camelize
  end
 if database_classes.include?(name) 
   name
 elsif database_classes.include?(name.underscore) 
   name.underscore
 else
   logger.progname =  'OrientDB#ClassName'
   logger.info{ "Classname #{name} not present in active  Database" }
   nil
 end
end

#connectObject



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/rest.rb', line 75

def connect 
  i = 0
  begin
    logger.progname = 'OrientDB#Connect'
    r= @res["/connect/#{ @database }" ].get
    if r.code == 204 
  logger.info{ "Connected to database #{@database} " }
  true 
  else
  logger.error{ "Connection to database #{@database}  could NOT be established" }
nil 
  end
  rescue RestClient::Unauthorized => e
    if i.zero?
  logger.info{ "Database #{@database} NOT present --> creating" }
  i=i+1
  create_database
  retry
    else
  Kernel.exit
    end
  end

end

#count_documents(**args) ⇒ Object



736
737
738
739
740
741
742
743
744
745
# File 'lib/rest.rb', line 736

def count_documents **args
  logger.progname = 'OrientDB#count_documents'

  query =  OrientSupport::OrientQuery.new args
  query.projection << 'COUNT (*)'
  result = get_documents raw: true, query: query 

  result.first['COUNT']

end

#create_class(newclass) ⇒ Object Also known as: open_class

creates a class and returns the a ActiveOrient::Model:Newclass-Class- (Constant) which is designed to take any documents stored in this class

Predefined attributes: version, cluster, record

Other attributes are assigned dynamically upon reading documents

The classname is Camelized by default, eg: classnames are always Capitalized, underscores (‘_’) indicate that the following letter is capitalized, too.

Other class-names can be used if a “$” is placed at the begining of the name-string. However, most of the model-based methods will not work in this case.



342
343
344
# File 'lib/rest.rb', line 342

def create_class   newclass
  create_classes( [ newclass ] ).first
end

#create_classes(classes) ⇒ Object

Creates classes and class-hierachies in OrientDB and in Ruby.

Takes an Array or a Hash as argument and returns an Array of successfull allocated Ruby-Classes

If the argument is an array, Basic-Classes are build.

Otherwise key/value pairs are assumend to follow this terminology

{ SuperClass => [ class, class, ...], SuperClass => [] , ... }


282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
# File 'lib/rest.rb', line 282

def create_classes classes
  # rebuild cashed classes-array
  # first the classes-string is camelized (this is used to allocate the ruby-class)
  # Then the database is queried for this string or the underscored string-variant 
  # the name-building process is independend from the method »class_name«
  database_classes requery: true
  consts = Array.new
  execute  transaction: false do 
    class_cmd = ->(s,n) do
 n = n.to_s.camelize
 consts << ActiveOrient::Model.orientdb_class( name: n)
 unless database_classes.include?(n) || database_classes.include?(n.underscore)
 { type: "cmd", language: 'sql', command:  "create class #{n} extends #{s}"  } 

 end 
    end  ## class_cmd
    
    if classes.is_a?(Array)
 classes.map do | n |
 n = n.to_s.camelize # capitalize
   consts << ActiveOrient::Model.orientdb_class( name: n)
   unless database_classes.include?( n ) || database_classes.include?(n.underscore)
    { type: "cmd", language: 'sql', command:  "create class #{n} " } 
   end
 end
    elsif classes.is_a?(Hash)
 classes.keys.map do | superclass | 
   items =  Array.new
   superClass =  superclass.to_s.camelize
   items <<  { type: "cmd", language: 'sql', command:  "create class #{superClass} abstract" }  unless  database_classes.flatten.include?( superClass ) || database_classes.flatten.include?( superClass.underscore ) 
   items << if classes[superclass].is_a?( String ) || classes[superclass].is_a?( Symbol )
    class_cmd[superClass, classes[superclass] ]
   elsif  classes[superclass].is_a?( Array )  
    classes[superclass].map{|n| class_cmd[superClass, n] }
   end 
   #puts items.flatten.map{|x| x[:command]}
   items  # returnvalue
 end.flatten 
    end.compact # erase nil-entries, in case the class is already allocated
  end
  # refresh cached class-informations
  database_classes requery: true
  # returns an array of allocated Constants/Classes
   consts
end

#create_database(type: 'plocal', database: @database) ⇒ Object

Creates a database with the given name and switches to this database as working-database Types are either ‘plocal’ or ‘memory’

returns the name of the working-database



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/rest.rb', line 123

def create_database type: 'plocal' , database: @database 
  logger.progname = 'OrientDB#CreateDatabase'
    old_d = @database
    @classes = []
    @database = database
    begin
      response = @res[database_uri{ type }].post  ""
    if response.code == 200
 logger.info{ "Database #{@database} successfully created and stored as working database"}
    else
 @database = old_d
 logger.error{ "Database #{name} was NOT created. Working Database is still #{@database} "}
    end
    rescue RestClient::InternalServerError => e
 @database = old_d
 logger.error{ "Database #{name} was NOT created. Working Database is still #{@database} "}
    end
    @database

end

#create_document(o_class, attributes: {}) ⇒ Object

Creates an Object in the Database and returns this as ActuveOrient::Model-Instance



653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
# File 'lib/rest.rb', line 653

def create_document o_class, attributes: {}
  attributes = yield if attributes.empty? && block_given?
  post_argument = { '@class' => class_name(o_class) }.merge(attributes).to_orient

  begin
    logger.progname = 'OrientDB#CreateDocument'
    response = @res[ document_uri ].post post_argument.to_json
    data= JSON.parse( response.body )
    ActiveOrient::Model.orientdb_class( name: data['@class']).new data
  rescue RestClient::InternalServerError => e
    response = JSON.parse( e.response)['errors'].pop
    logger.error { response['content'].split(':')[1..-1].join(':')  }
    logger.error { "No Object allocated" }
    nil # return_value
  end
end

#create_edge_class(name, superclass: 'E') ⇒ Object



352
353
354
# File 'lib/rest.rb', line 352

def create_edge_class name , superclass: 'E'
  create_classes( { superclass => name } ).first
end

#create_index(o_class, name:, on: :automatic, type: :unique) ⇒ Object



482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
# File 'lib/rest.rb', line 482

def create_index o_class  , name:, on: :automatic, type: :unique 

  execute  transaction: false do 
  c =  class_name o_class
  command = if on == :automatic
  "create index #{c}.#{name} #{type.to_s.upcase}"
elsif on.is_a? Array
  "create index #{name} on #{class_name(o_class)}( #{on.join(', ')}) #{type.to_s.upcase}"
else 
  nil
end
  [  { type: "cmd", language: 'sql', command:  command  }  ]
  end

end

#create_or_update_document(o_class, **args, &b) ⇒ Object

Creating a new Database-Entry ( where is omitted )

otherwise updating the Database-Entry (if present)

The optional Block should provide a hash with attributes(properties). These are used if a new dataset is created.



783
784
785
786
787
788
789
790
# File 'lib/rest.rb', line 783

def create_or_update_document o_class , **args, &b
  logger.progname =  'Rest#CreateOrUpdateDocument'
  r= update_or_create_documents o_class, **args, &b
  if r.size > 1
  logger.error { "multible documents updated by #{ generate_sql_list( where )}" }
  end
  r.first  # return_value
end

#create_properties(o_class, all_properties, &b) ⇒ Object

creates properties and optional an associated index as defined in the provided block

create_properties( classname or class, properties as hash ) { index }

The default-case

create_properties( :my_high_sophisticated_database_class,

con_id: { type: :integer }, details: { type: :link, linked_class: ‘Contracts’ } ) do contract_idx: :notunique end

A composite index

create_properties( :my_high_sophisticated_database_class,

con_id: { type: :integer }, symbol: { type: :string } ) do { name: ‘indexname’, on: [ :con_id , :details ] # default: all specified properties type: :notunique # default: :unique } end



547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
# File 'lib/rest.rb', line 547

def create_properties o_class, all_properties, &b

  all_properties_in_a_hash  =  HashWithIndifferentAccess.new
  all_properties.each{| field, args |  all_properties_in_a_hash.merge! translate_property_hash( field, args ) }

  begin
  count = if all_properties_in_a_hash.is_a?( Hash )
    response = @res[ property_uri(class_name(o_class)) ].post all_properties_in_a_hash.to_json
    if response.code == 201
 response.body.to_i
    else
 0
    end
  end
  rescue RestClient::InternalServerError => e
  response = JSON.parse( e.response)['errors'].pop
  error_message  = response['content'].split(':').last
# if error_message ~= /Missing linked class/
  logger.progname= 'OrientDB#CreatePropertes'
  logger.error { "Properties in #{class_name(o_class)} were NOT created" }
  logger.error { "Error-code #{response['code']} --> #{response['content'].split(':').last }" }
  nil
  end
  ### index
  if block_given?   && count == all_properties_in_a_hash.size
  index =  yield
  if index.is_a?( Hash ) 
    if index.size == 1
 create_index o_class, name: index.keys.first, on: all_properties_in_a_hash.keys, type: index.values.first
    else
 index_hash =  HashWithIndifferentAccess.new( type: :unique, on: all_properties_in_a_hash.keys ).merge index
 create_index o_class, index_hash # i [:name], on: index_hash[:on], type: index_hash[:type]
    end
  end
  end
  count  # return_value
end

#create_property(o_class, field, index: nil, **args) ⇒ Object

create a single property on class-level.

supported types: orientdb.com/docs/last/SQL-Create-Property.html

if index is to be specified, it’s defined in the optional block

create_property(class, field){ :unique | :notunique }                     --> creates an automatic-Index  on the given field
create_property( class, field){ { 


462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
# File 'lib/rest.rb', line 462

def create_property o_class, field, index: nil,  **args 
  logger.progname= 'OrientDB#CreateProperty'
  c= create_properties o_class,   {field =>  args} # translate_property_hash( field, args ) 
  if index.nil? && block_given?
    index = yield
  end
  if c==1 && index.present? 
    if index.is_a?( String ) || index.is_a?( Symbol ) 
 create_index o_class, name: field, type: index
    elsif index.is_a? Hash
 bez= index.keys.first
#     puts "index: #{index.inspect}"
#     puts "bez: #{bez} ---> #{index.keys.inspect}"
 create_index o_class, name: bez, type: index[bez], on: [ field ]
    end
  end

end

#create_vertex_class(name, superclass: 'V') ⇒ Object



348
349
350
# File 'lib/rest.rb', line 348

def create_vertex_class name , superclass: 'V'
  create_classes( { superclass => name } ).first
end

#database_classes(include_system_classes: false, requery: false) ⇒ Object Also known as: inspect_classes

returns an array with all names of the classes of the database caches the result.

parameter: include_system_classes: false|true, requery: false|true



256
257
258
259
260
261
262
263
264
265
# File 'lib/rest.rb', line 256

def database_classes include_system_classes: false, requery: false
  requery =  true if @classes.empty?
  if requery
  class_hierachie  requery: true
  system_classes = ["OFunction", "OIdentity", "ORIDs", "ORestricted", "ORole", "OSchedule", "OTriggered", "OUser", "_studio"]
  all_classes = get_classes( 'name' ).map( &:values).flatten
  @classes = include_system_classes ? all_classes : all_classes - system_classes 
  end
  @classes 
end

#delete_class(o_class) ⇒ Object

deletes the specified class and returns true on success

todo: remove all instances of the class



425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
# File 'lib/rest.rb', line 425

def delete_class o_class
  cl= class_name(o_class)
  logger.progname = 'OrientDB#DeleteClass'
  
  if database_classes.include? cl
  begin
  response = @res[class_uri{ cl } ].delete
  if response.code == 204
    # return_value: sussess of the removal
    !database_classes( requery: true ).include?(cl)
    # don't delete the ruby-class
    #     ActiveOrient::Model.send :remove_const, cl.to_sym if o_class.is_a?(Class)
  end
  rescue RestClient::InternalServerError => e
  if database_classes( requery: true).include?( cl )
    logger.error{ "Class #{cl} still present" }
    logger.error{ e.inspect }
    false
  else
    true
  end
  end
  else
  logger.info { "Class #{cl} not present. "}
  end

end

#delete_database(database:) ⇒ Object

deletes the database and returns true on success

after the removal of the database, the working-database might be empty



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/rest.rb', line 156

def delete_database database:
  @classes = []
  logger.progname = 'OrientDB#DropDatabase'
   old_ds =  @database
   change_database database
   begin
   response = @res[database_uri].delete
   if database == old_ds
change_database ""
logger.info{ "Working database deleted" }
   else
change_database old_ds
logger.info{ "Database #{database} deleted, working database is still #{@database} "}
   end
   rescue RestClient::InternalServerError => e
   logger.info{ "Database #{database} NOT deleted" }
   change_database old_ds
   end
  !response.nil?  && response.code == 204 ?  true : false

end

#delete_document(record_id) ⇒ Object



671
672
673
674
675
676
677
678
679
680
681
# File 'lib/rest.rb', line 671

def delete_document record_id
      logger.progname = 'OrientDB#DeleteDocument'
   begin
   response = @res[document_uri{ record_id } ].delete
   true if response.code == 204
   rescue RestClient::InternalServerError => e
   logger.error{ "Document #{ record_id }  NOT deleted" }
   false
   end

end

#delete_documents(o_class, where: {}) ⇒ Object

Deletes documents. They are defined by a query. All records which match the attributes are deleted. An Array with freed index-values is returned



826
827
828
829
830
831
832
833
834
835
836
# File 'lib/rest.rb', line 826

def delete_documents o_class, where: {}
   get_documents( from: o_class, where: where).map do |doc|
   if doc['@type']=='d'  # document
index = doc['@rid'][1,doc['@rid'].size] # omit the first character ('#')
r=@res[ document_uri{ index  }].delete
   index if  r.code==204 && r.body.empty? # return_value
   end

   end
  
end

#delete_edge(*rid) ⇒ Object

Deletes a single edge when providing a single rid-link (#00:00) Deletes multible edges when providing a list of rid-links Todo: implement delete_edges after querying the database in one statement



398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
# File 'lib/rest.rb', line 398

def delete_edge *rid
  rid =  rid.map do |mm|
  if mm.is_a?(String)
    if mm.rid?
 mm
    elsif mm.is_a?(Rest::Model)
 mm.rid
    else
 nil
    end
  end
  end.compact
  response=  execute transaction: false do 
  [ { type: "cmd", language: 'sql', command:  CGI.escapeHTML("delete edge #{rid.join(',') }")} ]
   end
   if response.is_a?( Array ) && response.size == 1
   response.pop # RETURN_VALUE
   else
   response
   end
end

#delete_property(o_class, field) ⇒ Object



585
586
587
588
589
590
591
592
593
594
595
# File 'lib/rest.rb', line 585

def delete_property o_class, field
      logger.progname = 'OrientDB#DeleteProperty'
   begin
   response = @res[property_uri( class_name(o_class)){ field } ].delete
   true if response.code == 204
   rescue RestClient::InternalServerError => e
   logger.error{ "Property #{ field } in  class #{ class_name(o_class) } NOT deleted" }
   false
   end

end

#execute(classname = 'Myquery', transaction: true) ⇒ Object

Executes a list of commands and returns the result-array (if present)

structure of the provided block: [{ type: “cmd”,

  language: "sql",
  command: "create class Person extends V"
  }, 
  (...)
]

It  was first used by ActiveOrient::Query.execute_queries
Later I discovered that some Queries are not interpretated correctly by #GetDocuments
but are  without Error via batch-processing.
For Instance,  this valid query 
> 'select expand( first_list[5].second_list[9] ) from base where label = 9 ' <
can only be  via batch


933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
# File 'lib/rest.rb', line 933

def execute classname = 'Myquery', transaction: true  
  #puts "classname##execute:: #{classname}"
  batch =  { transaction: transaction, operations: yield }
  unless batch[:operations].blank?
#       puts "post: #{batch.to_json}"
  response = @res[ batch_uri ].post batch.to_json 
# puts "response: #{JSON.parse(response.body)['result'].inspect}"
  if response.code == 200
    if response.body['result'].present?
 result= JSON.parse(response.body)['result']
 result.map do |x| 
   if x.is_a? Hash 
    if x.has_key?('@class')
     ActiveOrient::Model.orientdb_class( name: x['@class']).new x
    elsif x.has_key?( 'value' )
x['value']
    else
#     puts "ActiveOrient::Execute"
#     puts "o_class: #{o_class.inspect}"
ActiveOrient::Model.orientdb_class( name: classname).new x
    end
   end
 end.compact # return_value

    else
 response.body
    end
  else
    nil
  end
  end
rescue RestClient::InternalServerError => e
  raise

end

#fv(s) ⇒ Object

:nodoc:



240
241
242
# File 'lib/rest.rb', line 240

def fv s   # :nodoc:
   @all_classes.find_all{ |x| x[ 'superClass' ]== s }.map{| v| v[ 'name' ]} #.camelize } 
end

#fx(v) ⇒ Object

:nodoc:



244
245
246
# File 'lib/rest.rb', line 244

def fx v # :nodoc:
  fv(v).map{ |x| ar =  fx( x ) ; ar.empty? ? x :  [ x , ar  ] }
end

#get_class_properties(o_class) ⇒ Object

:nodoc:



597
598
599
# File 'lib/rest.rb', line 597

def get_class_properties o_class   #  :nodoc:
  JSON.parse( @res[ class_uri{ class_name(o_class) } ].get )
end

#get_classes(*attributes) ⇒ Object

returns an Array with (unmodified) Class-attribute-hash-Elements

get_classes 'name', 'superClass'
returns
  [ {"name"=>"E", "superClass"=>""}, 
  {"name"=>"OFunction", "superClass"=>""}, 
  {"name"=>"ORole", "superClass"=>"OIdentity"}
  (...)
]


200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/rest.rb', line 200

def get_classes *attributes
  i = 0
  begin
  #puts "database_uri #{database_uri}"
  response =   @res[database_uri].get
  if response.code == 200
    classes=  JSON.parse( response.body )['classes' ]
    unless attributes.empty?
 classes.map{|y| y.select{| v,_| attributes.include?(v) } }
    else
 classes
    end

    #.map{ |y| y.name }
  else
    []
  end
  rescue JSON::ParserError
  if i.zero?
    i = i + 1
    retry
  else
    raise
  end
  end

end

#get_databasesObject

returns an Array with available Database-Names as Elements



113
114
115
# File 'lib/rest.rb', line 113

def get_databases
   JSON.parse( @res["/listDatabases"].get.body)['databases']
end

#get_document(rid) ⇒ Object

Retrieves a Document from the Database as ActiveOrient::Model::class The argument can either be a rid (#[x}:y) or a link(x:y) If no Document is found, nil is returned

In the optional block, a subset of properties can be defined (as array of names)



844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
# File 'lib/rest.rb', line 844

def get_document rid

  rid = rid[1 .. rid.length] if rid[0]=='#'

  response = @res[ document_uri { rid } ].get 

  raw_data = JSON.parse( response.body) #.merge( "#no_links" => "#no_links" )
  ActiveOrient::Model.orientdb_class( name: raw_data['@class']).new raw_data
  
rescue RestClient::InternalServerError => e
  if e.http_body.split(':').last =~ /was not found|does not exist in database/
    nil
  else
    logger.progname 'OrientDB#GetDocument'
    logger.error "something went wrong"
    logger.error e.http_body.inspect
    raise
  end
end

#get_documents(raw: false, query: nil, **args) ⇒ Object

retrieves documents from a query

If raw is specified, the JSON-Array is returned, eg

{ "@type"=>"d", 
  "@rid"=>"#15:1", 
  "@version"=>1, 
  "@class"=>"DocumebntKlasse10", 
  "con_id"=>343, 
  "symbol"=>"EWTZ"
}

otherwise a ActiveModel-Instance of o_class is created and returned



695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
# File 'lib/rest.rb', line 695

def get_documents   raw: false, query: nil, **args 
      query =  OrientSupport::OrientQuery.new(  args ) if query.nil?
      i=0
      begin
        logger.progname = 'OrientDB#GetDocuments'
  url =    query_sql_uri + query.compose( destination: :rest) + "/#{query.get_limit}" 
  response =  @res[URI.encode(url) ].get
  r=JSON.parse( response.body )['result'].map do |document |
    # parameter: raw is set --> don't initilize a model object
    if raw 
      document 
      # query returns an anonymus class: Use the provided Block or the Dummy-Model MyQuery
    elsif document['@class'].blank?  
      block_given? ? yield.new( document ) : ActiveOrient::Model::MyQuery.new( document )
    else 
      ActiveOrient::Model.orientdb_class( name: document['@class']).new document
    end
  end
      rescue RestClient::InternalServerError => e
  response = JSON.parse( e.response)['errors'].pop
  logger.error { response['content'].split(':').last  }
  i=i+1
  if i > 1
    raise
  else
    query.dataset_name = query.database_class.underscore
    logger.info { "trying to query using #{o_class}" }
    retry
  end
      rescue URI::InvalidURIError => e
  logger.error "Invalid URI detected"
  logger.error query.to_s
  logger.info "trying batch processing "
  sql_cmd = -> (command) { { type: "cmd", language: "sql", command: command } }
  response= execute { [ sql_cmd[ query.to_s ] ] }
  logger.info "success: to avoid this delay use ActiveOrient::Model#query_database insteed"
        response
      end

end

#get_ressourceObject

called in the beginning or after a 404-Error



69
70
71
72
73
# File 'lib/rest.rb', line 69

def get_ressource
   = [ default_server[:user].to_s , default_server[:password].to_s ] 
  server_adress = [ default_server[:protocol] ,"://" , default_server[ :server ],  ":" , default_server[ :port ]].map(&:to_s).join('')
  RestClient::Resource.new( server_adress, * )
end

#loggerObject

borrowed from active_support



27
# File 'lib/rest.rb', line 27

mattr_accessor :logger

#nexus_edge(o_class, attributes: {}, from:, to:, unique: false) ⇒ Object

nexus_edge connects two documents/vertexes The parameter o_class can be either a class or a string



361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
# File 'lib/rest.rb', line 361

def nexus_edge o_class , attributes: {}, from:,  to:, unique: false
  logger.progname = "ActiveOrient::OrientDB#NexusEdge"
  if from.is_a? Array
  from.map{|f| nexus_edge o_class, attributes: attributes, from: f, to: to, unique: unique }
  elsif to.is_a? Array
  to.map{|t| nexus_edge o_class, attributes: attributes, from: from, to: t, unique: unique }
  else

  if unique
    wwhere = { out: from.to_orient,  in: to.to_orient }.merge(attributes.to_orient) 
    existing_edge = get_documents( from: o_class, where: wwhere )
    if  existing_edge.first.is_a?( ActiveOrient::Model )
 logger.debug { "reusing  edge #{class_name(o_class)} from #{from.to_orient} to #{to.to_orient} " }
 return existing_edge.first  
    end
  end
  logger.debug { "creating edge #{class_name(o_class)} from #{from.to_orient} to #{to.to_orient} " }
  response=  execute( o_class, transaction: false) do 
    #[ { type: "cmd", language: 'sql', command:  CGI.escapeHTML("create edge #{class_name(o_class)} from #{translate_to_rid[m]} to #{to.to_orient}; ")} ]
    attr_string =  attributes.blank? ? "" : "set #{ generate_sql_list attributes.to_orient }"
    [ { type: "cmd", language: 'sql', 
   command:  "create edge #{class_name(o_class)} from #{from.to_orient} to #{to.to_orient} #{attr_string}"} ]
  end
  if response.is_a?(Array) && response.size == 1
    response.pop # RETURN_VALUE
  else
    response
  end
  end
end

#patch_document(rid) ⇒ Object

Lazy Updating of the given Document.



866
867
868
869
870
871
872
873
874
875
876
877
878
# File 'lib/rest.rb', line 866

def patch_document rid
  logger.progname =  'Rest#PatchDocument'
  content = yield
  if content.is_a? Hash
  content.each do | key, value |
# puts "content: #{key}, #{value.class}"
# content[key]= value.to_orient #if value.is_a? ActiveOrient::Model
  end
  @res[ document_uri { rid } ].patch content.to_orient.to_json
  else
  logger.error { "FAILED: The Block must provide an Hash  with properties to be updated"}
  end
end

#preallocate_class_properties(o_class) ⇒ Object

If properties are allocated on class-level, they can be preinitialized using this method. This is disabled for now, because it does not seem nessesary



630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
# File 'lib/rest.rb', line 630

def preallocate_class_properties o_class
  p= get_class_properties( o_class )['properties']
  unless p.nil? || p.blank?
      predefined_attributes = p.map do | property |
  [ property['name'] ,
  case property['type']
  when 'LINKMAP'
    Array.new
  when 'STRING'
    ''
  else
    nil
  end  ]
      end.to_h
  else
    {}
  end
end


601
602
603
604
605
606
607
608
# File 'lib/rest.rb', line 601

def print_class_properties o_class
  puts "Detected Properties for class #{class_name(o_class)}"

  rp = get_class_properties o_class 
  n=  rp['name']
  puts rp['properties'].map{|x| [ n+'.'+x['name'], x['type'],x['linkedClass'] ].compact.join(' -> ' )}.join("\n")

end

#ressourceObject

included for development , should be removed in production release



63
64
65
# File 'lib/rest.rb', line 63

def ressource
  @res
end

#update_documents(o_class, set:, where: {}) ⇒ Object



891
892
893
894
# File 'lib/rest.rb', line 891

def update_documents o_class, set: , where: {}
  url = "update #{class_name(o_class)}  set "<< generate_sql_list(set) << compose_where(where)
  response = @res[ URI.encode( command_sql_uri << url) ].post '' #url.to_json 
end

#update_or_create_documents(o_class, set: {}, where: {}, **args, &b) ⇒ Object

Based on the query specified in :where records are updated according to :set

Returns an Array of updated documents

The optional Block should provide a hash with attributes(properties). These are used if a new dataset is created.

### das ist noch nicht rund. #



802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
# File 'lib/rest.rb', line 802

def update_or_create_documents o_class , set: {}, where: {} , **args , &b
  logger.progname =  'Rest#UpdateOrCreateDocuments'
  if where.blank?
  [ create_document( o_class, attributes: set ) ]
  else
  set.extract!( where.keys ) # removes any keys from where in set
  possible_documents = get_documents from: class_name( o_class ), where: where,  **args
  if possible_documents.empty?
    if block_given?
 more_where =   yield   # do Preparations prior to the creation of the dataset
            # if the block returns a Hash , it is merged into the insert_query.
 where.merge! more_where if more_where.is_a?(Hash)
    end
    [ create_document( o_class, attributes: set.merge(where) )  ]
  else 
 possible_documents.map{| doc | doc.update( set: set ) }
  end
  end
end