Module: ModelClass

Includes:
OrientSupport::Support
Included in:
ActiveOrient::Model
Defined in:
lib/model/the_class.rb

Instance Method Summary collapse

Methods included from OrientSupport::Support

#as, #compose_where, #generate_sql_list, #while_s

Instance Method Details



640
641
642
643
644
645
# File 'lib/model/the_class.rb', line 640

def add_edge_link name:, direction: :out, edge:
  dir =  direction.to_s == "out" ? :out : :in
  define_method(name.to_sym) do
    return self["#{dir}_#{edge.classname}"].map{|x| x["in"]}
  end
end

#allObject

get all the elements of the class



395
396
397
# File 'lib/model/the_class.rb', line 395

def all
query.execute
end

#alter_property(property, attribute: "DEFAULT", alteration:) ⇒ Object



651
652
653
# File 'lib/model/the_class.rb', line 651

def alter_property property, attribute: "DEFAULT", alteration:  # :nodoc:
  orientdb.alter_property self, property: property, attribute: attribute, alteration: alteration
end

#classnameObject

GET ###############



379
380
381
# File 'lib/model/the_class.rb', line 379

def classname  # :nodoc: #
   ref_name
end

#count(**args) ⇒ Object

Examples

TestClass.count where: 'last_access is NULL'  # only records where 'last_access' is not set
TestClass.count                               # all records


417
418
419
# File 'lib/model/the_class.rb', line 417

def count **args
query( **( { projection:  'COUNT(*)' }.merge args )).execute(reduce: true){|x|  x[:"COUNT(*)"]}
end

#create(**attributes) ⇒ Object

Universal method to create a new record. It’s overloaded to create specific kinds, eg. edge and vertex and is called only for abstract classes

Example:

V.create_class :test
Test.create string_attribute: 'a string', symbol_attribute: :a_symbol, array_attribute: [34,45,67]
Test.create link_attribute: Test.create( :a_new_attribute => 'new' )


174
175
176
177
178
179
180
181
182
183
184
# File 'lib/model/the_class.rb', line 174

def create **attributes
   attributes.merge :created_at => DateTime.new
	result = db.create_record self, attributes: attributes
	if result.nil
		logger.error('Model::Class'){ "Table #{refname}:  create failed:  #{attributes.inspect}" }
	elsif block_given?
		yield result
	else
		result  # return value
	end
end

#create_class(*c) ⇒ Object

creates an inherent class



158
159
160
# File 'lib/model/the_class.rb', line 158

def create_class *c
	orientdb.create_class( *c ){ self }
end

#create_index(name, **attributes) ⇒ Object

Pagination.create_property :col2, type: :integer Pagination.create_property :col3, type: :string Pagination.create_property :col4, type: :integer Pagination.create_index :composite, :on => [:col1, :col2, :col3], type: ‘dictionary’



345
346
347
# File 'lib/model/the_class.rb', line 345

def create_index name, **attributes
  orientdb.create_index self, name: name, **attributes
end

#create_properties(argument_hash, &b) ⇒ Object

Create more Properties in the Schema of the Class



320
321
322
# File 'lib/model/the_class.rb', line 320

def create_properties argument_hash, &b
  orientdb.create_properties self, argument_hash, &b
end

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

Create a Property in the Schema of the Class and optionally create an automatic index

Examples:

create_property  :customer_id, type: :integer, index: :unique
create_property(  :name, type: :string ) {  :unique  }
create_property(  :name, type: :string ) { name: 'some_index', on: :automatic, type: :unique  }
create_property  :in,  type: :link, linked_class: V    (used by edges)

:call-seq: create_property(field (required), type: :a_supported_type’, linked_class: nil

supported types: :bool :double :datetime = :date :float :decimal

:embedded_list = :list :embedded_map = :map :embedded_set = :set

:int :integer :link_list :link_map :link_set

If ‘:list`, `:map`, `:set`, `:link`, `:link_list`, `:link_map` or `:link_set` is specified a `linked_class:` parameter can be specified. Argument is the OrientDB-Class-Constant

Raises:

  • (ArgumentError)


255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
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
# File 'lib/model/the_class.rb', line 255

def create_property field, type: :integer, index: nil,  **args
	arguments =  args.values.map do |y| 
		if y.is_a?(Class)  && ActiveOrient.database_classes.values.include?(y) 
			y.ref_name 
		elsif  ActiveOrient.database_classes.keys.include?(y.to_s) 
			y 
		else
			puts ActiveOrient.database_classes.inspect
			puts "YY : #{y.to_s} #{y.class}"
			raise ArgumentError , "database class #{y.to_s} not allocated"
		end
	end.compact.join(',')

	supported_types = {
		:bool          => "BOOLEAN",
		:double        => "BYTE",
		:datetime      => "DATE",
		:date			     => "DATE",
		:float         => "FLOAT",
		:decimal       => "DECIMAL",
		:embedded_list => "EMBEDDEDLIST",
		:list          => "EMBEDDEDLIST",
		:embedded_map  => "EMBEDDEDMAP",
		:map           => "EMBEDDEDMAP",
		:embedded_set  => "EMBEDDEDSET",
		:set           => "EMBEDDEDSET",
		:string        => "STRING",
		:int           => "INTEGER",
		:integer       => "INTEGER",
		:link          => "LINK",
		:link_list     => "LINKLIST",
		:link_map      => "LINKMAP",
		:link_set      => "LINKSET",
	}

	## if the »type« argument is a string, it is used unchanged
	type =  supported_types[type] if type.is_a?(Symbol)

	raise ArgumentError , "unsupported type" if type.nil?
s= " CREATE PROPERTY #{ref_name}.#{field} #{type} #{arguments}" 
puts s
db.execute {  s }

i =  block_given? ? yield : index
## supported format of block:  index: { name: 'something' , on: :automatic, type: :unique } 
## or                                 { name: 'something' , on: :automatic, type: :unique }  # 
## or                                 {                                some_name: :unique }  # manual index
## or                                 {                                           :unique }  # automatic index
if i.is_a? Hash  
	att=  i.key( :index ) ?   i.values.first : i
	name, on, type = if  att.size == 1  && att[:type].nil? 
										 [att.keys.first,  field,  att.values.first ]
									 else  
										 [ att[:name] || field , att[:on] || field , att[:type] || :unique ]
									 end
	create_index( name , on: on, type: type)
elsif i.is_a?(Symbol)  || i.is_a?(String)
	create_index field, type: i
end

# orientdb.create_property self, field, **keyword_arguments, &b
end

#delete_property(field) ⇒ Object

Delete a property from the class



584
585
586
# File 'lib/model/the_class.rb', line 584

def delete_property field
  orientdb.delete_property self, field
end

#delete_record(*rid) ⇒ Object Also known as: delete_document

Delete record(s) specified by their rid’s



590
591
592
# File 'lib/model/the_class.rb', line 590

def delete_record *rid
  db.delete_record rid
end

#delete_records(where: {}, **args) ⇒ Object Also known as: delete

Query the database and delete the records of the resultset

Returns the count of datasets effected



598
599
600
601
602
603
604
605
606
# File 'lib/model/the_class.rb', line 598

def delete_records where: {} , **args
	if args[:all] == true 
		where = {}
	else
		where.merge!(args) if where.is_a?(Hash)
		return 0 if where.empty?
	end
   orientdb.delete_records( self, where: where   ).count
end

#first(**args) ⇒ Object

get the first element of the class



401
402
403
# File 'lib/model/the_class.rb', line 401

def first **args
   query( **( { order: "@rid" , limit: 1 }.merge args)).execute(reduce: true)
end

#get(rid) ⇒ Object

get elements by rid



385
386
387
388
389
390
391
# File 'lib/model/the_class.rb', line 385

def get rid
  if @excluded.blank?
    db.get_record(rid)
  else
    db.execute{ "select expand( @this.exclude( #{@excluded.map(&:to_or).join(",")})) from #{rid} "} 
  end
end

#get_records(**args) ⇒ Object Also known as: get_documents

»GetRecords« uses the REST-Interface to query the database. The alternative »QueryDatabase« submits the query via Execute.

Both methods rely on OrientSupport::OrientQuery and its capacity to support complex query-builds. The method requires a hash of arguments. The following keys are supported:

projection:

SQL-Queries use »select« to specify a projection (ie. ‘select sum(a), b+5 as z from class where …`)

In ruby »select« is a method of enumeration. To specify what to »select« from in the query-string we use »projection«, which accepts different arguments

projection: a_string --> inserts the sting as it appears
projection: an OrientSupport::OrientQuery-Object --> performs a sub-query and uses the result for further querying though the given parameters.
projection: [a, b, c] --> "a, b, c" (inserts a comma-separated list)
projection: {a: b, "sum(x)" => f} --> "a as b, sum(x) as f" (renames properties and uses functions)

distinct:

Constructs a query like »select distinct(property) [as property] from …«

distinct: :property -->  the result is mapped to the property »distinct«.
distinct: [:property] --> the result replaces the property
distinct: {property: :some_name} -->  the result is mapped to ModelInstance.some_name

order:

Sorts the result-set. If new properties were introduced via select:, distinct: etc. Sorting takes place on these properties

 order: :property {property: asc, property: desc}[property, property, ..  ](orderdirection is 'asc')

Further supported Parameter:

group_by
skip
limit
unwind

see orientdb- documentation (https://orientdb.com/docs/last/SQL-Query.html)

query:

Instead of providing the parameter to »get_records«, a OrientSupport::OrientQuery can build and tested prior to the method-call. The OrientQuery-Object is then provided with the query-parameter. I.e.

q = OrientSupport::OrientQuery.new
ORD.create_class :test_model
   q.from TestModel
   q.where {name: 'Thomas'}
   count = TestModel.count query: q
   q.limit 10
   0.step(count,10) do |x|
      q.skip = x
      puts TestModel.get_documents(query: q).map{|x| x.adress }.join('\t')
   end
  prints a Table with 10 columns.


496
497
498
# File 'lib/model/the_class.rb', line 496

def get_records **args
  db.get_records(from: self, **args){self}
end

#indexesObject

list all Indexes



350
351
352
# File 'lib/model/the_class.rb', line 350

def indexes
	properties[:indexes]
end

#last(**args) ⇒ Object

get the last element of the class



408
409
410
# File 'lib/model/the_class.rb', line 408

def last **args
   query( **( { order: {"@rid" => 'desc'} , limit: 1 }.merge args)).execute(reduce: true)
end

setter method to initialise a dummy ActiveOrient::Model class to enable multi-level access to links and linklists



75
76
77
78
79
80
81
82
83
84
85
# File 'lib/model/the_class.rb', line 75

def link_list *property
	property.each do |p|
		
		the_dummy_class = orientdb.allocate_class_in_ruby("dummy_"+p.to_s)
		the_dummy_class.ref_name =  ref_name + "." +  p.to_s
		singleton_class.send :define_method, p do
			the_dummy_class
		end
	end

end

#migrate_property(property, to:, linked_class: nil, via: 'tzr983') ⇒ Object



355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
# File 'lib/model/the_class.rb', line 355

def migrate_property property, to: , linked_class: nil, via: 'tzr983'
	if linked_class.nil?
		create_property  via, type: to
	else
		create_property  via, type: to, linked_class: linked_class
	end
#			my_count = query.kind(:update!).set( "#{via} = #{property} ").execute(reduce: true){|c| c[:count]}
#			logger.info{" migrate property: #{count} records prosessed"}
	  all.each{ |r| r.update set:{ via => r[property.to_sym] }}
		nullify =  query.kind(:update!).set( property: nil ).execute(reduce: true){|c| c[:count]}
#		  raise "migrate property: count of erased items( #{nullify} differs from total count (#{my_count}) " if nullify != my_count
		db.execute{" alter property #{ref_name}.#{via} name '#{property}' "}
		logger.info{ "successfully migrated #{property} to #{:to} " }








end

#namespace_prefixObject

Set the namespace_prefix for database-classes.

If a namespace is set by

ActiveOrient::Init.define_namespace { ModuleName }

ActiveOrient translates this to

ModuleName::CamelizedClassName

The database-class becomes

modulename_class_name

If the namespace is set to a class (Object, ActiveOrient::Model ) namespace_prefix returns an empty string.

Override to change its behavior



51
52
53
# File 'lib/model/the_class.rb', line 51

def namespace_prefix 
  namespace.is_a?(Class )? '' : namespace.to_s.downcase+'_' 
end

#naming_convention(name = nil) ⇒ Object

NamingConvention provides a translation from database-names to class-names.

It can be overwritten to provide different conventions for different classes, eg. Vertexes or edges and to introduce distinct naming-conventions in different namespaces

To overwrite use

 class Model # < ActiveOrient::Model[:: ...]
   def self.naming_convention
   ( conversion code )
   end
end


25
26
27
28
29
30
31
32
33
34
# File 'lib/model/the_class.rb', line 25

def naming_convention name=nil  
  nc =  name.present?? name.to_s : ref_name
   if namespace_prefix.present?
  	 nc.split(namespace_prefix).last.camelize
   else
     nc.camelize
   end
	rescue
nil
end

#orientdb_class(name:, superclass: nil) ⇒ Object

orientdb_class is used to refer a ActiveOrient:Model-Object providing its name

Parameter: name: string or symbol


60
61
62
63
64
65
66
67
# File 'lib/model/the_class.rb', line 60

def orientdb_class name:, superclass: nil  # :nodoc:    # public method: autoload_class

  ActiveOrient.database_classes[name.to_s].presence || ActiveOrient::Model
rescue NoMethodError => e
  logger.error { "Error in orientdb_class: is ActiveOrient.database_classes initialized ? \n\n\n" }
  logger.error{ e.backtrace.map {|l| "  #{l}\n"}.join  }
  Kernel.exit
end

Print the properties of the class



431
432
433
# File 'lib/model/the_class.rb', line 431

def print_properties
  orientdb.print_class_properties self
end

#propertiesObject Also known as: get_class_properties

Get the properties of the class



423
424
425
426
# File 'lib/model/the_class.rb', line 423

def properties
  object = orientdb.get_class_properties self
  {:properties => object['properties'], :indexes => object['indexes']}
end

#query(**args) ⇒ Object

returns a OrientSupport::OrientQuery



188
189
190
# File 'lib/model/the_class.rb', line 188

def query **args
	OrientSupport::OrientQuery.new( **( {from: self}.merge args))
end

#query_database(query, set_from: true) ⇒ Object

QueryDatabase sends the Query directly to the database.

The query returns a hash if a result set is expected

select  {something} as {result} (...)

leads to

[ { :{result}  =>  {result of query} } ]

It can be modified further by passing a block, ie.

	q =  OrientSupport::OrientQuery.new( from: :base )

.projection( ‘first_list.second_list as second_list’ ) .where( label: 9 )

q.to_s  => 'select first_list[5].second_list[9] as second_list from base where label = 9 '

second_list = Base.query_database( q ){|x| x}.first

The query returns (a list of) documents of type ActiveOrient::Model if a document is queried i.e.

q = OrientSupport::OrientQuery.new from: :base q.projection ‘expand( first_list.second_list)’ #note: no ‘as’ statement result2 = Base.query_database( q ).first => #<SecondList:0x000000000284e840 @metadata={}, @d=nil, @attributes=“@class”=>“second_list”>

query_database is used on model-level and submits

select (...) from class

#query performs queries on the instance-level and submits

select (...) from #{a}:{b}


564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
# File 'lib/model/the_class.rb', line 564

def query_database query, set_from: true
  # note: the parameter is not used anymore
query.from self if query.is_a?(OrientSupport::OrientQuery) && query.from.nil?
  result = db.execute{  query.to_s  }
result = if block_given?
					 result.is_a?(Array) ? result.map{|x| yield(x) } : yield(result)
				 else
					 result
				 end
  if result.is_a? Array  
    OrientSupport::Array.new work_on: self, work_with: result
  else
    result
  end  # return value
end

#require_model_file(dir = nil) ⇒ Object

requires the file specified in the model-dir

In fact, the model-files are loaded instead of required. Thus, even after recreation of a class (Class.delete_class, ORD.create_class classname) custom methods declared in the model files are present.

If a class is destroyed (i.e. the database class is deleted), the ruby-class and its methods vanish, too.

The directory specified is expanded by the namespace. The parameter itself is the base-dir.

Example:

Namespace:  HC
model_dir : 'lib/model'
searched directory: 'lib/model/hc'

ActiveOrient::Model.modeldir is aimed to be set to the application dir. It may be a String, Pathname or an array of strings or pathnames.

The parameter ‘dir` is used internally and by gems to ensure that basic methods are loaded first.



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/model/the_class.rb', line 111

def require_model_file  dir = nil
logger.progname = 'ModelClass#RequireModelFile'
# model-dir can either be a string or an array of string or pathnames
default =  [ActiveOrient::Model.model_dir].flatten
# access the default dir's first
the_directories = case dir
									when String, Pathname
									  default.present? ? 	[dir] + default : [dir]
									when Array
										default.present? ? dir + default  : dir
									else
										default.present? ? default : []
									end.uniq.compact
the_directories.uniq.map do |raw_directory|
	the_directory = Pathname( raw_directory )
	if File.exists?( the_directory )
		model= self.to_s.underscore + ".rb"
		filename =   the_directory +  model
		if  File.exists?(filename )
			if load filename
				logger.debug{ "#{filename} sucessfully loaded"  }
				self #return_value
			else
				logger.error{ "#{filename} load error" }
				nil #return_value
			end
		else
			logger.debug{ "model-file not present: #{filename}  --> skipping" }
			nil #return_value
		end
	else
		logger.error{ "Directory #{ the_directory  } not present " }
		nil  #return_value
	end
end.compact.present?  # return true only if at least one model-file is present

rescue TypeError => e
	puts "THE CLASS#require_model_file -> TypeError:  #{e.message}" 
	puts "Working on #{self.to_s} -> #{self.superclass}"
	puts "Class_hierarchy: #{orientdb.class_hierarchy.inspect}."
	print e.backtrace.join("\n") 
	raise
	#
end

#update(where:, set: {}, **arg) ⇒ Object

same as update!, but returns a list of updated records



227
228
229
230
231
# File 'lib/model/the_class.rb', line 227

def update where:  , set: {},  **arg
	# In OrientDB V.3 the database only returns the affected rid's 
	# We have to update the contents manually, this is done in the execute-block
	query( kind: :update, set: set.merge(arg), where: where).execute{|y| y[:$current].reload!}
end

#update!(where: nil, set: {}, **arg) ⇒ Object Also known as: update_all

Sets a value to certain attributes, overwrites existing entries, creates new attributes if necessary

returns the count of affected records

IB::Account.update connected: false
IB::Account.update where: "account containsText 'F'", set:{ connected: false }

# or

IB::Account.update  connected: false, where: "account containsText 'F'"


219
220
221
# File 'lib/model/the_class.rb', line 219

def update! where: nil , set: {},  **arg
query( kind: :update!, set: set.merge(arg), where: where).execute(reduce: true){|y| y[:count]}
end

#upsert(set: nil, where:, **args) ⇒ Object

Creates or updates records. Parameter:

  • set: A hash of attributes to insert or update unconditionally

  • where: A string or hash as condition which should return just one record.

The where-part should be covered with an unique-index.

returns the affected record, if the where-condition is set properly. Otherwise upsert acts as »update« and returns all updated records (as array).



204
205
206
207
# File 'lib/model/the_class.rb', line 204

def upsert set: nil, where: , **args
set = where if set.nil?
query( **args.merge( kind: :upsert, set: set, where: where )).execute(reduce: true){|y| y[:$current].reload!}
end

#where(*attributes) ⇒ Object Also known as: custom_where

Performs a query on the Class and returns an Array of ActiveOrient:Model-Records.

Fall-back method, is overloaded by Vertex.where

Is aliased by »custom_where»

Example:

Log.where priority: 'high'
--> submitted database-request: query/hc_database/sql/select from Log where priority = 'high'/-1
=> [ #<Log:0x0000000480f7d8 @metadata={ ... },  ...

Multiple arguments are joined via “and” eg:

Aktie.where symbol: 'TSL, exchange: 'ASX'
---> select  from aktie where symbol = 'TLS' and exchange = 'ASX'


520
521
522
523
# File 'lib/model/the_class.rb', line 520

def where *attributes 
  q= OrientSupport::OrientQuery.new  where: attributes 
  query_database( q)
end