active-orient 0.4 → 0.80

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