active-orient 0.4 → 0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/Gemfile +8 -3
  4. data/Guardfile +12 -4
  5. data/README.md +221 -201
  6. data/VERSION +1 -1
  7. data/active-orient.gemspec +3 -2
  8. data/bin/active-orient-console +35 -0
  9. data/config/boot.rb +84 -16
  10. data/config/config.yml +10 -0
  11. data/config/connect.yml +6 -2
  12. data/create_project +19 -0
  13. data/examples/books.rb +86 -39
  14. data/examples/createTime.rb +91 -0
  15. data/examples/streets.rb +85 -84
  16. data/examples/test_commands.rb +92 -0
  17. data/examples/test_commands_2.rb +54 -0
  18. data/examples/test_commands_3.rb +48 -0
  19. data/examples/test_commands_4.rb +28 -0
  20. data/examples/time_graph/Gemfile +21 -0
  21. data/examples/time_graph/Guardfile +26 -0
  22. data/examples/time_graph/README.md +129 -0
  23. data/examples/time_graph/bin/active-orient-console +35 -0
  24. data/examples/time_graph/config/boot.rb +119 -0
  25. data/examples/time_graph/config/config.yml +8 -0
  26. data/examples/time_graph/config/connect.yml +17 -0
  27. data/examples/time_graph/config/init_db.rb +59 -0
  28. data/examples/time_graph/createTime.rb +51 -0
  29. data/examples/time_graph/lib/createTime.rb +82 -0
  30. data/examples/time_graph/model/day_of.rb +3 -0
  31. data/examples/time_graph/model/e.rb +6 -0
  32. data/examples/time_graph/model/edge.rb +53 -0
  33. data/examples/time_graph/model/monat.rb +19 -0
  34. data/examples/time_graph/model/stunde.rb +16 -0
  35. data/examples/time_graph/model/tag.rb +29 -0
  36. data/examples/time_graph/model/time_base.rb +6 -0
  37. data/examples/time_graph/model/time_of.rb +4 -0
  38. data/examples/time_graph/model/v.rb +3 -0
  39. data/examples/time_graph/model/vertex.rb +32 -0
  40. data/examples/time_graph/spec/lib/create_time_spec.rb +50 -0
  41. data/examples/time_graph/spec/rest_helper.rb +37 -0
  42. data/examples/time_graph/spec/spec_helper.rb +46 -0
  43. data/lib/active-orient.rb +56 -6
  44. data/lib/base.rb +149 -147
  45. data/lib/base_properties.rb +40 -41
  46. data/lib/class_utils.rb +301 -0
  47. data/lib/database_utils.rb +97 -0
  48. data/lib/init.rb +35 -0
  49. data/lib/java-api.rb +437 -0
  50. data/lib/jdbc.rb +211 -0
  51. data/lib/model/edge.rb +53 -0
  52. data/lib/model/model.rb +77 -0
  53. data/lib/model/the_class.rb +480 -0
  54. data/lib/model/the_record.rb +310 -0
  55. data/lib/model/vertex.rb +32 -0
  56. data/lib/orient.rb +113 -50
  57. data/lib/orientdb_private.rb +48 -0
  58. data/lib/other.rb +280 -0
  59. data/lib/query.rb +71 -73
  60. data/lib/rest/change.rb +124 -0
  61. data/lib/rest/create.rb +474 -0
  62. data/lib/rest/delete.rb +133 -0
  63. data/lib/rest/operations.rb +150 -0
  64. data/lib/rest/read.rb +150 -0
  65. data/lib/rest/rest.rb +111 -0
  66. data/lib/rest_disabled.rb +24 -0
  67. data/lib/support.rb +387 -296
  68. data/old_lib_functions/two_general_class.rb +139 -0
  69. data/usecase.md +49 -36
  70. data/usecase_oo.md +59 -0
  71. metadata +73 -9
  72. data/lib/model.rb +0 -461
  73. data/lib/rest.rb +0 -1036
  74. data/test.rb +0 -4
@@ -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
@@ -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
@@ -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