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.
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