orientdb4r 0.2.7 → 0.2.8

Sign up to get free protection for your applications and to get access to all the features.
data/changelog.txt CHANGED
@@ -3,6 +3,10 @@
3
3
  - 'rest-client' replaced by 'excon' for HTTP communication with Keep-Alive
4
4
  - introduced support for distributed server
5
5
 
6
- 0.3.0 07/07/12
6
+ 0.2.8 /07/07/xx
7
+ - changed and stabilized exception handling
8
+ - added feature Client#create_class(:properties)
9
+
10
+ 0.2.7 07/07/12
7
11
  - changed design to support distributed server
8
12
  - added method Client#class_exists?
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,46 @@
1
+ require 'study_case'
2
+
3
+ # This class tests performance on a simple flat class.
4
+ class FlatClassPerf < FStudy::Case
5
+
6
+ # def db; 'perf'; end
7
+
8
+ def drop
9
+ client.drop_class 'User'
10
+ end
11
+ def model
12
+ drop
13
+ client.create_class 'User' do |c|
14
+ c.property 'username', :string, :mandatory => true
15
+ c.property 'name', :string, :mandatory => true
16
+ c.property 'admin', :boolean
17
+ end
18
+ end
19
+ def del
20
+ client.command 'DELETE FROM User'
21
+ end
22
+ def data
23
+ 1.upto(10) do |i|
24
+ Orientdb4r::logger.info "...done: #{i}" if 0 == (i % 1000)
25
+ first_name = dg.word
26
+ surname = dg.word
27
+ begin
28
+ client.create_document({ '@class' => 'User', \
29
+ :username => "#{first_name}.#{surname}", \
30
+ :name => "#{first_name.capitalize} #{surname.capitalize}", \
31
+ :admin => (0 == rand(2)) })
32
+ rescue Exception => e
33
+ Orientdb4r::logger.error e
34
+ end
35
+ end
36
+ end
37
+
38
+ def count
39
+ puts client.query 'SELECT count(*) FROM User'
40
+ end
41
+
42
+ end
43
+
44
+ c = FlatClassPerf.new
45
+ c.run
46
+ puts 'OK'
Binary file
Binary file
@@ -0,0 +1,87 @@
1
+ require 'orientdb4r'
2
+
3
+ module FStudy
4
+
5
+ ###
6
+ # This class represents a elaboration infrastructure.
7
+ class Case
8
+
9
+ attr_reader :dg
10
+
11
+ def initialize
12
+ @dg = DataGenerator.new
13
+ end
14
+
15
+ def db
16
+ 'temp' # default DB is 'temp'
17
+ end
18
+ def host
19
+ 'localhost'
20
+ end
21
+
22
+ def client
23
+ Orientdb4r.client :host => host
24
+ end
25
+
26
+ def watch method
27
+ start = Time.now
28
+ self.send method.to_sym
29
+ Orientdb4r::logger.info "method '#{method}' performed in #{Time.now - start} [s]"
30
+ end
31
+
32
+ def run
33
+ threads = []
34
+ thread_cnt = 1
35
+ thc = ARGV.select { |arg| arg if arg =~ /-th=\d+/ }
36
+ thread_cnt = thc[0][4..-1].to_i unless thc.empty?
37
+
38
+ if thread_cnt > 1
39
+ 1.upto(thread_cnt) do
40
+ threads << Thread.new do
41
+ run_threaded
42
+ end
43
+ end
44
+ else
45
+ run_threaded
46
+ end
47
+ threads.each { |th| th.join }
48
+ end
49
+
50
+
51
+ private
52
+
53
+ def run_threaded
54
+ Orientdb4r::logger.info "started thread #{Thread.current}"
55
+ client.connect :database => db, :user => 'admin', :password => 'admin'
56
+ ARGV.each do |arg|
57
+ watch arg[2..-1].to_sym if arg =~ /^--\w/
58
+ end
59
+ client.disconnect
60
+ end
61
+
62
+ end
63
+
64
+
65
+ class DataGenerator
66
+
67
+ def initialize
68
+ @words = IO.readlines('/usr/share/dict/words')
69
+ 0.upto(@words.size - 1) do |i|
70
+ word = @words[i]
71
+ word.strip!.downcase!
72
+ idx = word.index("'")
73
+ word = word[0..(idx - 1)] unless idx.nil?
74
+ @words[i] = word
75
+ end
76
+ @words.uniq!
77
+ Orientdb4r::logger.info "DataGenerator: #{@words.size} words"
78
+ end
79
+
80
+ # Gets a random word.
81
+ def word
82
+ @words[rand(@words.size)]
83
+ end
84
+
85
+ end
86
+
87
+ end
@@ -0,0 +1,251 @@
1
+ require 'study_case'
2
+
3
+ # This class represents model and data for Technical Feasibility Study.
4
+ # See here for more info:
5
+ # https://github.com/veny/orientdb4r/wiki/Technical-Feasibility
6
+ class TechnicalFeasibility < FStudy::Case
7
+
8
+ def db; 'perf'; end
9
+
10
+ # Dropes the document model.
11
+ def drop
12
+ classes_definition.reverse.each do |clazz|
13
+ client.drop_class clazz[:class]
14
+ puts "droped class: #{clazz[:class]}"
15
+ end
16
+ end
17
+
18
+ # Creates the document model.
19
+ def model
20
+ drop
21
+ classes_definition.each do |clazz|
22
+ class_name = clazz.delete :class
23
+ client.create_class(class_name, clazz)
24
+ puts "created class: #{class_name}"
25
+ end
26
+ end
27
+
28
+ # Deletes data.
29
+ def del
30
+ classes_definition.each do |clazz|
31
+ client.command "DELETE FROM #{clazz[:class]}"
32
+ puts "deleted from class: #{clazz[:class]}"
33
+ end
34
+ end
35
+
36
+ # Prepares data.
37
+ def data
38
+ communities = insert_communities
39
+ units = insert_org_units
40
+ users = insert_users(10000, units, communities)
41
+ insert_contacts(users, 7) # max pro user => ~ coeficient 4.0 pro user
42
+ contents = insert_contents(users, communities, 1.0) # coeficient pro user
43
+ insert_activities(users, communities, contents, 100) # coeficient pro user
44
+ end
45
+
46
+ def count
47
+ puts client.query 'SELECT count(*) FROM User'
48
+ end
49
+
50
+
51
+ private
52
+
53
+ def insert_communities
54
+ communities = [
55
+ { :name => 'Pianists' },
56
+ { :name => 'Violinists' },
57
+ { :name => 'Dog fanciers' },
58
+ { :name => 'Cat fanciers' },
59
+ { :name => 'Soccer fans' },
60
+ { :name => 'Ski fans' },
61
+ { :name => 'Basket fans' },
62
+ { :name => 'Gourmets' },
63
+ { :name => 'Scifi reader' },
64
+ { :name => 'Comic reader' }
65
+ ]
66
+
67
+ start = Time.now
68
+ communities.each do |c|
69
+ c['@class'] = 'Community'
70
+ c[:rid] = client.create_document(c)
71
+ end
72
+ puts "Created Communities: #{communities.size}, time = #{Time.now - start} [s]"
73
+ communities
74
+ end
75
+
76
+ def insert_org_units
77
+ units = [
78
+ { :name => 'BigCompany', :descendants => [ 'Automotive', 'Research', 'Infrastructure' ] },
79
+ { :name => 'Automotive', :descendants => [ 'Sales', 'Marketing', 'Design' ] },
80
+ { :name => 'Sales' },
81
+ { :name => 'Marketing' },
82
+ { :name => 'Design' },
83
+ { :name => 'Research', :descendants => [ 'Scientist', 'Spies' ] },
84
+ { :name => 'Scientist' },
85
+ { :name => 'Spies' },
86
+ { :name => 'Infrastructure', :descendants => [ 'Accounting', 'HumanResources' ] },
87
+ { :name => 'Accounting' },
88
+ { :name => 'HumanResources' , :descendants => [ 'Recruitment' ] },
89
+ { :name => 'Recruitment' }
90
+ ]
91
+
92
+ start = Time.now
93
+ units.each { |unit| insert_org_unit_helper(unit, units) }
94
+ puts "Created OrgUnits: #{units.size}, time = #{Time.now - start} [s]"
95
+ units
96
+ end
97
+ def insert_org_unit_helper(unit, all)
98
+ return if unit.include? :rid
99
+
100
+ if unit.include? :descendants
101
+ # recursion
102
+ unit[:descendants].each do |desc_name|
103
+ next_unit = all.select { |u| u if u[:name] == desc_name }[0]
104
+ insert_org_unit_helper(next_unit, all) unless next_unit.include? :rid
105
+ end
106
+ end
107
+
108
+ cloned = unit.clone
109
+ cloned['@class'] = 'OrgUnit'
110
+ cloned.delete :descendants
111
+
112
+ if unit.include? :descendants
113
+ descendants = []
114
+ unit[:descendants].each do |name|
115
+ descendants << all.select { |ou| ou if ou[:name] == name }[0][:rid]
116
+ end
117
+ cloned[:descendants] = descendants
118
+ end
119
+
120
+ rid = client.create_document(cloned)
121
+ unit[:rid] = rid
122
+ end
123
+
124
+ def insert_users(count, units, communities)
125
+ start = Time.now
126
+ users = []
127
+ 1.upto(count) do
128
+ firstname = dg.word
129
+ surname = dg.word
130
+ username = "#{firstname}.#{surname}"
131
+ # random distribution of Units (1) & Communities (0..3)
132
+ unit = units[rand(units.size)]
133
+ comms = []
134
+ 0.upto(rand(4)) { |i| comms << communities[rand(communities.size)][:rid] if i > 0 }
135
+
136
+ user = { '@class' => 'User', \
137
+ :username => username, \
138
+ :firstname => firstname.capitalize, \
139
+ :surname => surname.capitalize, \
140
+ :unit => unit[:rid], \
141
+ :communities => comms }
142
+ rid = client.create_document(user)
143
+ user[:rid] = rid
144
+ users << user
145
+ end
146
+ puts "Created Users: #{users.size}, time = #{Time.now - start} [s]"
147
+ users
148
+ end
149
+
150
+ def insert_contacts(users, max_contacts)
151
+ start = Time.now
152
+ count = 0
153
+ types = [:friend, :family, :coworker, :enemy]
154
+ 0.upto(users.size - 1) do |i|
155
+ a = users[i]
156
+ 0.upto(rand(max_contacts)) do
157
+ b = users[rand(users.size)]
158
+ client.create_document({'@class' => 'Contact', :a => a[:rid], :b => b[:rid], :type => rand(types.size)})
159
+ count += 1
160
+ end
161
+ end
162
+ puts "Created Contacts: #{count}, time = #{Time.now - start} [s]"
163
+ end
164
+
165
+ def insert_contents(users, communities, user_coef = 1.0)
166
+ start = Time.now
167
+ contents = []
168
+ classes = [['Article', 'body'], ['Gallery', 'description'], ['Term', 'topic']]
169
+ limit = (users.size * user_coef).to_i
170
+
171
+ 1.upto(limit) do
172
+ clazz = classes[rand(classes.size)]
173
+ content = {'@class' => clazz[0], :title => "#{dg.word} #{dg.word}", clazz[1] => dg.word}
174
+ # random distribution of Users (1) & Communities (0..3)
175
+ content[:author] = users[rand(users.size)][:rid]
176
+ comms = []
177
+ 0.upto(rand(4)) { |i| comms << communities[rand(communities.size)][:rid] if i > 0 }
178
+ content[:communities] = comms
179
+
180
+ rid = client.create_document(content)
181
+ content[:rid] = rid
182
+ contents << content
183
+ end
184
+ puts "Created ContentTypes: #{contents.size}, time = #{Time.now - start} [s]"
185
+ contents
186
+ end
187
+
188
+ def insert_activities(users, communities, contents, user_coef)
189
+ start = Time.now
190
+ types = [:login, :logout, :likeit, :rated, :saw, :commented]
191
+ limit = users.size * user_coef
192
+ 1.upto(limit) do |i|
193
+ puts "... #{i}" if 0 == i % 100000
194
+ type = rand(types.size)
195
+ activity = { '@class' => 'Activity', :type => type, :stamp => (Time.now.to_i - rand(3600*23*30)) }
196
+ # random distribution of Users (1) & the same communities as user
197
+ user = users[rand(users.size)]
198
+ activity[:source] = user[:rid]
199
+ activity[:communities] = user[:communities]
200
+ # content
201
+ content = (types[type] == :login or types[type] == :logout) ? nil : contents[rand(contents.size)]
202
+ activity[content['@class'].downcase] = content[:rid] unless content.nil?
203
+
204
+ client.create_document(activity)
205
+ end
206
+ puts "Created Activities: #{limit}, time = #{Time.now - start} [s]"
207
+ end
208
+
209
+ def classes_definition
210
+ # TODO rdbms_id
211
+ [
212
+ { :class => 'OrgUnit', :properties => [
213
+ { :property => 'name', :type => :string, :mandatory => true },
214
+ # TODO domain
215
+ { :property => 'descendants', :type => :linkset, :linked_class => 'OrgUnit' }]},
216
+ { :class => 'Community', :properties => [
217
+ { :property => 'name', :type => :string, :mandatory => true }]},
218
+ { :class => 'User', :properties => [
219
+ { :property => 'username', :type => :string, :mandatory => true },
220
+ { :property => 'firstname', :type => :string, :mandatory => true },
221
+ { :property => 'surname', :type => :string, :mandatory => true },
222
+ # TODO roles
223
+ { :property => 'unit', :type => :link, :linked_class => 'OrgUnit', :mandatory => true },
224
+ { :property => 'communities', :type => :linkset, :linked_class => 'Community' }]},
225
+ { :class => 'Contact', :properties => [
226
+ { :property => 'type', :type => :integer, :mandatory => true },
227
+ { :property => 'a', :type => :link, :linked_class => 'User', :mandatory => true },
228
+ { :property => 'b', :type => :link, :linked_class => 'User', :mandatory => true }]},
229
+ { :class => 'Content', :properties => [
230
+ { :property => 'title', :type => :string, :mandatory => true },
231
+ { :property => 'author', :type => :link, :linked_class => 'User', :mandatory => true },
232
+ { :property => 'accessible_in', :type => :linkset, :linked_class => 'Community' }]},
233
+ { :class => 'Article', :extends => 'Content', :properties => [
234
+ { :property => 'body', :type => :string, :mandatory => true }]},
235
+ { :class => 'Gallery', :extends => 'Content', :properties => [
236
+ { :property => 'description', :type => :string, :mandatory => true }]},
237
+ { :class => 'Term', :extends => 'Content', :properties => [
238
+ { :property => 'topic', :type => :string, :mandatory => true }]},
239
+ { :class => 'Activity', :properties => [
240
+ { :property => 'type', :type => :integer, :mandatory => true },
241
+ { :property => 'stamp', :type => :integer, :mandatory => true },
242
+ { :property => 'source', :type => :link, :linked_class => 'User', :mandatory => true },
243
+ { :property => 'communities', :type => :linkset, :linked_class => 'Community' }]}
244
+ ]
245
+ end
246
+
247
+ end
248
+
249
+ c = TechnicalFeasibility.new
250
+ c.run
251
+ puts 'OK'
@@ -106,7 +106,7 @@ module Orientdb4r
106
106
  # Creates a new class in the schema.
107
107
  def create_class(name, options={})
108
108
  raise ArgumentError, "class name is blank" if blank?(name)
109
- opt_pattern = { :extends => :optional , :cluster => :optional, :force => false }
109
+ opt_pattern = { :extends => :optional , :cluster => :optional, :force => false, :properties => :optional }
110
110
  verify_options(options, opt_pattern)
111
111
 
112
112
  sql = "CREATE CLASS #{name}"
@@ -117,6 +117,19 @@ module Orientdb4r
117
117
 
118
118
  command sql
119
119
 
120
+ # properties given?
121
+ if options.include? :properties
122
+ props = options[:properties]
123
+ raise ArgumentError, 'properties have to be an array' unless props.is_a? Array
124
+
125
+ props.each do |prop|
126
+ raise ArgumentError, 'property definition has to be a hash' unless prop.is_a? Hash
127
+ prop_name = prop.delete :property
128
+ prop_type = prop.delete :type
129
+ create_property(name, prop_name, prop_type, prop)
130
+ end
131
+ end
132
+
120
133
  if block_given?
121
134
  proxy = Orientdb4r::Utils::Proxy.new(self, name)
122
135
  def proxy.property(property, type, options={})
@@ -145,7 +158,7 @@ module Orientdb4r
145
158
  rslt = true
146
159
  begin
147
160
  get_class name
148
- rescue NotFoundError
161
+ rescue OrientdbError
149
162
  rslt = false
150
163
  end
151
164
  rslt
@@ -18,6 +18,12 @@ module Orientdb4r
18
18
  end
19
19
 
20
20
 
21
+ ###
22
+ # Identifies technology backgound of this node implementation.
23
+ def identification
24
+ raise NotImplementedError, 'this should be overridden by subclass'
25
+ end
26
+
21
27
  ###
22
28
  # Cleans up resources used by the node.
23
29
  def cleanup