oklahoma_mixer 0.2.0 → 0.3.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.
Files changed (52) hide show
  1. data/CHANGELOG.rdoc +7 -0
  2. data/README.rdoc +4 -0
  3. data/Rakefile +1 -1
  4. data/TODO.rdoc +2 -3
  5. data/lib/oklahoma_mixer.rb +7 -20
  6. data/lib/oklahoma_mixer/array_list.rb +21 -7
  7. data/lib/oklahoma_mixer/array_list/c.rb +9 -1
  8. data/lib/oklahoma_mixer/b_tree_database.rb +25 -9
  9. data/lib/oklahoma_mixer/b_tree_database/c.rb +5 -0
  10. data/lib/oklahoma_mixer/cursor.rb +3 -0
  11. data/lib/oklahoma_mixer/cursor/c.rb +2 -0
  12. data/lib/oklahoma_mixer/error.rb +2 -0
  13. data/lib/oklahoma_mixer/extensible_string.rb +4 -2
  14. data/lib/oklahoma_mixer/extensible_string/c.rb +2 -0
  15. data/lib/oklahoma_mixer/fixed_length_database.rb +11 -3
  16. data/lib/oklahoma_mixer/fixed_length_database/c.rb +2 -0
  17. data/lib/oklahoma_mixer/hash_database.rb +24 -7
  18. data/lib/oklahoma_mixer/hash_database/c.rb +2 -0
  19. data/lib/oklahoma_mixer/hash_map.rb +42 -0
  20. data/lib/oklahoma_mixer/hash_map/c.rb +27 -0
  21. data/lib/oklahoma_mixer/query.rb +73 -0
  22. data/lib/oklahoma_mixer/query/c.rb +51 -0
  23. data/lib/oklahoma_mixer/table_database.rb +402 -0
  24. data/lib/oklahoma_mixer/table_database/c.rb +104 -0
  25. data/lib/oklahoma_mixer/utilities.rb +26 -1
  26. data/test/{b_tree_binary_data_test.rb → b_tree_database/b_tree_binary_data_test.rb} +2 -2
  27. data/test/{b_tree_tuning_test.rb → b_tree_database/b_tree_tuning_test.rb} +2 -2
  28. data/test/{cursor_based_iteration_test.rb → b_tree_database/cursor_based_iteration_test.rb} +2 -2
  29. data/test/{duplicate_storage_test.rb → b_tree_database/duplicate_storage_test.rb} +18 -12
  30. data/test/{binary_data_test.rb → core_database/binary_data_test.rb} +2 -2
  31. data/test/{file_system_test.rb → core_database/file_system_test.rb} +0 -0
  32. data/test/{getting_and_setting_keys_test.rb → core_database/getting_and_setting_keys_test.rb} +31 -60
  33. data/test/{iteration_test.rb → core_database/iteration_test.rb} +2 -2
  34. data/test/{transactions_test.rb → core_database/transactions_test.rb} +0 -0
  35. data/test/core_database/tuning_test.rb +31 -0
  36. data/test/{fixed_length_tuning_test.rb → fixed_length_database/fixed_length_tuning_test.rb} +0 -0
  37. data/test/{getting_and_setting_by_id_test.rb → fixed_length_database/getting_and_setting_by_id_test.rb} +8 -0
  38. data/test/{shared_binary_data.rb → shared/binary_data_tests.rb} +1 -1
  39. data/test/{tuning_test.rb → shared/hash_tuning_tests.rb} +18 -42
  40. data/test/{shared_iteration.rb → shared/iteration_tests.rb} +8 -7
  41. data/test/{key_range_test.rb → shared/key_range_test.rb} +0 -0
  42. data/test/{order_test.rb → shared/order_test.rb} +0 -0
  43. data/test/shared/storage_tests.rb +65 -0
  44. data/test/{top_level_interface_test.rb → shared/top_level_interface_test.rb} +39 -2
  45. data/test/{shared_tuning.rb → shared/tuning_tests.rb} +1 -1
  46. data/test/table_database/document_iteration_test.rb +22 -0
  47. data/test/table_database/document_storage_test.rb +225 -0
  48. data/test/table_database/index_test.rb +57 -0
  49. data/test/table_database/query_test.rb +866 -0
  50. data/test/table_database/table_tuning_test.rb +56 -0
  51. data/test/test_helper.rb +27 -0
  52. metadata +35 -36
@@ -0,0 +1,57 @@
1
+ require "test_helper"
2
+
3
+ class TestIndex < Test::Unit::TestCase
4
+ def setup
5
+ @db = tdb
6
+ end
7
+
8
+ def teardown
9
+ @db.close
10
+ remove_db_files
11
+ end
12
+
13
+ def test_an_index_can_be_added_to_a_column
14
+ assert(@db.add_index(:column, :string), "Index not added")
15
+ end
16
+
17
+ def test_an_index_can_be_rebuilt_by_adding_it_again
18
+ 2.times do
19
+ assert(@db.add_index(:column, :string), "Index not added")
20
+ end
21
+ end
22
+
23
+ def test_string_numeric_token_and_qgram_indexes_are_supported
24
+ %w[lexical string decimal numeric token qgram].each do |type|
25
+ assert(@db.add_index(type, type), "Index not added")
26
+ end
27
+ end
28
+
29
+ def test_an_unknown_index_type_fails_with_an_error
30
+ assert_raise(OKMixer::Error::IndexError) do
31
+ @db.add_index(:column, :unknown)
32
+ end
33
+ end
34
+
35
+ def test_adding_an_existing_index_with_keep_mode_returns_false
36
+ assert(@db.add_index(:column, :string), "Index not added")
37
+ assert(!@db.add_index(:column, :string, :keep), "Index readded")
38
+ end
39
+
40
+ def test_an_existing_index_can_be_removed
41
+ assert(@db.add_index(:column, :string), "Index not added")
42
+ assert(@db.remove_index(:column), "Index not removed")
43
+ end
44
+
45
+ def test_removing_a_nonexisting_index_returns_false
46
+ assert(!@db.remove_index(:missing), "Index removed")
47
+ end
48
+
49
+ def test_an_existing_index_can_be_optimized
50
+ assert(@db.add_index(:column, :string), "Index not added")
51
+ assert(@db.optimize_index(:column), "Index not optimized")
52
+ end
53
+
54
+ def test_optimizing_a_nonexisting_index_returns_false
55
+ assert(!@db.optimize_index(:missing), "Index optimized")
56
+ end
57
+ end
@@ -0,0 +1,866 @@
1
+ require "test_helper"
2
+
3
+ class TestQuery < Test::Unit::TestCase
4
+ def setup
5
+ @db = tdb
6
+ end
7
+
8
+ def teardown
9
+ @db.close
10
+ remove_db_files
11
+ end
12
+
13
+ def test_all_returns_primary_key_and_document_by_default
14
+ load_simple_data
15
+ results = @db.all
16
+ assert_equal( [ ["pk1", {"a" => "1", "b" => "2", "c" => "3"}],
17
+ ["pk2", { }] ], results.to_a.sort )
18
+ end
19
+
20
+ def test_all_returns_primary_key_and_document_if_selected
21
+ load_simple_data
22
+ %w[ key_and_doc keys_and_docs
23
+ primary_key_and_doc primary_keys_and_docs ].each do |kad|
24
+ results = @db.all(:select => kad)
25
+ assert_equal( [ ["pk1", {"a" => "1", "b" => "2", "c" => "3"}],
26
+ ["pk2", { }] ], results.to_a.sort )
27
+ end
28
+ end
29
+
30
+ def test_all_returns_primary_keys_only_if_selected
31
+ load_simple_data
32
+ %w[key keys primary_key primary_keys].each do |k|
33
+ results = @db.all(:select => k)
34
+ assert_equal(%w[pk1 pk2], results.sort)
35
+ end
36
+ end
37
+
38
+ def test_all_returns_documents_only_if_selected
39
+ load_simple_data
40
+ %w[doc docs document documents].each do |d|
41
+ results = @db.all(:select => d)
42
+ assert_equal( [{ }, {"a" => "1", "b" => "2", "c" => "3"}],
43
+ results.sort_by { |doc| doc.size } )
44
+ end
45
+ end
46
+
47
+ def test_all_selects_a_return_data_type_by_ruby_version_and_ordering
48
+ load_simple_data
49
+ if RUBY_VERSION < "1.9"
50
+ results = @db.all # not ordered
51
+ assert_equal( { "pk1" => {"a" => "1", "b" => "2", "c" => "3"},
52
+ "pk2" => { } }, results )
53
+ results = @db.all(:order => :a) # ordered
54
+ assert_equal( [ ["pk1", {"a" => "1", "b" => "2", "c" => "3"}],
55
+ ["pk2", { }] ], results )
56
+ else
57
+ results = @db.all
58
+ assert_equal( { "pk1" => {"a" => "1", "b" => "2", "c" => "3"},
59
+ "pk2" => { } }, results )
60
+ end
61
+ end
62
+
63
+ def test_all_can_be_forced_to_return_a_hash_of_hashes
64
+ load_simple_data
65
+ %w[hoh hohs hash_of_hash hash_of_hashes].each do |hoh|
66
+ results = @db.all(:return => hoh)
67
+ assert_equal( { "pk1" => {"a" => "1", "b" => "2", "c" => "3"},
68
+ "pk2" => { } }, results )
69
+ end
70
+ end
71
+
72
+ def test_all_can_be_forced_to_return_a_merged_array_of_hashes
73
+ load_simple_data
74
+ %w[aoh aohs array_of_hash array_of_hashes].each do |aoh|
75
+ results = @db.all(:return => aoh)
76
+ assert_equal( [ {:primary_key => "pk2"},
77
+ { :primary_key => "pk1",
78
+ "a" => "1",
79
+ "b" => "2",
80
+ "c" => "3" }],
81
+ results.sort_by { |doc| doc.size } )
82
+ end
83
+ end
84
+
85
+ def test_all_can_be_forced_to_return_an_array_of_arrays
86
+ load_simple_data
87
+ %w[aoa aoas array_of_array array_of_arrays].each do |aoa|
88
+ results = @db.all(:return => aoa)
89
+ assert_equal( [ ["pk1", {"a" => "1", "b" => "2", "c" => "3"}],
90
+ ["pk2", { }] ], results )
91
+ end
92
+ end
93
+
94
+ def test_all_will_pass_keys_and_documents_to_a_passed_block_and_return_self
95
+ load_simple_data
96
+ results = [ ]
97
+ assert_equal(@db, @db.all { |k, v| results << [k, v] })
98
+ assert_equal( [ ["pk1", {"a" => "1", "b" => "2", "c" => "3"}],
99
+ ["pk2", { }] ], results.sort )
100
+ end
101
+
102
+ def test_all_can_pass_keys_only_to_a_passed_block_and_return_self
103
+ load_simple_data
104
+ results = [ ]
105
+ assert_equal(@db, @db.all(:select => :keys) { |k| results << k })
106
+ assert_equal(%w[pk1 pk2], results.sort)
107
+ end
108
+
109
+ def test_all_can_pass_documents_only_to_a_passed_block_and_return_self
110
+ load_simple_data
111
+ results = [ ]
112
+ assert_equal(@db, @db.all(:select => :docs) { |v| results << v })
113
+ assert_equal( [{ }, {"a" => "1", "b" => "2", "c" => "3"}],
114
+ results.sort_by { |doc| doc.size } )
115
+ end
116
+
117
+ def test_all_with_a_block_does_not_modify_records_by_default
118
+ load_simple_data
119
+ assert_equal(@db, @db.all { })
120
+ assert_equal({"a" => "1", "b" => "2", "c" => "3"}, @db[:pk1])
121
+ assert_equal({ }, @db[:pk2])
122
+ end
123
+
124
+ def test_all_with_a_block_can_update_records
125
+ load_simple_data
126
+ assert_equal( @db, @db.all { |k, v|
127
+ if k == "pk1"
128
+ v["a"] = "1.1" # change
129
+ v.delete("c") # remove
130
+ v[:d] = 4 # add
131
+ :update
132
+ end
133
+ } )
134
+ assert_equal({"a" => "1.1", "b" => "2", "d" => "4"}, @db[:pk1])
135
+ end
136
+
137
+ def test_all_with_a_block_can_delete_records
138
+ load_simple_data
139
+ assert_equal(@db, @db.all { |_, v| v.empty? and :delete })
140
+ assert_equal({"a" => "1", "b" => "2", "c" => "3"}, @db[:pk1])
141
+ assert_nil(@db[:pk2])
142
+ end
143
+
144
+ def test_all_with_a_block_can_end_the_query
145
+ load_simple_data
146
+ results = [ ]
147
+ assert_equal(@db, @db.all { |k, _| results << k; :break })
148
+ assert_equal(1, results.size)
149
+ assert_match(/\Apk[12]\z/, results.first)
150
+ end
151
+
152
+ def test_all_with_a_block_can_combine_flags
153
+ load_simple_data
154
+ results = [ ]
155
+ assert_equal(@db, @db.all { |k, _| results << k; %w[delete break] })
156
+ assert_equal(1, results.size)
157
+ assert_match(/\Apk[12]\z/, results.first)
158
+ assert_equal(1, @db.size)
159
+ assert_match((%w[pk1 pk2] - results).first, @db.keys.first)
160
+ end
161
+
162
+ def test_all_fails_with_an_error_for_malformed_conditions
163
+ assert_raise(OKMixer::Error::QueryError) do
164
+ @db.all(:conditions => :first) # not column, operator, and expression
165
+ end
166
+ end
167
+
168
+ def test_all_fails_with_an_error_for_unknown_condition_operators
169
+ assert_raise(OKMixer::Error::QueryError) do
170
+ @db.all(:conditions => [:first, :unknown, "James"])
171
+ end
172
+ end
173
+
174
+ def test_all_can_accept_string_equal_conditions
175
+ load_condition_data
176
+ %w[== eql equal str_eql string_equal].each do |op|
177
+ ["", "s", "?", "s?"].each do |suffix|
178
+ next if op == "==" and suffix != ""
179
+ assert_equal( %w[james],
180
+ @db.all( :select => :keys,
181
+ :conditions => [:first, op + suffix, "James"] ) )
182
+ end
183
+ end
184
+ end
185
+
186
+ def test_all_can_accept_string_not_equal_conditions
187
+ load_condition_data
188
+ %w[! !_ not_].each do |n|
189
+ %w[= eql equal str_eql string_equal].each do |op|
190
+ ["", "s", "?", "s?"].each do |suffix|
191
+ next if op == "=" and suffix != ""
192
+ assert_equal( %w[dana jim],
193
+ @db.all( :select => :keys,
194
+ :conditions => [ :first,
195
+ n + op + suffix,
196
+ "James" ] ).sort )
197
+ end
198
+ end
199
+ end
200
+ end
201
+
202
+ def test_all_can_accept_string_include_conditions
203
+ load_condition_data
204
+ %w[include includes include? includes?].each do |op|
205
+ assert_equal( %w[dana james],
206
+ @db.all( :select => :keys,
207
+ :conditions => [:first, op, "a"] ).sort )
208
+ end
209
+ end
210
+
211
+ def test_all_can_accept_string_not_include_conditions
212
+ load_condition_data
213
+ %w[! !_ not_].each do |n|
214
+ %w[include includes include? includes?].each do |op|
215
+ assert_equal( %w[jim],
216
+ @db.all( :select => :keys,
217
+ :conditions => [:first, n + op, "a"] ) )
218
+ end
219
+ end
220
+ end
221
+
222
+ def test_all_can_accept_string_starts_with_conditions
223
+ load_condition_data
224
+ %w[start_with starts_with start_with? starts_with?].each do |op|
225
+ assert_equal( %w[james jim],
226
+ @db.all( :select => :keys,
227
+ :conditions => [:first, op, "J"] ).sort )
228
+ end
229
+ end
230
+
231
+ def test_all_can_accept_string_not_starts_with_conditions
232
+ load_condition_data
233
+ %w[! !_ not_].each do |n|
234
+ %w[start_with starts_with start_with? starts_with?].each do |op|
235
+ assert_equal( %w[dana],
236
+ @db.all( :select => :keys,
237
+ :conditions => [:first, n + op, "J"] ).sort )
238
+ end
239
+ end
240
+ end
241
+
242
+ def test_all_can_accept_string_ends_with_conditions
243
+ load_condition_data
244
+ %w[end_with ends_with end_with? ends_with?].each do |op|
245
+ assert_equal( [ ],
246
+ @db.all( :select => :keys,
247
+ :conditions => [:first, op, "z"] ) )
248
+ end
249
+ end
250
+
251
+ def test_all_can_accept_string_not_ends_with_conditions
252
+ load_condition_data
253
+ %w[! !_ not_].each do |n|
254
+ %w[end_with ends_with end_with? ends_with?].each do |op|
255
+ assert_equal( %w[dana james jim],
256
+ @db.all( :select => :keys,
257
+ :conditions => [:first, n + op, "z"] ).sort )
258
+ end
259
+ end
260
+ end
261
+
262
+ def test_all_can_accept_string_includes_all_tokens_conditions
263
+ load_condition_data
264
+ %w[ include_all includes_all
265
+ include_all_tokens includes_all_tokens
266
+ include_all? includes_all?
267
+ include_all_tokens? includes_all_tokens? ].each do |op|
268
+ assert_equal( %w[dana],
269
+ @db.all( :select => :keys,
270
+ :conditions => [:middle, op, "Leslie Ann"] ) )
271
+ end
272
+ end
273
+
274
+ def test_all_can_accept_string_not_includes_all_tokens_conditions
275
+ load_condition_data
276
+ %w[! !_ not_].each do |n|
277
+ %w[ include_all includes_all
278
+ include_all_tokens includes_all_tokens
279
+ include_all? includes_all?
280
+ include_all_tokens? includes_all_tokens? ].each do |op|
281
+ assert_equal( %w[james jim],
282
+ @db.all( :select => :keys,
283
+ :conditions => [:middle, n + op, "Leslie Ann"] ).
284
+ sort )
285
+ end
286
+ end
287
+ end
288
+
289
+ def test_all_can_accept_string_includes_any_token_conditions
290
+ load_condition_data
291
+ %w[ include_any includes_any
292
+ include_any_token includes_any_token
293
+ include_any? includes_any?
294
+ include_any_token? includes_any_token? ].each do |op|
295
+ assert_equal( %w[dana],
296
+ @db.all( :select => :keys,
297
+ :conditions => [:middle, op, "Ann Joe"] ) )
298
+ end
299
+ end
300
+
301
+ def test_all_can_accept_string_not_includes_any_token_conditions
302
+ load_condition_data
303
+ %w[! !_ not_].each do |n|
304
+ %w[ include_any includes_any
305
+ include_any_token includes_any_token
306
+ include_any? includes_any?
307
+ include_any_token? includes_any_token? ].each do |op|
308
+ assert_equal( %w[james jim],
309
+ @db.all( :select => :keys,
310
+ :conditions => [:middle, n + op, "Ann Joe"] ).
311
+ sort )
312
+ end
313
+ end
314
+ end
315
+
316
+ def test_all_can_accept_string_equals_any_token_conditions
317
+ load_condition_data
318
+ %w[ eql_any eql_any
319
+ eql_any_token eql_any_token
320
+ eql_any? eql_any?
321
+ eql_any_token? eql_any_token?
322
+ equals_any equals_any
323
+ equals_any_token equals_any_token
324
+ equals_any? equals_any?
325
+ equals_any_token? equals_any_token? ].each do |op|
326
+ assert_equal( %w[dana james],
327
+ @db.all( :select => :keys,
328
+ :conditions => [:first, op, "Dana James"] ).sort )
329
+ end
330
+ end
331
+
332
+ def test_all_can_accept_string_not_equals_any_token_conditions
333
+ load_condition_data
334
+ %w[! !_ not_].each do |n|
335
+ %w[ eql_any eql_any
336
+ eql_any_token eql_any_token
337
+ eql_any? eql_any?
338
+ eql_any_token? eql_any_token?
339
+ equals_any equals_any
340
+ equals_any_token equals_any_token
341
+ equals_any? equals_any?
342
+ equals_any_token? equals_any_token? ].each do |op|
343
+ assert_equal( %w[jim],
344
+ @db.all( :select => :keys,
345
+ :conditions => [:first, n + op, "Dana James"] ) )
346
+ end
347
+ end
348
+ end
349
+
350
+ def test_all_can_accept_string_matches_regexp_conditions
351
+ load_condition_data
352
+ %w[=~ match].each do |op|
353
+ ["", "es", "?", "es?"].each do |suffix|
354
+ next if op == "=~" and suffix != ""
355
+ assert_equal( %w[james jim],
356
+ @db.all( :select => :keys,
357
+ :conditions => [:first, op + suffix, /J.m/] ).
358
+ sort )
359
+ end
360
+ end
361
+ end
362
+
363
+ def test_all_can_accept_string_not_matches_regexp_conditions
364
+ load_condition_data
365
+ %w[! !_ not_].each do |n|
366
+ %w[~ match].each do |op|
367
+ ["", "es", "?", "es?"].each do |suffix|
368
+ next if op == "~" and suffix != ""
369
+ assert_equal( %w[dana],
370
+ @db.all( :select => :keys,
371
+ :conditions => [ :first,
372
+ n + op + suffix,
373
+ /J.m/ ] ) )
374
+ end
375
+ end
376
+ end
377
+ end
378
+
379
+ def test_all_can_accept_number_equal_conditions
380
+ load_condition_data
381
+ %w[== eql equal num_eql number_equal].each do |op|
382
+ ["", "s", "?", "s?"].each do |suffix|
383
+ next if op == "==" and suffix != ""
384
+ assert_equal( %w[james],
385
+ @db.all( :select => :keys,
386
+ :conditions => [:age, op + suffix, 33] ) )
387
+ end
388
+ end
389
+ end
390
+
391
+ def test_all_can_accept_number_not_equal_conditions
392
+ load_condition_data
393
+ %w[! !_ not_].each do |n|
394
+ %w[= eql equal num_eql number_equal].each do |op|
395
+ ["", "s", "?", "s?"].each do |suffix|
396
+ next if op == "=" and suffix != ""
397
+ assert_equal( %w[dana jim],
398
+ @db.all( :select => :keys,
399
+ :conditions => [:age, n + op + suffix, 33] ).
400
+ sort )
401
+ end
402
+ end
403
+ end
404
+ end
405
+
406
+ def test_all_can_accept_number_greater_than_conditions
407
+ load_condition_data
408
+ assert_equal( %w[dana jim], @db.all( :select => :keys,
409
+ :conditions => [:age, :>, 33] ).sort )
410
+ end
411
+
412
+ def test_all_can_accept_number_not_greater_than_conditions
413
+ load_condition_data
414
+ assert_equal( %w[james], @db.all( :select => :keys,
415
+ :conditions => [:age, "!>", 33] ) )
416
+ end
417
+
418
+ def test_all_can_accept_number_greater_than_or_equal_to_conditions
419
+ load_condition_data
420
+ assert_equal( %w[dana jim], @db.all( :select => :keys,
421
+ :conditions => [:age, :>=, 34] ).sort )
422
+ end
423
+
424
+ def test_all_can_accept_number_not_greater_than_or_equal_to_conditions
425
+ load_condition_data
426
+ assert_equal( %w[james], @db.all( :select => :keys,
427
+ :conditions => [:age, "!>=", 34] ) )
428
+ end
429
+
430
+ def test_all_can_accept_number_less_than_conditions
431
+ load_condition_data
432
+ assert_equal( %w[dana james],
433
+ @db.all( :select => :keys,
434
+ :conditions => [:age, :<, 53] ).sort )
435
+ end
436
+
437
+ def test_all_can_accept_number_not_less_than_conditions
438
+ load_condition_data
439
+ assert_equal( %w[jim], @db.all( :select => :keys,
440
+ :conditions => [:age, "!<", 53] ) )
441
+ end
442
+
443
+ def test_all_can_accept_number_less_than_or_equal_to_conditions
444
+ load_condition_data
445
+ assert_equal( %w[dana james],
446
+ @db.all( :select => :keys,
447
+ :conditions => [:age, :<=, 34] ).sort )
448
+ end
449
+
450
+ def test_all_can_accept_number_not_less_than_or_equal_to_conditions
451
+ load_condition_data
452
+ assert_equal( %w[jim], @db.all( :select => :keys,
453
+ :conditions => [:age, "!<=", 34] ) )
454
+ end
455
+
456
+ def test_all_can_accept_number_between_conditions
457
+ load_condition_data
458
+ %w[between between?].each do |op|
459
+ assert_equal( %w[dana james],
460
+ @db.all( :select => :keys,
461
+ :conditions => [:age, op, "33 50"] ).sort )
462
+ end
463
+ end
464
+
465
+ def test_all_can_accept_number_not_between_conditions
466
+ load_condition_data
467
+ %w[! !_ not_].each do |n|
468
+ %w[between between?].each do |op|
469
+ assert_equal( %w[jim],
470
+ @db.all( :select => :keys,
471
+ :conditions => [:age, n + op, "33 50"] ) )
472
+ end
473
+ end
474
+ end
475
+
476
+ def test_all_can_accept_any_number_conditions
477
+ load_condition_data
478
+ %w[any_num any_number any_num? any_number?].each do |op|
479
+ assert_equal( %w[james jim],
480
+ @db.all( :select => :keys,
481
+ :conditions => [:age, op, "33 53"] ).sort )
482
+ end
483
+ end
484
+
485
+ def test_all_can_accept_not_any_number_conditions
486
+ load_condition_data
487
+ %w[! !_ not_].each do |n|
488
+ %w[any_num any_number any_num? any_number?].each do |op|
489
+ assert_equal( %w[dana],
490
+ @db.all( :select => :keys,
491
+ :conditions => [:age, n + op, "33 53"] ) )
492
+ end
493
+ end
494
+ end
495
+
496
+ def test_all_can_accept_phrase_search_conditions
497
+ load_search_data
498
+ %w[phrase_search phrase_search?].each do |op|
499
+ assert_equal( %w[james jim],
500
+ @db.all( :select => :keys,
501
+ :conditions => [:name, op, "Edward Gray"] ).sort )
502
+ end
503
+ end
504
+
505
+ def test_all_can_accept_not_phrase_search_conditions
506
+ load_search_data
507
+ %w[! !_ not_].each do |n|
508
+ %w[phrase_search phrase_search?].each do |op|
509
+ assert_equal( %w[dana],
510
+ @db.all( :select => :keys,
511
+ :conditions => [:name, n + op, "Edward Gray"] ) )
512
+ end
513
+ end
514
+ end
515
+
516
+ # FIXME: could not determine how to test Full-Text token searches
517
+
518
+ def test_all_can_accept_expression_search_conditions
519
+ load_search_data
520
+ %w[expression_search expression_search?].each do |op|
521
+ assert_equal( %w[dana james],
522
+ @db.all( :select => :keys,
523
+ :conditions => [:name, op, "James || Ann"] ).sort )
524
+ end
525
+ end
526
+
527
+ def test_all_can_accept_not_expression_search_conditions
528
+ load_search_data
529
+ %w[! !_ not_].each do |n|
530
+ %w[expression_search expression_search?].each do |op|
531
+ assert_equal( %w[jim],
532
+ @db.all( :select => :keys,
533
+ :conditions => [ :name,
534
+ n + op,
535
+ "James || Ann" ] ) )
536
+ end
537
+ end
538
+ end
539
+
540
+ def test_all_can_set_conditions_for_the_primary_key
541
+ load_condition_data
542
+ ["", :pk, :primary_key].each do |key|
543
+ assert_equal( %w[james jim],
544
+ @db.all( :select => :keys,
545
+ :conditions => [key, :starts_with?, "j"] ).sort )
546
+ end
547
+ end
548
+
549
+ def test_all_can_accept_multiple_conditions
550
+ load_condition_data
551
+ assert_equal( %w[dana james],
552
+ @db.all( :select => :keys,
553
+ :conditions => [ [:last, :==, "Gray"],
554
+ [:age, "!=", 53] ] ).sort )
555
+ end
556
+
557
+ def test_all_fails_with_an_error_for_malformed_order
558
+ assert_raise(OKMixer::Error::QueryError) do
559
+ @db.all(:order => %w[too many args])
560
+ end
561
+ end
562
+
563
+ def test_all_fails_with_an_error_for_unknown_order_type
564
+ assert_raise(OKMixer::Error::QueryError) do
565
+ @db.all(:order => [:first, :unknown])
566
+ end
567
+ end
568
+
569
+ def test_all_accepts_an_order_field_used_to_arrange_results
570
+ load_order_data
571
+ assert_equal( %w[first middle last],
572
+ @db.all(:select => :keys, :order => :str) )
573
+ end
574
+
575
+ def test_all_order_defaults_to_string_ascending
576
+ load_order_data
577
+ assert_equal( %w[1 11 2], @db.all(:select => :docs, :order => :num).
578
+ map { |doc| doc["num"] } )
579
+ end
580
+
581
+ def test_all_order_can_be_forced_to_string_descending
582
+ load_order_data
583
+ %w[ASC STR_ASC].each do |order|
584
+ assert_equal( %w[first middle last],
585
+ @db.all(:select => :keys, :order => [:str, order]) )
586
+ end
587
+ end
588
+
589
+ def test_all_order_can_be_set_to_string_descending
590
+ load_order_data
591
+ %w[DESC STR_DESC].each do |order|
592
+ assert_equal( %w[last middle first],
593
+ @db.all(:select => :keys, :order => [:str, order]) )
594
+ end
595
+ end
596
+
597
+ def test_all_order_can_be_set_to_numeric_ascending
598
+ load_order_data
599
+ assert_equal( %w[first middle last],
600
+ @db.all(:select => :keys, :order => [:num, :NUM_ASC]) )
601
+ end
602
+
603
+ def test_all_order_can_be_set_to_numeric_descending
604
+ load_order_data
605
+ assert_equal( %w[last middle first],
606
+ @db.all(:select => :keys, :order => [:num, :NUM_DESC]) )
607
+ end
608
+
609
+ def test_all_can_order_the_primary_key
610
+ load_order_data
611
+ ["", :pk, :primary_key].each do |key|
612
+ assert_equal( %w[first last middle],
613
+ @db.all(:select => :keys, :order => key) )
614
+ end
615
+ end
616
+
617
+ def test_all_accepts_a_limit_for_returned_results
618
+ load_order_data
619
+ assert_equal( %w[first middle], @db.all( :select => :keys,
620
+ :order => :str,
621
+ :limit => 2 ) )
622
+ end
623
+
624
+ def test_all_accepts_an_offset_with_a_limit
625
+ load_order_data
626
+ assert_equal( %w[last], @db.all( :select => :keys,
627
+ :order => :str,
628
+ :limit => 2,
629
+ :offset => 2 ) )
630
+ end
631
+
632
+ def test_first_is_all_with_limit_one_removed_from_the_array
633
+ load_order_data
634
+ assert_equal("first", @db.first(:select => :keys, :order => :str))
635
+ assert_nil(@db.first(:select => :keys, :order => :str, :offset => 10))
636
+ end
637
+
638
+ def test_count_returns_a_count_of_records_matched
639
+ load_condition_data
640
+ assert_equal(2, @db.count(:conditions => [:first, :starts_with?, "J"]))
641
+ end
642
+
643
+ def test_paginate_requires_a_page_argument
644
+ assert_raise(OKMixer::Error::QueryError) do
645
+ @db.paginate({ }) # no :page argument
646
+ end
647
+ end
648
+
649
+ def test_paginate_requires_a_page_greater_than_zero_with_nil_allowed
650
+ assert_raise(OKMixer::Error::QueryError) do
651
+ @db.paginate({:page => 0})
652
+ end
653
+ end
654
+
655
+ def test_paginate_requires_per_page_be_greater_than_zero_if_provided
656
+ assert_raise(OKMixer::Error::QueryError) do
657
+ @db.paginate({:page => nil, :per_page => 0})
658
+ end
659
+ end
660
+
661
+ def test_paginate_defaults_to_thirty_for_a_per_page_count
662
+ load_search_data
663
+ assert_equal(30, @db.paginate(:page => nil).per_page)
664
+ end
665
+
666
+ def test_paginate_returns_result_sets_in_pages_regardless_of_type
667
+ load_search_data
668
+ %w[hoh aoa aoh].each do |results_mode|
669
+ page = @db.paginate( :order => :primary_key,
670
+ :return => results_mode.to_sym,
671
+ :per_page => 2,
672
+ :page => nil )
673
+ keys = page.to_a.map { |result|
674
+ result.respond_to?(:first) ? result.first : result[:primary_key]
675
+ }
676
+ assert_equal(%w[dana james], keys)
677
+ assert_equal(2, page.size)
678
+ assert_kind_of(OKMixer::TableDatabase::Paginated, page)
679
+ assert_equal(1, page.current_page)
680
+ assert_equal(2, page.per_page)
681
+ assert_equal(3, page.total_entries)
682
+ assert_equal(2, page.total_pages)
683
+ assert(!page.out_of_bounds?, "The first page was out of bounds")
684
+ assert_equal(0, page.offset)
685
+ assert_nil(page.previous_page)
686
+ assert_equal(2, page.next_page)
687
+ end
688
+ end
689
+
690
+ def test_paginate_adjusts_limit_and_offset_by_page_and_per_page
691
+ load_search_data
692
+ page = @db.paginate( :select => :keys,
693
+ :order => :primary_key,
694
+ :per_page => 2,
695
+ :page => 2 )
696
+ assert_equal(%w[jim], page)
697
+ assert_equal(1, page.size)
698
+ assert_equal(2, page.current_page)
699
+ assert_equal(2, page.per_page)
700
+ assert_equal(3, page.total_entries)
701
+ assert_equal(2, page.total_pages)
702
+ assert(!page.out_of_bounds?, "The last page was out of bounds")
703
+ assert_equal(2, page.offset)
704
+ assert_equal(1, page.previous_page)
705
+ assert_nil(page.next_page)
706
+ end
707
+
708
+ def test_paginate_detects_out_of_bounds_pages
709
+ load_search_data
710
+ page = @db.paginate( :select => :keys,
711
+ :order => :primary_key,
712
+ :per_page => 2,
713
+ :page => 3 )
714
+ assert_equal([ ], page)
715
+ assert_equal(0, page.size)
716
+ assert_equal(3, page.current_page)
717
+ assert_equal(2, page.per_page)
718
+ assert_equal(3, page.total_entries)
719
+ assert_equal(2, page.total_pages)
720
+ assert(page.out_of_bounds?, "An out of bounds page was not detected")
721
+ assert_equal(4, page.offset)
722
+ assert_equal(2, page.previous_page)
723
+ assert_nil(page.next_page)
724
+ end
725
+
726
+ def test_union_returns_the_set_union_of_multiple_queries
727
+ load_condition_data
728
+ assert_equal( [ [ "dana", { "first" => "Dana",
729
+ "middle" => "Ann Leslie",
730
+ "last" => "Gray",
731
+ "age" => "34" } ],
732
+ [ "james", { "first" => "James",
733
+ "last" => "Gray",
734
+ "age" => "33" } ] ],
735
+ @db.union( { :conditions => [:first, :ends_with?, "es"],
736
+ :order => :first },
737
+ {:conditions => [:age, :==, 34]} ).to_a )
738
+ end
739
+
740
+ def test_union_respects_select_and_return_on_the_first_query
741
+ load_condition_data
742
+ assert_equal( %w[dana james],
743
+ @db.union( { :select => :keys,
744
+ :conditions => [:first, :ends_with?, "es"],
745
+ :order => :first },
746
+ {:conditions => [:age, :==, 34]} ) )
747
+ assert_equal( [ [ "dana", { "first" => "Dana",
748
+ "middle" => "Ann Leslie",
749
+ "last" => "Gray",
750
+ "age" => "34" } ],
751
+ [ "james", { "first" => "James",
752
+ "last" => "Gray",
753
+ "age" => "33" } ] ],
754
+ @db.union( { :select => :keys_and_docs,
755
+ :return => :aoa,
756
+ :conditions => [:first, :ends_with?, "es"],
757
+ :order => :first },
758
+ {:conditions => [:age, :==, 34]} ) )
759
+ end
760
+
761
+ def test_intersection_returns_the_set_intersection_of_multiple_queries
762
+ load_condition_data
763
+ assert_equal( [ [ "dana", { "first" => "Dana",
764
+ "middle" => "Ann Leslie",
765
+ "last" => "Gray",
766
+ "age" => "34" } ],
767
+ [ "james", { "first" => "James",
768
+ "last" => "Gray",
769
+ "age" => "33" } ] ],
770
+ @db.isect( { :conditions => [:first, :include?, "a"],
771
+ :order => :first },
772
+ {:conditions => [:last, :==, "Gray"]} ).to_a )
773
+ end
774
+
775
+ def test_intersection_respects_select_and_return_on_the_first_query
776
+ load_condition_data
777
+ assert_equal( [ { "first" => "Dana",
778
+ "middle" => "Ann Leslie",
779
+ "last" => "Gray",
780
+ "age" => "34" },
781
+ { "first" => "James",
782
+ "last" => "Gray",
783
+ "age" => "33" } ],
784
+ @db.isect( { :select => :docs,
785
+ :conditions => [:first, :include?, "a"],
786
+ :order => :first },
787
+ {:conditions => [:last, :==, "Gray"]} ) )
788
+ assert_equal( { "dana" => { "first" => "Dana",
789
+ "middle" => "Ann Leslie",
790
+ "last" => "Gray",
791
+ "age" => "34" },
792
+ "james" => { "first" => "James",
793
+ "last" => "Gray",
794
+ "age" => "33" } },
795
+ @db.isect( { :select => :keys_and_docs,
796
+ :return => :hoh,
797
+ :conditions => [:first, :include?, "a"],
798
+ :order => :first },
799
+ {:conditions => [:last, :==, "Gray"]} ) )
800
+ end
801
+
802
+ def test_difference_returns_the_set_difference_of_multiple_queries
803
+ load_condition_data
804
+ assert_equal( [ [ "dana", { "first" => "Dana",
805
+ "middle" => "Ann Leslie",
806
+ "last" => "Gray",
807
+ "age" => "34" } ],
808
+ [ "james", { "first" => "James",
809
+ "last" => "Gray",
810
+ "age" => "33" } ] ],
811
+ @db.diff( { :conditions => [:last, :==, "Gray"],
812
+ :order => :first },
813
+ {:conditions => [:first, :==, "Jim"]} ).to_a )
814
+ end
815
+
816
+ def test_difference_respects_select_and_return_on_the_first_query
817
+ load_condition_data
818
+ assert_equal( %w[dana james],
819
+ @db.diff( { :select => :keys,
820
+ :conditions => [:last, :==, "Gray"],
821
+ :order => :first },
822
+ {:conditions => [:first, :==, "Jim"]} ) )
823
+ assert_equal( [ { :primary_key => "dana",
824
+ "first" => "Dana",
825
+ "middle" => "Ann Leslie",
826
+ "last" => "Gray",
827
+ "age" => "34" },
828
+ { :primary_key => "james",
829
+ "first" => "James",
830
+ "last" => "Gray",
831
+ "age" => "33" } ],
832
+ @db.diff( { :select => :keys_and_docs,
833
+ :return => :aoh,
834
+ :conditions => [:last, :==, "Gray"],
835
+ :order => :first },
836
+ {:conditions => [:first, :==, "Jim"]} ) )
837
+ end
838
+
839
+ private
840
+
841
+ def load_simple_data
842
+ @db[:pk1] = {:a => 1, :b => 2, :c => 3}
843
+ @db[:pk2] = { }
844
+ end
845
+
846
+ def load_condition_data
847
+ @db[:dana] = { :first => "Dana",
848
+ :middle => "Ann Leslie",
849
+ :last => "Gray",
850
+ :age => 34 }
851
+ @db[:james] = {:first => "James", :last => "Gray", :age => 33}
852
+ @db[:jim] = {:first => "Jim", :last => "Gray", :age => 53}
853
+ end
854
+
855
+ def load_search_data
856
+ @db[:dana] = {:name => "Dana Ann Leslie Gray"}
857
+ @db[:james] = {:name => "James Edward Gray II"}
858
+ @db[:jim] = {:name => "Jim Edward Gray"}
859
+ end
860
+
861
+ def load_order_data
862
+ @db[:middle] = {:str => :b, :num => 2}
863
+ @db[:last] = {:str => :c, :num => 11}
864
+ @db[:first] = {:str => :a, :num => 1}
865
+ end
866
+ end