oklahoma_mixer 0.2.0 → 0.3.0

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