mongoose 0.1.0

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.
@@ -0,0 +1,113 @@
1
+ module Mongoose
2
+
3
+ #-------------------------------------------------------------------------------
4
+ # Database class
5
+ #-------------------------------------------------------------------------------
6
+ class Database
7
+ attr_reader :path, :tables
8
+
9
+ #-----------------------------------------------------------------------------
10
+ # initialize
11
+ #-----------------------------------------------------------------------------
12
+ def initialize(params={})
13
+ @path = params[:path] || './'
14
+
15
+ Table.db = self
16
+
17
+ @tables = {}
18
+
19
+ Dir.foreach(@path) do |filename|
20
+ next unless File.extname(filename) == TBL_HDR_EXT
21
+
22
+ table_name = File.basename(filename, ".*").to_sym
23
+ init_table(table_name)
24
+ end
25
+ end
26
+
27
+ #-----------------------------------------------------------------------------
28
+ # close
29
+ #-----------------------------------------------------------------------------
30
+ def close
31
+ @tables.each_key { |tbl_class| tbl_class.close }
32
+ end
33
+
34
+ #-----------------------------------------------------------------------------
35
+ # create_table
36
+ #-----------------------------------------------------------------------------
37
+ def create_table(table_name)
38
+ raise "Table already exists!" if table_exists?(table_name)
39
+
40
+ class_name = get_class_name_from_table_name(table_name)
41
+
42
+ tbl_header = {}
43
+ tbl_header[:table_name] = table_name
44
+ tbl_header[:class_name] = class_name
45
+ tbl_header[:last_id_used] = 0
46
+ tbl_header[:deleted_recs_counter] = 0
47
+ tbl_header[:columns] = []
48
+ tbl_header[:columns] << { :name => :id, :data_type => :integer,
49
+ :class => IDColumn.to_s }
50
+
51
+ File.open(File.join(@path, table_name.to_s + TBL_HDR_EXT), 'w') do |f|
52
+ YAML.dump(tbl_header, f)
53
+ end
54
+
55
+ fptr = File.open(File.join(@path, table_name.to_s + TBL_EXT), 'w')
56
+ fptr.close
57
+
58
+ init_table(table_name)
59
+
60
+ yield Object.const_get(class_name) if block_given?
61
+ end
62
+
63
+ #-----------------------------------------------------------------------------
64
+ # drop_table
65
+ #-----------------------------------------------------------------------------
66
+ def drop_table(table_name)
67
+ class_name = get_class_name_from_table_name(table_name)
68
+
69
+ @tables[Object.const_get(class_name)][:columns].each do |c|
70
+ if c.indexed?
71
+ File.delete(c.index_file_name) if File.exists?(c.index_file_name)
72
+ end
73
+ end
74
+
75
+ File.delete(File.join(@path, table_name.to_s + TBL_HDR_EXT))
76
+ File.delete(File.join(@path, table_name.to_s + TBL_EXT)) if \
77
+ File.exists?(File.join(@path, table_name.to_s + TBL_EXT))
78
+
79
+ @tables.delete(Object.const_get(class_name))
80
+ end
81
+
82
+ #-----------------------------------------------------------------------------
83
+ # table_exists?
84
+ #-----------------------------------------------------------------------------
85
+ def table_exists?(table_name)
86
+ return File.exists?(File.join(@path, table_name.to_s + TBL_HDR_EXT))
87
+ end
88
+
89
+ private
90
+
91
+ #-----------------------------------------------------------------------------
92
+ # init_table
93
+ #-----------------------------------------------------------------------------
94
+ def init_table(table_name)
95
+ class_name = get_class_name_from_table_name(table_name)
96
+
97
+ @tables[Object.full_const_get(class_name)] = { :class_name => class_name,
98
+ :table_name => table_name, :columns => [], :query => [],
99
+ :last_id_used => nil, :deleted_recs_counter => nil }
100
+
101
+ Object.full_const_get(class_name).init_table
102
+ end
103
+
104
+ #-----------------------------------------------------------------------------
105
+ # get_class_name_from_table_name
106
+ #-----------------------------------------------------------------------------
107
+ def get_class_name_from_table_name(table_name)
108
+ return table_name.to_s.split('_').inject('') { |full_name, word|
109
+ full_name + word.capitalize }
110
+ end
111
+ end
112
+
113
+ end
@@ -0,0 +1,114 @@
1
+ module Mongoose
2
+
3
+ class LinearSearch
4
+ def initialize(col)
5
+ @col = col
6
+ end
7
+
8
+ def search_table(&search)
9
+ col_index = nil
10
+ @col.tbl_class.columns.each_with_index do |c,i|
11
+ if c.name == @col.name
12
+ col_index = i
13
+ break
14
+ end
15
+ end
16
+
17
+ result = []
18
+
19
+ @col.tbl_class.with_table do |fptr|
20
+ begin
21
+ while true
22
+ fpos = fptr.tell
23
+
24
+ rec_arr = Marshal.load(fptr)
25
+
26
+ next if rec_arr[0]
27
+
28
+ value = rec_arr[col_index+1]
29
+
30
+ if search.call(value)
31
+ result << rec_arr[1]
32
+ end
33
+ end
34
+ rescue EOFError
35
+ end
36
+ return result
37
+ end
38
+ end
39
+
40
+ def >(other)
41
+ return search_table do |table_value|
42
+ if table_value.nil?
43
+ false
44
+ else
45
+ table_value > other
46
+ end
47
+ end
48
+ end
49
+
50
+ def >=(other)
51
+ return search_table do |table_value|
52
+ if table_value.nil?
53
+ false
54
+ else
55
+ table_value >= other
56
+ end
57
+ end
58
+ end
59
+
60
+ def <(other)
61
+ return search_table do |table_value|
62
+ if table_value.nil?
63
+ false
64
+ else
65
+ table_value < other
66
+ end
67
+ end
68
+ end
69
+
70
+ def <=(other)
71
+ return search_table do |table_value|
72
+ if table_value.nil?
73
+ false
74
+ else
75
+ table_value <= other
76
+ end
77
+ end
78
+ end
79
+
80
+ def ==(other)
81
+ return search_table do |table_value|
82
+ if table_value.nil?
83
+ false
84
+ else
85
+ table_value == other
86
+ end
87
+ end
88
+ end
89
+
90
+ def one_of(*other)
91
+ return search_table do |table_value|
92
+ other.include?(table_value)
93
+ end
94
+ end
95
+
96
+ def between(search_start, search_end, start_inclusive=false,
97
+ end_inclusive=false)
98
+ return search_table do |table_value|
99
+ if table_value < search_start
100
+ false
101
+ elsif table_value == search_start and not start_inclusive
102
+ false
103
+ elsif table_value == search_end and not end_inclusive
104
+ false
105
+ elsif table_value > search_end
106
+ false
107
+ else
108
+ true
109
+ end
110
+ end
111
+ end
112
+ end
113
+
114
+ end
@@ -0,0 +1,412 @@
1
+ require 'pp'
2
+
3
+ module Mongoose
4
+
5
+ #---------------------------------------------------------------------------
6
+ # SkipList Class
7
+ #---------------------------------------------------------------------------
8
+ class SkipList
9
+ VERSION = "0.1"
10
+ MAX_LEVEL = 31
11
+ OPTIMAL_PROBABILITY = 0.25
12
+
13
+ attr_reader :size
14
+
15
+ #-----------------------------------------------------------------------
16
+ # initialize
17
+ #-----------------------------------------------------------------------
18
+ #++
19
+ # Create a new skip list instance.
20
+ #
21
+ def initialize
22
+ @size = 0
23
+
24
+ # Create header and footer nodes.
25
+ @footer = FooterNode.new
26
+ @header = HeaderNode.new
27
+
28
+ # Point all header.forward references to footer node.
29
+ 0.upto(MAX_LEVEL) { |i| @header.forward[i] = @footer }
30
+
31
+ # This attribute will hold the actual level of the skip list.
32
+ @level = 0
33
+ end
34
+
35
+ #-----------------------------------------------------------------------
36
+ # store
37
+ #-----------------------------------------------------------------------
38
+ #++
39
+ # If key not found, will insert new record, otherwise will update value
40
+ # of existing record.
41
+ #
42
+ def store(search_key, new_value)
43
+ # This array will be used to determine which records need their
44
+ # forward array re-adjusted after a new node is created.
45
+ update = []
46
+
47
+ # Start off at the header node.
48
+ x = @header
49
+
50
+ # Starting at the current highest level of the skip list, walk from
51
+ # left to right at that level, until you see that the next node's
52
+ # key is greater than the key you are searching for. When this
53
+ # happend, you want to add the current node you are on to the list
54
+ # of nodes that need to have their forward arrays updated. Next,
55
+ # you want to drop down a level on the current node and start
56
+ # walking forward again, until you again see that the next node's
57
+ # key is bigger than the search key. In this way, you are walking
58
+ # through the skip list, constantly moving to the right and moving
59
+ # down, until you reach level 0 and are on the node whose key is
60
+ # either equal to the search key (in which case an update will take
61
+ # place), or the highest key in the list that is still lower than
62
+ # the search key (in which case, you have found the place to do an
63
+ # insert).
64
+ @level.downto(0) do |i|
65
+ while x.forward[i].key < search_key
66
+ x = x.forward[i]
67
+ end
68
+ update[i] = x
69
+ end
70
+
71
+ x = x.forward[0]
72
+
73
+ # If the search key was found, simply update the value of the node.
74
+ if x.key == search_key
75
+ x.value << new_value unless x.value.include?(new_value)
76
+ # If this is an insert, determine the number of levels it will have
77
+ # using a random number. This is what keeps the skip list balanced.
78
+ else
79
+ lvl = random_level
80
+
81
+ # If the new level is higher than the actual current level, we
82
+ # need to make sure that the header node gets updated at these
83
+ # levels. Then, we set the actual current level equal to the
84
+ # new level.
85
+ if lvl > @level
86
+ (@level + 1).upto(lvl) { |i| update[i] = @header }
87
+ @level = lvl
88
+ end
89
+
90
+ # Create a new node.
91
+ x = Node.new(lvl, search_key, [new_value])
92
+
93
+ # Now, we need to update all of the nodes that will be affected
94
+ # by the insertion of the new node. These are nodes whose
95
+ # forward array either will point to the new node and the new
96
+ # node itself.
97
+ 0.upto(lvl) do |i|
98
+ x.forward[i] = update[i].forward[i]
99
+ update[i].forward[i] = x
100
+ end
101
+
102
+ # Increment the size attribute by one.
103
+ @size += 1
104
+ end
105
+ end
106
+
107
+ def remove(search_key, value)
108
+ update = []
109
+
110
+ x = @header
111
+
112
+ @level.downto(0) do |i|
113
+ while x.forward[i].key < search_key
114
+ x = x.forward[i]
115
+ end
116
+ update[i] = x
117
+ end
118
+
119
+ x = x.forward[0]
120
+
121
+ if x.key == search_key
122
+ x.value.delete(value)
123
+
124
+ if x.value.empty?
125
+ 0.upto(@level) do |i|
126
+ break unless update[i].forward[i] == x
127
+ update[i].forward[i] = x.forward[i]
128
+ end
129
+
130
+ while @level > 0 and @header.forward[@level] == @footer
131
+ @level -= 1
132
+ end
133
+
134
+ @size -= 1
135
+ end
136
+ end
137
+ end
138
+
139
+ def search(search_key)
140
+ result = []
141
+ x = @header
142
+
143
+ @level.downto(0) do |i|
144
+ while x.forward[i].key < search_key
145
+ x = x.forward[i]
146
+ end
147
+ end
148
+
149
+ x = x.forward[0]
150
+
151
+ return x.value if x.key == search_key
152
+ end
153
+
154
+ def one_of(*other)
155
+ result = []
156
+ other.each do |o|
157
+ result.concat(search(o))
158
+ end
159
+ return result
160
+ end
161
+
162
+ def ==(other)
163
+ return search(other)
164
+ end
165
+
166
+ def >(search_key)
167
+ result = []
168
+ x = @header
169
+
170
+ @level.downto(0) do |i|
171
+ while x.forward[i].key < search_key
172
+ x = x.forward[i]
173
+ end
174
+ end
175
+
176
+ x = x.forward[0]
177
+
178
+ x = x.forward[0] if x.key == search_key
179
+
180
+ while x != @footer
181
+ result.concat(x.value)
182
+ x = x.forward[0]
183
+ end
184
+
185
+ return result
186
+ end
187
+
188
+ def >=(search_key)
189
+ result = []
190
+ x = @header
191
+
192
+ @level.downto(0) do |i|
193
+ while x.forward[i].key < search_key
194
+ x = x.forward[i]
195
+ end
196
+ end
197
+
198
+ x = x.forward[0]
199
+
200
+ while x != @footer
201
+ result.concat(x.value)
202
+ x = x.forward[0]
203
+ end
204
+
205
+ return result
206
+ end
207
+
208
+ def <(search_key)
209
+ result = []
210
+ x = @header
211
+
212
+ x = x.forward[0]
213
+
214
+ while x != @footer and x.key < search_key
215
+ result.concat(x.value)
216
+ x = x.forward[0]
217
+ end
218
+
219
+ return result
220
+ end
221
+
222
+ def <=(search_key)
223
+ result = []
224
+ x = @header
225
+
226
+ x = x.forward[0]
227
+
228
+ while x != @footer and x.key <= search_key
229
+ result.concat(x.value)
230
+ x = x.forward[0]
231
+ end
232
+
233
+ return result
234
+ end
235
+
236
+ def [](search_key)
237
+ return search(search_key)
238
+ end
239
+
240
+ def between(search_start, search_end, start_inclusive=false,
241
+ end_inclusive=false)
242
+ result = []
243
+ x = @header
244
+
245
+ @level.downto(0) do |i|
246
+ while x.forward[i].key < search_start
247
+ x = x.forward[i]
248
+ end
249
+ end
250
+
251
+ x = x.forward[0]
252
+
253
+ x = x.forward[0] if x.key == search_start and not start_inclusive
254
+
255
+ while x != @footer and x.key < search_end
256
+ result.concat(x.value)
257
+ x = x.forward[0]
258
+ end
259
+
260
+ result = result.concat(x.value) if x.key == search_end and end_inclusive
261
+ return result
262
+ end
263
+
264
+ def each
265
+ x = @header.forward[0]
266
+
267
+ while x != @footer
268
+ yield x.key, x.value
269
+ x = x.forward[0]
270
+ end
271
+ end
272
+
273
+ def load(key, value)
274
+ # This array will be used to determine which records need their
275
+ # forward array re-adjusted after a new node is created.
276
+ update = []
277
+
278
+ # Start off at the header node.
279
+ x = @header
280
+
281
+ # Starting at the current highest level of the skip list, walk from
282
+ # left to right at that level, until you reach the footer node. When
283
+ # this happens, you want to add the current node you are on to the list
284
+ # of nodes that need to have their forward arrays updated. Next,
285
+ # you want to drop down a level on the current node and start
286
+ # walking forward again, until you again see that the next node is the
287
+ # footer node. In this way, you are walking through the skip list,
288
+ # constantly moving to the right and moving down, until you reach level
289
+ # 0 and are on the node whose key is the highest key in the list (in
290
+ # which case, you have found the place to do an insert).
291
+ @level.downto(0) do |i|
292
+ while x.forward[i] != @footer
293
+ x = x.forward[i]
294
+ end
295
+ update[i] = x
296
+ end
297
+
298
+ lvl = random_level
299
+
300
+ # If the new level is higher than the actual current level, we
301
+ # need to make sure that the header node gets updated at these
302
+ # levels. Then, we set the actual current level equal to the
303
+ # new level.
304
+ if lvl > @level
305
+ (@level + 1).upto(lvl) { |i| update[i] = @header }
306
+ @level = lvl
307
+ end
308
+
309
+ # Create a new node.
310
+ x = Node.new(lvl, key, value)
311
+
312
+ # Now, we need to update all of the nodes that will be affected
313
+ # by the insertion of the new node. These are nodes whose
314
+ # forward array either will point to the new node and the new
315
+ # node itself.
316
+ 0.upto(lvl) do |i|
317
+ x.forward[i] = update[i].forward[i]
318
+ update[i].forward[i] = x
319
+ end
320
+
321
+ # Increment the size attribute by one.
322
+ @size += 1
323
+ end
324
+
325
+ def dump
326
+ x = @header
327
+ puts '---------------- Header -----------------------------'
328
+ puts x
329
+ x.forward.each_with_index do |f,i|
330
+ puts '**** Forward entry %d ****' % i
331
+ puts f.key
332
+ puts f.value unless f.is_a?(FooterNode)
333
+ end
334
+ puts '---------------- End Header -------------------------'
335
+
336
+ while not x.forward[0].is_a?(FooterNode)
337
+ x = x.forward[0]
338
+ puts '---------------- Node -------------------------'
339
+ puts x
340
+ puts x.key
341
+ puts x.value
342
+ puts x.lvl
343
+ x.forward.each_with_index do |f,i|
344
+ puts '**** Forward entry %d ****' % i
345
+ puts f.key
346
+ puts f.value unless f.is_a?(FooterNode)
347
+ end
348
+ puts '---------------- End Node ---------------------'
349
+ end
350
+
351
+ puts '--------------------- Footer ---------------------'
352
+ pp x.forward[0]
353
+ puts '--------------------- End Footer -----------------'
354
+ end
355
+
356
+ private
357
+
358
+ def random_level
359
+ lvl = 0
360
+
361
+ while rand < OPTIMAL_PROBABILITY and lvl < MAX_LEVEL
362
+ lvl += 1
363
+ end
364
+
365
+ return lvl
366
+ end
367
+ end
368
+
369
+ class HeaderNode
370
+ attr_accessor :forward
371
+ attr_reader :key
372
+
373
+ def initialize
374
+ @forward = []
375
+ @key = HeaderKey.new
376
+ end
377
+ end
378
+
379
+ class HeaderKey
380
+ def ==(other)
381
+ false
382
+ end
383
+ end
384
+
385
+ class FooterNode
386
+ attr_reader :key
387
+
388
+ def initialize
389
+ @key = FooterKey.new
390
+ end
391
+ end
392
+
393
+ class FooterKey
394
+ def <(other)
395
+ return false
396
+ end
397
+ end
398
+
399
+
400
+ class Node
401
+ attr_accessor :forward, :value
402
+ attr_reader :forward, :key, :lvl
403
+
404
+ def initialize(lvl, key, value)
405
+ @lvl = lvl
406
+ @forward = []
407
+ @key = key
408
+ @value = value
409
+ end
410
+ end
411
+
412
+ end