active-orient 0.4 → 0.5
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/.gitignore +1 -0
- data/Gemfile +8 -3
- data/Guardfile +12 -4
- data/README.md +221 -201
- data/VERSION +1 -1
- data/active-orient.gemspec +3 -2
- data/bin/active-orient-console +35 -0
- data/config/boot.rb +84 -16
- data/config/config.yml +10 -0
- data/config/connect.yml +6 -2
- data/create_project +19 -0
- data/examples/books.rb +86 -39
- data/examples/createTime.rb +91 -0
- data/examples/streets.rb +85 -84
- data/examples/test_commands.rb +92 -0
- data/examples/test_commands_2.rb +54 -0
- data/examples/test_commands_3.rb +48 -0
- data/examples/test_commands_4.rb +28 -0
- data/examples/time_graph/Gemfile +21 -0
- data/examples/time_graph/Guardfile +26 -0
- data/examples/time_graph/README.md +129 -0
- data/examples/time_graph/bin/active-orient-console +35 -0
- data/examples/time_graph/config/boot.rb +119 -0
- data/examples/time_graph/config/config.yml +8 -0
- data/examples/time_graph/config/connect.yml +17 -0
- data/examples/time_graph/config/init_db.rb +59 -0
- data/examples/time_graph/createTime.rb +51 -0
- data/examples/time_graph/lib/createTime.rb +82 -0
- data/examples/time_graph/model/day_of.rb +3 -0
- data/examples/time_graph/model/e.rb +6 -0
- data/examples/time_graph/model/edge.rb +53 -0
- data/examples/time_graph/model/monat.rb +19 -0
- data/examples/time_graph/model/stunde.rb +16 -0
- data/examples/time_graph/model/tag.rb +29 -0
- data/examples/time_graph/model/time_base.rb +6 -0
- data/examples/time_graph/model/time_of.rb +4 -0
- data/examples/time_graph/model/v.rb +3 -0
- data/examples/time_graph/model/vertex.rb +32 -0
- data/examples/time_graph/spec/lib/create_time_spec.rb +50 -0
- data/examples/time_graph/spec/rest_helper.rb +37 -0
- data/examples/time_graph/spec/spec_helper.rb +46 -0
- data/lib/active-orient.rb +56 -6
- data/lib/base.rb +149 -147
- data/lib/base_properties.rb +40 -41
- data/lib/class_utils.rb +301 -0
- data/lib/database_utils.rb +97 -0
- data/lib/init.rb +35 -0
- data/lib/java-api.rb +437 -0
- data/lib/jdbc.rb +211 -0
- data/lib/model/edge.rb +53 -0
- data/lib/model/model.rb +77 -0
- data/lib/model/the_class.rb +480 -0
- data/lib/model/the_record.rb +310 -0
- data/lib/model/vertex.rb +32 -0
- data/lib/orient.rb +113 -50
- data/lib/orientdb_private.rb +48 -0
- data/lib/other.rb +280 -0
- data/lib/query.rb +71 -73
- data/lib/rest/change.rb +124 -0
- data/lib/rest/create.rb +474 -0
- data/lib/rest/delete.rb +133 -0
- data/lib/rest/operations.rb +150 -0
- data/lib/rest/read.rb +150 -0
- data/lib/rest/rest.rb +111 -0
- data/lib/rest_disabled.rb +24 -0
- data/lib/support.rb +387 -296
- data/old_lib_functions/two_general_class.rb +139 -0
- data/usecase.md +49 -36
- data/usecase_oo.md +59 -0
- metadata +73 -9
- data/lib/model.rb +0 -461
- data/lib/rest.rb +0 -1036
- data/test.rb +0 -4
data/lib/jdbc.rb
ADDED
@@ -0,0 +1,211 @@
|
|
1
|
+
require 'orientdb'
|
2
|
+
require_relative "database_utils.rb" #common methods without rest.specific content
|
3
|
+
require_relative "class_utils.rb" #common methods without rest.specific content
|
4
|
+
require_relative "orientdb_private.rb"
|
5
|
+
|
6
|
+
|
7
|
+
module ActiveOrient
|
8
|
+
|
9
|
+
|
10
|
+
class API
|
11
|
+
include OrientSupport::Support
|
12
|
+
include DatabaseUtils
|
13
|
+
include ClassUtils
|
14
|
+
include OrientDbPrivate
|
15
|
+
include OrientDB
|
16
|
+
|
17
|
+
mattr_accessor :logger # borrowed from active_support
|
18
|
+
mattr_accessor :default_server
|
19
|
+
attr_reader :database # Used to read the working database
|
20
|
+
|
21
|
+
#### INITIALIZATION ####
|
22
|
+
|
23
|
+
|
24
|
+
def initialize database: nil, connect: true, preallocate: true
|
25
|
+
self.logger = Logger.new('/dev/stdout') unless logger.present?
|
26
|
+
self.default_server = {
|
27
|
+
:server => 'localhost',
|
28
|
+
:port => 2480,
|
29
|
+
:protocol => 'http',
|
30
|
+
:user => 'root',
|
31
|
+
:password => 'root',
|
32
|
+
:database => 'temp'
|
33
|
+
}.merge default_server.presence || {}
|
34
|
+
@database = database || default_server[:database]
|
35
|
+
@all_classes=[]
|
36
|
+
#puts ["remote:#{default_server[:server]}/#{@database}",
|
37
|
+
# default_server[:user], default_server[:password] ]
|
38
|
+
connect() if connect
|
39
|
+
# @db = DocumentDatabase.connect("remote:#{default_server[:server]}/#{@database}",
|
40
|
+
# default_server[:user], default_server[:password] )
|
41
|
+
ActiveOrient::Model.api = self
|
42
|
+
preallocate_classes if preallocate
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
def db
|
47
|
+
@db
|
48
|
+
end
|
49
|
+
|
50
|
+
# Used for the connection on the server
|
51
|
+
#
|
52
|
+
|
53
|
+
# Used to connect to the database
|
54
|
+
|
55
|
+
def connect
|
56
|
+
|
57
|
+
@db = DocumentDatabase.connect("remote:#{default_server[:server]}/#{@database}",
|
58
|
+
default_server[:user], default_server[:password] )
|
59
|
+
@classes = get_database_classes
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
|
64
|
+
def get_classes *attributes
|
65
|
+
classes= @db.metadata.schema.classes.map{|x| { 'name' => x.name , 'superClass' => x.get_super_class.nil? ? '': x.get_super_class.name } }
|
66
|
+
unless attributes.empty?
|
67
|
+
classes.map{|y| y.select{|v,_| attributes.include?(v)}}
|
68
|
+
else
|
69
|
+
classes
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
def create_classes classes , &b
|
76
|
+
consts = allocate_classes_in_ruby( classes , &b )
|
77
|
+
|
78
|
+
all_classes = consts.is_a?( Array) ? consts.flatten : [consts]
|
79
|
+
get_database_classes(requery: true)
|
80
|
+
selected_classes = all_classes.map do | this_class |
|
81
|
+
this_class unless get_database_classes(requery: false).include?( this_class.ref_name ) rescue nil
|
82
|
+
end.compact.uniq
|
83
|
+
command= selected_classes.map do | database_class |
|
84
|
+
## improper initialized ActiveOrient::Model-classes lack a ref_name class-variable
|
85
|
+
next if database_class.ref_name.blank?
|
86
|
+
c = if database_class.superclass == ActiveOrient::Model || database_class.superclass.ref_name.blank?
|
87
|
+
puts "CREATE CLASS #{database_class.ref_name} "
|
88
|
+
OClassImpl.create @db, database_class.ref_name
|
89
|
+
else
|
90
|
+
puts "CREATE CLASS #{database_class.ref_name} EXTENDS #{database_class.superclass.ref_name}"
|
91
|
+
OClassImpl.create @db, superClass: database_class.ref_name
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# update the internal class hierarchy
|
96
|
+
get_database_classes requery: true
|
97
|
+
# return all allocated classes, no matter whether they had to be created in the DB or not.
|
98
|
+
# keep the format of the input-parameter
|
99
|
+
consts.shift if block_given? && consts.is_a?( Array) # remove the first element
|
100
|
+
# remove traces of superclass-allocations
|
101
|
+
if classes.is_a? Hash
|
102
|
+
consts = Hash[ consts ]
|
103
|
+
consts.each_key{ |x| consts[x].delete_if{|y| y == x} if consts[x].is_a? Array }
|
104
|
+
end
|
105
|
+
consts
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
|
110
|
+
def delete_class o_class
|
111
|
+
@db.schema.drop_class classname(o_class)
|
112
|
+
get_database_classes requery: true
|
113
|
+
end
|
114
|
+
=begin
|
115
|
+
Creates properties and optional an associated index as defined in the provided block
|
116
|
+
create_properties(classname or class, properties as hash){index}
|
117
|
+
|
118
|
+
The default-case
|
119
|
+
create_properties(:my_high_sophisticated_database_class,
|
120
|
+
con_id: {type: :integer},
|
121
|
+
details: {type: :link, linked_class: 'Contracts'}) do
|
122
|
+
contract_idx: :notunique
|
123
|
+
end
|
124
|
+
|
125
|
+
A composite index
|
126
|
+
create_properties(:my_high_sophisticated_database_class,
|
127
|
+
con_id: {type: :integer},
|
128
|
+
symbol: {type: :string}) do
|
129
|
+
{name: 'indexname',
|
130
|
+
on: [:con_id, :details] # default: all specified properties
|
131
|
+
type: :notunique # default: :unique
|
132
|
+
}
|
133
|
+
end
|
134
|
+
=end
|
135
|
+
|
136
|
+
def create_properties o_class, **all_properties, &b
|
137
|
+
logger.progname = 'JavaApi#CreateProperties'
|
138
|
+
ap = all_properties
|
139
|
+
created_properties = ap.map do |property, specification |
|
140
|
+
puts "specification: #{specification.inspect}"
|
141
|
+
field_type = ( specification.is_a?( Hash) ? specification[:type] : specification ).downcase.to_sym rescue :string
|
142
|
+
the_other_class = specification.is_a?(Hash) ? specification[:other_class] : nil
|
143
|
+
other_class = if the_other_class.present?
|
144
|
+
@db.get_class( the_other_class)
|
145
|
+
end
|
146
|
+
index = ap.is_a?(Hash) ? ap[:index] : nil
|
147
|
+
if other_class.present?
|
148
|
+
@db.get_class(classname(o_class)).add property,[ field_type, other_class ], { :index => index }
|
149
|
+
else
|
150
|
+
@db.get_class(classname(o_class)).add property, field_type, { :index => index }
|
151
|
+
end
|
152
|
+
end
|
153
|
+
if block_given?
|
154
|
+
attr = yield
|
155
|
+
index_parameters = case attr
|
156
|
+
when String, Symbol
|
157
|
+
{ name: attr }
|
158
|
+
when Hash
|
159
|
+
{ name: attr.keys.first , type: attr.values.first, on: all_properties.keys.map(&:to_s) }
|
160
|
+
else
|
161
|
+
nil
|
162
|
+
end
|
163
|
+
create_index o_class, **index_parameters unless index_parameters.blank?
|
164
|
+
end
|
165
|
+
created_properties.size # return_value
|
166
|
+
|
167
|
+
end
|
168
|
+
|
169
|
+
def get_properties o_class
|
170
|
+
@db.get_class(classname(o_class)).propertiesMap
|
171
|
+
end
|
172
|
+
|
173
|
+
|
174
|
+
|
175
|
+
def create_index o_class, name:, on: :automatic, type: :unique
|
176
|
+
logger.progname = 'JavaApi#CreateIndex'
|
177
|
+
begin
|
178
|
+
c = @db.get_class( classname( o_class ))
|
179
|
+
index = if on == :automatic
|
180
|
+
nil # not implemented
|
181
|
+
elsif on.is_a? Array
|
182
|
+
c.createIndex name.to_s, INDEX_TYPES[type], *on
|
183
|
+
else
|
184
|
+
c.createIndex name.to_s, INDEX_TYPES[type], on
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def create_record o_class, attributes: {}
|
190
|
+
logger.progname = 'HavaApi#CreateRecord'
|
191
|
+
attributes = yield if attributes.empty? && block_given?
|
192
|
+
new_record = insert_document( o_class, attributes.to_orient )
|
193
|
+
|
194
|
+
|
195
|
+
end
|
196
|
+
alias create_document create_record
|
197
|
+
|
198
|
+
def insert_document o_class, attributes
|
199
|
+
d = Document.create @db, classname(o_class), **attributes
|
200
|
+
d.save
|
201
|
+
ActiveOrient::Model.get_model_class(classname(o_class)).new attributes.merge( { "@rid" => d.rid,
|
202
|
+
"@version" => d.version,
|
203
|
+
"@type" => 'd',
|
204
|
+
"@class" => classname(o_class) } )
|
205
|
+
|
206
|
+
|
207
|
+
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
data/lib/model/edge.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# to do
|
2
|
+
# instead of creating a class, use a module which is included on startup
|
3
|
+
# then, after specifying the namespace and before autoaccolating the database-classes create the proper E-Base-class and include this stuff
|
4
|
+
class E < ActiveOrient::Model
|
5
|
+
## link to the library-class
|
6
|
+
class << self
|
7
|
+
=begin
|
8
|
+
establish contrains on Edges
|
9
|
+
|
10
|
+
Edges are uniq!
|
11
|
+
|
12
|
+
Creates individual indices for child-classes if applied to the class itself.
|
13
|
+
=end
|
14
|
+
def uniq_index
|
15
|
+
create_property :in, type: :link, linked_class: :V
|
16
|
+
create_property :out, type: :link, linked_class: :V
|
17
|
+
create_index "#{self.name}_idx", on: [ :in, :out ]
|
18
|
+
end
|
19
|
+
=begin
|
20
|
+
Instantiate a new Edge between two Vertices
|
21
|
+
|
22
|
+
The parameters »from« **or** »to« can take a list of model-records. Then subsequent edges are created.
|
23
|
+
|
24
|
+
:call-seq:
|
25
|
+
Model.create from:, to:, attributes:{}
|
26
|
+
=end
|
27
|
+
|
28
|
+
|
29
|
+
def create **keyword_arguments
|
30
|
+
new_edge = db.create_edge self, **keyword_arguments
|
31
|
+
new_edge = new_edge.pop if new_edge.is_a?( Array) && new_edge.size == 1
|
32
|
+
# vertices must be reloaded
|
33
|
+
|
34
|
+
new_edge # returns the created edge (or an array of created edges
|
35
|
+
end
|
36
|
+
|
37
|
+
# to do
|
38
|
+
# def delete
|
39
|
+
# delete an edge (as class method)
|
40
|
+
# and
|
41
|
+
# def remove
|
42
|
+
# delete an edge (as instance method)
|
43
|
+
#
|
44
|
+
def delete where: attributes
|
45
|
+
puts "work in progress"
|
46
|
+
end
|
47
|
+
|
48
|
+
# remove works on record-level
|
49
|
+
end
|
50
|
+
def remove
|
51
|
+
db.delete_edge self
|
52
|
+
end
|
53
|
+
end
|
data/lib/model/model.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
require_relative "the_class.rb"
|
2
|
+
require_relative "the_record.rb"
|
3
|
+
|
4
|
+
module ActiveOrient
|
5
|
+
class Model < ActiveOrient::Base
|
6
|
+
|
7
|
+
include BaseProperties
|
8
|
+
include ModelRecord # For objects (file: lib/record.rb)
|
9
|
+
extend ModelClass # For classes
|
10
|
+
|
11
|
+
=begin
|
12
|
+
Example:
|
13
|
+
ActiveOrient::Model.autoload_object "#00:00"
|
14
|
+
|
15
|
+
either retrieves the object from the rid_store or loads it from the DB.
|
16
|
+
|
17
|
+
The rid_store is updated!
|
18
|
+
|
19
|
+
To_do: fetch for version in the db and load the object if a change is detected
|
20
|
+
|
21
|
+
Note: This function is not in ModelClass since it needs to use @@rid_store
|
22
|
+
=end
|
23
|
+
|
24
|
+
def self.autoload_object rid
|
25
|
+
rid = rid[1..-1] if rid[0]=='#'
|
26
|
+
if rid.rid?
|
27
|
+
if @@rid_store[rid].present?
|
28
|
+
@@rid_store[rid] # return_value
|
29
|
+
else
|
30
|
+
db.get_record(rid)
|
31
|
+
end
|
32
|
+
else
|
33
|
+
logger.progname = "ActiveOrient::Model#AutoloadObject"
|
34
|
+
logger.info{"#{rid} is not a valid rid."}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
## to prevent errors when calling to_a
|
39
|
+
def to_ary # :nodoc:
|
40
|
+
attributes.to_a
|
41
|
+
end
|
42
|
+
|
43
|
+
def document # :nodoc:
|
44
|
+
@d
|
45
|
+
end
|
46
|
+
|
47
|
+
=begin
|
48
|
+
Deletes the database class and removes the ruby-class
|
49
|
+
=end
|
50
|
+
def self.delete_class
|
51
|
+
orientdb.delete_class self
|
52
|
+
## namespace is defined in config/boot
|
53
|
+
namespace.send(:remove_const, naming_convention.to_sym)
|
54
|
+
end
|
55
|
+
|
56
|
+
# provides an unique accessor on the Class
|
57
|
+
# works with a class-variable, its unique through all Subclasses
|
58
|
+
mattr_accessor :orientdb # points to the instance of the REST-DB-Client used for Administration
|
59
|
+
# i.e. creation and deleting of classes and databases
|
60
|
+
mattr_accessor :db # points to the instance of the Client used for Database-Queries
|
61
|
+
mattr_accessor :api
|
62
|
+
# mattr_accessor :logger ... already inherented from ::Base
|
63
|
+
mattr_accessor :namespace # Namespace in which Model records are initialized, a constant ( defined in config.yml )
|
64
|
+
mattr_accessor :model_dir # path to model-files
|
65
|
+
|
66
|
+
# mattr_accessor :ref_name
|
67
|
+
# Used to read the metadata
|
68
|
+
attr_reader :metadata
|
69
|
+
|
70
|
+
# provides an accessor at class level
|
71
|
+
# its unique on all instances
|
72
|
+
class << self
|
73
|
+
attr_accessor :ref_name
|
74
|
+
attr_accessor :abstract
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,480 @@
|
|
1
|
+
module ModelClass
|
2
|
+
|
3
|
+
########### CLASS FUNCTIONS ######### SELF ####
|
4
|
+
|
5
|
+
|
6
|
+
######## INITIALIZE A RECORD FROM A CLASS ########
|
7
|
+
|
8
|
+
|
9
|
+
=begin
|
10
|
+
NamingConvention provides a translation from database-names to class-names.
|
11
|
+
|
12
|
+
Should provide
|
13
|
+
to_s.capitalize_first_letter
|
14
|
+
as minimum.
|
15
|
+
Can be overwritten to provide different conventions for different classes, eg. Vertexes or edges.
|
16
|
+
|
17
|
+
To overwrite use
|
18
|
+
class Model < ActiveOrient::Model[:: ...]
|
19
|
+
def self.naming_convention
|
20
|
+
( conversion code )
|
21
|
+
end
|
22
|
+
end
|
23
|
+
=end
|
24
|
+
def naming_convention name=nil # :nodoc:
|
25
|
+
name.present? ? name.to_s.camelize : ref_name.camelize
|
26
|
+
end
|
27
|
+
|
28
|
+
=begin
|
29
|
+
orientdb_class is used to create or refer a ActiveOrient:Model:{class} by providing its name
|
30
|
+
|
31
|
+
Parameter: name: string or symbol
|
32
|
+
Parameter: superclass: If class, then this is used unmodified
|
33
|
+
If string or symbol, its used to reference an existing class
|
34
|
+
if :find_ME, its derived from the classes-hash
|
35
|
+
Attention: If a class is created by orientdb_class, its only allocated in ruby-space.
|
36
|
+
The class is thus not present in the classes-array, which reflects the database-classes.
|
37
|
+
If a class depending on a superclass is to be created, the superclass is derived from
|
38
|
+
the classes-array. In such a case, the allocation only works, if the class itself is
|
39
|
+
used as parameter "superclass"
|
40
|
+
i.e.
|
41
|
+
ActiveOrient::Model.orientdb_class name: 'hurra'
|
42
|
+
AvtiveOrient::Model.orientdb_class name: 'hip_hip' , superclass: Hurra
|
43
|
+
=end
|
44
|
+
|
45
|
+
def orientdb_class name:, superclass: nil # :nodoc: # public method: autoload_class
|
46
|
+
logger.progname = "ModelClass#OrientDBClass"
|
47
|
+
# @s-class is a cash for actual String -> Class relations
|
48
|
+
@s_class = HashWithIndifferentAccess.new( V: V, E: E) unless @s_class.present?
|
49
|
+
|
50
|
+
update_my_array = ->(s) { @s_class[s.ref_name] = s unless @s_class[s.ref_name].present? }
|
51
|
+
get_class = ->(n) { @s_class[n] }
|
52
|
+
|
53
|
+
|
54
|
+
ref_name = name.to_s
|
55
|
+
klass = if superclass.present? # superclass is parameter, use if class, otherwise transfer to class
|
56
|
+
s= if superclass.is_a? Class
|
57
|
+
namespace.send( :const_get, superclass.to_s )
|
58
|
+
else
|
59
|
+
superclass = orientdb.get_db_superclass( ref_name ) if superclass == :find_ME
|
60
|
+
if superclass.present?
|
61
|
+
namespace.send( :const_get, get_class[superclass].to_s )
|
62
|
+
else
|
63
|
+
self
|
64
|
+
end
|
65
|
+
end
|
66
|
+
Class.new(s)
|
67
|
+
else
|
68
|
+
Class.new(self)
|
69
|
+
end
|
70
|
+
# namespace is defined in config/boot
|
71
|
+
name = klass.naming_convention ref_name #
|
72
|
+
if namespace.send :const_defined?, name
|
73
|
+
retrieved_class = namespace.send :const_get, name
|
74
|
+
else
|
75
|
+
|
76
|
+
new_class = namespace.send :const_set, name, klass
|
77
|
+
new_class.ref_name = ref_name
|
78
|
+
update_my_array[new_class]
|
79
|
+
# logger.debug{"created:: Class #{new_class} < #{new_class.superclass} "}
|
80
|
+
# logger.debug{"database-table:: #{ref_name} "}
|
81
|
+
new_class # return_value
|
82
|
+
end
|
83
|
+
rescue NameError => e
|
84
|
+
logger.error "ModelClass #{name.inspect} cannot be initialized."
|
85
|
+
logger.error e.message
|
86
|
+
logger.error e.backtrace.map {|l| " #{l}\n"}.join
|
87
|
+
nil # return_value
|
88
|
+
#end
|
89
|
+
end
|
90
|
+
=begin
|
91
|
+
Retrieves the preallocated class derived from ActiveOrient::Model
|
92
|
+
|
93
|
+
Only classes noted in the @classes-Array of orientdb are fetched.
|
94
|
+
=end
|
95
|
+
def get_model_class name
|
96
|
+
if orientdb.database_classes.include?(name)
|
97
|
+
orientdb_class name: name, superclass: :find_ME
|
98
|
+
else
|
99
|
+
nil
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
|
105
|
+
=begin
|
106
|
+
requires the file specified in the model-dir
|
107
|
+
|
108
|
+
In fact, the model-files are loaded instead of required. After recreation of a class (Class.delete_class,
|
109
|
+
ORD.create_class classname) custom methods declared in the model files are present. Required modelfiles are
|
110
|
+
gone, if the class is destroyed, but the interpreter thinks, they have already been required. Rebuilding the
|
111
|
+
class does not reestablish the connection to the required model file.
|
112
|
+
|
113
|
+
Actual only a flat directory is supported. However -the Parameter model has the format: [ superclass, class ]. Its possible to extend the method adress a model-tree.
|
114
|
+
=end
|
115
|
+
def require_model_file
|
116
|
+
logger.progname = 'ModelClass#RequireModelFile'
|
117
|
+
if File.exists?( ActiveOrient::Model.model_dir )
|
118
|
+
model= model.flatten.last if model.is_a?( Array )
|
119
|
+
filename = ActiveOrient::Model.model_dir + "/" + self.to_s.underscore + '.rb'
|
120
|
+
puts "REQUIRE_MODEL_FILE: #{self.to_s} <-- #{self.superclass}"
|
121
|
+
if File.exists?(filename )
|
122
|
+
if load filename
|
123
|
+
logger.info{ "#{filename} sucessfully loaded" }
|
124
|
+
else
|
125
|
+
logger.error{ "#{filename} load error" }
|
126
|
+
end
|
127
|
+
else
|
128
|
+
logger.info{ "model-file not present: #{filename}" }
|
129
|
+
end
|
130
|
+
else
|
131
|
+
logger.info{ "Directory #{ ActiveOrient::Model.model_dir } not present " }
|
132
|
+
end
|
133
|
+
rescue TypeError => e
|
134
|
+
puts "TypeError: #{e.message}"
|
135
|
+
puts "Working on #{self.to_s} -> #{self.superclass}"
|
136
|
+
puts "Class_hierarchy: #{orientdb.class_hierarchy.inspect}."
|
137
|
+
print e.backtrace.join("\n")
|
138
|
+
raise
|
139
|
+
#
|
140
|
+
end
|
141
|
+
|
142
|
+
########## CREATE ############
|
143
|
+
|
144
|
+
=begin
|
145
|
+
Universal method to create a new record.
|
146
|
+
It's obverloaded to create specific kinds, eg. edges
|
147
|
+
|
148
|
+
Example:
|
149
|
+
ORD.create_class :test
|
150
|
+
Test.create string_attribute: 'a string', symbol_attribute: :a_symbol, array_attribute: [34,45,67]
|
151
|
+
Test.create link_attribute: Test.create( :a_new_attribute => 'new' )
|
152
|
+
|
153
|
+
=end
|
154
|
+
def create **attributes
|
155
|
+
attributes.merge :created_at => Time.new
|
156
|
+
db.create_record self, attributes: attributes
|
157
|
+
end
|
158
|
+
|
159
|
+
=begin
|
160
|
+
Creates or updates a record.
|
161
|
+
Parameter:
|
162
|
+
set: A hash of attributes to insert or update unconditionally
|
163
|
+
where: A string or hash as condition which should return just one record.
|
164
|
+
|
165
|
+
The where-part should be covered with an unique-index.
|
166
|
+
If :where is omitted, #Upsert becomes #Create, attributes are taken from :set.
|
167
|
+
|
168
|
+
returns the affected record
|
169
|
+
=end
|
170
|
+
def upsert set: {}, where: {}, &b
|
171
|
+
db.upsert self, set: set, where: where, &b
|
172
|
+
end
|
173
|
+
=begin
|
174
|
+
Create a new Instance of the Class with the applied attributes if does not exists,
|
175
|
+
otherwise update it. It returns the freshly instantiated Objects
|
176
|
+
=end
|
177
|
+
|
178
|
+
def update_or_create_records set: {}, where: {}, **args, &b
|
179
|
+
db.update_or_create_records self, set: set, where: where, **args, &b
|
180
|
+
end
|
181
|
+
|
182
|
+
alias update_or_create_documents update_or_create_records
|
183
|
+
alias create_or_update_document upsert
|
184
|
+
alias update_or_create upsert
|
185
|
+
|
186
|
+
|
187
|
+
=begin
|
188
|
+
Create a Property in the Schema of the Class
|
189
|
+
:call-seq: Model.create_property(field (required), type:'string', linked_class: nil, index: nil) do
|
190
|
+
index
|
191
|
+
end
|
192
|
+
|
193
|
+
Examples:
|
194
|
+
|
195
|
+
create_property :customer_id, type: integer, index: :unique
|
196
|
+
create_property :name, type: :string, index: :not_unique
|
197
|
+
create_property :in, type: :link, linked_class: :V (used by edges)
|
198
|
+
=end
|
199
|
+
|
200
|
+
def create_property field, **keyword_arguments, &b
|
201
|
+
orientdb.create_property self, field, **keyword_arguments, &b
|
202
|
+
end
|
203
|
+
|
204
|
+
# Create more Properties in the Schema of the Class
|
205
|
+
|
206
|
+
def create_properties argument_hash, &b
|
207
|
+
orientdb.create_properties self, argument_hash, &b
|
208
|
+
end
|
209
|
+
|
210
|
+
|
211
|
+
# Add an Index
|
212
|
+
def create_index name, **attributes
|
213
|
+
orientdb.create_index self, name: name, **attributes
|
214
|
+
end
|
215
|
+
|
216
|
+
########## GET ###############
|
217
|
+
|
218
|
+
def classname # :nodoc: #
|
219
|
+
ref_name
|
220
|
+
end
|
221
|
+
|
222
|
+
# get elements by rid
|
223
|
+
|
224
|
+
def get rid
|
225
|
+
db.get_record rid
|
226
|
+
end
|
227
|
+
|
228
|
+
# get all the elements of the class
|
229
|
+
|
230
|
+
def all
|
231
|
+
db.get_records from: self
|
232
|
+
end
|
233
|
+
|
234
|
+
# get the first element of the class
|
235
|
+
|
236
|
+
def first where: {}
|
237
|
+
db.get_records(from: self, where: where, limit: 1).pop
|
238
|
+
end
|
239
|
+
|
240
|
+
# get the last element of the class
|
241
|
+
|
242
|
+
def last where: {}
|
243
|
+
db.get_records(from: self, where: where, order: {"@rid" => 'desc'}, limit: 1).pop
|
244
|
+
end
|
245
|
+
# Used to count of the elements in the class
|
246
|
+
|
247
|
+
def count **args
|
248
|
+
orientdb.count from: self, **args
|
249
|
+
end
|
250
|
+
|
251
|
+
# Get the properties of the class
|
252
|
+
|
253
|
+
def get_properties
|
254
|
+
object = orientdb.get_class_properties self
|
255
|
+
HashWithIndifferentAccess.new :properties => object['properties'], :indexes => object['indexes']
|
256
|
+
end
|
257
|
+
alias get_class_properties get_properties
|
258
|
+
|
259
|
+
# Print the properties of the class
|
260
|
+
|
261
|
+
def print_class_properties
|
262
|
+
orientdb.print_class_properties self
|
263
|
+
end
|
264
|
+
|
265
|
+
=begin
|
266
|
+
Parameter projection:
|
267
|
+
»select« is a method of enumeration, we use »projection:« to specify anything between »select« and »from« in the query-string.
|
268
|
+
projection: a_string --> inserts the sting as it appearsb
|
269
|
+
an OrientSupport::OrientQuery-Object --> performs a sub-query and uses the result for further querying though the given parameters.
|
270
|
+
[a, b, c] --> "a, b, c" (inserts a comma-separated list)
|
271
|
+
{a: b, "sum(x)" => f} --> "a as b, sum(x) as f" (renames properties and uses functions)
|
272
|
+
|
273
|
+
Parameter distinct:
|
274
|
+
Performs a Query like » select distinct(property) [as property] from ...«
|
275
|
+
distinct: :property --> the result is mapped to the property »distinct«.
|
276
|
+
[:property] --> the result replaces the property
|
277
|
+
{property: :some_name} --> the result is mapped to ModelInstance.some_name
|
278
|
+
|
279
|
+
Parameter Order
|
280
|
+
Sorts the result-set. If new properties are introduced via select:, distinct: etc. Sorting takes place on these properties
|
281
|
+
order: :property {property: asc, property: desc}[property, property, .. ](orderdirection is 'asc')
|
282
|
+
|
283
|
+
Further supported Parameter:
|
284
|
+
group_by
|
285
|
+
skip
|
286
|
+
limit
|
287
|
+
unwind
|
288
|
+
|
289
|
+
see orientdb- documentation (https://orientdb.com/docs/last/SQL-Query.html)
|
290
|
+
|
291
|
+
Parameter query:
|
292
|
+
Instead of providing the parameter, the OrientSupport::OrientQuery can build and tested before the method-call. The OrientQuery-Object can be provided with the query-parameter. I.e.
|
293
|
+
q = OrientSupport::OrientQuery.new
|
294
|
+
ORD.create_class :test_model
|
295
|
+
q.from TestModel
|
296
|
+
q.where {name: 'Thomas'}
|
297
|
+
count = TestModel.count query: q
|
298
|
+
q.limit 10
|
299
|
+
0.step(count,10) do |x|
|
300
|
+
q.skip = x
|
301
|
+
puts TestModel.get_documents(query: q).map{|x| x.adress }.join('\t')
|
302
|
+
end
|
303
|
+
prints a Table with 10 columns.
|
304
|
+
=end
|
305
|
+
|
306
|
+
def get_records **args
|
307
|
+
db.get_records(from: self, **args){self}
|
308
|
+
end
|
309
|
+
alias get_documents get_records
|
310
|
+
|
311
|
+
=begin
|
312
|
+
Performs a query on the Class and returns an Array of ActiveOrient:Model-Records.
|
313
|
+
|
314
|
+
Example:
|
315
|
+
Log.where priority: 'high'
|
316
|
+
--> submited database-request: query/hc_database/sql/select from Log where priority = 'high'/-1
|
317
|
+
=> [ #<Log:0x0000000480f7d8 @metadata={ ... }, ...
|
318
|
+
=end
|
319
|
+
|
320
|
+
def custom_where search_string
|
321
|
+
q = OrientSupport::OrientQuery.new from: self, where: search_string
|
322
|
+
puts q.compose
|
323
|
+
query_database q
|
324
|
+
end
|
325
|
+
def where **attributes
|
326
|
+
##puts "ATTRIBUTES: "+attributes.inspect
|
327
|
+
q = OrientSupport::OrientQuery.new from: self, where: attributes
|
328
|
+
query_database q
|
329
|
+
end
|
330
|
+
=begin
|
331
|
+
Performs a Match-Query
|
332
|
+
|
333
|
+
The Query starts at the given ActiveOrient::Model-Class. The where-cause narrows the sample to certain
|
334
|
+
records. In the simplest version this can be returnd:
|
335
|
+
|
336
|
+
Industry.match where:{ name: "Communications" }
|
337
|
+
=> #<ActiveOrient::Model::Query:0x00000004309608 @metadata={"type"=>"d", "class"=>nil, "version"=>0, "fieldTypes"=>"Industries=x"}, @attributes={"Industries"=>"#21:1", (...)}>
|
338
|
+
|
339
|
+
The attributes are the return-Values of the Match-Query. Unless otherwise noted, the pluralized Model-Classname is used as attribute in the result-set.
|
340
|
+
|
341
|
+
I.match( where: { name: 'Communications' }).first.Industries
|
342
|
+
|
343
|
+
is the same then
|
344
|
+
Industry.where name: "Communications"
|
345
|
+
|
346
|
+
|
347
|
+
The Match-Query uses this result-set as start for subsequent queries on connected records.
|
348
|
+
These connections are defined in the Block
|
349
|
+
|
350
|
+
var = Industry.match do | query |
|
351
|
+
query.connect :in, count: 2, as: 'Subcategories'
|
352
|
+
puts query.to_s # print the query send to the database
|
353
|
+
query # important: block has to return the query
|
354
|
+
end
|
355
|
+
=> MATCH {class: Industry, as: Industries} <-- {} <-- { as: Subcategories } RETURN Industries, Subcategories
|
356
|
+
|
357
|
+
The result-set has two attributes: Industries and Subcategories, pointing to the filtered datasets.
|
358
|
+
|
359
|
+
By using subsequent »connect« and »statement« method-calls even complex Match-Queries can be clearly constructed.
|
360
|
+
|
361
|
+
=end
|
362
|
+
|
363
|
+
def match where: {}
|
364
|
+
query= OrientSupport::OrientQuery.new kind: :match, start:{ class: self.classname }
|
365
|
+
query.match_statements[0].where = where unless where.empty?
|
366
|
+
if block_given?
|
367
|
+
query_database yield(query), set_from: false
|
368
|
+
else
|
369
|
+
logger.progname = 'ActiveOrient::Model#Match'
|
370
|
+
logger.error{ "Query-Details have to be specified in a Block" }
|
371
|
+
end
|
372
|
+
|
373
|
+
end
|
374
|
+
|
375
|
+
|
376
|
+
# Get the superclass of the class
|
377
|
+
|
378
|
+
def superClass
|
379
|
+
{ superclass => superclass.ref_name }
|
380
|
+
# logger.progname = 'ActiveOrient::Model#Superclass'
|
381
|
+
# r = orientdb.get_classes('name', 'superClass').detect{|x|
|
382
|
+
# x["name"].downcase == new.class.to_s.downcase.split(':')[-1].to_s
|
383
|
+
# }['superClass']
|
384
|
+
# if r.empty?
|
385
|
+
# logger.info{"#{self} does not have any superclass. Probably it is a Document"}
|
386
|
+
# end
|
387
|
+
# return r
|
388
|
+
end
|
389
|
+
|
390
|
+
=begin
|
391
|
+
QueryDatabase sends the Query, direct to the database.
|
392
|
+
The result is not nessessary an Object of self.
|
393
|
+
However, if the query does not return an array of Active::Model-Objects, then the entries become self
|
394
|
+
=end
|
395
|
+
|
396
|
+
def query_database query, set_from: true
|
397
|
+
query.from self if set_from && query.is_a?(OrientSupport::OrientQuery) && query.from.nil?
|
398
|
+
sql_cmd = -> (command) {{ type: "cmd", language: "sql", command: command }}
|
399
|
+
db.execute do
|
400
|
+
sql_cmd[query.to_s]
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
404
|
+
########### DELETE ###############
|
405
|
+
|
406
|
+
# Delete a property from the class
|
407
|
+
|
408
|
+
def delete_property field
|
409
|
+
orientdb.delete_property self, field
|
410
|
+
end
|
411
|
+
|
412
|
+
# Delete a record
|
413
|
+
|
414
|
+
def delete_record *rid
|
415
|
+
db.delete_record rid
|
416
|
+
end
|
417
|
+
alias delete_document delete_record
|
418
|
+
|
419
|
+
# Delete a record from the class
|
420
|
+
|
421
|
+
def delete_records where: {}
|
422
|
+
orientdb.delete_records self, where: where
|
423
|
+
end
|
424
|
+
alias delete_documents delete_records
|
425
|
+
|
426
|
+
|
427
|
+
########### UPDATE #############
|
428
|
+
|
429
|
+
# Update records of a class
|
430
|
+
|
431
|
+
def update_records set:, where:
|
432
|
+
db.update_records self, set: set, where: where
|
433
|
+
end
|
434
|
+
alias update_documents update_records
|
435
|
+
|
436
|
+
##################### EXPERIMENT #################
|
437
|
+
|
438
|
+
=begin
|
439
|
+
Suppose that you created a graph where vertexes month is connected with
|
440
|
+
the vertexes day by the edge TIMEOF.
|
441
|
+
Suppose we want to find all the days in the first month and in the third month..
|
442
|
+
|
443
|
+
Usually we can do in the following way.
|
444
|
+
|
445
|
+
ORD.create_class "Month"
|
446
|
+
(.. put some records into Month ... )
|
447
|
+
firstmonth = Month.first
|
448
|
+
thirdmonth = month.all[2]
|
449
|
+
days_firstmonth = firstmonth.out_TIMEOF.map{|x| x.in}
|
450
|
+
days_thirdmonth = thirdmonth.out_TIMEOF.map{|x| x.in}
|
451
|
+
|
452
|
+
However we can obtain the same result with the following command
|
453
|
+
|
454
|
+
Month.add_edge_link name: "days", direction: "out", edge: TIME_OF
|
455
|
+
firstmonth = month.first
|
456
|
+
thirdmonth = month.all[2]
|
457
|
+
days_firstmonth = firstmonth.days
|
458
|
+
days_thirdmonth = thirdmonth.days
|
459
|
+
|
460
|
+
To get their value you can do:
|
461
|
+
thirdmonth.days.value
|
462
|
+
=end
|
463
|
+
|
464
|
+
|
465
|
+
def add_edge_link name:, direction: :out, edge:
|
466
|
+
dir = direction.to_s == "out" ? :out : :in
|
467
|
+
define_method(name.to_sym) do
|
468
|
+
return self["#{dir}_#{edge.classname}"].map{|x| x["in"]}
|
469
|
+
end
|
470
|
+
end
|
471
|
+
|
472
|
+
=begin
|
473
|
+
See http://orientdb.com/docs/2.1/SQL-Alter-Property.html
|
474
|
+
=end
|
475
|
+
|
476
|
+
def alter_property property:, attribute: "DEFAULT", alteration:
|
477
|
+
orientdb.alter_property self, property: property, attribute: attribute, alteration: alteration
|
478
|
+
end
|
479
|
+
|
480
|
+
end
|