active-orient 0.4 → 0.80

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 (61) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -0
  3. data/.graphs.txt.swp +0 -0
  4. data/Gemfile +9 -5
  5. data/Guardfile +12 -4
  6. data/README.md +70 -281
  7. data/VERSION +1 -1
  8. data/active-orient.gemspec +9 -7
  9. data/bin/active-orient-0.6.gem +0 -0
  10. data/bin/active-orient-console +97 -0
  11. data/changelog.md +60 -0
  12. data/config/boot.rb +70 -17
  13. data/config/config.yml +10 -0
  14. data/config/connect.yml +11 -6
  15. data/examples/books.rb +154 -65
  16. data/examples/streets.rb +89 -85
  17. data/graphs.txt +70 -0
  18. data/lib/active-orient.rb +78 -6
  19. data/lib/base.rb +266 -168
  20. data/lib/base_properties.rb +76 -65
  21. data/lib/class_utils.rb +187 -0
  22. data/lib/database_utils.rb +99 -0
  23. data/lib/init.rb +80 -0
  24. data/lib/java-api.rb +442 -0
  25. data/lib/jdbc.rb +211 -0
  26. data/lib/model/custom.rb +29 -0
  27. data/lib/model/e.rb +6 -0
  28. data/lib/model/edge.rb +114 -0
  29. data/lib/model/model.rb +134 -0
  30. data/lib/model/the_class.rb +657 -0
  31. data/lib/model/the_record.rb +313 -0
  32. data/lib/model/vertex.rb +371 -0
  33. data/lib/orientdb_private.rb +48 -0
  34. data/lib/other.rb +423 -0
  35. data/lib/railtie.rb +68 -0
  36. data/lib/rest/change.rb +150 -0
  37. data/lib/rest/create.rb +287 -0
  38. data/lib/rest/delete.rb +150 -0
  39. data/lib/rest/operations.rb +222 -0
  40. data/lib/rest/read.rb +189 -0
  41. data/lib/rest/rest.rb +120 -0
  42. data/lib/rest_disabled.rb +24 -0
  43. data/lib/support/conversions.rb +42 -0
  44. data/lib/support/default_formatter.rb +7 -0
  45. data/lib/support/errors.rb +41 -0
  46. data/lib/support/logging.rb +38 -0
  47. data/lib/support/orient.rb +305 -0
  48. data/lib/support/orientquery.rb +647 -0
  49. data/lib/support/query.rb +92 -0
  50. data/rails.md +154 -0
  51. data/rails/activeorient.rb +32 -0
  52. data/rails/config.yml +10 -0
  53. data/rails/connect.yml +17 -0
  54. metadata +89 -30
  55. data/lib/model.rb +0 -461
  56. data/lib/orient.rb +0 -98
  57. data/lib/query.rb +0 -88
  58. data/lib/rest.rb +0 -1036
  59. data/lib/support.rb +0 -347
  60. data/test.rb +0 -4
  61. data/usecase.md +0 -91
@@ -0,0 +1,150 @@
1
+ module RestDelete
2
+
3
+ ######### DATABASE ##########
4
+
5
+ =begin
6
+ Deletes the database and returns true on success
7
+ After the removal of the database, the working-database might be empty
8
+ =end
9
+
10
+ def delete_database database:
11
+ logger.progname = 'RestDelete#DeleteDatabase'
12
+ old_ds = ActiveOrient.database
13
+ change_database database
14
+ begin
15
+ response = nil
16
+ ActiveOrient.db_pool.checkout do | conn |
17
+ response = conn["/database/#{ActiveOrient.database}"].delete
18
+ end
19
+ if database == old_ds
20
+ change_database 'temp'
21
+ logger.info{"Working database deleted, switched to temp"}
22
+ else
23
+ change_database old_ds
24
+ logger.info{"Database #{database} deleted, working database is still #{ActiveOrient.database}"}
25
+ end
26
+ rescue RestClient::InternalServerError => e
27
+ change_database old_ds
28
+ logger.info{"Database #{database} NOT deleted, working database is still #{ActiveOrient.database}"}
29
+ end
30
+ !response.nil? && response.code == 204 ? true : false
31
+ end
32
+
33
+ ######### CLASS ##########
34
+
35
+ =begin
36
+ Deletes the specified class and returns true on success
37
+
38
+ The class-entries in ActiveOrient.database_classes and ORD.database_classes are removed.
39
+
40
+ The Ruby-Class itself is untouched.
41
+ However, any furter operation leads to an Error.
42
+
43
+ todo: remove all instances of the class
44
+ =end
45
+
46
+ def delete_class o_class
47
+ cl = classname(o_class).to_s
48
+ return if cl.nil?
49
+ logger.progname = 'RestDelete#DeleteClass'
50
+
51
+ begin
52
+ ## to do: if cl contains special characters, enclose with backticks
53
+ response = nil
54
+ ActiveOrient.db_pool.checkout do | conn |
55
+ response = conn["/class/#{ActiveOrient.database}/#{cl}"].delete
56
+ end
57
+ if response.code == 204
58
+ logger.info{"Class #{cl} deleted."}
59
+
60
+ ActiveOrient.database_classes.delete(cl)
61
+ end
62
+ rescue RestClient::InternalServerError => e
63
+ sentence= JSON.parse( e.response)['errors'].last['content']
64
+ if ActiveOrient.database_classes.has_key? cl
65
+ logger.error{"Class #{cl} still present."}
66
+ logger.error{ sentence }
67
+ false
68
+ else
69
+ logger.error{e.inspect}
70
+ true
71
+ end
72
+ rescue Exception => e
73
+ logger.error{e.message}
74
+ logger.error{e.inspect}
75
+ end
76
+ end
77
+
78
+ ############## RECORD #############
79
+
80
+ =begin
81
+ Deletes a single Record when providing a single rid-link (#00:00) or a record
82
+
83
+ Deletes multible Records when providing a list of rid-links or a record
84
+
85
+ Todo: implement delete_edges after querying the database in one statement
86
+
87
+ Example:
88
+ V.create_class :test
89
+ record = Test.new something: 'something'
90
+ V.db.delete_record record
91
+
92
+ records= (1..100).map{|x| Test.create something: x }
93
+ V.db.delete_record *records
94
+
95
+ delete_records provides the removal of datasets after quering the database.
96
+ =end
97
+
98
+ def delete_record *o
99
+ logger.progname = "ActiveOrient::RestDelete#DeleteRecord"
100
+ #o.map( &:to_orient ).map do |r|
101
+ o.orient_flatten.map do |r|
102
+ rr = r.to_human
103
+ begin
104
+ ActiveOrient::Base.remove_rid r
105
+ # rest_resource["/document/#{ActiveOrient.database}/#{r[1..-1].to_or}"].delete
106
+ ActiveOrient.db_pool.checkout do | conn |
107
+ conn["/document/#{ActiveOrient.database}/#{r.rid}"].delete
108
+ end
109
+
110
+ rescue RestClient::InternalServerError => e
111
+ logger.error{"Record #{rr} NOT deleted"}
112
+ rescue RestClient::ResourceNotFound
113
+ logger.error{"Record #{rr} does not exist in the database"}
114
+ rescue RestClient::BadRequest => e
115
+ logger.error{"tried to delete #{rr}, but something went wrong"}
116
+ raise
117
+ else
118
+ logger.info{"Record #{rr} deleted"}
119
+ end
120
+ end
121
+ end
122
+ alias delete_document delete_record
123
+
124
+ =begin
125
+ Deletes records. They are defined by a query. All records which match the attributes are deleted.
126
+ An Array with freed index-values is returned
127
+ =end
128
+
129
+ def delete_records o_class, where: {}
130
+ logger.progname = 'RestDelete#DeleteRecords'
131
+ get_records(from: o_class, where: where).each{|y| delete_record y}
132
+ end
133
+ alias delete_documents delete_records
134
+
135
+ ################ PROPERTY #############
136
+
137
+ def delete_property o_class, field
138
+ logger.progname = 'RestDelete#DeleteProperty'
139
+ begin
140
+ response = ActiveOrient.db_pool.checkout do | conn |
141
+ r = conn["/property/#{ActiveOrient.database}/#{classname(o_class)}/#{field}"].delete
142
+ true if r.code == 204
143
+ end
144
+ rescue RestClient::InternalServerError => e
145
+ logger.error{"Property #{field} in class #{classname(o_class)} NOT deleted" }
146
+ false
147
+ end
148
+ end
149
+
150
+ end
@@ -0,0 +1,222 @@
1
+ module RestOperations
2
+
3
+ # Execute a predefined Function
4
+
5
+ # untested
6
+ def call_function *args
7
+ # puts "uri:#{function_uri { args.join('/') } }"
8
+ begin
9
+ term = args.join('/')
10
+ rest_resource = Thread.current['resource'] || get_resource
11
+ rest_resource["/function/#{@database}/#{term}"].post ''
12
+ rescue RestClient::InternalServerError => e
13
+ puts JSON.parse(e.http_body)
14
+ end
15
+ end
16
+
17
+ # Used to count the Records in relation of the arguments
18
+ #
19
+ # Overwritten by Model#Count
20
+ def count **args
21
+ logger.progname = 'RestOperations#CountRecords'
22
+ query = OrientSupport::OrientQuery.new args
23
+ query.projection 'COUNT(*)'
24
+ result = get_records raw: true, query: query
25
+ result.first["COUNT(*)"] rescue 0 # return_value
26
+ end
27
+ =begin
28
+ --
29
+ ## historic method
30
+ # def manipulate_relation record, method, array, items # :nodoc: #
31
+ # execute_array = Array.new
32
+ # method = method.to_s.upcase
33
+ #
34
+ # add_2_execute_array = -> (it) do
35
+ # command = "UPDATE ##{record.rid} #{method} #{array} = #{it.to_or } " #updating}"
36
+ # command.gsub!(/\"/,"") if it.is_a? Array
37
+ # puts "COMMAND:: #{command}"
38
+ # execute_array << {type: "cmd", language: "sql", command: command}
39
+ # end
40
+ #
41
+ # items.to_a.each{|x| add_2_execute_array[x] }
42
+ ## puts "******************"
43
+ ## puts record.inspect
44
+ ## puts "-----"
45
+ ## puts execute_array.join('\n')
46
+ # r= execute{ execute_array }
47
+ # puts record.inspect
48
+ # puts r.inspect
49
+ ## puts "******************"
50
+ # if r.present?
51
+ # case method
52
+ # when 'ADD'
53
+ # items.each{|x| record.attributes[array] << x}
54
+ # when 'REMOVE'
55
+ # items.map{|x| record.attributes[array].delete x}
56
+ # else
57
+ # end
58
+ # record.increment_version
59
+ # end
60
+ # end
61
+ ++
62
+ =end
63
+
64
+
65
+ =begin
66
+ Executes a list of commands and returns the result-array (if present)
67
+
68
+ (External use)
69
+
70
+ If soley a string is provided in the block, a minimal database-console is realized.
71
+ i.e.
72
+
73
+ ORD.execute{ 'select from #25:0' }
74
+
75
+ (Internal Use)
76
+
77
+ Structure of the provided block:
78
+ [{type: "cmd", language: "sql", command: "create class Person extends V"}, (...)]
79
+ --
80
+ It was first used by ActiveOrient::Query.execute_queries
81
+ Later I (topofocus) discovered that some Queries are not interpretated correctly by #GetRecords but are submitted without Error via batch-processing.
82
+ For instance, this valid query
83
+ select expand(first_list[5].second_list[9]) from base where label = 9
84
+ can only be submitted via batch
85
+ ++
86
+ Parameters:
87
+
88
+ transaction: true|false Perform the batch as transaction
89
+ tolerate_error_code: /a regular expression/
90
+ Statements to execute are provided via block
91
+ These statements are translated to json and transmitted to the database. Example:
92
+
93
+ { type: "cmd",
94
+ language: 'sql',
95
+ command: "CREATE EDGE #{classname(o_class)} FROM #{from.to_orient} TO #{to.to_orient}"}
96
+
97
+ Multible statements are transmitted at once if the Block provides an Array of statements.
98
+
99
+
100
+ =end
101
+
102
+ def read_transaction
103
+ @transaction
104
+ end
105
+
106
+ def manage_transaction kind, command
107
+ @transaction = [] unless @transaction.is_a?(Array)
108
+
109
+ # in any case: add statement to array
110
+ command.is_a?(Array) ? command.each{|c| @transaction << c} : @transaction << command
111
+
112
+ # if kind is prepare, we a done.
113
+ # now, combine anything
114
+ unless kind == :prepare
115
+ commands = @transaction.map{|y| y if y.is_a? String }.compact
116
+ @transaction.delete_if{|y| y if y.is_a?(String)}
117
+ #puts "tn #{commands.inspect}"
118
+ @transaction << { type: 'script', language: 'sql', script: commands } unless commands.empty?
119
+ # elsif transaction == false
120
+ # @transaction = commands.first
121
+ # else
122
+ # transaction = true
123
+ # @transaction << { type: 'cmd', language: 'sql', command: commands.first }
124
+
125
+ # transaction is true only for multible statements
126
+ # batch[:transaction] = transaction & batch[:operations].size >1
127
+ # logger.info{ @transaction.map{|y|y[:command]}.join(";\n ") }
128
+ # logger.info{ @transaction.map{|y|y[:script]}.join(";\n ") }
129
+ # batch= { transaction: transaction, operations: @transaction }
130
+ # puts "batch: #{batch.inspect}"
131
+
132
+ # @res["/batch/#{ActiveOrient.database}"].post batch.to_json
133
+ end
134
+ end
135
+
136
+ # execute the command
137
+ #
138
+ # thread-safe ( transaction = false)
139
+ #
140
+ #
141
+ def execute transaction: nil,
142
+ command: nil,
143
+ tolerated_error_code: nil,
144
+ process_error: true,
145
+ raw: nil
146
+
147
+ if block_given?
148
+ command = yield
149
+ end
150
+ unless command.present?
151
+ logger.error { "No Command provided to execute" }
152
+ return nil
153
+ end
154
+ if ( transaction.present? || command.is_a?(Array) )
155
+ logger.error "calling manage_transaction NOT IMPLEMENTED YET!"
156
+ manage_transaction transaction, command
157
+ end
158
+
159
+ logger.info command.to_s
160
+ _execute( tolerated_error_code, process_error, raw) do
161
+
162
+ ActiveOrient.db_pool.checkout do | conn |
163
+ conn["/command/#{ActiveOrient.database}/sql"].post command.to_s #.to_json
164
+ end
165
+ end
166
+
167
+ # rest_resource.delete #if resource.present?
168
+
169
+ end
170
+
171
+
172
+ def _execute tolerated_error_code, process_error, raw
173
+
174
+ logger.progname= "Execute"
175
+
176
+ begin
177
+ response = yield
178
+ rescue RestClient::BadRequest => f
179
+ # extract the misspelled query in logfile and abort
180
+ sentence= JSON.parse( f.response)['errors'].last['content']
181
+ logger.fatal{ " BadRequest --> #{sentence.split("\n")[1]} " }
182
+ puts "Query not recognized"
183
+ puts sentence
184
+ raise
185
+ rescue RestClient::Conflict => e # (409)
186
+ # most probably the server is busy. we wait for a second print an Error-Message and retry
187
+ sleep(1)
188
+ logger.error{ e.inspect }
189
+ logger.error{ "RestClient::Error(409): Server is signaling a conflict ... retrying" }
190
+ retry
191
+ rescue RestClient::InternalServerError => e
192
+ sentence= JSON.parse( e.response)['errors'].last['content']
193
+ if tolerated_error_code.present? && e.response =~ tolerated_error_code
194
+ logger.debug('RestOperations#Execute'){ "tolerated_error::#{e.message}"}
195
+ logger.debug('RestOperations#Execute'){ e.message }
196
+ nil # return value
197
+ else
198
+ if process_error
199
+ logger.error{sentence}
200
+ #logger.error{ e.backtrace.map {|l| " #{l}\n"}.join }
201
+ # logger.error{e.message.to_s}
202
+ else
203
+ raise
204
+ end
205
+ end
206
+ rescue Errno::EADDRNOTAVAIL => e
207
+ sleep(2)
208
+ retry
209
+ else # code to execute if no exception is raised
210
+ if response.code == 200
211
+ result=JSON.parse(response.body)['result']
212
+ if raw.present?
213
+ result
214
+ else
215
+ result.from_orient
216
+ end # raw present?
217
+ else
218
+ logger.error { "code : #{response.code}" }
219
+ end
220
+ end
221
+ end
222
+ end
@@ -0,0 +1,189 @@
1
+ module RestRead
2
+
3
+ ############# DATABASE #############
4
+
5
+ # Returns an Array with available Database-Names as Elements
6
+ #
7
+ # ORD.get_databases
8
+ # => ["temp", "GratefulDeadConcerts", (...)]
9
+ def get_databases
10
+
11
+ ActiveOrient.db_pool.checkout do | conn |
12
+ JSON.parse(conn["/listDatabases"].get.body)['databases']
13
+ end
14
+ end
15
+
16
+ =begin
17
+ Returns an Array with (unmodified) Class-attribute-hash-Elements
18
+
19
+ »get_classes 'name', 'superClass'« returns
20
+ [ {"name"=>"E", "superClass"=>""},
21
+ {"name"=>"OFunction", "superClass"=>""},
22
+ {"name"=>"ORole", "superClass"=>"OIdentity"}
23
+ (...) ]
24
+ =end
25
+ def get_classes *attributes
26
+ begin
27
+ response = ActiveOrient.db_pool.checkout do | conn |
28
+ conn["/database/#{ActiveOrient.database}"].get
29
+ end
30
+ if response.code == 200
31
+ classes = JSON.parse(response.body)['classes']
32
+ unless attributes.empty?
33
+ classes.map{|y| y.select{|v,_| attributes.include?(v)}}
34
+ else
35
+ classes
36
+ end
37
+ else
38
+ []
39
+ end
40
+ rescue Exception => e
41
+ logger.progname = 'RestRead#GetClasses'
42
+ logger.error{e.message}
43
+ end
44
+ end
45
+
46
+
47
+ ############### CLASS ################
48
+
49
+ # Returns a JSON of the property of a class
50
+ #
51
+ # ORD.create_vertex_class a:
52
+ # ORD.get_class_properties A
53
+ # => {"name"=>"a", "superClass"=>"V", "superClasses"=>["V"], "alias"=>nil, "abstract"=>false, "strictmode"=>false, "clusters"=>[65, 66, 67, 68], "defaultCluster"=>65, "clusterSelection"=>"round-robin", "records"=>3}
54
+ #
55
+ def get_class_properties o_class
56
+ ActiveOrient.db_pool.checkout do | conn |
57
+ JSON.parse(conn["/class/#{ActiveOrient.database}/#{classname(o_class)}"].get)
58
+ end
59
+ rescue => e
60
+ logger.error e.message
61
+ nil
62
+ end
63
+
64
+
65
+ def print_class_properties o_class
66
+ puts "Detected Properties for class #{classname(o_class)}"
67
+ rp = get_class_properties o_class
68
+ n = rp['name']
69
+ if rp['properties'].nil?
70
+ puts "No property available"
71
+ else
72
+ puts rp['properties'].map{|x| "\t"+[n+'.'+x['name'], x['type'],x['linkedClass']].compact.join("\t-> ")}.join("\n")
73
+ end
74
+ rescue NoMethodError
75
+ puts "Class #{o_class} not present in database"
76
+ end
77
+
78
+ ############## OBJECT #################
79
+
80
+ =begin
81
+ Retrieves a Record from the Database
82
+
83
+ The argument can either be a rid "#{x}:{y}" or a link "{x}:{y}".
84
+
85
+ (to be specific: it must provide the methods rid? and to_orient, the latter must return the rid: "#[a}:{b}".)
86
+
87
+ If no Record is found, nil is returned
88
+
89
+ The rid-cache is not used or updated
90
+ =end
91
+
92
+ def get_record rid
93
+ begin
94
+ logger.progname = 'RestRead#GetRecord'
95
+ if rid.rid?
96
+
97
+ response = ActiveOrient.db_pool.checkout do | conn |
98
+ conn["/document/#{ActiveOrient.database}/#{rid.to_orient[1..-1]}"].get
99
+ end
100
+ raw_data = JSON.parse(response.body)
101
+ # ActiveOrient::Model.use_or_allocate( raw_data['@rid'] ) do
102
+ the_object= ActiveOrient::Model.orientdb_class(name: raw_data['@class']).new raw_data
103
+ ActiveOrient::Base.store_rid( the_object ) # update cache
104
+ else
105
+ logger.error { "Wrong parameter #{rid.inspect}. " }
106
+ nil
107
+ end
108
+ rescue RestClient::InternalServerError => e
109
+ if e.http_body.split(':').last =~ /was not found|does not exist in database/
110
+ nil
111
+ else
112
+ logger.error { "Something went wrong" }
113
+ logger.error { e.http_body.inspect }
114
+ raise
115
+ end
116
+ rescue RestClient::ResourceNotFound => e
117
+ logger.error { "RID: #{rid} ---> No Record present " }
118
+ ActiveOrient::Model.remove_rid rid # remove rid from cache
119
+ nil
120
+ rescue NoMethodError => e
121
+ logger.fatal { "---------------- Serious Trouble ----------------" }
122
+ logger.fatal { "GetRecord raw-data: #{raw_data}" }
123
+ logger.error { "GetRecord could not allocate Model-Instance" }
124
+ logger.error { "is a model file required but missing?" }
125
+ raise
126
+
127
+ rescue Exception => e
128
+ logger.error { "Something went wrong" }
129
+ logger.error { "RID: #{rid} - #{e.message}" }
130
+ raise
131
+ end
132
+ end
133
+ alias get_document get_record
134
+
135
+ =begin
136
+ Retrieves Records from a query
137
+
138
+ If raw is specified, the JSON-Array is returned, e.g.
139
+ {"@type"=>"d", "@rid"=>"#15:1", "@version"=>1, "@class"=>"DocumebntKlasse10", "con_id"=>343, "symbol"=>"EWTZ"}
140
+
141
+ Otherwise ActiveModel-Instances are created and returned.
142
+ In this case cached data are used in favour and its not checked if the database contents have changed.
143
+ =end
144
+
145
+ def get_records raw: false, query: nil, **args
146
+ query = OrientSupport::OrientQuery.new(args) if query.nil?
147
+ begin
148
+ logger.progname = 'RestRead#GetRecords'
149
+ response = ActiveOrient.db_pool.checkout do | conn |
150
+ url = "/query/#{ActiveOrient.database}/sql/" + query.compose(destination: :rest) + "/#{query.get_limit}"
151
+ conn[URI.encode(url)].get
152
+ end
153
+ JSON.parse(response.body)['result'].map do |record|
154
+ if raw
155
+ record
156
+ # query returns an anonymus class: Use the provided Block or the Dummy-Model MyQuery
157
+ elsif record['@class'].blank?
158
+ block_given? ? yield.new(record) : ActiveOrient::Model.orientdb_class(name: 'query' ).new( record )
159
+ else
160
+ the_object = ActiveOrient::Model.orientdb_class(name: record['@class']).new record
161
+ ActiveOrient::Base.store_rid( the_object ) # update cache
162
+ # end
163
+ end
164
+ end
165
+ # returns an array of updated objects
166
+
167
+ rescue RestClient::BadRequest => e
168
+ #puts e.inspect
169
+ logger.error { "-"*30 }
170
+ logger.error { "REST_READ#GET_RECORDS.URL ---> Wrong Query" }
171
+ logger.error { query.compose( destination: :rest).to_s }
172
+ logger.error { "Fired Statement: #{url.to_s} " }
173
+ response=""
174
+ rescue RestClient::InternalServerError => e
175
+ response = JSON.parse(e.response)['errors'].pop
176
+ logger.error{ "Interbak Server ERROR" }
177
+ logger.error{response['content'].split(':').last}
178
+ rescue URI::InvalidURIError => e
179
+ logger.error{"Invalid URI detected"}
180
+ logger.error query.to_s
181
+ logger.info{"Trying batch processing"}
182
+ response = execute{ query.to_s}
183
+ logger.info{"Success: to avoid this delay use ActiveOrient::Model#query_database instead"}
184
+ response
185
+ end
186
+ end
187
+ alias get_documents get_records
188
+
189
+ end