mongo 1.10.0-java

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 (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