active-orient 0.4 → 0.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|