jmongo 1.0.3 → 1.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.
Files changed (96) hide show
  1. data/Gemfile +8 -0
  2. data/Gemfile.lock +43 -0
  3. data/Rakefile +72 -0
  4. data/jmongo.gemspec +84 -6
  5. data/lib/jmongo.rb +6 -14
  6. data/lib/jmongo/collection.rb +196 -114
  7. data/lib/jmongo/connection.rb +39 -13
  8. data/lib/jmongo/cursor.rb +161 -63
  9. data/lib/jmongo/db.rb +119 -30
  10. data/lib/jmongo/exceptions.rb +39 -0
  11. data/lib/jmongo/mongo-2.6.5.gb1.jar +0 -0
  12. data/lib/jmongo/mongo/bson.rb +130 -0
  13. data/lib/jmongo/mongo/collection.rb +185 -0
  14. data/lib/jmongo/mongo/connection.rb +45 -0
  15. data/lib/jmongo/mongo/db.rb +31 -0
  16. data/lib/jmongo/mongo/jmongo.rb +44 -0
  17. data/lib/jmongo/mongo/mongo.rb +98 -0
  18. data/lib/jmongo/mongo/ruby_ext.rb +38 -0
  19. data/lib/jmongo/mongo/utils.rb +136 -0
  20. data/lib/jmongo/version.rb +1 -1
  21. data/test-results.txt +98 -0
  22. data/test/auxillary/1.4_features.rb +166 -0
  23. data/test/auxillary/authentication_test.rb +68 -0
  24. data/test/auxillary/autoreconnect_test.rb +41 -0
  25. data/test/auxillary/fork_test.rb +30 -0
  26. data/test/auxillary/repl_set_auth_test.rb +58 -0
  27. data/test/auxillary/slave_connection_test.rb +36 -0
  28. data/test/auxillary/threaded_authentication_test.rb +101 -0
  29. data/test/bson/binary_test.rb +15 -0
  30. data/test/bson/bson_test.rb +657 -0
  31. data/test/bson/byte_buffer_test.rb +208 -0
  32. data/test/bson/hash_with_indifferent_access_test.rb +38 -0
  33. data/test/bson/json_test.rb +17 -0
  34. data/test/bson/object_id_test.rb +138 -0
  35. data/test/bson/ordered_hash_test.rb +245 -0
  36. data/test/bson/test_helper.rb +46 -0
  37. data/test/bson/timestamp_test.rb +46 -0
  38. data/test/collection_test.rb +933 -0
  39. data/test/connection_test.rb +325 -0
  40. data/test/conversions_test.rb +121 -0
  41. data/test/cursor_fail_test.rb +75 -0
  42. data/test/cursor_message_test.rb +43 -0
  43. data/test/cursor_test.rb +547 -0
  44. data/test/data/empty_data +0 -0
  45. data/test/data/sample_data +0 -0
  46. data/test/data/sample_file.pdf +0 -0
  47. data/test/data/small_data.txt +1 -0
  48. data/test/db_api_test.rb +739 -0
  49. data/test/db_connection_test.rb +15 -0
  50. data/test/db_test.rb +325 -0
  51. data/test/grid_file_system_test.rb +260 -0
  52. data/test/grid_io_test.rb +210 -0
  53. data/test/grid_test.rb +259 -0
  54. data/test/load/thin/config.ru +6 -0
  55. data/test/load/thin/config.yml.template +6 -0
  56. data/test/load/thin/load.rb +24 -0
  57. data/test/load/unicorn/config.ru +6 -0
  58. data/test/load/unicorn/load.rb +23 -0
  59. data/test/load/unicorn/unicorn.rb.template +29 -0
  60. data/test/replica_sets/connect_test.rb +111 -0
  61. data/test/replica_sets/connection_string_test.rb +29 -0
  62. data/test/replica_sets/count_test.rb +36 -0
  63. data/test/replica_sets/insert_test.rb +54 -0
  64. data/test/replica_sets/pooled_insert_test.rb +58 -0
  65. data/test/replica_sets/query_secondaries.rb +109 -0
  66. data/test/replica_sets/query_test.rb +52 -0
  67. data/test/replica_sets/read_preference_test.rb +43 -0
  68. data/test/replica_sets/refresh_test.rb +123 -0
  69. data/test/replica_sets/replication_ack_test.rb +71 -0
  70. data/test/replica_sets/rs_test_helper.rb +27 -0
  71. data/test/safe_test.rb +68 -0
  72. data/test/support/hash_with_indifferent_access.rb +186 -0
  73. data/test/support/keys.rb +45 -0
  74. data/test/support_test.rb +19 -0
  75. data/test/test_helper.rb +111 -0
  76. data/test/threading/threading_with_large_pool_test.rb +90 -0
  77. data/test/threading_test.rb +88 -0
  78. data/test/tools/auth_repl_set_manager.rb +14 -0
  79. data/test/tools/keyfile.txt +1 -0
  80. data/test/tools/repl_set_manager.rb +377 -0
  81. data/test/unit/collection_test.rb +128 -0
  82. data/test/unit/connection_test.rb +85 -0
  83. data/test/unit/cursor_test.rb +127 -0
  84. data/test/unit/db_test.rb +96 -0
  85. data/test/unit/grid_test.rb +51 -0
  86. data/test/unit/node_test.rb +73 -0
  87. data/test/unit/pool_manager_test.rb +47 -0
  88. data/test/unit/pool_test.rb +9 -0
  89. data/test/unit/read_test.rb +101 -0
  90. data/test/unit/safe_test.rb +125 -0
  91. data/test/uri_test.rb +92 -0
  92. metadata +170 -99
  93. data/lib/jmongo/ajrb.rb +0 -189
  94. data/lib/jmongo/jmongo_jext.rb +0 -302
  95. data/lib/jmongo/mongo-2.6.3.jar +0 -0
  96. data/lib/jmongo/utils.rb +0 -61
@@ -0,0 +1,46 @@
1
+ require File.join(File.dirname(__FILE__), '..', '..', 'lib', 'bson')
2
+ require 'rubygems' if RUBY_VERSION < '1.9.0' && ENV['C_EXT']
3
+ require 'test/unit'
4
+
5
+ def silently
6
+ warn_level = $VERBOSE
7
+ $VERBOSE = nil
8
+ result = yield
9
+ $VERBOSE = warn_level
10
+ result
11
+ end
12
+
13
+ begin
14
+ require 'rubygems' if RUBY_VERSION < "1.9.0" && !ENV['C_EXT']
15
+ silently { require 'shoulda' }
16
+ silently { require 'mocha' }
17
+ rescue LoadError
18
+ puts <<MSG
19
+
20
+ This test suite requires shoulda and mocha.
21
+ You can install them as follows:
22
+ gem install shoulda
23
+ gem install mocha
24
+
25
+ MSG
26
+
27
+ exit
28
+ end
29
+
30
+ require 'bson_ext/cbson' if !(RUBY_PLATFORM =~ /java/) && ENV['C_EXT']
31
+
32
+ class Test::Unit::TestCase
33
+ include BSON
34
+
35
+ def assert_raise_error(klass, message)
36
+ begin
37
+ yield
38
+ rescue => e
39
+ assert_equal klass, e.class
40
+ assert e.message.include?(message), "#{e.message} does not include #{message}."
41
+ else
42
+ flunk "Expected assertion #{klass} but none was raised."
43
+ end
44
+ end
45
+
46
+ end
@@ -0,0 +1,46 @@
1
+ require './test/bson/test_helper'
2
+
3
+ class TimestampTest < Test::Unit::TestCase
4
+
5
+ def test_timestamp_equality
6
+ t1 = Timestamp.new(5000, 200)
7
+ t2 = Timestamp.new(5000, 200)
8
+ assert_equal t2, t1
9
+ end
10
+
11
+ def test_timestamp_range
12
+ t = 1;
13
+ while(t < 1_000_000_000 ) do
14
+ ts = Timestamp.new(t, 0)
15
+ doc = {:ts => ts}
16
+ bson = BSON::BSON_CODER.serialize(doc)
17
+ assert_equal doc[:ts], BSON::BSON_CODER.deserialize(bson)['ts']
18
+ t = t * 10
19
+ end
20
+ end
21
+
22
+ def test_timestamp_32bit_compatibility
23
+ max_32bit_fixnum = (1 << 30) - 1
24
+ test_val = max_32bit_fixnum + 10
25
+
26
+ ts = Timestamp.new(test_val, test_val)
27
+ doc = {:ts => ts}
28
+ bson = BSON::BSON_CODER.serialize(doc)
29
+ assert_equal doc[:ts], BSON::BSON_CODER.deserialize(bson)['ts']
30
+ end
31
+
32
+ def test_implements_array_for_backward_compatibility
33
+ silently do
34
+ ts = Timestamp.new(5000, 200)
35
+ assert_equal 200, ts[0]
36
+ assert_equal 5000, ts[1]
37
+
38
+ array = ts.map {|t| t }
39
+ assert_equal 2, array.length
40
+
41
+ assert_equal 200, array[0]
42
+ assert_equal 5000, array[1]
43
+ end
44
+ end
45
+
46
+ end
@@ -0,0 +1,933 @@
1
+ require './test/test_helper'
2
+
3
+ CONNECTION ||= Mongo::Connection.new(TEST_HOST, TEST_PORT, :op_timeout => 10)
4
+ $db = CONNECTION.db(MONGO_TEST_DB)
5
+
6
+ VERSION = CONNECTION.server_version
7
+ apr VERSION
8
+
9
+ def clear_collections
10
+ $db.collection_names.each do |n|
11
+ $db.drop_collection(n) unless n =~ /system/
12
+ end
13
+ end
14
+
15
+ clear_collections
16
+
17
+ $test = $db.collection("test")
18
+
19
+ class TestCollection < MiniTest::Unit::TestCase
20
+
21
+ def setup
22
+ clear_collections
23
+ end
24
+
25
+ def test_capped_method
26
+ $db.drop_collection('normal')
27
+
28
+ $db.create_collection('normal')
29
+ assert !$db['normal'].capped?
30
+ $db.drop_collection('normal')
31
+
32
+ $db.create_collection('c', :capped => true, :size => 100_000)
33
+ assert $db['c'].capped?
34
+ $db.drop_collection('c')
35
+ end
36
+
37
+ def test_optional_pk_factory
38
+ @coll_default_pk = $db.collection('stuff')
39
+ assert_equal BSON::ObjectId, @coll_default_pk.pk_factory
40
+ @coll_default_pk = $db.create_collection('more-stuff')
41
+ assert_equal BSON::ObjectId, @coll_default_pk.pk_factory
42
+
43
+ # Create a db with a pk_factory.
44
+ @db = Connection.new(ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost',
45
+ ENV['MONGO_RUBY_DRIVER_PORT'] || Connection::DEFAULT_PORT).db(MONGO_TEST_DB, :pk => Object.new)
46
+ @coll = @db.collection('coll-with-pk')
47
+ assert @coll.pk_factory.is_a?(Object)
48
+
49
+ @coll = @db.create_collection('created_coll_with_pk')
50
+ assert @coll.pk_factory.is_a?(Object)
51
+ end
52
+
53
+ class TestPK
54
+ def self.create_pk
55
+ end
56
+ end
57
+
58
+ def test_pk_factory_on_collection
59
+ @coll2 = Collection.new('foo', $db, :pk => TestPK)
60
+ assert_equal TestPK, @coll2.pk_factory
61
+ end
62
+
63
+ def test_valid_names
64
+ assert_raises Mongo::InvalidNSName do
65
+ $db["te$t"]
66
+ end
67
+
68
+ assert_raises Mongo::InvalidNSName do
69
+ $db['$main']
70
+ end
71
+
72
+ assert $db['$cmd']
73
+ assert $db['oplog.$main']
74
+ end
75
+
76
+ def test_collection
77
+ assert_kind_of Collection, $db["test"]
78
+ assert_equal $db["test"].name(), $db.collection("test").name()
79
+ assert_equal $db["test"].name(), $db[:test].name()
80
+
81
+ assert_kind_of Collection, $db["test"]["foo"]
82
+ assert_equal $db["test"]["foo"].name(), $db.collection("test.foo").name()
83
+ assert_equal $db["test"]["foo"].name(), $db["test.foo"].name()
84
+
85
+ $db["test"]["foo"].remove
86
+ $db["test"]["foo"].insert("x" => 5)
87
+ assert_equal 5, $db.collection("test.foo").find_one()["x"]
88
+ end
89
+
90
+ def test_rename_collection
91
+ $db.drop_collection('foo1')
92
+ $db.drop_collection('bar1')
93
+
94
+ @col = $db.create_collection('foo1')
95
+ assert_equal 'foo1', @col.name
96
+
97
+ @col.rename('bar1')
98
+ assert_equal 'bar1', @col.name
99
+ end
100
+
101
+ def test_nil_id
102
+ skip("The Java driver does not allow nil _id")
103
+ assert_equal 5, $test.insert({"_id" => 5, "foo" => "bar"}, {:safe => true})
104
+ assert_equal 5, $test.save({"_id" => 5, "foo" => "baz"}, {:safe => true})
105
+ assert_equal nil, $test.find_one("foo" => "bar")
106
+ assert_equal "baz", $test.find_one(:_id => 5)["foo"]
107
+ assert_raises OperationFailure do
108
+ $test.insert({"_id" => 5, "foo" => "bar"}, {:safe => true})
109
+ end
110
+
111
+ assert_equal nil, $test.insert({"_id" => nil, "foo" => "bar"}, {:safe => true})
112
+ assert_equal nil, $test.save({"_id" => nil, "foo" => "baz"}, {:safe => true})
113
+ assert_equal nil, $test.find_one("foo" => "bar")
114
+ assert_equal "baz", $test.find_one(:_id => nil)["foo"]
115
+ assert_raises OperationFailure do
116
+ $test.insert({"_id" => nil, "foo" => "bar"}, {:safe => true})
117
+ end
118
+ assert_raises OperationFailure do
119
+ $test.insert({:_id => nil, "foo" => "bar"}, {:safe => true})
120
+ end
121
+ end
122
+
123
+ if VERSION > "1.1"
124
+ def setup_for_distinct
125
+ $test.remove
126
+ $test.insert([{:a => 0, :b => {:c => "a"}},
127
+ {:a => 1, :b => {:c => "b"}},
128
+ {:a => 1, :b => {:c => "c"}},
129
+ {:a => 2, :b => {:c => "a"}},
130
+ {:a => 3},
131
+ {:a => 3}])
132
+ end
133
+
134
+ def test_distinct_queries
135
+ setup_for_distinct
136
+ assert_equal [0, 1, 2, 3], $test.distinct(:a).sort
137
+ assert_equal ["a", "b", "c"], $test.distinct("b.c").sort
138
+ end
139
+
140
+ if VERSION >= "1.2"
141
+ def test_filter_collection_with_query
142
+ setup_for_distinct
143
+ assert_equal [2, 3], $test.distinct(:a, {:a => {"$gt" => 1}}).sort
144
+ end
145
+
146
+ def test_filter_nested_objects
147
+ setup_for_distinct
148
+ assert_equal ["a", "b"], $test.distinct("b.c", {"b.c" => {"$ne" => "c"}}).sort
149
+ end
150
+ end
151
+ end
152
+
153
+ def test_safe_insert
154
+ $test.create_index("hello", :unique => true)
155
+ a = {"hello" => "world"}
156
+ $test.insert(a)
157
+ $test.insert(a)
158
+ assert($db.get_last_error['err'].include?("11000"))
159
+
160
+ assert_raises OperationFailure do
161
+ $test.insert(a, :safe => true)
162
+ end
163
+ end
164
+
165
+ def test_bulk_insert_with_continue_on_error
166
+ if VERSION >= "2.0"
167
+ $test.create_index([["foo", 1]], :unique => true)
168
+ docs = []
169
+ docs << {:foo => 1}
170
+ docs << {:foo => 1}
171
+ docs << {:foo => 2}
172
+ docs << {:foo => 3}
173
+ assert_raises OperationFailure do
174
+ $test.insert(docs, :safe => true)
175
+ end
176
+ assert_equal 1, $test.count
177
+ $test.remove
178
+
179
+ docs = []
180
+ docs << {:foo => 1}
181
+ docs << {:foo => 1}
182
+ docs << {:foo => 2}
183
+ docs << {:foo => 3}
184
+ docs << {:foo => 3}
185
+ assert_raises OperationFailure do
186
+ $test.insert(docs, :safe => true, :continue_on_error => true)
187
+ end
188
+ assert_equal 3, $test.count
189
+
190
+ $test.remove
191
+ $test.drop_index("foo_1")
192
+ end
193
+ end
194
+
195
+ def test_maximum_insert_size
196
+ skip("The Java driver does not enforce a maximum")
197
+ docs = []
198
+ 16.times do
199
+ docs << {'foo' => 'a' * 1_000_000}
200
+ end
201
+
202
+ assert_raises InvalidOperation do
203
+ $test.insert(docs)
204
+ end
205
+ end
206
+
207
+ def test_update
208
+ id1 = $test.save("x" => 5)
209
+ $test.update({}, {"$inc" => {"x" => 1}})
210
+ assert_equal 1, $test.count()
211
+ assert_equal 6, $test.find_one(:_id => id1)["x"]
212
+
213
+ id2 = $test.save("x" => 1)
214
+ $test.update({"x" => 6}, {"$inc" => {"x" => 1}})
215
+ assert_equal 7, $test.find_one(:_id => id1)["x"]
216
+ assert_equal 1, $test.find_one(:_id => id2)["x"]
217
+ end
218
+
219
+ def test_multi_update
220
+ $test.save("num" => 10)
221
+ $test.save("num" => 10)
222
+ $test.save("num" => 10)
223
+ assert_equal 3, $test.count
224
+
225
+ $test.update({"num" => 10}, {"$set" => {"num" => 100}}, :multi => true)
226
+ $test.find.each do |doc|
227
+ assert_equal 100, doc["num"]
228
+ end
229
+ end
230
+
231
+ def test_upsert
232
+ $test.update({"page" => "/"}, {"$inc" => {"count" => 1}}, :upsert => true)
233
+ $test.update({"page" => "/"}, {"$inc" => {"count" => 1}}, :upsert => true)
234
+
235
+ assert_equal 1, $test.count()
236
+ assert_equal 2, $test.find_one()["count"]
237
+ end
238
+
239
+ def test_safe_update
240
+ $test.create_index("x", :unique => true)
241
+ $test.insert("x" => 5)
242
+ $test.insert("x" => 10)
243
+
244
+ # Can update an indexed collection.
245
+ $test.update({}, {"$inc" => {"x" => 1}})
246
+ assert !$db.error?
247
+
248
+ # Can't duplicate an index.
249
+ assert_raises OperationFailure do
250
+ $test.update({}, {"x" => 10}, :safe => true)
251
+ end
252
+ end
253
+
254
+ def test_safe_save
255
+ $test.create_index("hello", :unique => true)
256
+
257
+ $test.save("hello" => "world")
258
+ $test.save("hello" => "world")
259
+
260
+ assert_raises OperationFailure do
261
+ $test.save({"hello" => "world"}, :safe => true)
262
+ end
263
+ end
264
+
265
+ def test_safe_remove
266
+ @conn = standard_connection
267
+ @db = @conn[MONGO_TEST_DB]
268
+ @test = @db['test-safe-remove']
269
+ @test.save({:a => 50})
270
+ assert_equal 1, @test.remove({}, :safe => true)["n"]
271
+ @test.drop
272
+ end
273
+
274
+ def test_remove_return_value
275
+ assert_equal true, $test.remove({})
276
+ end
277
+
278
+ def test_count
279
+
280
+ assert_equal 0, $test.count
281
+ $test.save(:x => 1)
282
+ $test.save(:x => 2)
283
+ assert_equal 2, $test.count
284
+
285
+ assert_equal 1, $test.count(:query => {:x => 1})
286
+ assert_equal 1, $test.count(:limit => 1)
287
+ assert_equal 0, $test.count(:skip => 2)
288
+ end
289
+
290
+ # Note: #size is just an alias for #count.
291
+ def test_size
292
+ assert_equal 0, $test.count
293
+ assert_equal $test.size, $test.count
294
+ $test.save("x" => 1)
295
+ $test.save("x" => 2)
296
+ assert_equal $test.size, $test.count
297
+ end
298
+
299
+ def test_no_timeout_option
300
+
301
+ assert_raises ArgumentError, "Timeout can be set to false only when #find is invoked with a block." do
302
+ $test.find({}, :timeout => false)
303
+ end
304
+
305
+ $test.find({}, :timeout => false) do |cursor|
306
+ assert_equal 0, cursor.count
307
+ end
308
+
309
+ $test.save("x" => 1)
310
+ $test.save("x" => 2)
311
+ $test.find({}, :timeout => false) do |cursor|
312
+ assert_equal 2, cursor.count
313
+ end
314
+ end
315
+
316
+ def test_default_timeout
317
+ cursor = $test.find
318
+ assert_equal true, cursor.timeout
319
+ end
320
+
321
+ def test_fields_as_hash
322
+ $test.save(:a => 1, :b => 1, :c => 1)
323
+
324
+ doc = $test.find_one({:a => 1}, :fields => {:b => 0})
325
+ assert_nil doc['b']
326
+ assert doc['a']
327
+ assert doc['c']
328
+
329
+ doc = $test.find_one({:a => 1}, :fields => {:a => 1, :b => 1})
330
+ assert_nil doc['c']
331
+ assert doc['a']
332
+ assert doc['b']
333
+
334
+
335
+ assert_raises Mongo::OperationFailure do
336
+ $test.find_one({:a => 1}, :fields => {:a => 1, :b => 0})
337
+ end
338
+ end
339
+
340
+ if VERSION >= "1.5.1"
341
+ def test_fields_with_slice
342
+ $test.save({:foo => [1, 2, 3, 4, 5, 6], :test => 'slice'})
343
+
344
+ doc = $test.find_one({:test => 'slice'}, :fields => {'foo' => {'$slice' => [0, 3]}})
345
+ assert_equal [1, 2, 3], doc['foo']
346
+ $test.remove
347
+ end
348
+ end
349
+
350
+ def test_find_one
351
+ id = $test.save("hello" => "world", "foo" => "bar")
352
+
353
+ assert_equal "world", $test.find_one()["hello"]
354
+ assert_equal $test.find_one(id), $test.find_one()
355
+ assert_equal $test.find_one(nil), $test.find_one()
356
+ assert_equal $test.find_one({}), $test.find_one()
357
+ assert_equal $test.find_one("hello" => "world"), $test.find_one()
358
+ assert_equal $test.find_one(BSON::OrderedHash["hello", "world"]), $test.find_one()
359
+
360
+ assert $test.find_one(nil, :fields => ["hello"]).include?("hello")
361
+ assert !$test.find_one(nil, :fields => ["foo"]).include?("hello")
362
+ assert_equal ["_id"], $test.find_one(nil, :fields => []).keys()
363
+
364
+ assert_equal nil, $test.find_one("hello" => "foo")
365
+ assert_equal nil, $test.find_one(BSON::OrderedHash["hello", "foo"])
366
+ assert_equal nil, $test.find_one(ObjectId.new)
367
+
368
+ assert_raises TypeError do
369
+ $test.find_one(6)
370
+ end
371
+ end
372
+
373
+ def test_insert_adds_id
374
+ doc = {"hello" => "world"}
375
+ $test.insert(doc)
376
+ assert(doc.include?(:_id))
377
+
378
+ docs = [{"hello" => "world"}, {"hello" => "world"}]
379
+ $test.insert(docs)
380
+ docs.each do |d|
381
+ assert(d.include?(:_id))
382
+ end
383
+ end
384
+
385
+ def test_save_adds_id
386
+ doc = {"hello" => "world"}
387
+ $test.save(doc)
388
+ assert(doc.include?(:_id))
389
+ end
390
+
391
+ def test_optional_find_block
392
+ 10.times do |i|
393
+ $test.save("i" => i)
394
+ end
395
+
396
+ x = nil
397
+ $test.find("i" => 2) { |cursor|
398
+ x = cursor.count()
399
+ }
400
+ assert_equal 1, x
401
+
402
+ i = 0
403
+ $test.find({}, :skip => 5) do |cursor|
404
+ cursor.each do |doc|
405
+ i = i + 1
406
+ end
407
+ end
408
+ assert_equal 5, i
409
+
410
+ c = nil
411
+ $test.find() do |cursor|
412
+ c = cursor
413
+ end
414
+ assert c.closed?
415
+ end
416
+
417
+ def test_map_reduce
418
+ $test << { "user_id" => 1 }
419
+ $test << { "user_id" => 2 }
420
+ m = "function() { emit(this.user_id, 1); }"
421
+ r = "function(k,vals) { return 1; }"
422
+ res = $test.map_reduce(m, r, :out => 'foo');
423
+ assert res.find_one({"_id" => 1})
424
+ assert res.find_one({"_id" => 2})
425
+ end
426
+
427
+ def test_map_reduce_with_code_objects
428
+ $test << { "user_id" => 1 }
429
+ $test << { "user_id" => 2 }
430
+ m = Code.new("function() { emit(this.user_id, 1); }")
431
+ r = Code.new("function(k,vals) { return 1; }")
432
+ res = $test.map_reduce(m, r, :out => 'foo');
433
+ assert res.find_one({"_id" => 1})
434
+ assert res.find_one({"_id" => 2})
435
+ end
436
+
437
+ def test_map_reduce_with_options
438
+ $test << { "user_id" => 1 }
439
+ $test << { "user_id" => 2 }
440
+ $test << { "user_id" => 3 }
441
+ m = Code.new("function() { emit(this.user_id, 1); }")
442
+ r = Code.new("function(k,vals) { return 1; }")
443
+ res = $test.map_reduce(m, r, :query => {"user_id" => {"$gt" => 1}}, :out => 'foo');
444
+ assert_equal 2, res.count
445
+ assert res.find_one({"_id" => 2})
446
+ assert res.find_one({"_id" => 3})
447
+ end
448
+
449
+ def test_map_reduce_with_raw_response
450
+ $test << { "user_id" => 1 }
451
+ $test << { "user_id" => 2 }
452
+ $test << { "user_id" => 3 }
453
+ m = Code.new("function() { emit(this.user_id, 1); }")
454
+ r = Code.new("function(k,vals) { return 1; }")
455
+ res = $test.map_reduce(m, r, :raw => true, :out => 'foo')
456
+ assert res["result"]
457
+ assert res["counts"]
458
+ assert res["timeMillis"]
459
+ end
460
+
461
+ def test_map_reduce_with_output_collection
462
+ $test << { "user_id" => 1 }
463
+ $test << { "user_id" => 2 }
464
+ $test << { "user_id" => 3 }
465
+ output_collection = "test-map-coll"
466
+ m = Code.new("function() { emit(this.user_id, 1); }")
467
+ r = Code.new("function(k,vals) { return 1; }")
468
+ res = $test.map_reduce(m, r, :raw => true, :out => output_collection)
469
+ assert_equal output_collection, res["result"]
470
+ assert res["counts"]
471
+ assert res["timeMillis"]
472
+ end
473
+
474
+ if VERSION >= "1.8.0"
475
+ def test_map_reduce_with_collection_merge
476
+ $test << {:user_id => 1}
477
+ $test << {:user_id => 2}
478
+ output_collection = "test-map-coll"
479
+ m = Code.new("function() { emit(this.user_id, {count: 1}); }")
480
+ r = Code.new("function(k,vals) { var sum = 0;" +
481
+ " vals.forEach(function(v) { sum += v.count;} ); return {count: sum}; }")
482
+ res = $test.map_reduce(m, r, :out => output_collection)
483
+
484
+ $test.remove
485
+ $test << {:user_id => 3}
486
+ res = $test.map_reduce(m, r, :out => {:merge => output_collection})
487
+ assert res.find.to_a.any? {|doc| doc["_id"] == 3 && doc["value"]["count"] == 1}
488
+
489
+ $test.remove
490
+ $test << {:user_id => 3}
491
+ res = $test.map_reduce(m, r, :out => {:reduce => output_collection})
492
+ assert res.find.to_a.any? {|doc| doc["_id"] == 3 && doc["value"]["count"] == 2}
493
+
494
+ assert_raises ArgumentError do
495
+ $test.map_reduce(m, r, :out => {:inline => 1})
496
+ end
497
+
498
+ $test.map_reduce(m, r, :raw => true, :out => {:inline => 1})
499
+ assert res["results"]
500
+ end
501
+ end
502
+
503
+ if VERSION > "1.3.0"
504
+ def test_find_and_modify
505
+ $test << { :a => 1, :processed => false }
506
+ $test << { :a => 2, :processed => false }
507
+ $test << { :a => 3, :processed => false }
508
+
509
+ $test.find_and_modify(:query => {}, :sort => [['a', -1]], :update => {"$set" => {:processed => true}})
510
+
511
+ assert $test.find_one({:a => 3})['processed']
512
+ end
513
+
514
+ def test_find_and_modify_with_invalid_options
515
+ $test << { :a => 1, :processed => false }
516
+ $test << { :a => 2, :processed => false }
517
+ $test << { :a => 3, :processed => false }
518
+
519
+ assert_raises Mongo::OperationFailure do
520
+ $test.find_and_modify(:blimey => {})
521
+ end
522
+ end
523
+ end
524
+
525
+ if VERSION >= "1.3.5"
526
+ def test_coll_stats
527
+ $test << {:n => 1}
528
+ $test.create_index("n")
529
+ stats = $test.stats
530
+ assert_equal "#{MONGO_TEST_$db}.test", stats['ns']
531
+ end
532
+ end
533
+
534
+ def test_saving_dates_pre_epoch
535
+ begin
536
+ $test.save({'date' => Time.utc(1600)})
537
+ assert_in_delta Time.utc(1600), $test.find_one()["date"], 2
538
+ rescue ArgumentError
539
+ # See note in test_date_before_epoch (BSONTest)
540
+ end
541
+ end
542
+
543
+ def test_save_symbol_find_string
544
+ $test.save(:foo => :mike, :foo1 => 'mike')
545
+
546
+ assert_equal :mike, $test.find_one(:foo => :mike)["foo"]
547
+ assert_equal :mike, $test.find_one("foo" => :mike)["foo"]
548
+ assert_equal 'mike', $test.find_one("foo" => :mike)["foo1"]
549
+
550
+ assert_equal :mike, $test.find_one(:foo => "mike")["foo"]
551
+ assert_equal :mike, $test.find_one("foo" => "mike")["foo"]
552
+ end
553
+
554
+ def test_limit_and_skip
555
+ 10.times do |i|
556
+ $test.save(:foo => i)
557
+ end
558
+
559
+ assert_equal 5, $test.find({}, :skip => 5).next_document()["foo"]
560
+ assert_equal nil, $test.find({}, :skip => 10).next_document()
561
+
562
+ assert_equal 5, $test.find({}, :limit => 5).to_a.length
563
+
564
+ assert_equal 3, $test.find({}, :skip => 3, :limit => 5).next_document()["foo"]
565
+ assert_equal 5, $test.find({}, :skip => 3, :limit => 5).to_a.length
566
+ end
567
+
568
+ def test_large_limit
569
+ 2000.times do |i|
570
+ $test.insert("x" => i, "y" => "mongomongo" * 1000)
571
+ end
572
+
573
+ assert_equal 2000, $test.count
574
+
575
+ i = 0
576
+ y = 0
577
+ $test.find({}, :limit => 1900).each do |doc|
578
+ i += 1
579
+ y += doc["x"]
580
+ end
581
+
582
+ assert_equal 1900, i
583
+ assert_equal 1804050, y
584
+ end
585
+
586
+ def test_small_limit
587
+ $test.insert("x" => "hello world")
588
+ $test.insert("x" => "goodbye world")
589
+
590
+ assert_equal 2, $test.count
591
+
592
+ x = 0
593
+ $test.find({}, :limit => 1).each do |doc|
594
+ x += 1
595
+ assert_equal "hello world", doc["x"]
596
+ end
597
+
598
+ assert_equal 1, x
599
+ end
600
+
601
+ def test_find_with_transformer
602
+ klass = Struct.new(:id, :a)
603
+ transformer = Proc.new { |doc| klass.new(doc['_id'], doc['a']) }
604
+ cursor = $test.find({}, :transformer => transformer)
605
+ assert_equal(transformer, cursor.transformer)
606
+ end
607
+
608
+ def test_find_one_with_transformer
609
+ klass = Struct.new(:id, :a)
610
+ transformer = Proc.new { |doc| klass.new(doc['_id'], doc['a']) }
611
+ id = $test.insert('a' => 1)
612
+ doc = $test.find_one(id, :transformer => transformer)
613
+ assert_instance_of(klass, doc)
614
+ end
615
+
616
+ def test_ensure_index
617
+ $test.drop_indexes
618
+ $test.insert("x" => "hello world")
619
+ assert_equal 1, $test.index_information.keys.count #default index
620
+
621
+ $test.ensure_index([["x", Mongo::DESCENDING]], {})
622
+ assert_equal 2, $test.index_information.keys.count
623
+ assert $test.index_information.keys.include? "x_-1"
624
+
625
+ $test.ensure_index([["x", Mongo::ASCENDING]])
626
+ assert $test.index_information.keys.include? "x_1"
627
+
628
+ $test.ensure_index([["type", 1], ["date", -1]])
629
+ assert $test.index_information.keys.include? "type_1_date_-1"
630
+
631
+ $test.drop_index("x_1")
632
+ assert_equal 3, $test.index_information.keys.count
633
+ $test.drop_index("x_-1")
634
+ assert_equal 2, $test.index_information.keys.count
635
+
636
+ $test.ensure_index([["x", Mongo::DESCENDING]], {})
637
+ assert_equal 3, $test.index_information.keys.count
638
+ assert $test.index_information.keys.include? "x_-1"
639
+
640
+ # Make sure that drop_index expires cache properly
641
+ $test.ensure_index([['a', 1]])
642
+ assert $test.index_information.keys.include?("a_1")
643
+ $test.drop_index("a_1")
644
+ assert !$test.index_information.keys.include?("a_1")
645
+ $test.ensure_index([['a', 1]])
646
+ assert $test.index_information.keys.include?("a_1")
647
+ $test.drop_index("a_1")
648
+ end
649
+
650
+ end
651
+
652
+ require 'minitest/spec'
653
+
654
+ describe "Grouping" do
655
+ before do
656
+ $test.remove
657
+ $test.save("a" => 1)
658
+ $test.save("b" => 1)
659
+ @initial = {"count" => 0}
660
+ @reduce_function = "function (obj, prev) { prev.count += inc_value; }"
661
+ @grp_opts = {:initial => @initial, :reduce => BSON::Code.new(@reduce_function, {"inc_value" => 1})}
662
+ end
663
+
664
+ it "should fail if missing required options" do
665
+ lambda { $test.group(:initial => {}) }.must_raise Mongo::MongoArgumentError
666
+ lambda { $test.group(:reduce => "foo") }.must_raise Mongo::MongoArgumentError
667
+ end
668
+
669
+ it "should group results using eval form" do
670
+ @grp_opts[:reduce] = BSON::Code.new(@reduce_function, {"inc_value" => 0.5})
671
+ $test.group( @grp_opts )[0]["count"].must_equal 1
672
+
673
+ @grp_opts[:reduce] = BSON::Code.new(@reduce_function, {"inc_value" => 1})
674
+ $test.group( @grp_opts )[0]["count"].must_equal 2
675
+
676
+ @grp_opts[:reduce] = BSON::Code.new(@reduce_function, {"inc_value" => 2})
677
+ $test.group( @grp_opts )[0]["count"].must_equal 4
678
+ end
679
+
680
+ it "should finalize grouped results" do
681
+ @grp_opts[:finalize] = "function(doc) {doc.f = doc.count + 200; }"
682
+ $test.group( @grp_opts )[0]["f"].must_equal 202
683
+ end
684
+ end
685
+
686
+ describe "Grouping with key" do
687
+ before do
688
+ $test.remove
689
+ $test.save("a" => 1, "pop" => 100)
690
+ $test.save("a" => 1, "pop" => 100)
691
+ $test.save("a" => 2, "pop" => 100)
692
+ $test.save("a" => 2, "pop" => 100)
693
+ @initial = {"count" => 0, "foo" => 1}
694
+ @reduce_function = "function (obj, prev) { prev.count += obj.pop; }"
695
+ end
696
+
697
+ it "should group" do
698
+ result = $test.group(:key => ['a'], :initial => @initial, :reduce => @reduce_function)
699
+ true.must_equal result.all? { |r| r['count'] == 200 }
700
+ end
701
+ end
702
+
703
+ describe "Grouping with a key function" do
704
+ before do
705
+ $test.remove
706
+ $test.save("a" => 1)
707
+ $test.save("a" => 2)
708
+ $test.save("a" => 3)
709
+ $test.save("a" => 4)
710
+ $test.save("a" => 5)
711
+ @initial = {"count" => 0}
712
+ @keyf = "function (doc) { if(doc.a % 2 == 0) { return {even: true}; } else {return {odd: true}} };"
713
+ @reduce = "function (obj, prev) { prev.count += 1; }"
714
+ end
715
+
716
+ it "should group results" do
717
+ results = $test.group(:keyf => @keyf, :initial => @initial, :reduce => @reduce).sort {|a, b| a['count'] <=> b['count']}
718
+ true.must_equal results[0]['even'] && results[0]['count'] == 2.0
719
+ true.must_equal results[1]['odd'] && results[1]['count'] == 3.0
720
+ end
721
+
722
+ it "should group filtered results" do
723
+ results = $test.group(:keyf => @keyf, :cond => {:a => {'$ne' => 2}},
724
+ :initial => @initial, :reduce => @reduce).sort {|a, b| a['count'] <=> b['count']}
725
+ true.must_equal results[0]['even'] && results[0]['count'] == 1.0
726
+ true.must_equal results[1]['odd'] && results[1]['count'] == 3.0
727
+ end
728
+ end
729
+
730
+ describe "A collection with two records" do
731
+ before do
732
+ @collection = $db.collection('test-collection')
733
+ @collection.remove
734
+ @collection.insert({:name => "Jones"})
735
+ @collection.insert({:name => "Smith"})
736
+ end
737
+
738
+ it "should have two records" do
739
+ @collection.size.must_equal 2
740
+ end
741
+
742
+ it "should remove the two records" do
743
+ @collection.remove()
744
+ @collection.size.must_equal 0
745
+ end
746
+
747
+ it "should remove all records if an empty document is specified" do
748
+ @collection.remove({})
749
+ @collection.find.count.must_equal 0
750
+ end
751
+
752
+ it "should remove only matching records" do
753
+ @collection.remove({:name => "Jones"})
754
+ @collection.size.must_equal 1
755
+ end
756
+ end
757
+
758
+ describe "Drop index " do
759
+ before do
760
+ $db.drop_collection('test-collection')
761
+ @collection = $db.collection('test-collection')
762
+ end
763
+
764
+ it "should drop an index" do
765
+ @collection.create_index([['a', Mongo::ASCENDING]])
766
+ assert @collection.index_information['a_1']
767
+ @collection.drop_index([['a', Mongo::ASCENDING]])
768
+ assert_nil @collection.index_information['a_1']
769
+ end
770
+
771
+ it "should drop an index which was given a specific name" do
772
+ @collection.create_index([['a', Mongo::DESCENDING]], {:name => 'i_will_not_fear'})
773
+ assert @collection.index_information['i_will_not_fear']
774
+ @collection.drop_index([['a', Mongo::DESCENDING]])
775
+ assert_nil @collection.index_information['i_will_not_fear']
776
+ end
777
+
778
+ it "should drops an composite index" do
779
+ @collection.create_index([['a', Mongo::DESCENDING], ['b', Mongo::ASCENDING]])
780
+ assert @collection.index_information['a_-1_b_1']
781
+ @collection.drop_index([['a', Mongo::DESCENDING], ['b', Mongo::ASCENDING]])
782
+ assert_nil @collection.index_information['a_-1_b_1']
783
+ end
784
+
785
+ it "should drops an index with symbols" do
786
+ @collection.create_index([['a', Mongo::DESCENDING], [:b, Mongo::ASCENDING]])
787
+ assert @collection.index_information['a_-1_b_1']
788
+ @collection.drop_index([['a', Mongo::DESCENDING], [:b, Mongo::ASCENDING]])
789
+ assert_nil @collection.index_information['a_-1_b_1']
790
+ end
791
+ end
792
+
793
+ describe "Creating indexes " do
794
+ before do
795
+ $db.drop_collection('geo')
796
+ $db.drop_collection('test-collection')
797
+ @collection = $db.collection('test-collection')
798
+ @geo = $db.collection('geo')
799
+ end
800
+
801
+ it "should create index using symbols" do
802
+ @collection.create_index :foo, :name => :bar
803
+ @geo.create_index :goo, :name => :baz
804
+ assert @collection.index_information['bar']
805
+ @collection.drop_index :bar
806
+ assert_nil @collection.index_information['bar']
807
+ assert @geo.index_information['baz']
808
+ @geo.drop_index(:baz)
809
+ assert_nil @geo.index_information['baz']
810
+ end
811
+
812
+ it "should create a geospatial index" do
813
+ @geo.save({'loc' => [-100, 100]})
814
+ @geo.create_index([['loc', Mongo::GEO2D]])
815
+ assert @geo.index_information['loc_2d']
816
+ end
817
+
818
+ it "should create a unique index" do
819
+ @collection.create_index([['a', Mongo::ASCENDING]], :unique => true)
820
+ assert @collection.index_information['a_1']['unique'] == true
821
+ end
822
+
823
+ it "should drop duplicates" do
824
+ @collection.insert({:a => 1})
825
+ @collection.insert({:a => 1})
826
+ assert_equal 2, @collection.find({:a => 1}).count
827
+ @collection.create_index([['a', Mongo::ASCENDING]], :unique => true, :dropDups => true)
828
+ assert_equal 1, @collection.find({:a => 1}).count
829
+ end
830
+
831
+ it "should drop duplicates with ruby-like drop_dups key" do
832
+ @collection.insert({:a => 1})
833
+ @collection.insert({:a => 1})
834
+ assert_equal 2, @collection.find({:a => 1}).count
835
+ @collection.create_index([['a', Mongo::ASCENDING]], :unique => true, :drop_dups => true)
836
+ assert_equal 1, @collection.find({:a => 1}).count
837
+ end
838
+
839
+ it "should drop duplicates with ensure_index and drop_dups key" do
840
+ @collection.insert({:a => 1})
841
+ @collection.insert({:a => 1})
842
+ assert_equal 2, @collection.find({:a => 1}).count
843
+ @collection.ensure_index([['a', Mongo::ASCENDING]], :unique => true, :drop_dups => true)
844
+ assert_equal 1, @collection.find({:a => 1}).count
845
+ end
846
+
847
+ it "should create an index in the background" do
848
+ if VERSION > '1.3.1'
849
+ @collection.create_index([['b', Mongo::ASCENDING]], :background => true)
850
+ assert @collection.index_information['b_1']['background'] == true
851
+ else
852
+ assert true
853
+ end
854
+ end
855
+
856
+ it "should require an array of arrays" do
857
+ assert_raises Mongo::MongoArgumentError do
858
+ @collection.create_index(['c', Mongo::ASCENDING])
859
+ end
860
+ end
861
+
862
+ it "should enforce proper index types" do
863
+ assert_raises Mongo::MongoArgumentError do
864
+ @collection.create_index([['c', 'blah']])
865
+ end
866
+ end
867
+
868
+ it "should raise an error if index name is greater than 128" do
869
+ assert_raises Mongo::OperationFailure do
870
+ @collection.create_index([['a' * 25, 1], ['b' * 25, 1],
871
+ ['c' * 25, 1], ['d' * 25, 1], ['e' * 25, 1]])
872
+ end
873
+ end
874
+
875
+ it "should allow for an alternate name to be specified" do
876
+ @collection.create_index([['a' * 25, 1], ['b' * 25, 1],
877
+ ['c' * 25, 1], ['d' * 25, 1], ['e' * 25, 1]], :name => 'foo_index')
878
+ assert @collection.index_information['foo_index']
879
+ end
880
+
881
+ it "should allow creation of multiple indexes" do
882
+ assert @collection.create_index([['a', 1]])
883
+ assert @collection.create_index([['a', 1]])
884
+ end
885
+
886
+ describe "with an index created" do
887
+ before do
888
+ @collection.create_index([['b', 1], ['a', 1]])
889
+ end
890
+
891
+ it "should return properly ordered index information" do
892
+ assert @collection.index_information['b_1_a_1']
893
+ end
894
+ end
895
+ end
896
+
897
+ # describe "Capped collections" do
898
+ # before do
899
+ # $db.drop_collection('log')
900
+ # @capped = $db.create_collection('log', :capped => true, :size => 1024)
901
+
902
+ # 10.times { |n| @capped.insert({:n => n}) }
903
+ # end
904
+
905
+ # it "should find using a standard cursor" do
906
+ # cursor = @capped.find
907
+ # 10.times do
908
+ # assert cursor.next_document
909
+ # end
910
+ # assert_nil cursor.next_document
911
+ # @capped.insert({:n => 100})
912
+ # assert_nil cursor.next_document
913
+ # end
914
+
915
+ # it "should fail tailable cursor on a non-capped collection" do
916
+ # col = $db['regular-collection']
917
+ # col.insert({:a => 1000})
918
+ # tail = Cursor.new(col, :tailable => true, :order => [['$natural', 1]])
919
+ # assert_raises OperationFailure do
920
+ # tail.next_document
921
+ # end
922
+ # end
923
+
924
+ # it "should find using a tailable cursor" do
925
+ # tail = Cursor.new(@capped, :tailable => true, :order => [['$natural', 1]])
926
+ # 10.times do
927
+ # assert tail.next_document
928
+ # end
929
+ # assert_nil tail.next_document
930
+ # @capped.insert({:n => 100})
931
+ # assert tail.next_document
932
+ # end
933
+ # end