orientdb4r 0.2.7 → 0.2.8

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