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/class_utils.rb
ADDED
@@ -0,0 +1,301 @@
|
|
1
|
+
module ClassUtils
|
2
|
+
# ClassUitils is included in Rest- and Java-Api-classes
|
3
|
+
|
4
|
+
=begin
|
5
|
+
Returns a valid database-class name, nil if the class does not exists
|
6
|
+
=end
|
7
|
+
|
8
|
+
def classname name_or_class # :nodoc:
|
9
|
+
name = case name_or_class
|
10
|
+
when ActiveOrient::Model
|
11
|
+
name_or_class.class.ref_name
|
12
|
+
when Class
|
13
|
+
name_or_class.ref_name
|
14
|
+
# name_or_class.to_s.split('::').last
|
15
|
+
else
|
16
|
+
name_or_class.to_s #.to_s.camelcase # capitalize_first_letter
|
17
|
+
end
|
18
|
+
## 16/5/31 : reintegrating functionality to check wether the classname is
|
19
|
+
# present in the database or not
|
20
|
+
if database_classes.include?(name)
|
21
|
+
name
|
22
|
+
elsif database_classes.include?(name.underscore)
|
23
|
+
name.underscore
|
24
|
+
else
|
25
|
+
logger.progname = 'ClassUtils#Classname'
|
26
|
+
logger.warn{ "Classname #{name_or_class.inspect} ://: #{name} not present in #{ActiveOrient.database}" }
|
27
|
+
nil
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
=begin
|
32
|
+
create a single class and provide properties as well
|
33
|
+
|
34
|
+
ORB.create_class( the_class_name as String or Symbol (nessesary) ,
|
35
|
+
properties: a Hash with property- and Index-descriptions (optional)){
|
36
|
+
{superclass: The name of the superclass as String or Symbol ,
|
37
|
+
abstract: true|false } (optional Block provides a Hash ) }
|
38
|
+
|
39
|
+
=end
|
40
|
+
def create_class classname, properties: nil, &b
|
41
|
+
the_class= create_classes( classname, &b )
|
42
|
+
# if multible classes are specified, don't process properties
|
43
|
+
# ( if multible classes need the same properties, consider a nested class-design )
|
44
|
+
if the_class.is_a?(Array)
|
45
|
+
if the_class.size == 1
|
46
|
+
the_class = the_class.first
|
47
|
+
else
|
48
|
+
properties = nil
|
49
|
+
end
|
50
|
+
end
|
51
|
+
create_properties( the_class.ref_name , properties ) if properties.present?
|
52
|
+
the_class # return_value
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
=begin
|
57
|
+
AllocateClassesInRuby acts as a proxy to OrientdbClass,
|
58
|
+
takes a classes-array as argument
|
59
|
+
=end
|
60
|
+
def allocate_classes_in_ruby classes # :nodoc:
|
61
|
+
generate_ruby_object = ->( name, superclass, abstract ) do
|
62
|
+
begin
|
63
|
+
# if the class is prefined, use specs from get_classes
|
64
|
+
or_def = get_classes('name', 'superClass', 'abstract' ).detect{|x| x['name']== name.to_s }
|
65
|
+
superclass, abstract = or_def.reject{|k,v| k=='name'}.values unless or_def.nil?
|
66
|
+
# print "GENERATE_RUBY_CLASS: #{name} / #{superclass} \n"
|
67
|
+
m= ActiveOrient::Model.orientdb_class name: name, superclass: superclass
|
68
|
+
m.abstract = abstract
|
69
|
+
m.ref_name = name.to_s
|
70
|
+
#puts "--> #{m.object_id}"
|
71
|
+
m
|
72
|
+
rescue NoMethodError => w
|
73
|
+
logger.progname = "ClassUtils#AllocateClassesInRuby"
|
74
|
+
logger.error{ w.message }
|
75
|
+
logger.error{ w.backtrace.map {|l| " #{l}\n"}.join }
|
76
|
+
nil
|
77
|
+
# raise
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
superclass, abstract = if block_given?
|
83
|
+
s = yield
|
84
|
+
if s.is_a? Hash
|
85
|
+
[s[:superclass],s[:abstract]]
|
86
|
+
else
|
87
|
+
[s,false]
|
88
|
+
end
|
89
|
+
else
|
90
|
+
[nil,false]
|
91
|
+
end
|
92
|
+
#superclass_object = generate_ruby_object[superclass,nil,nil] if superclass.present?
|
93
|
+
|
94
|
+
consts = case classes
|
95
|
+
when Array
|
96
|
+
next_superclass= superclass
|
97
|
+
classes.map do |singleclass|
|
98
|
+
if singleclass.is_a?( String) || singleclass.is_a?( Symbol)
|
99
|
+
next_superclass = generate_ruby_object[singleclass,superclass,abstract]
|
100
|
+
elsif singleclass.is_a?(Array) || singleclass.is_a?(Hash)
|
101
|
+
allocate_classes_in_ruby( singleclass){ {superclass: next_superclass, abstract: abstract}}
|
102
|
+
end
|
103
|
+
end
|
104
|
+
when Hash
|
105
|
+
classes.keys.map do| h_superclass |
|
106
|
+
[ generate_ruby_object[h_superclass,superclass,abstract],
|
107
|
+
allocate_classes_in_ruby(classes[ h_superclass ]){{ superclass: h_superclass, abstract: abstract }} ]
|
108
|
+
end
|
109
|
+
when String, Symbol
|
110
|
+
generate_ruby_object[classes,superclass, abstract]
|
111
|
+
end
|
112
|
+
# consts.unshift superclass_object if superclass_object.present? rescue [ superclass_object, consts ]
|
113
|
+
consts
|
114
|
+
end
|
115
|
+
=begin
|
116
|
+
Creating a new Database-Entry (where is omitted)
|
117
|
+
Or updating the Database-Entry (if present)
|
118
|
+
|
119
|
+
The optional Block should provide a hash with attributes (properties). These are used if a new dataset is created.
|
120
|
+
Based on the query specified in :where records are updated according to :set
|
121
|
+
|
122
|
+
Returns an Array of updated documents
|
123
|
+
=end
|
124
|
+
|
125
|
+
def update_or_create_records o_class, set: {}, where: {}, **args, &b
|
126
|
+
logger.progname = 'ClassUtils#UpdateOrCreateRecords'
|
127
|
+
if where.blank?
|
128
|
+
[create_record(o_class, attributes: set)]
|
129
|
+
else
|
130
|
+
set.extract!(where.keys) if where.is_a?(Hash) # removes any keys from where in set
|
131
|
+
possible_records = get_records from: classname(o_class), where: where, **args
|
132
|
+
if possible_records.empty?
|
133
|
+
if block_given?
|
134
|
+
more_where = yield # do Preparations prior to the creation of the dataset
|
135
|
+
# if the block returns a Hash, it is merged into the insert_query.
|
136
|
+
where.merge! more_where if more_where.is_a?(Hash)
|
137
|
+
end
|
138
|
+
[create_record(o_class, attributes: set.merge(where))]
|
139
|
+
else
|
140
|
+
possible_records.map{|doc| doc.update(set: set)} unless set.empty?
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
=begin
|
147
|
+
create_edge connects two vertexes
|
148
|
+
|
149
|
+
The parameter o_class can be either a class or a string
|
150
|
+
|
151
|
+
if batch is specified, the edge-statement is prepared and returned
|
152
|
+
else the statement is transmitted to the database
|
153
|
+
|
154
|
+
The method takes a block as well.
|
155
|
+
It must provide a Hash with :from and :to- Key's, e.g.
|
156
|
+
Vertex1, Vertex2 are two vertex-classes and TheEdge is an edge-class
|
157
|
+
|
158
|
+
record1 = ( 1 .. 100 ).map{ |y| Vertex1.create( testentry: y } }
|
159
|
+
record2 = ( :a .. :z ).map{ |y| Vertex2.create( testentry: y } }
|
160
|
+
|
161
|
+
edges = ORD.create_edge( TheEdge ) do | attributes |
|
162
|
+
('a'.ord .. 'z'.ord).map do |o|
|
163
|
+
{ from: record1.find{|x| x.testentry == o },
|
164
|
+
to: record2.find{ |x| x.testentry.ord == o },
|
165
|
+
attributes: attributes.merge{ key: o.chr } }
|
166
|
+
end
|
167
|
+
or
|
168
|
+
|
169
|
+
edges = ORD.create_edge( TheEdge ) do | attributes |
|
170
|
+
('a'.ord .. 'z'.ord).map do |o|
|
171
|
+
{ from: Vertex1.where( testentry: o ).pop ,
|
172
|
+
to: Vertex2.where( testentry.ord => o).pop ,
|
173
|
+
attributes: attributes.merge{ key: o.chr } }
|
174
|
+
end
|
175
|
+
|
176
|
+
Benefits: The statements are transmitted as batch.
|
177
|
+
|
178
|
+
The pure-ruby-solution minimizes traffic to the database-server and is prefered.
|
179
|
+
|
180
|
+
=end
|
181
|
+
def create_edge o_class, attributes: {}, from:nil, to:nil, unique: false, batch: nil
|
182
|
+
logger.progname = "ClassUtils#CreateEdge"
|
183
|
+
|
184
|
+
# -------------------------------
|
185
|
+
create_command = -> (attr, from, to ) do
|
186
|
+
attr_string = attr.blank? ? "" : "SET #{generate_sql_list attr}"
|
187
|
+
if from.present? && to.present?
|
188
|
+
[from, to].each{|y| remove_record_from_hash y }
|
189
|
+
{ type: "cmd",
|
190
|
+
language: 'sql',
|
191
|
+
command: "CREATE EDGE #{classname(o_class)} FROM #{from.to_orient} TO #{to.to_orient} #{attr_string}"
|
192
|
+
}
|
193
|
+
end
|
194
|
+
end
|
195
|
+
# -------------------------------
|
196
|
+
|
197
|
+
if block_given?
|
198
|
+
a = yield(attributes)
|
199
|
+
command = if a.is_a? Array
|
200
|
+
batch = nil
|
201
|
+
command= a.map do | record |
|
202
|
+
# puts record.inspect
|
203
|
+
this_attributes = record[:attributes].presence || attributes
|
204
|
+
create_command[ this_attributes, record[:from], record[:to]]
|
205
|
+
end
|
206
|
+
else
|
207
|
+
this_attributes = a[:attributes].presence || attributes
|
208
|
+
command = create_command[ this_attributes, a[:from], a[:to]]
|
209
|
+
end
|
210
|
+
elsif from.is_a?( Array ) && to.is_a?(Array)
|
211
|
+
command = Array.new
|
212
|
+
while from.size >1
|
213
|
+
this_attributes = attributes.is_a?(Array) ? attributes.shift : attributes
|
214
|
+
command << create_command[ this_attributes, from.shift, to.shift]
|
215
|
+
end
|
216
|
+
elsif from.is_a? Array
|
217
|
+
command = from.map{|f| create_command[ attributes, f, to] }
|
218
|
+
elsif to.is_a? Array
|
219
|
+
command = to.map{|f| create_command[ attributes, from, f] }
|
220
|
+
# puts "COMMAND: #{command.inspect}"
|
221
|
+
elsif from.present? && to.present?
|
222
|
+
# if unique
|
223
|
+
# wwhere = {out: from.to_orient, in: to.to_orient }.merge(attributes.to_orient)
|
224
|
+
# existing_edge = get_records(from: o_class, where: wwhere)
|
225
|
+
# if existing_edge.size >1
|
226
|
+
# logger.error{ "Unique specified, but there are #{existing_edge.size} Records in the Database. returning the first"}
|
227
|
+
# command = existing_edge.first
|
228
|
+
# batch = true
|
229
|
+
# elsif existing_edge.size ==1 && existing_edge.first.is_a?(ActiveOrient::Model)
|
230
|
+
# #logger.debug {"Reusing edge #{classname(o_class)} from #{from.to_orient} to #{to.to_orient}"}
|
231
|
+
# command= existing_edge.first
|
232
|
+
# batch= true
|
233
|
+
# else
|
234
|
+
command = create_command[ attributes, from, to]
|
235
|
+
# end
|
236
|
+
# #logger.debug {"Creating edge #{classname(o_class)} from #{from.to_orient} to #{to.to_orient}"}
|
237
|
+
#
|
238
|
+
elsif from.to_orient.nil? || to.to_orient.nil?
|
239
|
+
logger.error{ "Parameter :from or :to is missing"}
|
240
|
+
else
|
241
|
+
# for or to are not set
|
242
|
+
return nil
|
243
|
+
end
|
244
|
+
# puts "RUNNING EDGE: #{command.inspect}"
|
245
|
+
if command.present?
|
246
|
+
begin
|
247
|
+
response = execute(transaction: false, tolerated_error_code: /found duplicated key/) do
|
248
|
+
command.is_a?(Array) ? command.flatten.compact : [ command ]
|
249
|
+
end
|
250
|
+
if response.is_a?(Array) && response.size == 1
|
251
|
+
response.pop # RETURN_VALUE
|
252
|
+
else
|
253
|
+
response # return value (the normal case)
|
254
|
+
end
|
255
|
+
rescue ArgumentError => e
|
256
|
+
puts "ArgumentError "
|
257
|
+
puts e.inspect
|
258
|
+
end # begin
|
259
|
+
end
|
260
|
+
|
261
|
+
end
|
262
|
+
=begin
|
263
|
+
Deletes the specified vertices and unloads referenced edges from the cache
|
264
|
+
=end
|
265
|
+
def delete_vertex *vertex
|
266
|
+
create_command = -> do
|
267
|
+
{ type: "cmd",
|
268
|
+
language: 'sql',
|
269
|
+
command: "DELETE VERTEX #{vertex.map{|x| x.to_orient }.join(',')} "
|
270
|
+
}
|
271
|
+
end
|
272
|
+
|
273
|
+
vertex.each{|v| v.edges.each{| e | remove_record_from_hash e} }
|
274
|
+
execute{ create_command[] }
|
275
|
+
end
|
276
|
+
|
277
|
+
=begin
|
278
|
+
Deletes the specified edges and unloads referenced vertices from the cache
|
279
|
+
=end
|
280
|
+
def delete_edge *edge
|
281
|
+
create_command = -> do
|
282
|
+
{ type: "cmd",
|
283
|
+
language: 'sql',
|
284
|
+
command: "DELETE EDGE #{edge.map{|x| x.to_orient }.join(',')} "
|
285
|
+
}
|
286
|
+
end
|
287
|
+
|
288
|
+
edge.each do |r|
|
289
|
+
[r.in, r.out].each{| e | remove_record_from_hash e}
|
290
|
+
remove_record_from_hash r
|
291
|
+
end
|
292
|
+
execute{ create_command[] }
|
293
|
+
end
|
294
|
+
|
295
|
+
private
|
296
|
+
def remove_record_from_hash r
|
297
|
+
obj= ActiveOrient::Base.get_rid(r.rid) unless r.nil?
|
298
|
+
ActiveOrient::Base.remove_rid( obj ) unless obj.nil?
|
299
|
+
end
|
300
|
+
|
301
|
+
end # module
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module DatabaseUtils
|
2
|
+
=begin
|
3
|
+
returns the classes set by OrientDB
|
4
|
+
Parameter: abstract: true|false
|
5
|
+
if abstract: true is given, only basic classes (Abstact-Classes) are returend
|
6
|
+
=end
|
7
|
+
def system_classes abstract: false
|
8
|
+
|
9
|
+
basic= [ "ORestricted", "OSchedule", "OTriggered", "OSequence"]
|
10
|
+
## "ORid" dropped in V2.2
|
11
|
+
extended = ["OIdentity","ORole", "OUser", "OFunction", "_studio"]
|
12
|
+
if abstract
|
13
|
+
basic
|
14
|
+
else
|
15
|
+
basic + extended
|
16
|
+
end
|
17
|
+
end
|
18
|
+
=begin
|
19
|
+
Returns the class_hierachy
|
20
|
+
|
21
|
+
To fetch all Vertices use:
|
22
|
+
class_hiearchie(base_class: 'V').flatten
|
23
|
+
To fetch all Edges uses:
|
24
|
+
class_hierachy(base_class: 'E').flatten
|
25
|
+
|
26
|
+
To retrieve the class hierarchy from Objects avoid calling classname, because it depends on class_hierarchy.
|
27
|
+
=end
|
28
|
+
|
29
|
+
def class_hierarchy base_class: '', system_classes: nil
|
30
|
+
@all_classes = get_classes('name', 'superClass') #if requery || @all_classes.blank?
|
31
|
+
def fv s # :nodoc:
|
32
|
+
@all_classes.find_all{|x| x['superClass']== s}.map{|v| v['name']}
|
33
|
+
end
|
34
|
+
|
35
|
+
def fx v # :nodoc:
|
36
|
+
fv(v.strip).map{|x| ar = fx(x); ar.empty? ? x : [x, ar]}
|
37
|
+
end
|
38
|
+
complete_hierarchy = fx base_class.to_s
|
39
|
+
complete_hierarchy - system_classes() - [ ["OIdentity", ["ORole", "OUser"]]] unless system_classes
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
=begin
|
44
|
+
Returns an array with all names of the classes of the database
|
45
|
+
caches the result.
|
46
|
+
Parameters: include_system_classes: false|true, requery: false|true
|
47
|
+
=end
|
48
|
+
|
49
|
+
def database_classes system_classes: nil, requery: false
|
50
|
+
requery = true if ActiveOrient.database_classes.blank?
|
51
|
+
if requery
|
52
|
+
class_hierarchy system_classes: system_classes #requery: true
|
53
|
+
all_classes = get_classes('name').map(&:values).sort.flatten
|
54
|
+
ActiveOrient.database_classes = system_classes.present? ? all_classes : all_classes - system_classes()
|
55
|
+
end
|
56
|
+
ActiveOrient.database_classes
|
57
|
+
end
|
58
|
+
|
59
|
+
def create_vertex_class *name, properties: nil
|
60
|
+
# create_class( :V ) unless database_classes.include? "V"
|
61
|
+
r= name.map{|n| create_class( n, properties: properties){ :V } }
|
62
|
+
@actual_class_hash = get_classes( 'name', 'superClass')
|
63
|
+
r.size == 1 ? r.pop : r
|
64
|
+
end
|
65
|
+
|
66
|
+
def create_edge_class *name, properties: nil
|
67
|
+
# not nessesary. In V 2.2m E+V are present after creation of a database
|
68
|
+
#create_class( :E ) unless database_classes.include? "E"
|
69
|
+
r = name.map{|n| create_class( n, properties: properties){ :E } }
|
70
|
+
@actual_class_hash = get_classes( 'name', 'superClass')
|
71
|
+
r.size == 1 ? r.pop : r # returns the created classes as array if multible classes are provided
|
72
|
+
end
|
73
|
+
|
74
|
+
=begin
|
75
|
+
Service-Method for Model#OrientdbClass
|
76
|
+
=end
|
77
|
+
|
78
|
+
def get_db_superclass name
|
79
|
+
@actual_class_hash = get_classes( 'name', 'superClass') if @actual_class_hash.nil?
|
80
|
+
z= @actual_class_hash.find{|x,y| x['name'] == name.to_s }
|
81
|
+
z['superclass'] unless z.nil?
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
=begin
|
86
|
+
preallocate classes reads any class from the @classes-Array and allocates adequat Ruby-Objects
|
87
|
+
=end
|
88
|
+
def preallocate_classes
|
89
|
+
# first fetch all non-system-classes
|
90
|
+
# io = class_hierarchy
|
91
|
+
# allocate them and call require_model_file on each model
|
92
|
+
# if something goes wrong, allocate_classes_in_ruby returns nil, thus compact prevents
|
93
|
+
# from calling NilClass.require_model_file
|
94
|
+
allocate_classes_in_ruby(class_hierarchy).flatten.compact.each &:require_model_file
|
95
|
+
end
|
96
|
+
|
97
|
+
end # module
|
data/lib/init.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
module ActiveOrient
|
2
|
+
module Init
|
3
|
+
|
4
|
+
=begin
|
5
|
+
Parameters: yml: hash from config.yml , namespace: Class to use as Namespace
|
6
|
+
|
7
|
+
=end
|
8
|
+
def define_namespace yml: {}, namespace: nil
|
9
|
+
ActiveOrient::Model.namespace = if namespace.present?
|
10
|
+
namespace
|
11
|
+
else
|
12
|
+
n= yml[:namespace].presence || :self
|
13
|
+
case n
|
14
|
+
when :self
|
15
|
+
ActiveOrient::Model
|
16
|
+
when :object
|
17
|
+
Object
|
18
|
+
when :active_orient
|
19
|
+
ActiveOrient
|
20
|
+
end
|
21
|
+
end
|
22
|
+
## initialitze Edge and Vertex classes in the namespace
|
23
|
+
# ActiveOrient::Model.orientdb_class( name:"E", superclass: "").new
|
24
|
+
# ActiveOrient::Model.orientdb_class( name:"V", superclass: "").new
|
25
|
+
end # define namespace
|
26
|
+
|
27
|
+
|
28
|
+
def vertex_and_edge_class
|
29
|
+
ORD.create_classes 'E', 'V'
|
30
|
+
E.ref_name = 'E'
|
31
|
+
V.ref_name = 'V'
|
32
|
+
|
33
|
+
end
|
34
|
+
end # module Init
|
35
|
+
end # module ActiveOrient
|
data/lib/java-api.rb
ADDED
@@ -0,0 +1,437 @@
|
|
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
|
+
module OrientDB
|
7
|
+
|
8
|
+
class Document
|
9
|
+
def update_attributes attributes
|
10
|
+
attributes.each do |y,x|
|
11
|
+
self[ y ] = x.to_orient
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end # class Document
|
15
|
+
end
|
16
|
+
|
17
|
+
module ActiveOrient
|
18
|
+
# class Date
|
19
|
+
# def proxy_object
|
20
|
+
# java.util.Date.new year, month - 1, day, 0, 0, 0
|
21
|
+
# end
|
22
|
+
# end
|
23
|
+
# class DateTime
|
24
|
+
# def proxy_object
|
25
|
+
# java.util.Date.new year, month - 1, day, hour, min, sec
|
26
|
+
# end
|
27
|
+
# end
|
28
|
+
|
29
|
+
class API
|
30
|
+
include OrientSupport::Support
|
31
|
+
include DatabaseUtils
|
32
|
+
include ClassUtils
|
33
|
+
include OrientDbPrivate
|
34
|
+
include OrientDB
|
35
|
+
|
36
|
+
mattr_accessor :logger # borrowed from active_support
|
37
|
+
attr_reader :database # Used to read the working database
|
38
|
+
|
39
|
+
#### INITIALIZATION ####
|
40
|
+
|
41
|
+
|
42
|
+
def initialize database: nil, connect: true, preallocate: true
|
43
|
+
self.logger = Logger.new('/dev/stdout') unless logger.present?
|
44
|
+
ActiveOrient.database= database if database.present?
|
45
|
+
connect() if connect
|
46
|
+
ActiveOrient::Model.db = self
|
47
|
+
preallocate_classes if preallocate
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
def db
|
52
|
+
@db
|
53
|
+
end
|
54
|
+
|
55
|
+
# Used for the connection on the server
|
56
|
+
#
|
57
|
+
|
58
|
+
# Used to connect to the database
|
59
|
+
|
60
|
+
def connect
|
61
|
+
begin
|
62
|
+
logger.progname = 'JavaApi#connect'
|
63
|
+
@db = DocumentDatabase.connect("remote:#{ActiveOrient.default_server[:server]}/#{ActiveOrient.database}",
|
64
|
+
ActiveOrient.default_server[:user], ActiveOrient.default_server[:password] )
|
65
|
+
rescue Java::ComOrientechnologiesOrientCoreException::OConfigurationException => e
|
66
|
+
logger.fatal{ e.message}
|
67
|
+
logger.fatal{ "ServerAdim not implemented in ActiveOrient#JavaApi "}
|
68
|
+
# OrientDB::ServerAdmin("remote:localhost").connect( default_server[:user], default_server[:password] )
|
69
|
+
# OrientDB::ServerAdmin.createDatabase(@database, "document", "remote");
|
70
|
+
# OrientDB::ServerAdmin.close();
|
71
|
+
Kernel.exit
|
72
|
+
end
|
73
|
+
database_classes( requery:true ) # returns all allocated database_classes
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
def get_classes *attributes
|
79
|
+
classes= @db.metadata.schema.classes.map{|x| { 'name' => x.name , 'superClass' => x.get_super_class.nil? ? '': x.get_super_class.name } }
|
80
|
+
unless attributes.empty?
|
81
|
+
classes.map{|y| y.select{|v,_| attributes.include?(v)}}
|
82
|
+
else
|
83
|
+
classes
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
def create_classes classes , &b
|
90
|
+
consts = allocate_classes_in_ruby( classes , &b )
|
91
|
+
|
92
|
+
all_classes = consts.is_a?( Array) ? consts.flatten : [consts]
|
93
|
+
database_classes(requery: true)
|
94
|
+
selected_classes = all_classes.map do | this_class |
|
95
|
+
this_class unless database_classes(requery: true).include?( this_class.ref_name ) rescue nil
|
96
|
+
end.compact.uniq
|
97
|
+
command= selected_classes.map do | database_class |
|
98
|
+
## improper initialized ActiveOrient::Model-classes lack a ref_name class-variable
|
99
|
+
next if database_class.ref_name.blank?
|
100
|
+
c = if database_class.superclass == ActiveOrient::Model || database_class.superclass.ref_name.blank?
|
101
|
+
puts "CREATE CLASS #{database_class.ref_name} "
|
102
|
+
OClassImpl.create @db, database_class.ref_name
|
103
|
+
else
|
104
|
+
puts "CREATE CLASS #{database_class.ref_name} EXTENDS #{database_class.superclass.ref_name}"
|
105
|
+
OClassImpl.create @db, database_class.ref_name, superClass: database_class.superclass.ref_name
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# update the internal class hierarchy
|
110
|
+
database_classes requery: true
|
111
|
+
# return all allocated classes, no matter whether they had to be created in the DB or not.
|
112
|
+
# keep the format of the input-parameter
|
113
|
+
consts.shift if block_given? && consts.is_a?( Array) # remove the first element
|
114
|
+
# remove traces of superclass-allocations
|
115
|
+
if classes.is_a? Hash
|
116
|
+
consts = Hash[ consts ]
|
117
|
+
consts.each_key{ |x| consts[x].delete_if{|y| y == x} if consts[x].is_a? Array }
|
118
|
+
end
|
119
|
+
consts
|
120
|
+
end
|
121
|
+
|
122
|
+
|
123
|
+
|
124
|
+
def delete_class o_class
|
125
|
+
begin
|
126
|
+
logger.progname = 'JavaApi#DeleteClass'
|
127
|
+
@db.schema.drop_class classname(o_class)
|
128
|
+
rescue Java::ComOrientechnologiesOrientCoreException::OSchemaException => e
|
129
|
+
logger.error{ e.message }
|
130
|
+
end
|
131
|
+
database_classes requery: true
|
132
|
+
end
|
133
|
+
=begin
|
134
|
+
Creates properties and optional an associated index as defined in the provided block
|
135
|
+
create_properties(classname or class, properties as hash){index}
|
136
|
+
|
137
|
+
The default-case
|
138
|
+
create_properties(:my_high_sophisticated_database_class,
|
139
|
+
con_id: {type: :integer},
|
140
|
+
details: {type: :link, linked_class: 'Contracts'}) do
|
141
|
+
contract_idx: :notunique
|
142
|
+
end
|
143
|
+
|
144
|
+
A composite index
|
145
|
+
create_properties(:my_high_sophisticated_database_class,
|
146
|
+
con_id: {type: :integer},
|
147
|
+
symbol: {type: :string}) do
|
148
|
+
{name: 'indexname',
|
149
|
+
on: [:con_id, :details] # default: all specified properties
|
150
|
+
type: :notunique # default: :unique
|
151
|
+
}
|
152
|
+
end
|
153
|
+
supported types:
|
154
|
+
{
|
155
|
+
:bool => "BOOLEAN",
|
156
|
+
:double => "BYTE",
|
157
|
+
:datetime => "DATE",
|
158
|
+
:float => "FLOAT",
|
159
|
+
:decimal => "DECIMAL",
|
160
|
+
:embedded_list => "EMBEDDEDLIST",
|
161
|
+
:list => "EMBEDDEDLIST",
|
162
|
+
:embedded_map => "EMBEDDEDMAP",
|
163
|
+
:map => "EMBEDDEDMAP",
|
164
|
+
:embedded_set => "EMBEDDEDSET",
|
165
|
+
:set => "EMBEDDEDSET",
|
166
|
+
:int => "INTEGER",
|
167
|
+
:integer => "INTEGER",
|
168
|
+
:link_list => "LINKLIST",
|
169
|
+
:link_map => "LINKMAP",
|
170
|
+
:link_set => "LINKSET",
|
171
|
+
}
|
172
|
+
|
173
|
+
=end
|
174
|
+
def create_properties o_class, **all_properties, &b
|
175
|
+
logger.progname = 'JavaApi#CreateProperties'
|
176
|
+
ap = all_properties
|
177
|
+
index = ap.is_a?(Hash) ? ap[:index] : nil
|
178
|
+
created_properties = ap.map do |property, specification |
|
179
|
+
field_type = ( specification.is_a?( Hash) ? specification[:type] : specification ).downcase.to_sym rescue :string
|
180
|
+
if specification.is_a?(Hash)
|
181
|
+
the_other_class = specification[:other_class].presence || specification[:linked_class]
|
182
|
+
other_class = @db.get_class( the_other_class.to_sym ) if the_other_class.present?
|
183
|
+
if other_class.present?
|
184
|
+
@db.get_class(classname(o_class)).add property,[ field_type, other_class ], { :index => index }
|
185
|
+
else
|
186
|
+
@db.get_class(classname(o_class)).add property, field_type, { :index => index }
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
if block_given?
|
191
|
+
attr = yield
|
192
|
+
index_parameters = case attr
|
193
|
+
when String, Symbol
|
194
|
+
{ name: attr }
|
195
|
+
when Hash
|
196
|
+
{ name: attr.keys.first , type: attr.values.first, on: all_properties.keys.map(&:to_s) }
|
197
|
+
else
|
198
|
+
nil
|
199
|
+
end
|
200
|
+
create_index o_class, **index_parameters unless index_parameters.blank?
|
201
|
+
end
|
202
|
+
created_properties.size # return_value
|
203
|
+
|
204
|
+
end
|
205
|
+
|
206
|
+
def get_properties o_class
|
207
|
+
@db.get_class(classname(o_class)).propertiesMap
|
208
|
+
end
|
209
|
+
|
210
|
+
|
211
|
+
|
212
|
+
def create_index o_class, name:, on: :automatic, type: :unique
|
213
|
+
logger.progname = 'JavaApi#CreateIndex'
|
214
|
+
begin
|
215
|
+
c = @db.get_class( classname( o_class ))
|
216
|
+
index = if on == :automatic
|
217
|
+
nil # not implemented
|
218
|
+
elsif on.is_a? Array
|
219
|
+
c.createIndex name.to_s, INDEX_TYPES[type], *on
|
220
|
+
else
|
221
|
+
c.createIndex name.to_s, INDEX_TYPES[type], on
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
def create_record o_class, attributes: {}
|
226
|
+
logger.progname = 'JavaApi#CreateRecord'
|
227
|
+
attributes = yield if attributes.empty? && block_given?
|
228
|
+
new_record = insert_document( classname(o_class), attributes.to_orient )
|
229
|
+
|
230
|
+
|
231
|
+
end
|
232
|
+
alias create_document create_record
|
233
|
+
|
234
|
+
def upsert o_class, set: {}, where: {}
|
235
|
+
logger.progname = 'JavaApi#Upsert'
|
236
|
+
if where.blank?
|
237
|
+
new_record = create_record(o_class, attributes: set)
|
238
|
+
yield new_record if block_given? # in case if insert execute optional block
|
239
|
+
new_record # return_value
|
240
|
+
else
|
241
|
+
specify_return_value = block_given? ? "" : "return after @this"
|
242
|
+
set.merge! where if where.is_a?( Hash ) # copy where attributes to set
|
243
|
+
command = "Update #{classname(o_class)} set #{generate_sql_list( set ){','}} upsert #{specify_return_value} #{compose_where where}"
|
244
|
+
result = @db.run_command command
|
245
|
+
|
246
|
+
case result
|
247
|
+
when Java::JavaUtil::ArrayList
|
248
|
+
update_document result[0]
|
249
|
+
when ActiveOrient::Model
|
250
|
+
result # just return the result
|
251
|
+
when String, Numeric
|
252
|
+
the_record= get_records(from: o_class, where: where, limit: 1).pop
|
253
|
+
if result.to_i == 1 # one dataset inserted, block is specified
|
254
|
+
yield the_record
|
255
|
+
end
|
256
|
+
the_record # return_value
|
257
|
+
else
|
258
|
+
logger.error{ "Unexpected result form Query \n #{command} \n Result: #{result}" }
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
262
|
+
def get_records raw: false, query: nil, **args
|
263
|
+
query = OrientSupport::OrientQuery.new(args) if query.nil?
|
264
|
+
logger.progname = 'JavaApi#GetRecords'
|
265
|
+
result = @db.custom query.compose
|
266
|
+
result.map do |record|
|
267
|
+
update_document record
|
268
|
+
end
|
269
|
+
end
|
270
|
+
alias get_documents get_records
|
271
|
+
|
272
|
+
# called by Model.autoload
|
273
|
+
def get_record rid
|
274
|
+
logger.progname = 'JavaApi#GetRecord'
|
275
|
+
rid = "#"+ rid unless rid[0]=='#'
|
276
|
+
record = @db.custom "select from #{rid}"
|
277
|
+
if record.count.zero?
|
278
|
+
logger.error{ "No record found for rid= #{rid}" }
|
279
|
+
else
|
280
|
+
yield( record[0] ) if block_given?
|
281
|
+
update_document record[0]
|
282
|
+
end
|
283
|
+
end
|
284
|
+
alias get_document get_record
|
285
|
+
|
286
|
+
=begin
|
287
|
+
executes a command as sql-query
|
288
|
+
|
289
|
+
|
290
|
+
=end
|
291
|
+
def execute transaction: true, tolerated_error_code: nil , process_error: true# Set up for classes
|
292
|
+
batch = {transaction: transaction, operations: yield}
|
293
|
+
|
294
|
+
logger.progname= "Execute"
|
295
|
+
unless batch[:operations].blank?
|
296
|
+
unless batch[:operations].is_a? Array
|
297
|
+
batch[:operations] = [batch[:operations]]
|
298
|
+
was_array = true
|
299
|
+
else
|
300
|
+
was_array = false
|
301
|
+
end
|
302
|
+
answer = batch[:operations].map do |command_record|
|
303
|
+
return if command_record.blank?
|
304
|
+
begin
|
305
|
+
response = @db.run_command command_record.is_a?(Hash) ? command_record[:command] : command_record
|
306
|
+
rescue Java::ComOrientechnologiesOrientCoreStorage::ORecordDuplicatedException => e
|
307
|
+
# puts e.inspect
|
308
|
+
# puts "GetMESSAGE: "+e.getMessage.split(/\r/).first
|
309
|
+
# puts "GetComponentName: "+e.getComponentName.to_s
|
310
|
+
# puts e.getMessage =~ tolerated_error_code
|
311
|
+
# puts "----"
|
312
|
+
if tolerated_error_code.present? && e.getMessage =~ tolerated_error_code
|
313
|
+
logger.info{ "tolerated_error::#{ e.get_message.split(/\r/).first }"}
|
314
|
+
next
|
315
|
+
else
|
316
|
+
# if process_error
|
317
|
+
logger.progname = 'JavaApi#Execute'
|
318
|
+
logger.error{ e }
|
319
|
+
# else
|
320
|
+
# puts e.inspect
|
321
|
+
# raise ArgumentError, e, caller
|
322
|
+
end
|
323
|
+
end
|
324
|
+
if response.is_a? Fixnum
|
325
|
+
response
|
326
|
+
else
|
327
|
+
response.map do | r |
|
328
|
+
if r.is_a? Document
|
329
|
+
if r.rid.rid?
|
330
|
+
update_document r
|
331
|
+
else
|
332
|
+
ActiveOrient::Model.orientdb_class( name: 'query').new r
|
333
|
+
end
|
334
|
+
else
|
335
|
+
puts "Strange things happen in execute: #{r.inspect}"
|
336
|
+
r.values
|
337
|
+
end
|
338
|
+
end # map response
|
339
|
+
end # branch response_is_a
|
340
|
+
end # map batch
|
341
|
+
answer.pop if answer.size==1 && answer.first.is_a?(Array)
|
342
|
+
end # unless
|
343
|
+
end
|
344
|
+
|
345
|
+
|
346
|
+
def delete_record *object_or_rid
|
347
|
+
object_or_rid.map do |o|
|
348
|
+
d= case o
|
349
|
+
when String
|
350
|
+
@db.custom "select from #{o}" if o.rid?
|
351
|
+
when ActiveOrient::Model
|
352
|
+
@db.custom "select from #{o.to_orient}"
|
353
|
+
when Array
|
354
|
+
o.map{|y| delete_record y }
|
355
|
+
return o
|
356
|
+
else
|
357
|
+
o
|
358
|
+
end
|
359
|
+
if d.is_a? Java::ComOrientechnologiesOrientCoreSqlQuery::OConcurrentResultSet
|
360
|
+
d.each &:delete
|
361
|
+
else
|
362
|
+
logger.progname = 'JavaApi#DeleteRecord'
|
363
|
+
logger.error{ "Removal Failed: #{d.inspect} " }
|
364
|
+
end
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
=begin
|
369
|
+
Transfer changes in attributes to document first and save the document
|
370
|
+
No SQL involved
|
371
|
+
=end
|
372
|
+
|
373
|
+
def update rid, attributes=nil, version=nil
|
374
|
+
record = ActiveOrient::Model.autoload_object rid.rid
|
375
|
+
record.document.update_attributes attributes if attributes.present?
|
376
|
+
record.document.save
|
377
|
+
record.attributes.merge! attributes if attributes.present?
|
378
|
+
record # return_value
|
379
|
+
end
|
380
|
+
|
381
|
+
# old code
|
382
|
+
# get_record( rid.rid ) do | db_obj |
|
383
|
+
# db_obj.update_attributes attributes
|
384
|
+
## puts db_obj.inspect
|
385
|
+
# db_obj.save
|
386
|
+
# end if rid.rid.present?
|
387
|
+
# end
|
388
|
+
|
389
|
+
|
390
|
+
|
391
|
+
#private
|
392
|
+
|
393
|
+
def insert_document o_class, attributes
|
394
|
+
logger.progname = 'JavaApi#InsertDocument'
|
395
|
+
d = Document.new classname(o_class)
|
396
|
+
d.update_attributes attributes
|
397
|
+
d.save
|
398
|
+
ActiveOrient::Model.get_model_class(o_class).new d
|
399
|
+
rescue Java::ComOrientechnologiesOrientCoreException::ODatabaseException => e
|
400
|
+
logger.fatal{ "Insert failed => #{d.inspect}"}
|
401
|
+
logger.error{ "Parameter: Class: #{classname(o_class)} attributes: #{attributes.inspect}" }
|
402
|
+
logger.fatal{ e }
|
403
|
+
rescue Java::ComOrientechnologiesOrientCoreException::OSchemaException => e
|
404
|
+
logger.error{ e }
|
405
|
+
|
406
|
+
logger.error{ "Parameter: DB: #{@db.name}, Class: #{classname(o_class)} attributes: #{attributes.inspect}" }
|
407
|
+
logger.error{ database_classes.inspect }
|
408
|
+
end
|
409
|
+
# returns a valid model-instance
|
410
|
+
def update_document java_document
|
411
|
+
if java_document.is_a? Document
|
412
|
+
o_class = java_document.class_name
|
413
|
+
java_document.save
|
414
|
+
d = java_document
|
415
|
+
ActiveOrient::Model.get_model_class(o_class).new java_document
|
416
|
+
else
|
417
|
+
logger.progname = 'JavaApi#UpdateDocument'
|
418
|
+
logger.error{ "Wrong Parameter: #{java_document.inspect} "}
|
419
|
+
end
|
420
|
+
end #def
|
421
|
+
|
422
|
+
|
423
|
+
def manipulate_relation record, method, array, items
|
424
|
+
java_document = record.document
|
425
|
+
method = method.to_s.downcase.to_sym
|
426
|
+
case method
|
427
|
+
when :add
|
428
|
+
items.each{|x| java_document[array] << x}
|
429
|
+
when :remove
|
430
|
+
items.each{|x| java_document[array].delete x}
|
431
|
+
else
|
432
|
+
|
433
|
+
end
|
434
|
+
java_document.save
|
435
|
+
end
|
436
|
+
end
|
437
|
+
end
|