mongo 1.10.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (116) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/LICENSE +190 -0
  5. data/README.md +149 -0
  6. data/Rakefile +31 -0
  7. data/VERSION +1 -0
  8. data/bin/mongo_console +43 -0
  9. data/ext/jsasl/target/jsasl.jar +0 -0
  10. data/lib/mongo.rb +90 -0
  11. data/lib/mongo/bulk_write_collection_view.rb +380 -0
  12. data/lib/mongo/collection.rb +1164 -0
  13. data/lib/mongo/collection_writer.rb +364 -0
  14. data/lib/mongo/connection.rb +19 -0
  15. data/lib/mongo/connection/node.rb +239 -0
  16. data/lib/mongo/connection/pool.rb +347 -0
  17. data/lib/mongo/connection/pool_manager.rb +325 -0
  18. data/lib/mongo/connection/sharding_pool_manager.rb +67 -0
  19. data/lib/mongo/connection/socket.rb +18 -0
  20. data/lib/mongo/connection/socket/socket_util.rb +37 -0
  21. data/lib/mongo/connection/socket/ssl_socket.rb +95 -0
  22. data/lib/mongo/connection/socket/tcp_socket.rb +86 -0
  23. data/lib/mongo/connection/socket/unix_socket.rb +39 -0
  24. data/lib/mongo/cursor.rb +719 -0
  25. data/lib/mongo/db.rb +735 -0
  26. data/lib/mongo/exception.rb +88 -0
  27. data/lib/mongo/functional.rb +21 -0
  28. data/lib/mongo/functional/authentication.rb +318 -0
  29. data/lib/mongo/functional/logging.rb +85 -0
  30. data/lib/mongo/functional/read_preference.rb +174 -0
  31. data/lib/mongo/functional/sasl_java.rb +48 -0
  32. data/lib/mongo/functional/uri_parser.rb +374 -0
  33. data/lib/mongo/functional/write_concern.rb +66 -0
  34. data/lib/mongo/gridfs.rb +18 -0
  35. data/lib/mongo/gridfs/grid.rb +112 -0
  36. data/lib/mongo/gridfs/grid_ext.rb +53 -0
  37. data/lib/mongo/gridfs/grid_file_system.rb +163 -0
  38. data/lib/mongo/gridfs/grid_io.rb +484 -0
  39. data/lib/mongo/legacy.rb +140 -0
  40. data/lib/mongo/mongo_client.rb +702 -0
  41. data/lib/mongo/mongo_replica_set_client.rb +523 -0
  42. data/lib/mongo/mongo_sharded_client.rb +159 -0
  43. data/lib/mongo/networking.rb +370 -0
  44. data/lib/mongo/utils.rb +19 -0
  45. data/lib/mongo/utils/conversions.rb +110 -0
  46. data/lib/mongo/utils/core_ext.rb +70 -0
  47. data/lib/mongo/utils/server_version.rb +69 -0
  48. data/lib/mongo/utils/support.rb +80 -0
  49. data/lib/mongo/utils/thread_local_variable_manager.rb +25 -0
  50. data/mongo.gemspec +36 -0
  51. data/test/functional/authentication_test.rb +35 -0
  52. data/test/functional/bulk_api_stress_test.rb +133 -0
  53. data/test/functional/bulk_write_collection_view_test.rb +1129 -0
  54. data/test/functional/client_test.rb +565 -0
  55. data/test/functional/collection_test.rb +2073 -0
  56. data/test/functional/collection_writer_test.rb +83 -0
  57. data/test/functional/conversions_test.rb +163 -0
  58. data/test/functional/cursor_fail_test.rb +63 -0
  59. data/test/functional/cursor_message_test.rb +57 -0
  60. data/test/functional/cursor_test.rb +625 -0
  61. data/test/functional/db_api_test.rb +819 -0
  62. data/test/functional/db_connection_test.rb +27 -0
  63. data/test/functional/db_test.rb +344 -0
  64. data/test/functional/grid_file_system_test.rb +285 -0
  65. data/test/functional/grid_io_test.rb +252 -0
  66. data/test/functional/grid_test.rb +273 -0
  67. data/test/functional/pool_test.rb +62 -0
  68. data/test/functional/safe_test.rb +98 -0
  69. data/test/functional/ssl_test.rb +29 -0
  70. data/test/functional/support_test.rb +62 -0
  71. data/test/functional/timeout_test.rb +58 -0
  72. data/test/functional/uri_test.rb +330 -0
  73. data/test/functional/write_concern_test.rb +118 -0
  74. data/test/helpers/general.rb +50 -0
  75. data/test/helpers/test_unit.rb +317 -0
  76. data/test/replica_set/authentication_test.rb +35 -0
  77. data/test/replica_set/basic_test.rb +174 -0
  78. data/test/replica_set/client_test.rb +341 -0
  79. data/test/replica_set/complex_connect_test.rb +77 -0
  80. data/test/replica_set/connection_test.rb +138 -0
  81. data/test/replica_set/count_test.rb +64 -0
  82. data/test/replica_set/cursor_test.rb +212 -0
  83. data/test/replica_set/insert_test.rb +140 -0
  84. data/test/replica_set/max_values_test.rb +145 -0
  85. data/test/replica_set/pinning_test.rb +55 -0
  86. data/test/replica_set/query_test.rb +73 -0
  87. data/test/replica_set/read_preference_test.rb +214 -0
  88. data/test/replica_set/refresh_test.rb +175 -0
  89. data/test/replica_set/replication_ack_test.rb +94 -0
  90. data/test/replica_set/ssl_test.rb +32 -0
  91. data/test/sharded_cluster/basic_test.rb +197 -0
  92. data/test/shared/authentication/basic_auth_shared.rb +286 -0
  93. data/test/shared/authentication/bulk_api_auth_shared.rb +259 -0
  94. data/test/shared/authentication/gssapi_shared.rb +164 -0
  95. data/test/shared/authentication/sasl_plain_shared.rb +96 -0
  96. data/test/shared/ssl_shared.rb +235 -0
  97. data/test/test_helper.rb +56 -0
  98. data/test/threading/basic_test.rb +120 -0
  99. data/test/tools/mongo_config.rb +608 -0
  100. data/test/tools/mongo_config_test.rb +160 -0
  101. data/test/unit/client_test.rb +347 -0
  102. data/test/unit/collection_test.rb +166 -0
  103. data/test/unit/connection_test.rb +325 -0
  104. data/test/unit/cursor_test.rb +299 -0
  105. data/test/unit/db_test.rb +136 -0
  106. data/test/unit/grid_test.rb +76 -0
  107. data/test/unit/mongo_sharded_client_test.rb +48 -0
  108. data/test/unit/node_test.rb +93 -0
  109. data/test/unit/pool_manager_test.rb +142 -0
  110. data/test/unit/read_pref_test.rb +115 -0
  111. data/test/unit/read_test.rb +159 -0
  112. data/test/unit/safe_test.rb +158 -0
  113. data/test/unit/sharding_pool_manager_test.rb +84 -0
  114. data/test/unit/write_concern_test.rb +175 -0
  115. metadata +260 -0
  116. metadata.gz.sig +0 -0
@@ -0,0 +1,2073 @@
1
+ # Copyright (C) 2009-2013 MongoDB, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'rbconfig'
16
+ require 'test_helper'
17
+
18
+ class CollectionTest < Test::Unit::TestCase
19
+ @@client ||= standard_connection(:op_timeout => 10)
20
+ @@db = @@client.db(TEST_DB)
21
+ @@test = @@db.collection("test")
22
+ @@version = @@client.server_version
23
+
24
+ LIMITED_MAX_BSON_SIZE = 1024
25
+ LIMITED_MAX_MESSAGE_SIZE = 3 * LIMITED_MAX_BSON_SIZE
26
+ LIMITED_TEST_HEADROOM = 50
27
+ LIMITED_VALID_VALUE_SIZE = LIMITED_MAX_BSON_SIZE - LIMITED_TEST_HEADROOM
28
+ LIMITED_INVALID_VALUE_SIZE = LIMITED_MAX_BSON_SIZE + Mongo::MongoClient::COMMAND_HEADROOM + 1
29
+
30
+ def setup
31
+ @@test.remove
32
+ end
33
+
34
+ @@wv0 = Mongo::MongoClient::RELEASE_2_4_AND_BEFORE
35
+ @@wv2 = Mongo::MongoClient::BATCH_COMMANDS
36
+ @@a_h = Mongo::MongoClient::APPEND_HEADROOM
37
+ @@s_h = Mongo::MongoClient::SERIALIZE_HEADROOM
38
+
39
+ MAX_SIZE_EXCEPTION_TEST = [
40
+ #[@@wv0, @@client.max_bson_size, nil, /xyzzy/], # succeeds standalone, fails whole suite
41
+ ]
42
+ MAX_SIZE_EXCEPTION_CRUBY_TEST = [
43
+ [@@wv0, @@client.max_bson_size + 1, BSON::InvalidDocument, /Document.* too large/]
44
+ ]
45
+ MAX_SIZE_EXCEPTION_JRUBY_TEST = [
46
+ [@@wv0, @@client.max_bson_size + 1, Mongo::OperationFailure, /object to insert too large/]
47
+ ]
48
+ MAX_SIZE_EXCEPTION_COMMANDS_TEST = [
49
+ #[@@wv2, @@client.max_bson_size, nil, /xyzzy/], # succeeds standalone, fails whole suite
50
+ [@@wv2, @@client.max_bson_size + 1, Mongo::OperationFailure, /object to insert too large/],
51
+ [@@wv2, @@client.max_bson_size + @@s_h, Mongo::OperationFailure, /object to insert too large/],
52
+ [@@wv2, @@client.max_bson_size + @@a_h, BSON::InvalidDocument, /Document.* too large/]
53
+ ]
54
+
55
+ @@max_size_exception_test = MAX_SIZE_EXCEPTION_TEST
56
+ @@max_size_exception_test += MAX_SIZE_EXCEPTION_CRUBY_TEST unless RUBY_PLATFORM == 'java'
57
+ #@@max_size_exception_test += MAX_SIZE_EXCEPTION_JRUBY_TEST if RUBY_PLATFORM == 'java'
58
+ @@max_size_exception_test += MAX_SIZE_EXCEPTION_COMMANDS_TEST if @@version >= "2.5.2"
59
+
60
+ def generate_sized_doc(size)
61
+ doc = {"_id" => BSON::ObjectId.new, "x" => "y"}
62
+ serialize_doc = BSON::BSON_CODER.serialize(doc, false, false, size)
63
+ doc = {"_id" => BSON::ObjectId.new, "x" => "y" * (1 + size - serialize_doc.size)}
64
+ assert_equal size, BSON::BSON_CODER.serialize(doc, false, false, size).size
65
+ doc
66
+ end
67
+
68
+ def with_max_wire_version(client, wire_version) # does not support replica sets
69
+ if client.wire_version_feature?(wire_version)
70
+ client.class.class_eval(%Q{
71
+ alias :old_max_wire_version :max_wire_version
72
+ def max_wire_version
73
+ #{wire_version}
74
+ end
75
+ })
76
+ yield wire_version
77
+ client.class.class_eval(%Q{
78
+ alias :max_wire_version :old_max_wire_version
79
+ })
80
+ end
81
+ end
82
+
83
+ def test_insert_batch_max_sizes
84
+ @@max_size_exception_test.each do |wire_version, size, exc, regexp|
85
+ with_max_wire_version(@@client, wire_version) do
86
+ @@test.remove
87
+ doc = generate_sized_doc(size)
88
+ begin
89
+ @@test.insert([doc.dup])
90
+ assert_equal nil, exc
91
+ rescue => e
92
+ assert_equal exc, e.class, "wire_version:#{wire_version}, size:#{size}, exc:#{exc} e:#{e.message.inspect} @@version:#{@@version}"
93
+ assert_match regexp, e.message
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ if @@version >= '2.5.4'
100
+
101
+ def test_single_delete_write_command
102
+ @@test.drop
103
+ @@test.insert([{ :a => 1 }, { :a => 1 }])
104
+
105
+ command = BSON::OrderedHash['delete', @@test.name,
106
+ :deletes, [{ :q => { :a => 1 }, :limit => 1 }],
107
+ :writeConcern, { :w => 1 },
108
+ :ordered, false]
109
+
110
+ result = @@db.command(command)
111
+ assert_equal 1, result['n']
112
+ assert_equal 1, result['ok']
113
+ assert_equal 1, @@test.count
114
+ end
115
+
116
+ def test_multi_ordered_delete_write_command
117
+ @@test.drop
118
+ @@test.insert([{ :a => 1 }, { :a => 1 }])
119
+
120
+ command = BSON::OrderedHash['delete', @@test.name,
121
+ :deletes, [{ :q => { :a => 1 }, :limit => 0 }],
122
+ :writeConcern, { :w => 1 },
123
+ :ordered, true]
124
+
125
+ result = @@db.command(command)
126
+ assert_equal 2, result['n']
127
+ assert_equal 1, result['ok']
128
+ assert_equal 0, @@test.count
129
+ end
130
+
131
+ def test_multi_unordered_delete_write_command
132
+ @@test.drop
133
+ @@test.insert([{ :a => 1 }, { :a => 1 }])
134
+
135
+ command = BSON::OrderedHash['delete', @@test.name,
136
+ :deletes, [{ :q => { :a => 1 }, :limit => 0 }],
137
+ :writeConcern, { :w => 1 },
138
+ :ordered, false]
139
+
140
+ result = @@db.command(command)
141
+ assert_equal 2, result['n']
142
+ assert_equal 1, result['ok']
143
+ assert_equal 0, @@test.count
144
+ end
145
+
146
+ def test_delete_write_command_with_no_concern
147
+ @@test.drop
148
+ @@test.insert([{ :a => 1 }, { :a => 1 }])
149
+
150
+ command = BSON::OrderedHash['delete', @@test.name,
151
+ :deletes, [{ :q => { :a => 1 }, :limit => 0 }],
152
+ :ordered, false]
153
+
154
+ result = @@db.command(command)
155
+ assert_equal 2, result['n']
156
+ assert_equal 1, result['ok']
157
+ assert_equal 0, @@test.count
158
+ end
159
+
160
+ def test_delete_write_command_with_error
161
+ @@test.drop
162
+ @@test.insert([{ :a => 1 }, { :a => 1 }])
163
+
164
+ command = BSON::OrderedHash['delete', @@test.name,
165
+ :deletes, [{ :q => { '$set' => { :a => 1 }}, :limit => 0 }],
166
+ :writeConcern, { :w => 1 },
167
+ :ordered, false]
168
+
169
+ assert_raise Mongo::OperationFailure do
170
+ @@db.command(command)
171
+ end
172
+ end
173
+
174
+ def test_single_insert_write_command
175
+ @@test.drop
176
+
177
+ command = BSON::OrderedHash['insert', @@test.name,
178
+ :documents, [{ :a => 1 }],
179
+ :writeConcern, { :w => 1 },
180
+ :ordered, false]
181
+
182
+ result = @@db.command(command)
183
+ assert_equal 1, result['ok']
184
+ assert_equal 1, @@test.count
185
+ end
186
+
187
+ def test_multi_ordered_insert_write_command
188
+ @@test.drop
189
+
190
+ command = BSON::OrderedHash['insert', @@test.name,
191
+ :documents, [{ :a => 1 }, { :a => 2 }],
192
+ :writeConcern, { :w => 1 },
193
+ :ordered, true]
194
+
195
+ result = @@db.command(command)
196
+ assert_equal 1, result['ok']
197
+ assert_equal 2, @@test.count
198
+ end
199
+
200
+ def test_multi_unordered_insert_write_command
201
+ @@test.drop
202
+
203
+ command = BSON::OrderedHash['insert', @@test.name,
204
+ :documents, [{ :a => 1 }, { :a => 2 }],
205
+ :writeConcern, { :w => 1 },
206
+ :ordered, false]
207
+
208
+ result = @@db.command(command)
209
+ assert_equal 1, result['ok']
210
+ assert_equal 2, @@test.count
211
+ end
212
+
213
+ def test_insert_write_command_with_no_concern
214
+ @@test.drop
215
+
216
+ command = BSON::OrderedHash['insert', @@test.name,
217
+ :documents, [{ :a => 1 }, { :a => 2 }],
218
+ :ordered, false]
219
+
220
+ result = @@db.command(command)
221
+ assert_equal 1, result['ok']
222
+ assert_equal 2, @@test.count
223
+ end
224
+
225
+ def test_insert_write_command_with_error
226
+ @@test.drop
227
+ @@test.ensure_index([[:a, 1]], { :unique => true })
228
+
229
+ command = BSON::OrderedHash['insert', @@test.name,
230
+ :documents, [{ :a => 1 }, { :a => 1 }],
231
+ :writeConcern, { :w => 1 },
232
+ :ordered, false]
233
+
234
+ assert_raise Mongo::OperationFailure do
235
+ @@db.command(command)
236
+ end
237
+ end
238
+
239
+ def test_single_update_write_command
240
+ @@test.drop
241
+ @@test.insert([{ :a => 1 }, { :a => 2 }])
242
+
243
+ command = BSON::OrderedHash['update', @@test.name,
244
+ :updates, [{ :q => { :a => 1 }, :u => { '$set' => { :a => 2 }}}],
245
+ :writeConcern, { :w => 1 }]
246
+
247
+ result = @@db.command(command)
248
+ assert_equal 1, result['ok']
249
+ assert_equal 1, result['n']
250
+ assert_equal 2, @@test.find({ :a => 2 }).count
251
+ end
252
+
253
+ def test_multi_ordered_update_write_command
254
+ @@test.drop
255
+ @@test.insert([{ :a => 1 }, { :a => 3 }])
256
+
257
+ command = BSON::OrderedHash['update', @@test.name,
258
+ :updates, [
259
+ { :q => { :a => 1 }, :u => { '$set' => { :a => 2 }}},
260
+ { :q => { :a => 3 }, :u => { '$set' => { :a => 4 }}}
261
+ ],
262
+ :writeConcern, { :w => 1 },
263
+ :ordered, true]
264
+
265
+ result = @@db.command(command)
266
+ assert_equal 1, result['ok']
267
+ assert_equal 2, result['n']
268
+ assert_equal 1, @@test.find({ :a => 2 }).count
269
+ assert_equal 1, @@test.find({ :a => 4 }).count
270
+ end
271
+
272
+ def test_multi_unordered_update_write_command
273
+ @@test.drop
274
+ @@test.insert([{ :a => 1 }, { :a => 3 }])
275
+
276
+ command = BSON::OrderedHash['update', @@test.name,
277
+ :updates, [
278
+ { :q => { :a => 1 }, :u => { '$set' => { :a => 2 }}},
279
+ { :q => { :a => 3 }, :u => { '$set' => { :a => 4 }}}
280
+ ],
281
+ :writeConcern, { :w => 1 },
282
+ :ordered, false]
283
+
284
+ result = @@db.command(command)
285
+ assert_equal 1, result['ok']
286
+ assert_equal 2, result['n']
287
+ assert_equal 1, @@test.find({ :a => 2 }).count
288
+ assert_equal 1, @@test.find({ :a => 4 }).count
289
+ end
290
+
291
+ def test_update_write_command_with_no_concern
292
+ @@test.drop
293
+ @@test.insert([{ :a => 1 }, { :a => 3 }])
294
+
295
+ command = BSON::OrderedHash['update', @@test.name,
296
+ :updates, [
297
+ { :q => { :a => 1 }, :u => { '$set' => { :a => 2 }}},
298
+ { :q => { :a => 3 }, :u => { '$set' => { :a => 4 }}}
299
+ ],
300
+ :ordered, false]
301
+
302
+ result = @@db.command(command)
303
+ assert_equal 1, result['ok']
304
+ assert_equal 2, result['n']
305
+ assert_equal 1, @@test.find({ :a => 2 }).count
306
+ assert_equal 1, @@test.find({ :a => 4 }).count
307
+ end
308
+
309
+ def test_update_write_command_with_error
310
+ @@test.drop
311
+ @@test.ensure_index([[:a, 1]], { :unique => true })
312
+ @@test.insert([{ :a => 1 }, { :a => 2 }])
313
+
314
+ command = BSON::OrderedHash['update', @@test.name,
315
+ :updates, [
316
+ { :q => { :a => 2 }, :u => { '$set' => { :a => 1 }}}
317
+ ],
318
+ :ordered, false]
319
+
320
+ assert_raise Mongo::OperationFailure do
321
+ @@db.command(command)
322
+ end
323
+ end
324
+ end
325
+
326
+ if @@version >= '2.5.1'
327
+
328
+ def test_aggregation_cursor
329
+ [10, 1000].each do |size|
330
+ @@test.drop
331
+ size.times {|i| @@test.insert({ :_id => i }) }
332
+ expected_sum = size.times.reduce(:+)
333
+
334
+ cursor = @@test.aggregate(
335
+ [{ :$project => {:_id => '$_id'}} ],
336
+ :cursor => {}
337
+ )
338
+
339
+ assert_equal Mongo::Cursor, cursor.class
340
+
341
+ cursor_sum = cursor.reduce(0) do |sum, doc|
342
+ sum += doc['_id']
343
+ end
344
+
345
+ assert_equal expected_sum, cursor_sum
346
+ end
347
+ @@test.drop
348
+ end
349
+
350
+ def test_aggregation_array
351
+ @@test.drop
352
+ 100.times {|i| @@test.insert({ :_id => i }) }
353
+ agg = @@test.aggregate([{ :$project => {:_id => '$_id'}} ])
354
+
355
+ assert agg.kind_of?(Array)
356
+
357
+ @@test.drop
358
+ end
359
+
360
+ def test_aggregation_cursor_invalid_ops
361
+ cursor = @@test.aggregate([], :cursor => {})
362
+ assert_raise(Mongo::InvalidOperation) { cursor.rewind! }
363
+ assert_raise(Mongo::InvalidOperation) { cursor.explain }
364
+ assert_raise(Mongo::InvalidOperation) { cursor.count }
365
+ end
366
+ end
367
+
368
+ def test_aggregation_invalid_read_pref
369
+ assert_raise Mongo::MongoArgumentError do
370
+ @@test.aggregate([], :read => :invalid_read_pref)
371
+ end
372
+ end
373
+
374
+ if @@version >= '2.5.3'
375
+ def test_aggregation_supports_explain
376
+ @@db.expects(:command).with do |selector, opts|
377
+ opts[:explain] == true
378
+ end.returns({ 'ok' => 1 })
379
+ @@test.aggregate([], :explain => true)
380
+ end
381
+
382
+ def test_aggregation_explain_returns_raw_result
383
+ response = @@test.aggregate([], :explain => true)
384
+ assert response['stages']
385
+ end
386
+ end
387
+
388
+ def test_capped_method
389
+ @@db.create_collection('normal')
390
+ assert !@@db['normal'].capped?
391
+ @@db.drop_collection('normal')
392
+
393
+ @@db.create_collection('c', :capped => true, :size => 100_000)
394
+ assert @@db['c'].capped?
395
+ @@db.drop_collection('c')
396
+ end
397
+
398
+ def test_optional_pk_factory
399
+ @coll_default_pk = @@db.collection('stuff')
400
+ assert_equal BSON::ObjectId, @coll_default_pk.pk_factory
401
+ @coll_default_pk = @@db.create_collection('more-stuff')
402
+ assert_equal BSON::ObjectId, @coll_default_pk.pk_factory
403
+
404
+ # Create a db with a pk_factory.
405
+ @db = MongoClient.new(ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost',
406
+ ENV['MONGO_RUBY_DRIVER_PORT'] || MongoClient::DEFAULT_PORT).db(TEST_DB, :pk => Object.new)
407
+ @coll = @db.collection('coll-with-pk')
408
+ assert @coll.pk_factory.is_a?(Object)
409
+
410
+ @coll = @db.create_collection('created_coll_with_pk')
411
+ assert @coll.pk_factory.is_a?(Object)
412
+ end
413
+
414
+ class PKTest
415
+ def self.create_pk
416
+ end
417
+ end
418
+
419
+ def test_pk_factory_on_collection
420
+ silently do
421
+ @coll = Collection.new('foo', @@db, PKTest)
422
+ assert_equal PKTest, @coll.pk_factory
423
+ end
424
+
425
+ @coll2 = Collection.new('foo', @@db, :pk => PKTest)
426
+ assert_equal PKTest, @coll2.pk_factory
427
+ end
428
+
429
+ def test_valid_names
430
+ assert_raise Mongo::InvalidNSName do
431
+ @@db["te$t"]
432
+ end
433
+
434
+ assert_raise Mongo::InvalidNSName do
435
+ @@db['$main']
436
+ end
437
+
438
+ assert @@db['$cmd']
439
+ assert @@db['oplog.$main']
440
+ end
441
+
442
+ def test_collection
443
+ assert_kind_of Collection, @@db["test"]
444
+ assert_equal @@db["test"].name(), @@db.collection("test").name()
445
+ assert_equal @@db["test"].name(), @@db[:test].name()
446
+
447
+ assert_kind_of Collection, @@db["test"]["foo"]
448
+ assert_equal @@db["test"]["foo"].name(), @@db.collection("test.foo").name()
449
+ assert_equal @@db["test"]["foo"].name(), @@db["test.foo"].name()
450
+
451
+ @@db["test"]["foo"].remove
452
+ @@db["test"]["foo"].insert("x" => 5)
453
+ assert_equal 5, @@db.collection("test.foo").find_one()["x"]
454
+ end
455
+
456
+ def test_rename_collection
457
+ @@db.drop_collection('foo1')
458
+ @@db.drop_collection('bar1')
459
+
460
+ @col = @@db.create_collection('foo1')
461
+ assert_equal 'foo1', @col.name
462
+
463
+ @col.rename('bar1')
464
+ assert_equal 'bar1', @col.name
465
+ end
466
+
467
+ def test_nil_id
468
+ assert_equal 5, @@test.insert({"_id" => 5, "foo" => "bar"})
469
+ assert_equal 5, @@test.save({"_id" => 5, "foo" => "baz"})
470
+ assert_equal nil, @@test.find_one("foo" => "bar")
471
+ assert_equal "baz", @@test.find_one(:_id => 5)["foo"]
472
+ assert_raise OperationFailure do
473
+ @@test.insert({"_id" => 5, "foo" => "bar"})
474
+ end
475
+
476
+ assert_equal nil, @@test.insert({"_id" => nil, "foo" => "bar"})
477
+ assert_equal nil, @@test.save({"_id" => nil, "foo" => "baz"})
478
+ assert_equal nil, @@test.find_one("foo" => "bar")
479
+ assert_equal "baz", @@test.find_one(:_id => nil)["foo"]
480
+ assert_raise OperationFailure do
481
+ @@test.insert({"_id" => nil, "foo" => "bar"})
482
+ end
483
+ assert_raise OperationFailure do
484
+ @@test.insert({:_id => nil, "foo" => "bar"})
485
+ end
486
+ end
487
+
488
+ if @@version > "1.1"
489
+ def setup_for_distinct
490
+ @@test.remove
491
+ @@test.insert([{:a => 0, :b => {:c => "a"}},
492
+ {:a => 1, :b => {:c => "b"}},
493
+ {:a => 1, :b => {:c => "c"}},
494
+ {:a => 2, :b => {:c => "a"}},
495
+ {:a => 3},
496
+ {:a => 3}])
497
+ end
498
+
499
+ def test_distinct_queries
500
+ setup_for_distinct
501
+ assert_equal [0, 1, 2, 3], @@test.distinct(:a).sort
502
+ assert_equal ["a", "b", "c"], @@test.distinct("b.c").sort
503
+ end
504
+
505
+ if @@version >= "1.2"
506
+ def test_filter_collection_with_query
507
+ setup_for_distinct
508
+ assert_equal [2, 3], @@test.distinct(:a, {:a => {"$gt" => 1}}).sort
509
+ end
510
+
511
+ def test_filter_nested_objects
512
+ setup_for_distinct
513
+ assert_equal ["a", "b"], @@test.distinct("b.c", {"b.c" => {"$ne" => "c"}}).sort
514
+ end
515
+ end
516
+ end
517
+
518
+ def test_safe_insert
519
+ @@test.create_index("hello", :unique => true)
520
+ begin
521
+ a = {"hello" => "world"}
522
+ @@test.insert(a)
523
+ @@test.insert(a, :w => 0)
524
+ assert(@@db.get_last_error['err'].include?("11000"))
525
+
526
+ assert_raise OperationFailure do
527
+ @@test.insert(a)
528
+ end
529
+ ensure
530
+ @@test.drop_indexes
531
+ end
532
+ end
533
+
534
+ def test_bulk_insert
535
+ docs = []
536
+ docs << {:foo => 1}
537
+ docs << {:foo => 2}
538
+ docs << {:foo => 3}
539
+ response = @@test.insert(docs)
540
+ assert_equal 3, response.length
541
+ assert response.all? {|id| id.is_a?(BSON::ObjectId)}
542
+ assert_equal 3, @@test.count
543
+ end
544
+
545
+ def test_bulk_insert_with_continue_on_error
546
+ if @@version >= "2.0"
547
+ @@test.create_index([["foo", 1]], :unique => true)
548
+ begin
549
+ docs = []
550
+ docs << {:foo => 1}
551
+ docs << {:foo => 1}
552
+ docs << {:foo => 2}
553
+ docs << {:foo => 3}
554
+ assert_raise OperationFailure do
555
+ @@test.insert(docs)
556
+ end
557
+ assert_equal 1, @@test.count
558
+ @@test.remove
559
+
560
+ docs = []
561
+ docs << {:foo => 1}
562
+ docs << {:foo => 1}
563
+ docs << {:foo => 2}
564
+ docs << {:foo => 3}
565
+ assert_raise OperationFailure do
566
+ @@test.insert(docs, :continue_on_error => true)
567
+ end
568
+ assert_equal 3, @@test.count
569
+
570
+ @@test.remove
571
+ ensure
572
+ @@test.drop_index("foo_1")
573
+ end
574
+ end
575
+ end
576
+
577
+ def test_bson_valid_with_collect_on_error
578
+ docs = []
579
+ docs << {:foo => 1}
580
+ docs << {:bar => 1}
581
+ doc_ids, error_docs = @@test.insert(docs, :collect_on_error => true)
582
+ assert_equal 2, @@test.count
583
+ assert_equal 2, doc_ids.count
584
+ assert_equal error_docs, []
585
+ end
586
+
587
+ def test_bson_invalid_key_serialize_error_with_collect_on_error
588
+ docs = []
589
+ docs << {:foo => 1}
590
+ docs << {:bar => 1}
591
+ invalid_docs = []
592
+ invalid_docs << {'$invalid-key' => 1}
593
+ invalid_docs << {'invalid.key' => 1}
594
+ docs += invalid_docs
595
+ assert_raise BSON::InvalidKeyName do
596
+ @@test.insert(docs, :collect_on_error => false)
597
+ end
598
+ assert_equal 2, @@test.count
599
+
600
+ doc_ids, error_docs = @@test.insert(docs, :collect_on_error => true)
601
+ assert_equal 2, @@test.count
602
+ assert_equal 2, doc_ids.count
603
+ assert_equal error_docs, invalid_docs
604
+ end
605
+
606
+ def test_bson_invalid_encoding_serialize_error_with_collect_on_error
607
+ # Broken for current JRuby
608
+ if RUBY_PLATFORM == 'java' then return end
609
+ docs = []
610
+ docs << {:foo => 1}
611
+ docs << {:bar => 1}
612
+ invalid_docs = []
613
+ invalid_docs << {"\223\372\226}" => 1} # non utf8 encoding
614
+ docs += invalid_docs
615
+
616
+ assert_raise BSON::InvalidStringEncoding do
617
+ @@test.insert(docs, :collect_on_error => false)
618
+ end
619
+ assert_equal 2, @@test.count
620
+
621
+ doc_ids, error_docs = @@test.insert(docs, :collect_on_error => true)
622
+ assert_equal 2, @@test.count
623
+ assert_equal 2, doc_ids.count
624
+ assert_equal error_docs, invalid_docs
625
+ end
626
+
627
+ def test_insert_one_error_doc_with_collect_on_error
628
+ invalid_doc = {'$invalid-key' => 1}
629
+ invalid_docs = [invalid_doc]
630
+ doc_ids, error_docs = @@test.insert(invalid_docs, :collect_on_error => true)
631
+ assert_equal [], doc_ids
632
+ assert_equal [invalid_doc], error_docs
633
+ end
634
+
635
+ def test_insert_empty_docs_raises_exception
636
+ assert_raise OperationFailure do
637
+ @@test.insert([])
638
+ end
639
+ end
640
+
641
+ def test_insert_empty_docs_with_collect_on_error_raises_exception
642
+ assert_raise OperationFailure do
643
+ @@test.insert([], :collect_on_error => true)
644
+ end
645
+ end
646
+
647
+ def limited_collection
648
+ conn = standard_connection(:connect => false)
649
+ admin_db = Object.new
650
+ admin_db.expects(:command).returns({
651
+ 'ok' => 1,
652
+ 'ismaster' => 1,
653
+ 'maxBsonObjectSize' => LIMITED_MAX_BSON_SIZE,
654
+ 'maxMessageSizeBytes' => LIMITED_MAX_MESSAGE_SIZE
655
+ })
656
+ conn.expects(:[]).with('admin').returns(admin_db)
657
+ conn.connect
658
+ return conn.db(TEST_DB)["test"]
659
+ end
660
+
661
+ def test_non_operation_failure_halts_insertion_with_continue_on_error
662
+ coll = limited_collection
663
+ coll.db.connection.stubs(:send_message_with_gle).raises(OperationTimeout).times(1)
664
+ docs = []
665
+ 10.times do
666
+ docs << {'foo' => 'a' * LIMITED_VALID_VALUE_SIZE}
667
+ end
668
+ assert_raise OperationTimeout do
669
+ coll.insert(docs, :continue_on_error => true)
670
+ end
671
+ end
672
+
673
+ def test_chunking_batch_insert
674
+ docs = []
675
+ 10.times do
676
+ docs << {'foo' => 'a' * LIMITED_VALID_VALUE_SIZE}
677
+ end
678
+ limited_collection.insert(docs)
679
+ assert_equal 10, limited_collection.count
680
+ end
681
+
682
+ def test_chunking_batch_insert_without_collect_on_error
683
+ docs = []
684
+ 4.times do
685
+ docs << {'foo' => 'a' * LIMITED_VALID_VALUE_SIZE}
686
+ end
687
+ invalid_docs = []
688
+ invalid_docs << {'$invalid-key' => 1} # non utf8 encoding
689
+ docs += invalid_docs
690
+ 4.times do
691
+ docs << {'foo' => 'a' * LIMITED_VALID_VALUE_SIZE}
692
+ end
693
+ assert_raise BSON::InvalidKeyName do
694
+ limited_collection.insert(docs, :collect_on_error => false)
695
+ end
696
+ end
697
+
698
+ def test_chunking_batch_insert_with_collect_on_error
699
+ # Broken for current JRuby
700
+ if RUBY_PLATFORM == 'java' then return end
701
+ docs = []
702
+ 4.times do
703
+ docs << {'foo' => 'a' * LIMITED_VALID_VALUE_SIZE}
704
+ end
705
+ invalid_docs = []
706
+ invalid_docs << {'$invalid-key' => 1} # non utf8 encoding
707
+ docs += invalid_docs
708
+ 4.times do
709
+ docs << {'foo' => 'a' * LIMITED_VALID_VALUE_SIZE}
710
+ end
711
+ doc_ids, error_docs = limited_collection.insert(docs, :collect_on_error => true)
712
+ assert_equal 8, doc_ids.count
713
+ assert_equal doc_ids.count, limited_collection.count
714
+ assert_equal error_docs, invalid_docs
715
+ end
716
+
717
+ def test_chunking_batch_insert_with_continue_on_error
718
+ docs = []
719
+ 4.times do
720
+ docs << {'foo' => 'a' * LIMITED_VALID_VALUE_SIZE}
721
+ end
722
+ docs << {'_id' => 'b', 'foo' => 'a'}
723
+ docs << {'_id' => 'b', 'foo' => 'c'}
724
+ 4.times do
725
+ docs << {'foo' => 'a' * LIMITED_VALID_VALUE_SIZE}
726
+ end
727
+ assert_raise OperationFailure do
728
+ limited_collection.insert(docs, :continue_on_error => true)
729
+ end
730
+ assert limited_collection.count >= 6, "write commands need headroom for doc wrapping overhead - count:#{limited_collection.count}"
731
+ end
732
+
733
+ def test_chunking_batch_insert_without_continue_on_error
734
+ docs = []
735
+ 4.times do
736
+ docs << {'foo' => 'a' * LIMITED_VALID_VALUE_SIZE}
737
+ end
738
+ docs << {'_id' => 'b', 'foo' => 'a'}
739
+ docs << {'_id' => 'b', 'foo' => 'c'}
740
+ 4.times do
741
+ docs << {'foo' => 'a' * LIMITED_VALID_VALUE_SIZE}
742
+ end
743
+ assert_raise OperationFailure do
744
+ limited_collection.insert(docs, :continue_on_error => false)
745
+ end
746
+ assert_equal 5, limited_collection.count
747
+ end
748
+
749
+ def test_maximum_insert_size
750
+ docs = []
751
+ 3.times do
752
+ docs << {'foo' => 'a' * LIMITED_VALID_VALUE_SIZE}
753
+ end
754
+ assert_equal limited_collection.insert(docs).length, 3
755
+ end
756
+
757
+ def test_maximum_document_size
758
+ assert_raise InvalidDocument do
759
+ limited_collection.insert({'foo' => 'a' * LIMITED_MAX_BSON_SIZE})
760
+ end
761
+ end
762
+
763
+ def test_maximum_save_size
764
+ assert limited_collection.save({'foo' => 'a' * LIMITED_VALID_VALUE_SIZE})
765
+ assert_raise InvalidDocument do
766
+ limited_collection.save({'foo' => 'a' * LIMITED_MAX_BSON_SIZE})
767
+ end
768
+ end
769
+
770
+ def test_maximum_remove_size
771
+ assert limited_collection.remove({'foo' => 'a' * LIMITED_VALID_VALUE_SIZE})
772
+ assert_raise InvalidDocument do
773
+ limited_collection.remove({'foo' => 'a' * LIMITED_MAX_BSON_SIZE})
774
+ end
775
+ end
776
+
777
+ def test_maximum_update_size
778
+ assert_raise InvalidDocument do
779
+ limited_collection.update(
780
+ {'foo' => 'a' * LIMITED_MAX_BSON_SIZE},
781
+ {'foo' => 'a' * LIMITED_VALID_VALUE_SIZE}
782
+ )
783
+ end
784
+
785
+ assert_raise InvalidDocument do
786
+ limited_collection.update(
787
+ {'foo' => 'a' * LIMITED_VALID_VALUE_SIZE},
788
+ {'foo' => 'a' * LIMITED_MAX_BSON_SIZE}
789
+ )
790
+ end
791
+
792
+ assert_raise InvalidDocument do
793
+ limited_collection.update(
794
+ {'foo' => 'a' * LIMITED_MAX_BSON_SIZE},
795
+ {'foo' => 'a' * LIMITED_MAX_BSON_SIZE}
796
+ )
797
+ end
798
+
799
+ assert limited_collection.update(
800
+ {'foo' => 'a' * LIMITED_VALID_VALUE_SIZE},
801
+ {'foo' => 'a' * LIMITED_VALID_VALUE_SIZE}
802
+ )
803
+ end
804
+
805
+ def test_maximum_query_size
806
+ assert limited_collection.find({'foo' => 'a' * LIMITED_VALID_VALUE_SIZE}).to_a
807
+ assert limited_collection.find(
808
+ {'foo' => 'a' * LIMITED_VALID_VALUE_SIZE},
809
+ {:fields => {'foo' => 'a' * LIMITED_VALID_VALUE_SIZE}}
810
+ ).to_a
811
+
812
+ assert_raise InvalidDocument do
813
+ limited_collection.find({'foo' => 'a' * LIMITED_INVALID_VALUE_SIZE}).to_a
814
+ end
815
+
816
+ assert_raise InvalidDocument do
817
+ limited_collection.find(
818
+ {'foo' => 'a' * LIMITED_VALID_VALUE_SIZE},
819
+ {:fields => {'foo' => 'a' * LIMITED_MAX_BSON_SIZE}}
820
+ ).to_a
821
+ end
822
+ end
823
+
824
+ #if @@version >= "1.5.1"
825
+ # def test_safe_mode_with_advanced_safe_with_invalid_options
826
+ # assert_raise_error ArgumentError, "Unknown key(s): wtime" do
827
+ # @@test.insert({:foo => 1}, :w => 2, :wtime => 1, :fsync => true)
828
+ # end
829
+ # assert_raise_error ArgumentError, "Unknown key(s): wtime" do
830
+ # @@test.update({:foo => 1}, {:foo => 2}, :w => 2, :wtime => 1, :fsync => true)
831
+ # end
832
+ #
833
+ # assert_raise_error ArgumentError, "Unknown key(s): wtime" do
834
+ # @@test.remove({:foo => 2}, :w => 2, :wtime => 1, :fsync => true)
835
+ # end
836
+ # end
837
+ #end
838
+
839
+ def test_safe_mode_with_journal_commit_option
840
+ with_default_journaling(@@client) do
841
+ @@test.insert({:foo => 1}, :j => true)
842
+ @@test.update({:foo => 1}, {:foo => 2}, :j => true)
843
+ @@test.remove({:foo => 2}, :j => true)
844
+ end
845
+ end
846
+
847
+ if @@version < "2.5.3"
848
+ def test_jnote_raises_exception
849
+ with_no_journaling(@@client) do
850
+ ex = assert_raise Mongo::WriteConcernError do
851
+ @@test.insert({:foo => 1}, :j => true)
852
+ end
853
+ result = ex.result
854
+ assert_true result.has_key?("jnote")
855
+ end
856
+ end
857
+
858
+ def test_wnote_raises_exception
859
+ ex = assert_raise Mongo::WriteConcernError do
860
+ @@test.insert({:foo => 1}, :w => 2)
861
+ end
862
+ result = ex.result
863
+ assert_true result.has_key?("wnote")
864
+ end
865
+ end
866
+
867
+ def test_update
868
+ id1 = @@test.save("x" => 5)
869
+ @@test.update({}, {"$inc" => {"x" => 1}})
870
+ assert_equal 1, @@test.count()
871
+ assert_equal 6, @@test.find_one(:_id => id1)["x"]
872
+
873
+ id2 = @@test.save("x" => 1)
874
+ @@test.update({"x" => 6}, {"$inc" => {"x" => 1}})
875
+ assert_equal 7, @@test.find_one(:_id => id1)["x"]
876
+ assert_equal 1, @@test.find_one(:_id => id2)["x"]
877
+ end
878
+
879
+ if @@version < "2.5.3"
880
+ def test_update_check_keys
881
+ @@test.save("x" => 1)
882
+ @@test.update({"x" => 1}, {"$set" => {"a.b" => 2}})
883
+ assert_equal 2, @@test.find_one("x" => 1)["a"]["b"]
884
+
885
+ assert_raise_error BSON::InvalidKeyName do
886
+ @@test.update({"x" => 1}, {"a.b" => 3})
887
+ end
888
+ end
889
+ end
890
+
891
+ if @@version >= "1.1.3"
892
+ def test_multi_update
893
+ @@test.save("num" => 10)
894
+ @@test.save("num" => 10)
895
+ @@test.save("num" => 10)
896
+ assert_equal 3, @@test.count
897
+
898
+ @@test.update({"num" => 10}, {"$set" => {"num" => 100}}, :multi => true)
899
+ @@test.find.each do |doc|
900
+ assert_equal 100, doc["num"]
901
+ end
902
+ end
903
+ end
904
+
905
+ def test_upsert
906
+ @@test.update({"page" => "/"}, {"$inc" => {"count" => 1}}, :upsert => true)
907
+ @@test.update({"page" => "/"}, {"$inc" => {"count" => 1}}, :upsert => true)
908
+
909
+ assert_equal 1, @@test.count()
910
+ assert_equal 2, @@test.find_one()["count"]
911
+ end
912
+
913
+ if @@version < "1.1.3"
914
+ def test_safe_update
915
+ @@test.create_index("x")
916
+ @@test.insert("x" => 5)
917
+
918
+ @@test.update({}, {"$inc" => {"x" => 1}})
919
+ assert @@db.error?
920
+
921
+ # Can't change an index.
922
+ assert_raise OperationFailure do
923
+ @@test.update({}, {"$inc" => {"x" => 1}})
924
+ end
925
+ @@test.drop
926
+ end
927
+ else
928
+ def test_safe_update
929
+ @@test.create_index("x", :unique => true)
930
+ @@test.insert("x" => 5)
931
+ @@test.insert("x" => 10)
932
+
933
+ # Can update an indexed collection.
934
+ @@test.update({}, {"$inc" => {"x" => 1}})
935
+ assert !@@db.error?
936
+
937
+ # Can't duplicate an index.
938
+ assert_raise OperationFailure do
939
+ @@test.update({}, {"x" => 10})
940
+ end
941
+ @@test.drop
942
+ end
943
+ end
944
+
945
+ def test_safe_save
946
+ @@test.create_index("hello", :unique => true)
947
+
948
+ @@test.save("hello" => "world")
949
+ @@test.save({"hello" => "world"}, :w => 0)
950
+
951
+ assert_raise OperationFailure do
952
+ @@test.save({"hello" => "world"})
953
+ end
954
+ @@test.drop
955
+ end
956
+
957
+ def test_mocked_safe_remove
958
+ @client = standard_connection
959
+ @db = @client[TEST_DB]
960
+ @test = @db['test-safe-remove']
961
+ @test.save({:a => 20})
962
+ @client.stubs(:receive).returns([[{'ok' => 0, 'err' => 'failed'}], 1, 0])
963
+
964
+ assert_raise OperationFailure do
965
+ @test.remove({})
966
+ end
967
+ @test.drop
968
+ end
969
+
970
+ def test_safe_remove
971
+ @client = standard_connection
972
+ @db = @client[TEST_DB]
973
+ @test = @db['test-safe-remove']
974
+ @test.remove
975
+ @test.save({:a => 50})
976
+ assert_equal 1, @test.remove({})["n"]
977
+ @test.drop
978
+ end
979
+
980
+ def test_remove_return_value
981
+ assert_equal true, @@test.remove({}, :w => 0)
982
+ end
983
+
984
+ def test_remove_with_limit
985
+ @@test.insert([{:n => 1},{:n => 2},{:n => 3}])
986
+ @@test.remove({}, :limit => 1)
987
+ assert_equal 2, @@test.count
988
+ @@test.remove({}, :limit => 0)
989
+ assert_equal 0, @@test.count
990
+ end
991
+
992
+ def test_count
993
+ @@test.drop
994
+
995
+ assert_equal 0, @@test.count
996
+ @@test.save(:x => 1)
997
+ @@test.save(:x => 2)
998
+ assert_equal 2, @@test.count
999
+
1000
+ assert_equal 1, @@test.count(:query => {:x => 1})
1001
+ assert_equal 1, @@test.count(:limit => 1)
1002
+ assert_equal 0, @@test.count(:skip => 2)
1003
+ end
1004
+
1005
+ # Note: #size is just an alias for #count.
1006
+ def test_size
1007
+ @@test.drop
1008
+
1009
+ assert_equal 0, @@test.count
1010
+ assert_equal @@test.size, @@test.count
1011
+ @@test.save("x" => 1)
1012
+ @@test.save("x" => 2)
1013
+ assert_equal @@test.size, @@test.count
1014
+ end
1015
+
1016
+ def test_no_timeout_option
1017
+ @@test.drop
1018
+
1019
+ assert_raise ArgumentError, "Timeout can be set to false only when #find is invoked with a block." do
1020
+ @@test.find({}, :timeout => false)
1021
+ end
1022
+
1023
+ @@test.find({}, :timeout => false) do |cursor|
1024
+ assert_equal 0, cursor.count
1025
+ end
1026
+
1027
+ @@test.save("x" => 1)
1028
+ @@test.save("x" => 2)
1029
+ @@test.find({}, :timeout => false) do |cursor|
1030
+ assert_equal 2, cursor.count
1031
+ end
1032
+ end
1033
+
1034
+ def test_default_timeout
1035
+ cursor = @@test.find
1036
+ assert_equal true, cursor.timeout
1037
+ end
1038
+
1039
+ def test_fields_as_hash
1040
+ @@test.save(:a => 1, :b => 1, :c => 1)
1041
+
1042
+ doc = @@test.find_one({:a => 1}, :fields => {:b => 0})
1043
+ assert_nil doc['b']
1044
+ assert doc['a']
1045
+ assert doc['c']
1046
+
1047
+ doc = @@test.find_one({:a => 1}, :fields => {:a => 1, :b => 1})
1048
+ assert_nil doc['c']
1049
+ assert doc['a']
1050
+ assert doc['b']
1051
+
1052
+
1053
+ assert_raise Mongo::OperationFailure do
1054
+ @@test.find_one({:a => 1}, :fields => {:a => 1, :b => 0})
1055
+ end
1056
+ end
1057
+
1058
+ if @@version >= '2.5.5'
1059
+ def test_meta_field_projection
1060
+ @@test.save({ :t => 'spam eggs and spam'})
1061
+ @@test.save({ :t => 'spam'})
1062
+ @@test.save({ :t => 'egg sausage and bacon'})
1063
+
1064
+ @@test.ensure_index([[:t, 'text']])
1065
+ assert @@test.find_one({ :$text => { :$search => 'spam' }},
1066
+ { :fields => [:t, { :score => { :$meta => 'textScore' } }] })
1067
+ end
1068
+
1069
+ def test_sort_by_meta
1070
+ @@test.save({ :t => 'spam eggs and spam'})
1071
+ @@test.save({ :t => 'spam'})
1072
+ @@test.save({ :t => 'egg sausage and bacon'})
1073
+
1074
+ @@test.ensure_index([[:t, 'text']])
1075
+ assert @@test.find({ :$text => { :$search => 'spam' }}).sort([:score, { '$meta' => 'textScore' }])
1076
+ assert @@test.find({ :$text => { :$search => 'spam' }}).sort(:score => { '$meta' =>'textScore' })
1077
+ end
1078
+ end
1079
+
1080
+ if @@version >= "1.5.1"
1081
+ def test_fields_with_slice
1082
+ @@test.save({:foo => [1, 2, 3, 4, 5, 6], :test => 'slice'})
1083
+
1084
+ doc = @@test.find_one({:test => 'slice'}, :fields => {'foo' => {'$slice' => [0, 3]}})
1085
+ assert_equal [1, 2, 3], doc['foo']
1086
+ @@test.remove
1087
+ end
1088
+ end
1089
+
1090
+ def test_find_one
1091
+ id = @@test.save("hello" => "world", "foo" => "bar")
1092
+
1093
+ assert_equal "world", @@test.find_one()["hello"]
1094
+ assert_equal @@test.find_one(id), @@test.find_one()
1095
+ assert_equal @@test.find_one(nil), @@test.find_one()
1096
+ assert_equal @@test.find_one({}), @@test.find_one()
1097
+ assert_equal @@test.find_one("hello" => "world"), @@test.find_one()
1098
+ assert_equal @@test.find_one(BSON::OrderedHash["hello", "world"]), @@test.find_one()
1099
+
1100
+ assert @@test.find_one(nil, :fields => ["hello"]).include?("hello")
1101
+ assert !@@test.find_one(nil, :fields => ["foo"]).include?("hello")
1102
+ assert_equal ["_id"], @@test.find_one(nil, :fields => []).keys()
1103
+
1104
+ assert_equal nil, @@test.find_one("hello" => "foo")
1105
+ assert_equal nil, @@test.find_one(BSON::OrderedHash["hello", "foo"])
1106
+ assert_equal nil, @@test.find_one(ObjectId.new)
1107
+
1108
+ assert_raise TypeError do
1109
+ @@test.find_one(6)
1110
+ end
1111
+ end
1112
+
1113
+ def test_find_one_with_max_time_ms
1114
+ with_forced_timeout(@@client) do
1115
+ assert_raise ExecutionTimeout do
1116
+ @@test.find_one({}, { :max_time_ms => 100 })
1117
+ end
1118
+ end
1119
+ end
1120
+
1121
+ def test_find_one_with_compile_regex_option
1122
+ regex = /.*/
1123
+ @@test.insert('r' => /.*/)
1124
+ assert_kind_of Regexp, @@test.find_one({})['r']
1125
+ assert_kind_of Regexp, @@test.find_one({}, :compile_regex => true)['r']
1126
+ assert_equal BSON::Regex, @@test.find_one({}, :compile_regex => false)['r'].class
1127
+ end
1128
+
1129
+ def test_insert_adds_id
1130
+ doc = {"hello" => "world"}
1131
+ @@test.insert(doc)
1132
+ assert(doc.include?(:_id))
1133
+
1134
+ docs = [{"hello" => "world"}, {"hello" => "world"}]
1135
+ @@test.insert(docs)
1136
+ docs.each do |d|
1137
+ assert(d.include?(:_id))
1138
+ end
1139
+ end
1140
+
1141
+ def test_save_adds_id
1142
+ doc = {"hello" => "world"}
1143
+ @@test.save(doc)
1144
+ assert(doc.include?(:_id))
1145
+ end
1146
+
1147
+ def test_optional_find_block
1148
+ 10.times do |i|
1149
+ @@test.save("i" => i)
1150
+ end
1151
+
1152
+ x = nil
1153
+ @@test.find("i" => 2) { |cursor|
1154
+ x = cursor.count()
1155
+ }
1156
+ assert_equal 1, x
1157
+
1158
+ i = 0
1159
+ @@test.find({}, :skip => 5) do |cursor|
1160
+ cursor.each do |doc|
1161
+ i = i + 1
1162
+ end
1163
+ end
1164
+ assert_equal 5, i
1165
+
1166
+ c = nil
1167
+ @@test.find() do |cursor|
1168
+ c = cursor
1169
+ end
1170
+ assert c.closed?
1171
+ end
1172
+
1173
+ def setup_aggregate_data
1174
+ # save some data
1175
+ @@test.save( {
1176
+ "_id" => 1,
1177
+ "title" => "this is my title",
1178
+ "author" => "bob",
1179
+ "posted" => Time.utc(2000),
1180
+ "pageViews" => 5 ,
1181
+ "tags" => [ "fun" , "good" , "fun" ],
1182
+ "comments" => [
1183
+ { "author" => "joe", "text" => "this is cool" },
1184
+ { "author" => "sam", "text" => "this is bad" }
1185
+ ],
1186
+ "other" => { "foo" => 5 }
1187
+ } )
1188
+
1189
+ @@test.save( {
1190
+ "_id" => 2,
1191
+ "title" => "this is your title",
1192
+ "author" => "dave",
1193
+ "posted" => Time.utc(2001),
1194
+ "pageViews" => 7,
1195
+ "tags" => [ "fun" , "nasty" ],
1196
+ "comments" => [
1197
+ { "author" => "barbara" , "text" => "this is interesting" },
1198
+ { "author" => "jenny", "text" => "i like to play pinball", "votes" => 10 }
1199
+ ],
1200
+ "other" => { "bar" => 14 }
1201
+ })
1202
+
1203
+ @@test.save( {
1204
+ "_id" => 3,
1205
+ "title" => "this is some other title",
1206
+ "author" => "jane",
1207
+ "posted" => Time.utc(2002),
1208
+ "pageViews" => 6 ,
1209
+ "tags" => [ "nasty", "filthy" ],
1210
+ "comments" => [
1211
+ { "author" => "will" , "text" => "i don't like the color" } ,
1212
+ { "author" => "jenny" , "text" => "can i get that in green?" }
1213
+ ],
1214
+ "other" => { "bar" => 14 }
1215
+ })
1216
+
1217
+ end
1218
+
1219
+ if @@version > '2.1.1'
1220
+ def test_reponds_to_aggregate
1221
+ assert_respond_to @@test, :aggregate
1222
+ end
1223
+
1224
+ def test_aggregate_requires_arguments
1225
+ assert_raise MongoArgumentError do
1226
+ @@test.aggregate()
1227
+ end
1228
+ end
1229
+
1230
+ def test_aggregate_requires_valid_arguments
1231
+ assert_raise MongoArgumentError do
1232
+ @@test.aggregate({})
1233
+ end
1234
+ end
1235
+
1236
+ def test_aggregate_pipeline_operator_format
1237
+ assert_raise Mongo::OperationFailure do
1238
+ @@test.aggregate([{"$project" => "_id"}])
1239
+ end
1240
+ end
1241
+
1242
+ def test_aggregate_pipeline_operators_using_strings
1243
+ setup_aggregate_data
1244
+ desired_results = [ {"_id"=>1, "pageViews"=>5, "tags"=>["fun", "good", "fun"]},
1245
+ {"_id"=>2, "pageViews"=>7, "tags"=>["fun", "nasty"]},
1246
+ {"_id"=>3, "pageViews"=>6, "tags"=>["nasty", "filthy"]} ]
1247
+ results = @@test.aggregate([{"$project" => {"tags" => 1, "pageViews" => 1}}])
1248
+ assert_equal desired_results, results
1249
+ end
1250
+
1251
+ def test_aggregate_pipeline_operators_using_symbols
1252
+ setup_aggregate_data
1253
+ desired_results = [ {"_id"=>1, "pageViews"=>5, "tags"=>["fun", "good", "fun"]},
1254
+ {"_id"=>2, "pageViews"=>7, "tags"=>["fun", "nasty"]},
1255
+ {"_id"=>3, "pageViews"=>6, "tags"=>["nasty", "filthy"]} ]
1256
+ results = @@test.aggregate([{"$project" => {:tags => 1, :pageViews => 1}}])
1257
+ assert_equal desired_results, results
1258
+ end
1259
+
1260
+ def test_aggregate_pipeline_multiple_operators
1261
+ setup_aggregate_data
1262
+ results = @@test.aggregate([{"$project" => {"tags" => 1, "pageViews" => 1}}, {"$match" => {"pageViews" => 7}}])
1263
+ assert_equal 1, results.length
1264
+ end
1265
+
1266
+ def test_aggregate_pipeline_unwind
1267
+ setup_aggregate_data
1268
+ desired_results = [ {"_id"=>1, "title"=>"this is my title", "author"=>"bob", "posted"=>Time.utc(2000),
1269
+ "pageViews"=>5, "tags"=>"fun", "comments"=>[{"author"=>"joe", "text"=>"this is cool"},
1270
+ {"author"=>"sam", "text"=>"this is bad"}], "other"=>{"foo"=>5 } },
1271
+ {"_id"=>1, "title"=>"this is my title", "author"=>"bob", "posted"=>Time.utc(2000),
1272
+ "pageViews"=>5, "tags"=>"good", "comments"=>[{"author"=>"joe", "text"=>"this is cool"},
1273
+ {"author"=>"sam", "text"=>"this is bad"}], "other"=>{"foo"=>5 } },
1274
+ {"_id"=>1, "title"=>"this is my title", "author"=>"bob", "posted"=>Time.utc(2000),
1275
+ "pageViews"=>5, "tags"=>"fun", "comments"=>[{"author"=>"joe", "text"=>"this is cool"},
1276
+ {"author"=>"sam", "text"=>"this is bad"}], "other"=>{"foo"=>5 } },
1277
+ {"_id"=>2, "title"=>"this is your title", "author"=>"dave", "posted"=>Time.utc(2001),
1278
+ "pageViews"=>7, "tags"=>"fun", "comments"=>[{"author"=>"barbara", "text"=>"this is interesting"},
1279
+ {"author"=>"jenny", "text"=>"i like to play pinball", "votes"=>10 }], "other"=>{"bar"=>14 } },
1280
+ {"_id"=>2, "title"=>"this is your title", "author"=>"dave", "posted"=>Time.utc(2001),
1281
+ "pageViews"=>7, "tags"=>"nasty", "comments"=>[{"author"=>"barbara", "text"=>"this is interesting"},
1282
+ {"author"=>"jenny", "text"=>"i like to play pinball", "votes"=>10 }], "other"=>{"bar"=>14 } },
1283
+ {"_id"=>3, "title"=>"this is some other title", "author"=>"jane", "posted"=>Time.utc(2002),
1284
+ "pageViews"=>6, "tags"=>"nasty", "comments"=>[{"author"=>"will", "text"=>"i don't like the color"},
1285
+ {"author"=>"jenny", "text"=>"can i get that in green?"}], "other"=>{"bar"=>14 } },
1286
+ {"_id"=>3, "title"=>"this is some other title", "author"=>"jane", "posted"=>Time.utc(2002),
1287
+ "pageViews"=>6, "tags"=>"filthy", "comments"=>[{"author"=>"will", "text"=>"i don't like the color"},
1288
+ {"author"=>"jenny", "text"=>"can i get that in green?"}], "other"=>{"bar"=>14 } }
1289
+ ]
1290
+ results = @@test.aggregate([{"$unwind"=> "$tags"}])
1291
+ assert_equal desired_results, results
1292
+ end
1293
+
1294
+ def test_aggregate_with_compile_regex_option
1295
+ # see SERVER-6470
1296
+ return unless @@version >= '2.3.2'
1297
+ @@test.insert({ 'r' => /.*/ })
1298
+ result1 = @@test.aggregate([])
1299
+ assert_kind_of Regexp, result1.first['r']
1300
+
1301
+ result2 = @@test.aggregate([], :compile_regex => false)
1302
+ assert_kind_of BSON::Regex, result2.first['r']
1303
+
1304
+ return unless @@version >= '2.5.1'
1305
+ result = @@test.aggregate([], :compile_regex => false, :cursor => {})
1306
+ assert_kind_of BSON::Regex, result.first['r']
1307
+ end
1308
+ end
1309
+
1310
+ if @@version >= "2.5.2"
1311
+ def test_out_aggregate
1312
+ out_collection = 'test_out'
1313
+ @@db.drop_collection(out_collection)
1314
+ setup_aggregate_data
1315
+ docs = @@test.find.to_a
1316
+ pipeline = [{:$out => out_collection}]
1317
+ @@test.aggregate(pipeline)
1318
+ assert_equal docs, @@db.collection(out_collection).find.to_a
1319
+ end
1320
+
1321
+ def test_out_aggregate_nonprimary_sym_warns
1322
+ ReadPreference::expects(:warn).with(regexp_matches(/rerouted to primary/))
1323
+ pipeline = [{:$out => 'test_out'}]
1324
+ @@test.aggregate(pipeline, :read => :secondary)
1325
+ end
1326
+
1327
+ def test_out_aggregate_nonprimary_string_warns
1328
+ ReadPreference::expects(:warn).with(regexp_matches(/rerouted to primary/))
1329
+ pipeline = [{'$out' => 'test_out'}]
1330
+ @@test.aggregate(pipeline, :read => :secondary)
1331
+ end
1332
+
1333
+ def test_out_aggregate_string_returns_raw_response
1334
+ pipeline = [{'$out' => 'test_out'}]
1335
+ response = @@test.aggregate(pipeline)
1336
+ assert response.respond_to?(:keys)
1337
+ end
1338
+
1339
+ def test_out_aggregate_sym_returns_raw_response
1340
+ pipeline = [{:$out => 'test_out'}]
1341
+ response = @@test.aggregate(pipeline)
1342
+ assert response.respond_to?(:keys)
1343
+ end
1344
+ end
1345
+
1346
+ if @@version > "1.1.1"
1347
+ def test_map_reduce
1348
+ @@test << { "user_id" => 1 }
1349
+ @@test << { "user_id" => 2 }
1350
+
1351
+ m = "function() { emit(this.user_id, 1); }"
1352
+ r = "function(k,vals) { return 1; }"
1353
+ res = @@test.map_reduce(m, r, :out => 'foo')
1354
+ assert res.find_one({"_id" => 1})
1355
+ assert res.find_one({"_id" => 2})
1356
+ end
1357
+
1358
+ def test_map_reduce_with_code_objects
1359
+ @@test << { "user_id" => 1 }
1360
+ @@test << { "user_id" => 2 }
1361
+
1362
+ m = Code.new("function() { emit(this.user_id, 1); }")
1363
+ r = Code.new("function(k,vals) { return 1; }")
1364
+ res = @@test.map_reduce(m, r, :out => 'foo')
1365
+ assert res.find_one({"_id" => 1})
1366
+ assert res.find_one({"_id" => 2})
1367
+ end
1368
+
1369
+ def test_map_reduce_with_options
1370
+ @@test.remove
1371
+ @@test << { "user_id" => 1 }
1372
+ @@test << { "user_id" => 2 }
1373
+ @@test << { "user_id" => 3 }
1374
+
1375
+ m = Code.new("function() { emit(this.user_id, 1); }")
1376
+ r = Code.new("function(k,vals) { return 1; }")
1377
+ res = @@test.map_reduce(m, r, :query => {"user_id" => {"$gt" => 1}}, :out => 'foo')
1378
+ assert_equal 2, res.count
1379
+ assert res.find_one({"_id" => 2})
1380
+ assert res.find_one({"_id" => 3})
1381
+ end
1382
+
1383
+ def test_map_reduce_with_raw_response
1384
+ m = Code.new("function() { emit(this.user_id, 1); }")
1385
+ r = Code.new("function(k,vals) { return 1; }")
1386
+ res = @@test.map_reduce(m, r, :raw => true, :out => 'foo')
1387
+ assert res["result"]
1388
+ assert res["counts"]
1389
+ assert res["timeMillis"]
1390
+ end
1391
+
1392
+ def test_map_reduce_with_output_collection
1393
+ output_collection = "test-map-coll"
1394
+ m = Code.new("function() { emit(this.user_id, 1); }")
1395
+ r = Code.new("function(k,vals) { return 1; }")
1396
+ res = @@test.map_reduce(m, r, :raw => true, :out => output_collection)
1397
+ assert_equal output_collection, res["result"]
1398
+ assert res["counts"]
1399
+ assert res["timeMillis"]
1400
+ end
1401
+
1402
+ def test_map_reduce_nonprimary_output_collection_reroutes
1403
+ output_collection = "test-map-coll"
1404
+ m = Code.new("function() { emit(this.user_id, 1); }")
1405
+ r = Code.new("function(k,vals) { return 1; }")
1406
+ Mongo::ReadPreference.expects(:warn).with(regexp_matches(/rerouted to primary/))
1407
+ res = @@test.map_reduce(m, r, :raw => true, :out => output_collection, :read => :secondary)
1408
+ end
1409
+
1410
+ if @@version >= "1.8.0"
1411
+ def test_map_reduce_with_collection_merge
1412
+ @@test << {:user_id => 1}
1413
+ @@test << {:user_id => 2}
1414
+ output_collection = "test-map-coll"
1415
+ m = Code.new("function() { emit(this.user_id, {count: 1}); }")
1416
+ r = Code.new("function(k,vals) { var sum = 0;" +
1417
+ " vals.forEach(function(v) { sum += v.count;} ); return {count: sum}; }")
1418
+ res = @@test.map_reduce(m, r, :out => output_collection)
1419
+
1420
+ @@test.remove
1421
+ @@test << {:user_id => 3}
1422
+ res = @@test.map_reduce(m, r, :out => {:merge => output_collection})
1423
+ assert res.find.to_a.any? {|doc| doc["_id"] == 3 && doc["value"]["count"] == 1}
1424
+
1425
+ @@test.remove
1426
+ @@test << {:user_id => 3}
1427
+ res = @@test.map_reduce(m, r, :out => {:reduce => output_collection})
1428
+ assert res.find.to_a.any? {|doc| doc["_id"] == 3 && doc["value"]["count"] == 2}
1429
+
1430
+ assert_raise ArgumentError do
1431
+ @@test.map_reduce(m, r, :out => {:inline => 1})
1432
+ end
1433
+
1434
+ @@test.map_reduce(m, r, :raw => true, :out => {:inline => 1})
1435
+ assert res["results"]
1436
+ end
1437
+
1438
+ def test_map_reduce_with_collection_output_to_other_db
1439
+ @@test << {:user_id => 1}
1440
+ @@test << {:user_id => 2}
1441
+
1442
+ m = Code.new("function() { emit(this.user_id, 1); }")
1443
+ r = Code.new("function(k,vals) { return 1; }")
1444
+ oh = BSON::OrderedHash.new
1445
+ oh[:replace] = 'foo'
1446
+ oh[:db] = TEST_DB
1447
+ res = @@test.map_reduce(m, r, :out => (oh))
1448
+ assert res["result"]
1449
+ assert res["counts"]
1450
+ assert res["timeMillis"]
1451
+ assert res.find.to_a.any? {|doc| doc["_id"] == 2 && doc["value"] == 1}
1452
+ end
1453
+ end
1454
+ end
1455
+
1456
+ if @@version >= '2.5.5'
1457
+ def test_aggregation_allow_disk_use
1458
+ @@db.expects(:command).with do |selector, opts|
1459
+ opts[:allowDiskUse] == true
1460
+ end.returns({ 'ok' => 1 })
1461
+ @@test.aggregate([], :allowDiskUse => true)
1462
+ end
1463
+
1464
+ def test_parallel_scan
1465
+ 8000.times { |i| @@test.insert({ :_id => i }) }
1466
+
1467
+ lock = Mutex.new
1468
+ doc_ids = Set.new
1469
+ threads = []
1470
+ cursors = @@test.parallel_scan(3)
1471
+ cursors.each_with_index do |cursor, i|
1472
+ threads << Thread.new do
1473
+ docs = cursor.to_a
1474
+ lock.synchronize do
1475
+ docs.each do |doc|
1476
+ doc_ids << doc['_id']
1477
+ end
1478
+ end
1479
+ end
1480
+ end
1481
+ threads.each(&:join)
1482
+ assert_equal 8000, doc_ids.count
1483
+ end
1484
+ end
1485
+
1486
+ if @@version > "1.3.0"
1487
+ def test_find_and_modify
1488
+ @@test << { :a => 1, :processed => false }
1489
+ @@test << { :a => 2, :processed => false }
1490
+ @@test << { :a => 3, :processed => false }
1491
+
1492
+ @@test.find_and_modify(:query => {},
1493
+ :sort => [['a', -1]],
1494
+ :update => {"$set" => {:processed => true}})
1495
+
1496
+ assert @@test.find_one({:a => 3})['processed']
1497
+ end
1498
+
1499
+ def test_find_and_modify_with_invalid_options
1500
+ @@test << { :a => 1, :processed => false }
1501
+ @@test << { :a => 2, :processed => false }
1502
+ @@test << { :a => 3, :processed => false }
1503
+
1504
+ assert_raise Mongo::OperationFailure do
1505
+ @@test.find_and_modify(:blimey => {})
1506
+ end
1507
+ end
1508
+
1509
+ def test_find_and_modify_with_full_response
1510
+ @@test << { :a => 1, :processed => false }
1511
+ @@test << { :a => 2, :processed => false }
1512
+ @@test << { :a => 3, :processed => false }
1513
+
1514
+ doc = @@test.find_and_modify(:query => {},
1515
+ :sort => [['a', -1]],
1516
+ :update => {"$set" => {:processed => true}},
1517
+ :full_response => true,
1518
+ :new => true)
1519
+
1520
+ assert doc['value']['processed']
1521
+ assert ['ok', 'value', 'lastErrorObject'].all? { |key| doc.key?(key) }
1522
+ end
1523
+ end
1524
+
1525
+ if @@version >= "1.3.5"
1526
+ def test_coll_stats
1527
+ @@test << {:n => 1}
1528
+ @@test.create_index("n")
1529
+
1530
+ assert_equal "#{TEST_DB}.test", @@test.stats['ns']
1531
+ @@test.drop
1532
+ end
1533
+ end
1534
+
1535
+ def test_saving_dates_pre_epoch
1536
+ if RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/ then return true end
1537
+ begin
1538
+ @@test.save({'date' => Time.utc(1600)})
1539
+ assert_in_delta Time.utc(1600), @@test.find_one()["date"], 2
1540
+ rescue ArgumentError
1541
+ # See note in test_date_before_epoch (BSONTest)
1542
+ end
1543
+ end
1544
+
1545
+ def test_save_symbol_find_string
1546
+ @@test.save(:foo => :mike)
1547
+
1548
+ assert_equal :mike, @@test.find_one(:foo => :mike)["foo"]
1549
+ assert_equal :mike, @@test.find_one("foo" => :mike)["foo"]
1550
+
1551
+ # TODO enable these tests conditionally based on server version (if >1.0)
1552
+ # assert_equal :mike, @@test.find_one(:foo => "mike")["foo"]
1553
+ # assert_equal :mike, @@test.find_one("foo" => "mike")["foo"]
1554
+ end
1555
+
1556
+ def test_batch_size
1557
+ n_docs = 6
1558
+ batch_size = n_docs/2
1559
+ n_docs.times do |i|
1560
+ @@test.save(:foo => i)
1561
+ end
1562
+
1563
+ doc_count = 0
1564
+ cursor = @@test.find({}, :batch_size => batch_size)
1565
+ cursor.next
1566
+ assert_equal batch_size, cursor.instance_variable_get(:@returned)
1567
+ doc_count += batch_size
1568
+ batch_size.times { cursor.next }
1569
+ assert_equal doc_count + batch_size, cursor.instance_variable_get(:@returned)
1570
+ doc_count += batch_size
1571
+ assert_equal n_docs, doc_count
1572
+ end
1573
+
1574
+ def test_batch_size_with_smaller_limit
1575
+ n_docs = 6
1576
+ batch_size = n_docs/2
1577
+ n_docs.times do |i|
1578
+ @@test.insert(:foo => i)
1579
+ end
1580
+
1581
+ cursor = @@test.find({}, :batch_size => batch_size, :limit => 2)
1582
+ cursor.next
1583
+ assert_equal 2, cursor.instance_variable_get(:@returned)
1584
+ end
1585
+
1586
+ def test_batch_size_with_larger_limit
1587
+ n_docs = 6
1588
+ batch_size = n_docs/2
1589
+ n_docs.times do |i|
1590
+ @@test.insert(:foo => i)
1591
+ end
1592
+
1593
+ doc_count = 0
1594
+ cursor = @@test.find({}, :batch_size => batch_size, :limit => n_docs + 5)
1595
+ cursor.next
1596
+ assert_equal batch_size, cursor.instance_variable_get(:@returned)
1597
+ doc_count += batch_size
1598
+ batch_size.times { cursor.next }
1599
+ assert_equal doc_count + batch_size, cursor.instance_variable_get(:@returned)
1600
+ doc_count += batch_size
1601
+ assert_equal n_docs, doc_count
1602
+ end
1603
+
1604
+ def test_batch_size_with_negative_limit
1605
+ n_docs = 6
1606
+ batch_size = n_docs/2
1607
+ n_docs.times do |i|
1608
+ @@test.insert(:foo => i)
1609
+ end
1610
+
1611
+ cursor = @@test.find({}, :batch_size => batch_size, :limit => -7)
1612
+ cursor.next
1613
+ assert_equal n_docs, cursor.instance_variable_get(:@returned)
1614
+ end
1615
+
1616
+ def test_limit_and_skip
1617
+ 10.times do |i|
1618
+ @@test.save(:foo => i)
1619
+ end
1620
+
1621
+ assert_equal 5, @@test.find({}, :skip => 5).next_document()["foo"]
1622
+ assert_equal nil, @@test.find({}, :skip => 10).next_document()
1623
+
1624
+ assert_equal 5, @@test.find({}, :limit => 5).to_a.length
1625
+
1626
+ assert_equal 3, @@test.find({}, :skip => 3, :limit => 5).next_document()["foo"]
1627
+ assert_equal 5, @@test.find({}, :skip => 3, :limit => 5).to_a.length
1628
+ end
1629
+
1630
+ def test_large_limit
1631
+ 2000.times do |i|
1632
+ @@test.insert("x" => i, "y" => "mongomongo" * 1000)
1633
+ end
1634
+
1635
+ assert_equal 2000, @@test.count
1636
+
1637
+ i = 0
1638
+ y = 0
1639
+ @@test.find({}, :limit => 1900).each do |doc|
1640
+ i += 1
1641
+ y += doc["x"]
1642
+ end
1643
+
1644
+ assert_equal 1900, i
1645
+ assert_equal 1804050, y
1646
+ end
1647
+
1648
+ def test_small_limit
1649
+ @@test.insert("x" => "hello world")
1650
+ @@test.insert("x" => "goodbye world")
1651
+
1652
+ assert_equal 2, @@test.count
1653
+
1654
+ x = 0
1655
+ @@test.find({}, :limit => 1).each do |doc|
1656
+ x += 1
1657
+ assert_equal "hello world", doc["x"]
1658
+ end
1659
+
1660
+ assert_equal 1, x
1661
+ end
1662
+
1663
+ def test_find_with_transformer
1664
+ klass = Struct.new(:id, :a)
1665
+ transformer = Proc.new { |doc| klass.new(doc['_id'], doc['a']) }
1666
+ cursor = @@test.find({}, :transformer => transformer)
1667
+ assert_equal(transformer, cursor.transformer)
1668
+ end
1669
+
1670
+ def test_find_one_with_transformer
1671
+ klass = Struct.new(:id, :a)
1672
+ transformer = Proc.new { |doc| klass.new(doc['_id'], doc['a']) }
1673
+ id = @@test.insert('a' => 1)
1674
+ doc = @@test.find_one(id, :transformer => transformer)
1675
+ assert_instance_of(klass, doc)
1676
+ end
1677
+
1678
+ def test_ensure_index
1679
+ @@test.drop_indexes
1680
+ @@test.insert("x" => "hello world")
1681
+ assert_equal 1, @@test.index_information.keys.count #default index
1682
+
1683
+ @@test.ensure_index([["x", Mongo::DESCENDING]], {})
1684
+ assert_equal 2, @@test.index_information.keys.count
1685
+ assert @@test.index_information.keys.include?("x_-1")
1686
+
1687
+ @@test.ensure_index([["x", Mongo::ASCENDING]])
1688
+ assert @@test.index_information.keys.include?("x_1")
1689
+
1690
+ @@test.ensure_index([["type", 1], ["date", -1]])
1691
+ assert @@test.index_information.keys.include?("type_1_date_-1")
1692
+
1693
+ @@test.drop_index("x_1")
1694
+ assert_equal 3, @@test.index_information.keys.count
1695
+ @@test.drop_index("x_-1")
1696
+ assert_equal 2, @@test.index_information.keys.count
1697
+
1698
+ @@test.ensure_index([["x", Mongo::DESCENDING]], {})
1699
+ assert_equal 3, @@test.index_information.keys.count
1700
+ assert @@test.index_information.keys.include?("x_-1")
1701
+
1702
+ # Make sure that drop_index expires cache properly
1703
+ @@test.ensure_index([['a', 1]])
1704
+ assert @@test.index_information.keys.include?("a_1")
1705
+ @@test.drop_index("a_1")
1706
+ assert !@@test.index_information.keys.include?("a_1")
1707
+ @@test.ensure_index([['a', 1]])
1708
+ assert @@test.index_information.keys.include?("a_1")
1709
+ @@test.drop_index("a_1")
1710
+ @@test.drop_indexes
1711
+ end
1712
+
1713
+ def test_ensure_index_timeout
1714
+ @@db.cache_time = 1
1715
+ coll = @@db['ensure_test']
1716
+ coll.expects(:generate_indexes).twice
1717
+ coll.ensure_index([['a', 1]])
1718
+
1719
+ # These will be cached
1720
+ coll.ensure_index([['a', 1]])
1721
+ coll.ensure_index([['a', 1]])
1722
+ coll.ensure_index([['a', 1]])
1723
+ coll.ensure_index([['a', 1]])
1724
+
1725
+ sleep(1)
1726
+ # This won't be, so generate_indexes will be called twice
1727
+ coll.ensure_index([['a', 1]])
1728
+ coll.drop
1729
+ end
1730
+
1731
+ if @@version > '2.0.0'
1732
+ def test_show_disk_loc
1733
+ @@test.save({:a => 1})
1734
+ @@test.save({:a => 2})
1735
+ assert @@test.find({:a => 1}, :show_disk_loc => true).show_disk_loc
1736
+ assert @@test.find({:a => 1}, :show_disk_loc => true).next['$diskLoc']
1737
+ @@test.remove
1738
+ end
1739
+
1740
+ def test_max_scan
1741
+ @@test.drop
1742
+ n = 100
1743
+ n.times do |i|
1744
+ @@test.save({:_id => i, :x => i % 10})
1745
+ end
1746
+ assert_equal(n, @@test.find.to_a.size)
1747
+ assert_equal(50, @@test.find({}, :max_scan => 50).to_a.size)
1748
+ assert_equal(10, @@test.find({:x => 2}).to_a.size)
1749
+ assert_equal(5, @@test.find({:x => 2}, :max_scan => 50).to_a.size)
1750
+ @@test.ensure_index([[:x, 1]])
1751
+ assert_equal(10, @@test.find({:x => 2}, :max_scan => n).to_a.size)
1752
+ @@test.drop
1753
+ end
1754
+ end
1755
+
1756
+ context "Grouping" do
1757
+ setup do
1758
+ @@test.remove
1759
+ @@test.save("a" => 1)
1760
+ @@test.save("b" => 1)
1761
+ @initial = {"count" => 0}
1762
+ @reduce_function = "function (obj, prev) { prev.count += inc_value; }"
1763
+ end
1764
+
1765
+ should "fail if missing required options" do
1766
+ assert_raise MongoArgumentError do
1767
+ @@test.group(:initial => {})
1768
+ end
1769
+
1770
+ assert_raise MongoArgumentError do
1771
+ @@test.group(:reduce => "foo")
1772
+ end
1773
+ end
1774
+
1775
+ should "group results using eval form" do
1776
+ assert_equal 1, @@test.group(:initial => @initial, :reduce => Code.new(@reduce_function, {"inc_value" => 0.5}))[0]["count"]
1777
+ assert_equal 2, @@test.group(:initial => @initial, :reduce => Code.new(@reduce_function, {"inc_value" => 1}))[0]["count"]
1778
+ assert_equal 4, @@test.group(:initial => @initial, :reduce => Code.new(@reduce_function, {"inc_value" => 2}))[0]["count"]
1779
+ end
1780
+
1781
+ should "finalize grouped results" do
1782
+ @finalize = "function(doc) {doc.f = doc.count + 200; }"
1783
+ assert_equal 202, @@test.group(:initial => @initial, :reduce => Code.new(@reduce_function, {"inc_value" => 1}), :finalize => @finalize)[0]["f"]
1784
+ end
1785
+ end
1786
+
1787
+ context "Grouping with key" do
1788
+ setup do
1789
+ @@test.remove
1790
+ @@test.save("a" => 1, "pop" => 100)
1791
+ @@test.save("a" => 1, "pop" => 100)
1792
+ @@test.save("a" => 2, "pop" => 100)
1793
+ @@test.save("a" => 2, "pop" => 100)
1794
+ @initial = {"count" => 0, "foo" => 1}
1795
+ @reduce_function = "function (obj, prev) { prev.count += obj.pop; }"
1796
+ end
1797
+
1798
+ should "group" do
1799
+ result = @@test.group(:key => :a, :initial => @initial, :reduce => @reduce_function)
1800
+ assert result.all? { |r| r['count'] == 200 }
1801
+ end
1802
+ end
1803
+
1804
+ context "Grouping with a key function" do
1805
+ setup do
1806
+ @@test.remove
1807
+ @@test.save("a" => 1)
1808
+ @@test.save("a" => 2)
1809
+ @@test.save("a" => 3)
1810
+ @@test.save("a" => 4)
1811
+ @@test.save("a" => 5)
1812
+ @initial = {"count" => 0}
1813
+ @keyf = "function (doc) { if(doc.a % 2 == 0) { return {even: true}; } else {return {odd: true}} };"
1814
+ @reduce = "function (obj, prev) { prev.count += 1; }"
1815
+ end
1816
+
1817
+ should "group results" do
1818
+ results = @@test.group(:keyf => @keyf, :initial => @initial, :reduce => @reduce).sort {|a, b| a['count'] <=> b['count']}
1819
+ assert results[0]['even'] && results[0]['count'] == 2.0
1820
+ assert results[1]['odd'] && results[1]['count'] == 3.0
1821
+ end
1822
+
1823
+ should "group filtered results" do
1824
+ results = @@test.group(:keyf => @keyf, :cond => {:a => {'$ne' => 2}},
1825
+ :initial => @initial, :reduce => @reduce).sort {|a, b| a['count'] <=> b['count']}
1826
+ assert results[0]['even'] && results[0]['count'] == 1.0
1827
+ assert results[1]['odd'] && results[1]['count'] == 3.0
1828
+ end
1829
+ end
1830
+
1831
+ context "A collection with two records" do
1832
+ setup do
1833
+ @collection = @@db.collection('test-collection')
1834
+ @collection.remove
1835
+ @collection.insert({:name => "Jones"})
1836
+ @collection.insert({:name => "Smith"})
1837
+ end
1838
+
1839
+ should "have two records" do
1840
+ assert_equal 2, @collection.size
1841
+ end
1842
+
1843
+ should "remove the two records" do
1844
+ @collection.remove()
1845
+ assert_equal 0, @collection.size
1846
+ end
1847
+
1848
+ should "remove all records if an empty document is specified" do
1849
+ @collection.remove({})
1850
+ assert_equal 0, @collection.find.count
1851
+ end
1852
+
1853
+ should "remove only matching records" do
1854
+ @collection.remove({:name => "Jones"})
1855
+ assert_equal 1, @collection.size
1856
+ end
1857
+ end
1858
+
1859
+ context "Drop index " do
1860
+ setup do
1861
+ @@db.drop_collection('test-collection')
1862
+ @collection = @@db.collection('test-collection')
1863
+ end
1864
+
1865
+ should "drop an index" do
1866
+ @collection.create_index([['a', Mongo::ASCENDING]])
1867
+ assert @collection.index_information['a_1']
1868
+ @collection.drop_index([['a', Mongo::ASCENDING]])
1869
+ assert_nil @collection.index_information['a_1']
1870
+ end
1871
+
1872
+ should "drop an index which was given a specific name" do
1873
+ @collection.create_index([['a', Mongo::DESCENDING]], {:name => 'i_will_not_fear'})
1874
+ assert @collection.index_information['i_will_not_fear']
1875
+ @collection.drop_index([['a', Mongo::DESCENDING]])
1876
+ assert_nil @collection.index_information['i_will_not_fear']
1877
+ end
1878
+
1879
+ should "drops an composite index" do
1880
+ @collection.create_index([['a', Mongo::DESCENDING], ['b', Mongo::ASCENDING]])
1881
+ assert @collection.index_information['a_-1_b_1']
1882
+ @collection.drop_index([['a', Mongo::DESCENDING], ['b', Mongo::ASCENDING]])
1883
+ assert_nil @collection.index_information['a_-1_b_1']
1884
+ end
1885
+
1886
+ should "drops an index with symbols" do
1887
+ @collection.create_index([['a', Mongo::DESCENDING], [:b, Mongo::ASCENDING]])
1888
+ assert @collection.index_information['a_-1_b_1']
1889
+ @collection.drop_index([['a', Mongo::DESCENDING], [:b, Mongo::ASCENDING]])
1890
+ assert_nil @collection.index_information['a_-1_b_1']
1891
+ end
1892
+ end
1893
+
1894
+ context "Creating indexes " do
1895
+ setup do
1896
+ @@db.drop_collection('geo')
1897
+ @@db.drop_collection('test-collection')
1898
+ @collection = @@db.collection('test-collection')
1899
+ @geo = @@db.collection('geo')
1900
+ end
1901
+
1902
+ should "create index using symbols" do
1903
+ @collection.create_index :foo, :name => :bar
1904
+ @geo.create_index :goo, :name => :baz
1905
+ assert @collection.index_information['bar']
1906
+ @collection.drop_index :bar
1907
+ assert_nil @collection.index_information['bar']
1908
+ assert @geo.index_information['baz']
1909
+ @geo.drop_index(:baz)
1910
+ assert_nil @geo.index_information['baz']
1911
+ end
1912
+
1913
+ #should "create a text index" do
1914
+ # @geo.save({'title' => "some text"})
1915
+ # @geo.create_index([['title', Mongo::TEXT]])
1916
+ # assert @geo.index_information['title_text']
1917
+ #end
1918
+
1919
+ should "create a hashed index" do
1920
+ @geo.save({'a' => 1})
1921
+ @geo.create_index([['a', Mongo::HASHED]])
1922
+ assert @geo.index_information['a_hashed']
1923
+ end
1924
+
1925
+ should "create a geospatial index" do
1926
+ @geo.save({'loc' => [-100, 100]})
1927
+ @geo.create_index([['loc', Mongo::GEO2D]])
1928
+ assert @geo.index_information['loc_2d']
1929
+ end
1930
+
1931
+ should "create a geoHaystack index" do
1932
+ @geo.save({ "_id" => 100, "pos" => { "long" => 126.9, "lat" => 35.2 }, "type" => "restaurant"})
1933
+ @geo.create_index([['pos', Mongo::GEOHAYSTACK], ['type', Mongo::ASCENDING]], :bucket_size => 1)
1934
+ assert @geo.index_information['pos_geoHaystack_type_1']
1935
+ end
1936
+
1937
+ should "create a geo 2dsphere index" do
1938
+ @collection.insert({"coordinates" => [ 5 , 5 ], "type" => "Point"})
1939
+ @geo.create_index([['coordinates', Mongo::GEO2DSPHERE]])
1940
+ assert @geo.index_information['coordinates_2dsphere']
1941
+ end
1942
+
1943
+ should "create a unique index" do
1944
+ @collection.create_index([['a', Mongo::ASCENDING]], :unique => true)
1945
+ assert @collection.index_information['a_1']['unique'] == true
1946
+ end
1947
+
1948
+ should "drop duplicates" do
1949
+ @collection.insert({:a => 1})
1950
+ @collection.insert({:a => 1})
1951
+ assert_equal 2, @collection.find({:a => 1}).count
1952
+ @collection.create_index([['a', Mongo::ASCENDING]], :unique => true, :dropDups => true)
1953
+ assert_equal 1, @collection.find({:a => 1}).count
1954
+ end
1955
+
1956
+ should "drop duplicates with ruby-like drop_dups key" do
1957
+ @collection.insert({:a => 1})
1958
+ @collection.insert({:a => 1})
1959
+ assert_equal 2, @collection.find({:a => 1}).count
1960
+ @collection.create_index([['a', Mongo::ASCENDING]], :unique => true, :drop_dups => true)
1961
+ assert_equal 1, @collection.find({:a => 1}).count
1962
+ end
1963
+
1964
+ should "drop duplicates with ensure_index and drop_dups key" do
1965
+ @collection.insert({:a => 1})
1966
+ @collection.insert({:a => 1})
1967
+ assert_equal 2, @collection.find({:a => 1}).count
1968
+ @collection.ensure_index([['a', Mongo::ASCENDING]], :unique => true, :drop_dups => true)
1969
+ assert_equal 1, @collection.find({:a => 1}).count
1970
+ end
1971
+
1972
+ should "create an index in the background" do
1973
+ if @@version > '1.3.1'
1974
+ @collection.create_index([['b', Mongo::ASCENDING]], :background => true)
1975
+ assert @collection.index_information['b_1']['background'] == true
1976
+ else
1977
+ assert true
1978
+ end
1979
+ end
1980
+
1981
+ should "require an array of arrays" do
1982
+ assert_raise MongoArgumentError do
1983
+ @collection.create_index(['c', Mongo::ASCENDING])
1984
+ end
1985
+ end
1986
+
1987
+ should "enforce proper index types" do
1988
+ assert_raise MongoArgumentError do
1989
+ @collection.create_index([['c', 'blah']])
1990
+ end
1991
+ end
1992
+
1993
+ should "raise an error if index name is greater than 128" do
1994
+ assert_raise Mongo::OperationFailure do
1995
+ @collection.create_index([['a' * 25, 1], ['b' * 25, 1],
1996
+ ['c' * 25, 1], ['d' * 25, 1], ['e' * 25, 1]])
1997
+ end
1998
+ end
1999
+
2000
+ should "allow for an alternate name to be specified" do
2001
+ @collection.create_index([['a' * 25, 1], ['b' * 25, 1],
2002
+ ['c' * 25, 1], ['d' * 25, 1], ['e' * 25, 1]], :name => 'foo_index')
2003
+ assert @collection.index_information['foo_index']
2004
+ end
2005
+
2006
+ should "generate indexes in the proper order" do
2007
+ key = BSON::OrderedHash['b', 1, 'a', 1]
2008
+ if @@version < '2.5.5'
2009
+ @collection.expects(:send_write) do |type, selector, documents, check_keys, opts, collection_name|
2010
+ assert_equal key, selector[:key]
2011
+ end
2012
+ else
2013
+ @collection.db.expects(:command) do |selector|
2014
+ assert_equal key, selector[:indexes].first[:key]
2015
+ end
2016
+ end
2017
+ @collection.create_index([['b', 1], ['a', 1]])
2018
+ end
2019
+
2020
+ should "allow creation of multiple indexes" do
2021
+ assert @collection.create_index([['a', 1]])
2022
+ assert @collection.create_index([['a', 1]])
2023
+ end
2024
+
2025
+ context "with an index created" do
2026
+ setup do
2027
+ @collection.create_index([['b', 1], ['a', 1]])
2028
+ end
2029
+
2030
+ should "return properly ordered index information" do
2031
+ assert @collection.index_information['b_1_a_1']
2032
+ end
2033
+ end
2034
+ end
2035
+
2036
+ context "Capped collections" do
2037
+ setup do
2038
+ @@db.drop_collection('log')
2039
+ @capped = @@db.create_collection('log', :capped => true, :size => LIMITED_MAX_BSON_SIZE)
2040
+
2041
+ 10.times { |n| @capped.insert({:n => n}) }
2042
+ end
2043
+
2044
+ should "find using a standard cursor" do
2045
+ cursor = @capped.find
2046
+ 10.times do
2047
+ assert cursor.next_document
2048
+ end
2049
+ assert_nil cursor.next_document
2050
+ @capped.insert({:n => 100})
2051
+ assert_nil cursor.next_document
2052
+ end
2053
+
2054
+ should "fail tailable cursor on a non-capped collection" do
2055
+ col = @@db['regular-collection']
2056
+ col.insert({:a => 1000})
2057
+ tail = Cursor.new(col, :tailable => true, :order => [['$natural', 1]])
2058
+ assert_raise OperationFailure do
2059
+ tail.next_document
2060
+ end
2061
+ end
2062
+
2063
+ should "find using a tailable cursor" do
2064
+ tail = Cursor.new(@capped, :tailable => true, :order => [['$natural', 1]])
2065
+ 10.times do
2066
+ assert tail.next_document
2067
+ end
2068
+ assert_nil tail.next_document
2069
+ @capped.insert({:n => 100})
2070
+ assert tail.next_document
2071
+ end
2072
+ end
2073
+ end