mongo 1.3.0 → 1.12.5

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 (185) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data/{LICENSE.txt → LICENSE} +1 -1
  4. data/README.md +122 -271
  5. data/Rakefile +25 -209
  6. data/VERSION +1 -0
  7. data/bin/mongo_console +31 -9
  8. data/lib/mongo/bulk_write_collection_view.rb +387 -0
  9. data/lib/mongo/collection.rb +576 -269
  10. data/lib/mongo/collection_writer.rb +364 -0
  11. data/lib/mongo/connection/node.rb +249 -0
  12. data/lib/mongo/connection/pool.rb +340 -0
  13. data/lib/mongo/connection/pool_manager.rb +320 -0
  14. data/lib/mongo/connection/sharding_pool_manager.rb +67 -0
  15. data/lib/mongo/connection/socket/socket_util.rb +37 -0
  16. data/lib/mongo/connection/socket/ssl_socket.rb +95 -0
  17. data/lib/mongo/connection/socket/tcp_socket.rb +87 -0
  18. data/lib/mongo/connection/socket/unix_socket.rb +39 -0
  19. data/lib/mongo/connection/socket.rb +18 -0
  20. data/lib/mongo/connection.rb +7 -875
  21. data/lib/mongo/cursor.rb +403 -117
  22. data/lib/mongo/db.rb +444 -243
  23. data/lib/mongo/exception.rb +145 -0
  24. data/lib/mongo/functional/authentication.rb +455 -0
  25. data/lib/mongo/functional/logging.rb +85 -0
  26. data/lib/mongo/functional/read_preference.rb +183 -0
  27. data/lib/mongo/functional/scram.rb +556 -0
  28. data/lib/mongo/functional/uri_parser.rb +409 -0
  29. data/lib/mongo/functional/write_concern.rb +66 -0
  30. data/lib/mongo/functional.rb +20 -0
  31. data/lib/mongo/gridfs/grid.rb +30 -24
  32. data/lib/mongo/gridfs/grid_ext.rb +6 -10
  33. data/lib/mongo/gridfs/grid_file_system.rb +38 -20
  34. data/lib/mongo/gridfs/grid_io.rb +84 -75
  35. data/lib/mongo/gridfs.rb +18 -0
  36. data/lib/mongo/legacy.rb +140 -0
  37. data/lib/mongo/mongo_client.rb +697 -0
  38. data/lib/mongo/mongo_replica_set_client.rb +535 -0
  39. data/lib/mongo/mongo_sharded_client.rb +159 -0
  40. data/lib/mongo/networking.rb +372 -0
  41. data/lib/mongo/{util → utils}/conversions.rb +29 -8
  42. data/lib/mongo/{util → utils}/core_ext.rb +28 -18
  43. data/lib/mongo/{util → utils}/server_version.rb +4 -6
  44. data/lib/mongo/{util → utils}/support.rb +29 -31
  45. data/lib/mongo/utils/thread_local_variable_manager.rb +25 -0
  46. data/lib/mongo/utils.rb +19 -0
  47. data/lib/mongo.rb +51 -50
  48. data/mongo.gemspec +29 -32
  49. data/test/functional/authentication_test.rb +39 -0
  50. data/test/functional/bulk_api_stress_test.rb +133 -0
  51. data/test/functional/bulk_write_collection_view_test.rb +1198 -0
  52. data/test/functional/client_test.rb +627 -0
  53. data/test/functional/collection_test.rb +2175 -0
  54. data/test/functional/collection_writer_test.rb +83 -0
  55. data/test/{conversions_test.rb → functional/conversions_test.rb} +47 -3
  56. data/test/functional/cursor_fail_test.rb +57 -0
  57. data/test/functional/cursor_message_test.rb +56 -0
  58. data/test/functional/cursor_test.rb +683 -0
  59. data/test/functional/db_api_test.rb +835 -0
  60. data/test/functional/db_connection_test.rb +25 -0
  61. data/test/functional/db_test.rb +348 -0
  62. data/test/functional/grid_file_system_test.rb +285 -0
  63. data/test/{grid_io_test.rb → functional/grid_io_test.rb} +72 -11
  64. data/test/{grid_test.rb → functional/grid_test.rb} +88 -15
  65. data/test/functional/pool_test.rb +136 -0
  66. data/test/functional/safe_test.rb +98 -0
  67. data/test/functional/ssl_test.rb +29 -0
  68. data/test/functional/support_test.rb +62 -0
  69. data/test/functional/timeout_test.rb +60 -0
  70. data/test/functional/uri_test.rb +446 -0
  71. data/test/functional/write_concern_test.rb +118 -0
  72. data/test/helpers/general.rb +50 -0
  73. data/test/helpers/test_unit.rb +476 -0
  74. data/test/replica_set/authentication_test.rb +37 -0
  75. data/test/replica_set/basic_test.rb +189 -0
  76. data/test/replica_set/client_test.rb +393 -0
  77. data/test/replica_set/connection_test.rb +138 -0
  78. data/test/replica_set/count_test.rb +66 -0
  79. data/test/replica_set/cursor_test.rb +220 -0
  80. data/test/replica_set/insert_test.rb +157 -0
  81. data/test/replica_set/max_values_test.rb +151 -0
  82. data/test/replica_set/pinning_test.rb +105 -0
  83. data/test/replica_set/query_test.rb +73 -0
  84. data/test/replica_set/read_preference_test.rb +219 -0
  85. data/test/replica_set/refresh_test.rb +211 -0
  86. data/test/replica_set/replication_ack_test.rb +95 -0
  87. data/test/replica_set/ssl_test.rb +32 -0
  88. data/test/sharded_cluster/basic_test.rb +203 -0
  89. data/test/shared/authentication/basic_auth_shared.rb +260 -0
  90. data/test/shared/authentication/bulk_api_auth_shared.rb +249 -0
  91. data/test/shared/authentication/gssapi_shared.rb +176 -0
  92. data/test/shared/authentication/sasl_plain_shared.rb +96 -0
  93. data/test/shared/authentication/scram_shared.rb +92 -0
  94. data/test/shared/ssl_shared.rb +235 -0
  95. data/test/test_helper.rb +53 -94
  96. data/test/threading/basic_test.rb +120 -0
  97. data/test/tools/mongo_config.rb +708 -0
  98. data/test/tools/mongo_config_test.rb +160 -0
  99. data/test/unit/client_test.rb +381 -0
  100. data/test/unit/collection_test.rb +89 -53
  101. data/test/unit/connection_test.rb +282 -32
  102. data/test/unit/cursor_test.rb +206 -8
  103. data/test/unit/db_test.rb +55 -13
  104. data/test/unit/grid_test.rb +43 -16
  105. data/test/unit/mongo_sharded_client_test.rb +48 -0
  106. data/test/unit/node_test.rb +93 -0
  107. data/test/unit/pool_manager_test.rb +111 -0
  108. data/test/unit/read_pref_test.rb +406 -0
  109. data/test/unit/read_test.rb +159 -0
  110. data/test/unit/safe_test.rb +69 -36
  111. data/test/unit/sharding_pool_manager_test.rb +84 -0
  112. data/test/unit/write_concern_test.rb +175 -0
  113. data.tar.gz.sig +3 -0
  114. metadata +227 -216
  115. metadata.gz.sig +0 -0
  116. data/docs/CREDITS.md +0 -123
  117. data/docs/FAQ.md +0 -116
  118. data/docs/GridFS.md +0 -158
  119. data/docs/HISTORY.md +0 -244
  120. data/docs/RELEASES.md +0 -33
  121. data/docs/REPLICA_SETS.md +0 -72
  122. data/docs/TUTORIAL.md +0 -247
  123. data/docs/WRITE_CONCERN.md +0 -28
  124. data/lib/mongo/exceptions.rb +0 -71
  125. data/lib/mongo/gridfs/grid_io_fix.rb +0 -38
  126. data/lib/mongo/repl_set_connection.rb +0 -342
  127. data/lib/mongo/test.rb +0 -20
  128. data/lib/mongo/util/pool.rb +0 -177
  129. data/lib/mongo/util/uri_parser.rb +0 -185
  130. data/test/async/collection_test.rb +0 -224
  131. data/test/async/connection_test.rb +0 -24
  132. data/test/async/cursor_test.rb +0 -162
  133. data/test/async/worker_pool_test.rb +0 -99
  134. data/test/auxillary/1.4_features.rb +0 -166
  135. data/test/auxillary/authentication_test.rb +0 -68
  136. data/test/auxillary/autoreconnect_test.rb +0 -41
  137. data/test/auxillary/fork_test.rb +0 -30
  138. data/test/auxillary/repl_set_auth_test.rb +0 -58
  139. data/test/auxillary/slave_connection_test.rb +0 -36
  140. data/test/auxillary/threaded_authentication_test.rb +0 -101
  141. data/test/bson/binary_test.rb +0 -15
  142. data/test/bson/bson_test.rb +0 -649
  143. data/test/bson/byte_buffer_test.rb +0 -208
  144. data/test/bson/hash_with_indifferent_access_test.rb +0 -38
  145. data/test/bson/json_test.rb +0 -17
  146. data/test/bson/object_id_test.rb +0 -154
  147. data/test/bson/ordered_hash_test.rb +0 -204
  148. data/test/bson/timestamp_test.rb +0 -24
  149. data/test/collection_test.rb +0 -910
  150. data/test/connection_test.rb +0 -309
  151. data/test/cursor_fail_test.rb +0 -75
  152. data/test/cursor_message_test.rb +0 -43
  153. data/test/cursor_test.rb +0 -483
  154. data/test/db_api_test.rb +0 -726
  155. data/test/db_connection_test.rb +0 -15
  156. data/test/db_test.rb +0 -287
  157. data/test/grid_file_system_test.rb +0 -243
  158. data/test/load/resque/load.rb +0 -21
  159. data/test/load/resque/processor.rb +0 -26
  160. data/test/load/thin/load.rb +0 -24
  161. data/test/load/unicorn/load.rb +0 -23
  162. data/test/load/unicorn/unicorn.rb +0 -29
  163. data/test/replica_sets/connect_test.rb +0 -94
  164. data/test/replica_sets/connection_string_test.rb +0 -32
  165. data/test/replica_sets/count_test.rb +0 -35
  166. data/test/replica_sets/insert_test.rb +0 -53
  167. data/test/replica_sets/pooled_insert_test.rb +0 -55
  168. data/test/replica_sets/query_secondaries.rb +0 -96
  169. data/test/replica_sets/query_test.rb +0 -51
  170. data/test/replica_sets/replication_ack_test.rb +0 -66
  171. data/test/replica_sets/rs_test_helper.rb +0 -27
  172. data/test/safe_test.rb +0 -68
  173. data/test/support/hash_with_indifferent_access.rb +0 -186
  174. data/test/support/keys.rb +0 -45
  175. data/test/support_test.rb +0 -18
  176. data/test/threading/threading_with_large_pool_test.rb +0 -90
  177. data/test/threading_test.rb +0 -87
  178. data/test/tools/auth_repl_set_manager.rb +0 -14
  179. data/test/tools/load.rb +0 -58
  180. data/test/tools/repl_set_manager.rb +0 -266
  181. data/test/tools/sharding_manager.rb +0 -202
  182. data/test/tools/test.rb +0 -4
  183. data/test/unit/pool_test.rb +0 -9
  184. data/test/unit/repl_set_connection_test.rb +0 -59
  185. data/test/uri_test.rb +0 -91
@@ -0,0 +1,1198 @@
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 'test_helper'
16
+ require 'json'
17
+
18
+ module Mongo
19
+ class Collection
20
+ public :batch_write
21
+ end
22
+
23
+ class BulkWriteCollectionView
24
+ public :update_doc?, :replace_doc?, :nil_tally, :merge_result
25
+
26
+ # for reference and future server direction
27
+ def generate_batch_commands(groups, write_concern)
28
+ groups.collect do |op_type, documents|
29
+ {
30
+ op_type => @collection.name,
31
+ Mongo::CollectionWriter::WRITE_COMMAND_ARG_KEY[op_type] => documents,
32
+ :ordered => @options[:ordered],
33
+ :writeConcern => write_concern
34
+ }
35
+ end
36
+ end
37
+ end
38
+
39
+ class MongoDBError
40
+ def inspect
41
+ "#{self.class.name}.new(#{message.inspect},#{error_code.inspect},#{result.inspect})"
42
+ end
43
+ end
44
+ end
45
+
46
+ module BSON
47
+ class InvalidDocument
48
+ def inspect
49
+ "#{self.class.name}.new(#{message.inspect})"
50
+ end
51
+ end
52
+ end
53
+
54
+ class BulkWriteCollectionViewTest < Test::Unit::TestCase
55
+ COLLECTION_NAME = 'test'
56
+ DUPLICATE_KEY_ERROR_CODE_SET = [11000, 11001, 12582, 16460].to_set
57
+
58
+ def assert_bulk_op_pushed(expected, view)
59
+ assert_equal expected, view.ops.last
60
+ end
61
+
62
+ def assert_is_bulk_write_collection_view(view)
63
+ assert_equal Mongo::BulkWriteCollectionView, view.class
64
+ end
65
+
66
+ def assert_bulk_exception(expected, message = '')
67
+ ex = assert_raise BulkWriteError, message do
68
+ pp yield
69
+ end
70
+ assert_equal(Mongo::ErrorCode::MULTIPLE_ERRORS_OCCURRED, ex.error_code, message)
71
+ assert_match_document(expected, ex.result, message)
72
+ end
73
+
74
+ def default_setup
75
+ @client = standard_connection
76
+ @version = @client.server_version
77
+ @db = @client[TEST_DB]
78
+ @collection = @db[COLLECTION_NAME]
79
+ @collection.drop
80
+ @bulk = @collection.initialize_ordered_bulk_op
81
+ @q = {:a => 1}
82
+ @u = {"$inc" => {:x => 1}}
83
+ @r = {:b => 2}
84
+ end
85
+
86
+ def sort_docs(docs)
87
+ docs.sort{|a,b| [a.keys, a.values] <=> [b.keys, b.values]}
88
+ end
89
+
90
+ def generate_sized_doc(size)
91
+ doc = {"_id" => BSON::ObjectId.new, "x" => "y"}
92
+ serialize_doc = BSON::BSON_CODER.serialize(doc, false, false, size)
93
+ doc = {"_id" => BSON::ObjectId.new, "x" => "y" * (1 + size - serialize_doc.size)}
94
+ assert_equal size, BSON::BSON_CODER.serialize(doc, false, false, size).size
95
+ doc
96
+ end
97
+
98
+ context "Bulk API Collection" do
99
+ setup do
100
+ default_setup
101
+ end
102
+
103
+ should "inspect" do
104
+ assert_equal String, @bulk.inspect.class
105
+ end
106
+
107
+ should "check first key is operation for #update_doc?" do
108
+ assert_not_nil @bulk.update_doc?({"$inc" => {:x => 1}})
109
+ assert_false @bulk.update_doc?({})
110
+ assert_nil @bulk.update_doc?({:x => 1})
111
+ end
112
+
113
+ should "check no top-level key is operation for #replace_doc?" do
114
+ assert_true @bulk.replace_doc?({:x => 1})
115
+ assert_true @bulk.replace_doc?({})
116
+ assert_false @bulk.replace_doc?({"$inc" => {:x => 1}})
117
+ assert_false @bulk.replace_doc?({:a => 1, "$inc" => {:x => 1}})
118
+ end
119
+
120
+ should "generate_batch_commands" do
121
+ groups = [
122
+ [:insert, [{:n => 0}]],
123
+ [:update, [{:n => 1}, {:n => 2}]],
124
+ [:delete, [{:n => 3}]],
125
+ [:insert, [{:n => 5}, {:n => 6}, {:n => 7}]],
126
+ [:update, [{:n => 8}]],
127
+ [:delete, [{:n => 9}, {:n => 10}]]
128
+ ]
129
+ write_concern = {:w => 1}
130
+ result = @bulk.generate_batch_commands(groups, write_concern)
131
+ expected = [
132
+ {:insert => COLLECTION_NAME, :documents => [{:n => 0}], :ordered => true, :writeConcern => {:w => 1}},
133
+ {:update => COLLECTION_NAME, :updates => [{:n => 1}, {:n => 2}], :ordered => true, :writeConcern => {:w => 1}},
134
+ {:delete => COLLECTION_NAME, :deletes => [{:n => 3}], :ordered => true, :writeConcern => {:w => 1}},
135
+ {:insert => COLLECTION_NAME, :documents => [{:n => 5}, {:n => 6}, {:n => 7}], :ordered => true, :writeConcern => {:w => 1}},
136
+ {:update => COLLECTION_NAME, :updates => [{:n => 8}], :ordered => true, :writeConcern => {:w => 1}},
137
+ {:delete => COLLECTION_NAME, :deletes => [{:n => 9}, {:n => 10}], :ordered => true, :writeConcern => {:w => 1}}
138
+ ]
139
+ assert_equal expected, result
140
+ end
141
+
142
+ should "Initialize an unordered bulk op - spec Bulk Operation Builder" do
143
+ @bulk = @collection.initialize_unordered_bulk_op
144
+ assert_is_bulk_write_collection_view(@bulk)
145
+ assert_equal @collection, @bulk.collection
146
+ assert_equal false, @bulk.options[:ordered]
147
+ end
148
+
149
+ should "Initialize an ordered bulk op - spec Bulk Operation Builder" do
150
+ assert_is_bulk_write_collection_view(@bulk)
151
+ assert_equal @collection, @bulk.collection
152
+ assert_equal true, @bulk.options[:ordered]
153
+ end
154
+ end
155
+
156
+ def big_example(bulk)
157
+ bulk.insert({:a => 1})
158
+ bulk.insert({:a => 2})
159
+ bulk.insert({:a => 3})
160
+ bulk.insert({:a => 4})
161
+ bulk.insert({:a => 5})
162
+ # Update one document matching the selector
163
+ bulk.find({:a => 1}).update_one({"$inc" => {:x => 1}})
164
+ # Update all documents matching the selector
165
+ bulk.find({:a => 2}).update({"$inc" => {:x => 2}})
166
+ # Replace entire document (update with whole doc replace)
167
+ bulk.find({:a => 3}).replace_one({:x => 3})
168
+ # Update one document matching the selector or upsert
169
+ bulk.find({:a => 1}).upsert.update_one({"$inc" => {:x => 1}})
170
+ # Update all documents matching the selector or upsert
171
+ bulk.find({:a => 2}).upsert.update({"$inc" => {:x => 2}})
172
+ # Replaces a single document matching the selector or upsert
173
+ bulk.find({:a => 3}).upsert.replace_one({:x => 3})
174
+ # Remove a single document matching the selector
175
+ bulk.find({:a => 4}).remove_one()
176
+ # Remove all documents matching the selector
177
+ bulk.find({:a => 5}).remove()
178
+ # Insert a document
179
+ bulk.insert({:x => 4})
180
+ end
181
+
182
+ def nil_tally_responses(responses, key)
183
+ result = {}
184
+ responses.each do |response|
185
+ @bulk.nil_tally(result, key, response[key])
186
+ end
187
+ result
188
+ end
189
+
190
+ context "Bulk API CollectionView" do
191
+ setup do
192
+ default_setup
193
+ end
194
+
195
+ # ----- INSERT -----
196
+
197
+ should "set :insert, :documents, terminate and return view for #insert" do
198
+ with_write_commands_and_operations(@db.connection) do |wire_version|
199
+ @collection.remove
200
+ document = {:a => 5}
201
+ result = @bulk.insert(document)
202
+ assert_is_bulk_write_collection_view(result)
203
+ assert_bulk_op_pushed [:insert, {:d => document}], @bulk
204
+ result = @bulk.execute
205
+ assert_match_document(
206
+ {
207
+ "ok" => 1,
208
+ "n" => 1,
209
+ "nInserted" => 1
210
+ }, result, "wire_version:#{wire_version}")
211
+ assert_equal 1, @collection.count
212
+ end
213
+ end
214
+
215
+ should "error out on $-prefixed keys with #insert" do
216
+ assert_raise BulkWriteError do
217
+ @bulk.insert({ "$key" => 1 })
218
+ @bulk.execute
219
+ end
220
+ end
221
+
222
+ should "attempt to run #insert with find() and succeed, ignoring find()" do
223
+ @bulk.find({}).insert({})
224
+ @bulk.execute
225
+ end
226
+
227
+ # ----- FIND -----
228
+
229
+ should "set :q and return view for #find" do
230
+ result = @bulk.find(@q)
231
+ assert_is_bulk_write_collection_view(result)
232
+ assert_equal @q, @bulk.op_args[:q]
233
+ @bulk.find({})
234
+ assert_equal({}, @bulk.op_args[:q])
235
+ @bulk.find(:a => 1)
236
+ assert_equal({:a => 1}, @bulk.op_args[:q])
237
+ end
238
+
239
+ should "raise an exception for empty #find" do
240
+ assert_raise MongoArgumentError do
241
+ @bulk.find({})
242
+ @bulk.execute
243
+ end
244
+ end
245
+
246
+ # ----- UPDATE -----
247
+
248
+ should "set :upsert for #upsert" do
249
+ result = @bulk.find(@q).upsert
250
+ assert_is_bulk_write_collection_view(result)
251
+ assert_true result.op_args[:upsert]
252
+ end
253
+
254
+ should "check arg for update, set :update, :u, :multi, terminate and return view for #update" do
255
+ with_write_commands_and_operations(@db.connection) do |wire_version|
256
+ @collection.remove
257
+ @collection.insert({:a => 1, :b => 1})
258
+ @collection.insert({:a => 2, :b => 1})
259
+ @collection.insert({:a => 2, :b => 1})
260
+ bulk = @collection.initialize_ordered_bulk_op
261
+
262
+ u = {"$inc" => {:b => 1}}
263
+ q = {:a => 2}
264
+
265
+ assert_raise_error(MongoArgumentError, "non-nil query must be set via find") do
266
+ bulk.update(u)
267
+ end
268
+ assert_raise_error(MongoArgumentError, "document must start with an operator") do
269
+ bulk.find(q).update(q)
270
+ end
271
+
272
+ result = bulk.find({:a => 2}).update(u)
273
+ assert_is_bulk_write_collection_view(result)
274
+ assert_bulk_op_pushed [:update, {:q => q, :u => u, :multi => true}], bulk
275
+
276
+ result = bulk.execute
277
+ assert_match_document(
278
+ {
279
+ "ok" => 1,
280
+ "n" => 2,
281
+ "nMatched" => 2,
282
+ "nModified" => batch_commands?(wire_version) ? 2 : nil,
283
+ }, result, "wire_version:#{wire_version}")
284
+ assert_equal 1, @collection.find({:b => 1}).count
285
+ end
286
+ end
287
+
288
+ # ----- UPDATE_ONE -----
289
+
290
+ should "check arg for update, set :update, :u, :multi, terminate and return view for #update_one" do
291
+ with_write_commands_and_operations(@db.connection) do |wire_version|
292
+ @collection.remove
293
+ @collection.insert({:a => 1})
294
+ @collection.insert({:a => 1, :b => 2})
295
+ bulk = @collection.initialize_ordered_bulk_op
296
+
297
+ assert_raise_error(MongoArgumentError, "non-nil query must be set via find") do
298
+ bulk.update_one(@u)
299
+ end
300
+ assert_raise_error(MongoArgumentError, "document must start with an operator") do
301
+ bulk.find(@q).update_one(@r)
302
+ end
303
+ result = bulk.find(@q).update_one(@u)
304
+ assert_is_bulk_write_collection_view(result)
305
+ assert_bulk_op_pushed [:update, {:q => @q, :u => @u, :multi => false}], bulk
306
+ result = bulk.execute
307
+ assert_match_document(
308
+ {
309
+ "ok" => 1,
310
+ "n" => 1,
311
+ "nMatched" => 1,
312
+ "nModified" => batch_commands?(wire_version) ? 1 : nil,
313
+ }, result, "wire_version:#{wire_version}")
314
+ assert_equal 2, @collection.count
315
+ end
316
+ end
317
+
318
+ should "error-out in server when $-prefixed key is passed to #update_one" do
319
+ assert_raise BulkWriteError do
320
+ oh = BSON::OrderedHash.new
321
+ oh["$key"] = 1
322
+ oh[:a] = 1
323
+ @bulk.find(@q).update(oh)
324
+ @bulk.execute
325
+ end
326
+ end
327
+
328
+ should "error-out in driver when first field passed to #update_one is not operator" do
329
+ assert_raise_error(MongoArgumentError, "document must start with an operator") do
330
+ oh = BSON::OrderedHash.new
331
+ oh[:a] = 1
332
+ oh["$key"] = 1
333
+ @bulk.find(@q).update(oh)
334
+ end
335
+ end
336
+
337
+ # ----- REPLACE -----
338
+
339
+ should "raise an error when we attempt to use replace" do
340
+ assert_raise NoMethodError do
341
+ bulk = @collection.initialize_ordered_bulk_op
342
+ bulk.find({:a => 2}).replace({:a => 1})
343
+ bulk.execute
344
+ end
345
+ end
346
+
347
+ # ----- REPLACE_ONE -----
348
+
349
+ should "check arg for replacement, set :update, :u, :multi, terminate and return view for #replace_one" do
350
+ with_write_commands_and_operations(@db.connection) do |wire_version|
351
+ @collection.remove
352
+ @collection.insert({:a => 1})
353
+ @collection.insert({:a => 1})
354
+ bulk = @collection.initialize_ordered_bulk_op
355
+ q = {:a => 1}
356
+ r = {:a => 2}
357
+
358
+ assert_raise_error(MongoArgumentError, "non-nil query must be set via find") do
359
+ bulk.replace_one(q)
360
+ end
361
+ assert_raise_error(MongoArgumentError, "document must not contain any operators") do
362
+ bulk.find(q).replace_one(@u)
363
+ end
364
+
365
+ result = bulk.find(q).replace_one(r)
366
+ assert_is_bulk_write_collection_view(result)
367
+ assert_bulk_op_pushed [:update, {:q => q, :u => r, :multi => false}], bulk
368
+
369
+ result = bulk.execute
370
+ assert_match_document(
371
+ {
372
+ "ok" => 1,
373
+ "n" => 1,
374
+ "nMatched" => 1,
375
+ "nModified" => batch_commands?(wire_version) ? 1 : nil,
376
+ }, result, "wire_version:#{wire_version}")
377
+ assert_equal 1, @collection.find(q).count
378
+ end
379
+ end
380
+
381
+ # ----- REMOVE -----
382
+
383
+ should "remove all documents when empty selector is passed to #remove" do
384
+ with_write_commands_and_operations(@db.connection) do |wire_version|
385
+ @collection.insert({:a => 1})
386
+ @collection.insert({:a => 2})
387
+ @bulk.find({}).remove
388
+ result = @bulk.execute
389
+ assert_equal 0, @collection.count
390
+ end
391
+ end
392
+
393
+ should "#remove only documents that match selector" do
394
+ with_write_commands_and_operations(@db.connection) do |wire_version|
395
+ @collection.remove
396
+ @collection.insert({:a => 1})
397
+ @collection.insert({:a => 2})
398
+ @bulk.find({:a => 1}).remove
399
+ result = @bulk.execute
400
+ assert_equal 1, @collection.count
401
+ # should fail if we re-execute
402
+ assert_raise_error(MongoArgumentError, "batch is empty") do
403
+ @bulk.execute
404
+ end
405
+ end
406
+ end
407
+
408
+ should "set :remove, :q, :limit, terminate and return view for #remove" do
409
+ assert_raise_error(MongoArgumentError, "non-nil query must be set via find") do
410
+ @bulk.remove
411
+ end
412
+ result = @bulk.find(@q).remove
413
+ assert_is_bulk_write_collection_view(result)
414
+ assert_bulk_op_pushed [:delete, {:q => @q, :limit => 0}], @bulk
415
+ end
416
+
417
+ # ----- REMOVE_ONE -----
418
+
419
+ should "set :remove, :q, :limit, terminate and return view for #remove_one" do
420
+ assert_raise_error(MongoArgumentError, "non-nil query must be set via find") do
421
+ @bulk.remove_one
422
+ end
423
+ result = @bulk.find(@q).remove_one
424
+ assert_is_bulk_write_collection_view(result)
425
+ assert_bulk_op_pushed [:delete, {:q => @q, :limit => 1}], @bulk
426
+ end
427
+
428
+ should "remove only one of several matching documents for #remove_one" do
429
+ with_write_commands_and_operations(@db.connection) do |wire_version|
430
+ @collection.remove
431
+ @collection.insert({:a => 1, :b => 1})
432
+ @collection.insert({:a => 1, :b => 2})
433
+ @bulk.find({:a => 1}).remove_one
434
+ result = @bulk.execute
435
+ assert_match_document(
436
+ {
437
+ "ok" => 1,
438
+ "n" => 1,
439
+ "nRemoved" => 1,
440
+ "nModified" => nil,
441
+ }, result, "wire_version:#{wire_version}")
442
+ assert_equal 1, @collection.count
443
+ end
444
+ end
445
+
446
+ # ----- UPSERT-UPDATE -----
447
+
448
+ should "handle single upsert - spec Handling upserts" do # chose array always for upserted value
449
+ with_write_commands_and_operations(@db.connection) do |wire_version|
450
+ @collection.remove
451
+ @collection.ensure_index(BSON::OrderedHash[:a, Mongo::ASCENDING], {:unique => true})
452
+ bulk = @collection.initialize_ordered_bulk_op
453
+
454
+ assert_raise_error(MongoArgumentError, "non-nil query must be set via find") do
455
+ @bulk.upsert.update({"$set" => {:a => 1}})
456
+ end
457
+
458
+ bulk.find({:a => 1}).upsert.update({'$set' => {:a => 2}})
459
+ result = bulk.execute
460
+ assert_match_document(
461
+ {
462
+ "ok" => 1,
463
+ "n" => 1,
464
+ "nMatched" => 0,
465
+ "nUpserted" => 1,
466
+ "nModified" => batch_commands?(wire_version) ? 0 : nil,
467
+ "upserted" => [
468
+ {"_id" => BSON::ObjectId('52a16767bb67fbc77e26a310'), "index" => 0}
469
+ ]
470
+ }, result, "wire_version:#{wire_version}")
471
+ end
472
+ end
473
+
474
+ should "run #upsert.update without affecting non-upsert updates" do
475
+ with_write_commands_and_operations(@db.connection) do |wire_version|
476
+ @collection.remove
477
+
478
+ bulk = @collection.initialize_unordered_bulk_op
479
+ bulk.find({:a => 1}).update({"$set" => {:x => 1}})
480
+ bulk.find({:a => 2}).upsert.update({"$set" => {:x => 2}})
481
+ result = bulk.execute
482
+ assert_match_document(
483
+ {
484
+ "ok" => 1,
485
+ "n" => 1,
486
+ "nMatched" => 0,
487
+ "nModified" => batch_commands?(wire_version) ? 0 : nil,
488
+ "nUpserted" => 1,
489
+ "upserted" => [
490
+ {"_id" => BSON::ObjectId('52a16767bb67fbc77e26a310'), "index" => 1}
491
+ ]
492
+ }, result, "wire_version:#{wire_version}")
493
+
494
+ # Repeat the batch and nMatched = 1, nUpserted = 0
495
+ bulk2 = @collection.initialize_unordered_bulk_op
496
+ bulk2.find({:a => 1}).update({"$set" => {:x => 1}})
497
+ bulk2.find({:a => 2}).upsert.update({"$set" => {:x => 2}})
498
+ result2 = bulk2.execute
499
+ assert_match_document(
500
+ {
501
+ "ok" => 1,
502
+ "n" => 1,
503
+ "nMatched" => 1,
504
+ "nModified" => batch_commands?(wire_version) ? 0 : nil
505
+ }, result2, "wire_version:#{wire_version}")
506
+ end
507
+ end
508
+
509
+ should "count nUpserted correctly when _id is not an ObjectId (upsert-update)" do
510
+ with_write_commands_and_operations(@db.connection) do |wire_version|
511
+ @collection.remove
512
+
513
+ bulk = @collection.initialize_unordered_bulk_op
514
+ bulk.find({:_id => 3}).upsert.update({"$set" => {:b => 3}})
515
+ result = bulk.execute
516
+ assert_match_document(
517
+ {
518
+ "ok" => 1,
519
+ "n" => 1,
520
+ "nMatched" => 0,
521
+ "nUpserted" => 1,
522
+ "nModified" => batch_commands?(wire_version) ? 0 : nil,
523
+ "upserted" => [
524
+ { "_id" => 3, "index" => 0 }
525
+ ]
526
+ }, result, "wire_version:#{wire_version}")
527
+ end
528
+ end
529
+
530
+ # ----- UPSERT-UPDATE_ONE -----
531
+
532
+ should "#upsert a document without affecting non-upsert update_ones" do
533
+ with_write_commands_and_operations(@db.connection) do |wire_version|
534
+ @collection.remove
535
+ bulk = @collection.initialize_unordered_bulk_op
536
+
537
+ bulk.find({:a => 1}).update_one({"$set" => {:x => 1}})
538
+ bulk.find({:a => 2}).upsert.update_one({"$set" => {:x => 2}})
539
+ result = bulk.execute
540
+ assert_match_document(
541
+ {
542
+ "ok" => 1,
543
+ "n" => 1,
544
+ "nMatched" => 0,
545
+ "nUpserted" => 1,
546
+ "nModified" => batch_commands?(wire_version) ? 0 : nil,
547
+ "upserted" => [
548
+ {"_id" => BSON::ObjectId('52a16767bb67fbc77e26a310'), "index" => 1}
549
+ ]
550
+ }, result, "wire_version:#{wire_version}")
551
+ end
552
+ end
553
+
554
+ should "only update one matching document with #upsert-update_one" do
555
+ with_write_commands_and_operations(@db.connection) do |wire_version|
556
+ @collection.remove
557
+ @collection.insert({:a => 1})
558
+ @collection.insert({:a => 1})
559
+
560
+ bulk = @collection.initialize_unordered_bulk_op
561
+ bulk.find({:a => 1}).update_one({"$set" => {:x => 1}})
562
+ result = bulk.execute
563
+ assert_match_document(
564
+ {
565
+ "ok" => 1,
566
+ "n" => 1,
567
+ "nMatched" => 1,
568
+ "nModified" => batch_commands?(wire_version) ? 1 : nil,
569
+ }, result, "wire_version:#{wire_version}")
570
+ end
571
+ end
572
+
573
+
574
+ should "count nUpserted correctly when _id is not an ObjectId (upsert-update_one)" do
575
+ with_write_commands_and_operations(@db.connection) do |wire_version|
576
+ @collection.remove
577
+ bulk = @collection.initialize_ordered_bulk_op
578
+ bulk.find({:_id => 2}).upsert.update_one({"$set" => {:x => 2}})
579
+ result = bulk.execute
580
+ assert_match_document(
581
+ {
582
+ "ok" => 1,
583
+ "n" => 1,
584
+ "nMatched" => 0,
585
+ "nUpserted" => 1,
586
+ "nModified" => batch_commands?(wire_version) ? 0 : nil,
587
+ "upserted" => [
588
+ {"_id" => 2, "index" => 0 }
589
+ ]
590
+ }, result, "wire_version:#{wire_version}")
591
+ end
592
+ end
593
+
594
+ # ----- UPSERT-REPLACE_ONE -----
595
+
596
+ should "not affect non-upsert replace_ones in same batch as #upsert-replace_one" do
597
+ with_write_commands_and_operations(@db.connection) do |wire_version|
598
+ @collection.remove
599
+ bulk = @collection.initialize_unordered_bulk_op
600
+ bulk.find({:a => 1}).replace_one({:x => 1})
601
+ bulk.find({:a => 2}).upsert.replace_one({:x => 2})
602
+ result = bulk.execute
603
+ assert_match_document(
604
+ {
605
+ "ok" => 1,
606
+ "n" => 1,
607
+ "nMatched" => 0,
608
+ "nUpserted" => 1,
609
+ "nModified" => batch_commands?(wire_version) ? 0 : nil,
610
+ "upserted" => [
611
+ {"_id" => BSON::ObjectId('52a16767bb67fbc77e26a310'), "index" => 1}
612
+ ]
613
+ }, result, "wire_version:#{wire_version}")
614
+ assert_equal 1, @collection.count
615
+ end
616
+ end
617
+
618
+ should "only replace one matching document with #upsert-replace_one" do
619
+ with_write_commands_and_operations(@db.connection) do |wire_version|
620
+ @collection.remove
621
+ @collection.insert({:a => 1})
622
+ @collection.insert({:a => 1})
623
+ bulk = @collection.initialize_unordered_bulk_op
624
+ bulk.find({:a => 1}).replace_one({:x => 1})
625
+ bulk.find({:a => 2}).upsert.replace_one({:x => 2})
626
+ result = bulk.execute
627
+ assert_match_document(
628
+ {
629
+ "ok" => 1,
630
+ "n" => 2,
631
+ "nMatched" => 1,
632
+ "nUpserted" => 1,
633
+ "nModified" => batch_commands?(wire_version) ? 1 : nil,
634
+ "upserted" => [
635
+ {"_id" => BSON::ObjectId('52a16767bb67fbc77e26a310'), "index" => 1}
636
+ ]
637
+ }, result, "wire_version:#{wire_version}")
638
+ assert_equal 3, @collection.count
639
+ end
640
+ end
641
+
642
+ should "tally given all numbers or return nil for #nil_tally" do
643
+ assert_equal({"nM" => 6}, nil_tally_responses([{"nM" => 1}, {"nM" => 2}, {"nM" => 3}], "nM"))
644
+ assert_equal({"nM" => nil}, nil_tally_responses([{"nM" => 1}, { }, {"nM" => 3}], "nM"))
645
+ assert_equal({"nM" => nil}, nil_tally_responses([{"nM" => 1}, {"nM" => nil}, {"nM" => 3}], "nM"))
646
+ end
647
+
648
+
649
+ should "count nUpserted correctly when _id is not an ObjectId (upsert-replace_one)" do
650
+ with_write_commands_and_operations(@db.connection) do |wire_version|
651
+ @collection.remove
652
+ bulk = @collection.initialize_unordered_bulk_op
653
+ bulk.find({:a => 1}).upsert.replace_one({:_id => 2})
654
+ result = bulk.execute
655
+ assert_match_document(
656
+ {
657
+ "ok" => 1,
658
+ "n" => 1,
659
+ "nMatched" => 0,
660
+ "nUpserted" => 1,
661
+ "nModified" => batch_commands?(wire_version) ? 0 : nil,
662
+ "upserted" => [
663
+ { "_id" => 2, "index" => 0 }
664
+ ]
665
+ }, result, "wire_version:#{wire_version}")
666
+ assert_equal 1, @collection.count
667
+ end
668
+ end
669
+
670
+ # ----- MIXED OPS, ORDERED -----
671
+
672
+ should "execute, return result and reset @ops for #execute" do
673
+ with_write_commands_and_operations(@db.connection) do |wire_version|
674
+ @collection.remove
675
+ @bulk.insert({:x => 1})
676
+ @bulk.insert({:x => 2})
677
+ write_concern = {:w => 1}
678
+ result = @bulk.execute(write_concern)
679
+ assert_equal({"ok" => 1, "n" => 2, "nInserted" => 2}, result, "wire_version:#{wire_version}")
680
+ assert_equal 2, @collection.count
681
+ assert_equal [], @bulk.ops
682
+ end
683
+ end
684
+
685
+ should "run ordered big example" do
686
+ with_write_commands_and_operations(@db.connection) do |wire_version|
687
+ @collection.remove
688
+ big_example(@bulk)
689
+ write_concern = {:w => 1} #{:w => 1. :j => 1} #nojournal for tests
690
+ result = @bulk.execute(write_concern)
691
+ assert_match_document(
692
+ {
693
+ "ok" => 1,
694
+ "n" => 14,
695
+ "nInserted" => 6,
696
+ "nMatched" => 5,
697
+ "nUpserted" => 1,
698
+ "nModified" => batch_commands?(wire_version) ? 5 : nil,
699
+ "nRemoved" => 2,
700
+ "upserted" => [
701
+ {
702
+ "index" => 10,
703
+ "_id" => BSON::ObjectId('52a1e4a4bb67fbc77e26a340')
704
+ }
705
+ ]
706
+ }, result, "wire_version:#{wire_version}")
707
+ assert_equal(batch_commands?(wire_version), result.has_key?("nModified"), "wire_version:#{wire_version}")
708
+ assert_false(@collection.find.to_a.empty?, "wire_version:#{wire_version}")
709
+ assert_equal [{"a"=>1, "x"=>2}, {"a"=>2, "x"=>4}, {"x"=>3}, {"x"=>3}, {"x"=>4}], sort_docs(@collection.find.to_a.collect { |doc| doc.delete("_id"); doc })
710
+ end
711
+ end
712
+
713
+ should "run spec Ordered Bulk Operations" do
714
+ with_write_commands_and_operations(@db.connection) do |wire_version|
715
+ @bulk.insert({:a => 1})
716
+ @bulk.insert({:a => 2})
717
+ @bulk.insert({:a => 3})
718
+ @bulk.find({:a => 2}).upsert.update({'$set' => {:a => 4}})
719
+ @bulk.find({:a => 1}).remove_one
720
+ @bulk.insert({:a => 5})
721
+ result = @bulk.execute({:w => 1})
722
+ assert_match_document(
723
+ {
724
+ "ok" => 1,
725
+ "n" => 6,
726
+ "nInserted" => 4,
727
+ "nMatched" => 1,
728
+ "nModified" => batch_commands?(wire_version) ? 1 : nil,
729
+ "nRemoved" => 1,
730
+ }, result, "wire_version:#{wire_version}")
731
+ end
732
+ end
733
+
734
+ # ----- MIXED OPS, UNORDERED -----
735
+
736
+ should "run unordered big example" do
737
+ with_write_commands_and_operations(@db.connection) do |wire_version|
738
+ @collection.remove
739
+ @bulk = @collection.initialize_unordered_bulk_op
740
+ big_example(@bulk)
741
+ write_concern = {:w => 1} #{:w => 1. :j => 1} #nojournal for tests
742
+ result = @bulk.execute(write_concern)
743
+ assert_equal(6, result["nInserted"])
744
+ assert_true(result["n"] > 0, "wire_version:#{wire_version}")
745
+ assert_equal(batch_commands?(wire_version), result.has_key?("nModified"), "wire_version:#{wire_version}")
746
+ assert_false(@collection.find.to_a.empty?, "wire_version:#{wire_version}")
747
+ end
748
+ end
749
+
750
+ should "run unordered big example with w 0" do
751
+ with_write_commands_and_operations(@db.connection) do |wire_version|
752
+ @collection.remove
753
+ @bulk = @collection.initialize_unordered_bulk_op
754
+ big_example(@bulk)
755
+ write_concern = {:w => 0}
756
+ result = @bulk.execute(write_concern)
757
+ assert_equal(true, result, "wire_version:#{wire_version}")
758
+ assert_false(@collection.find.to_a.empty?, "wire_version:#{wire_version}")
759
+ end
760
+ end
761
+
762
+ should "run unordered bulk operations in one batch per write-type" do
763
+ with_write_commands(@db.connection) do
764
+ @collection.expects(:batch_write).at_most(3).returns([[], [], [], []])
765
+ bulk = @collection.initialize_unordered_bulk_op
766
+ bulk.insert({:_id => 1, :a => 1})
767
+ bulk.find({:_id => 1, :a => 1}).update({"$inc" => {:x => 1}})
768
+ bulk.find({:_id => 1, :a => 1}).remove
769
+ bulk.insert({:_id => 2, :a => 2})
770
+ bulk.find({:_id => 2, :a => 2}).update({"$inc" => {:x => 2}})
771
+ bulk.find({:_id => 2, :a => 2}).remove
772
+ bulk.insert({:_id => 3, :a => 3})
773
+ bulk.find({:_id => 3, :a => 3}).update({"$inc" => {:x => 3}})
774
+ bulk.find({:_id => 3, :a => 3}).remove
775
+ result = bulk.execute # unordered varies, don't use assert_match_document
776
+ end
777
+ end
778
+
779
+ should "run spec Unordered Bulk Operations" do
780
+ with_write_commands_and_operations(@db.connection) do |wire_version|
781
+ bulk = @collection.initialize_unordered_bulk_op
782
+ bulk.insert({:_id => 1})
783
+ bulk.find({:_id => 2}).update_one({'$inc' => { :x => 1 }})
784
+ bulk.find({:_id => 3}).remove_one
785
+ bulk.insert({:_id => 4})
786
+ bulk.find({:_id => 5}).update_one({'$inc' => { :x => 1 }})
787
+ bulk.find({:_id => 6}).remove_one
788
+ result = nil
789
+ begin
790
+ result = bulk.execute
791
+ rescue => ex
792
+ result = ex.result
793
+ end
794
+ # for write commands internally the driver will execute 3. One each for the inserts, updates and removes.
795
+ end
796
+ end
797
+
798
+ # ----- EMPTY BATCH -----
799
+
800
+ should "handle empty bulk op" do
801
+ with_write_commands_and_operations(@db.connection) do |wire_version|
802
+ assert_raise_error(MongoArgumentError, Mongo::BulkWriteCollectionView::EMPTY_BATCH_MSG) do
803
+ @bulk.execute
804
+ end
805
+ end
806
+ end
807
+
808
+ should "handle insert of overly large document" do
809
+ large_doc = {"a" => "y"*(2*@client.max_message_size)}
810
+ with_write_commands_and_operations(@db.connection) do |wire_version|
811
+ ex = assert_raise Mongo::BulkWriteError do
812
+ @collection.remove
813
+ bulk = @collection.initialize_unordered_bulk_op
814
+ bulk.insert(large_doc)
815
+ puts "bulk.execute:#{bulk.execute.inspect}"
816
+ end
817
+ assert_equal 22, ex.result["writeErrors"].first["code"]
818
+ end
819
+ end
820
+
821
+ # ----- ORDERED, WITH ERRORS -----
822
+
823
+ should "handle error for duplicate key with offset" do
824
+ with_write_commands_and_operations(@db.connection) do |wire_version|
825
+ @collection.remove
826
+ @bulk.find({:a => 1}).update_one({"$inc" => {:x => 1}})
827
+ @bulk.insert({:_id => 1, :a => 1})
828
+ @bulk.insert({:_id => 1, :a => 2})
829
+ @bulk.insert({:_id => 3, :a => 3})
830
+ ex = assert_raise BulkWriteError do
831
+ @bulk.execute
832
+ end
833
+ result = ex.result
834
+ assert_match_document(
835
+ {
836
+ "ok" => 1,
837
+ "n" => 1,
838
+ "writeErrors" =>
839
+ [{
840
+ "index" => 2,
841
+ "code" => 11000,
842
+ "errmsg" => /duplicate key error/
843
+ }],
844
+ "code" => 65,
845
+ "errmsg" => "batch item errors occurred",
846
+ "nInserted" => 1,
847
+ "nMatched" => 0,
848
+ "nModified" => batch_commands?(wire_version) ? 0 : nil
849
+ }, result, "wire_version:#{wire_version}")
850
+ end
851
+ end
852
+
853
+ should "handle error for serialization with offset" do
854
+ with_write_commands_and_operations(@db.connection) do |wire_version|
855
+ @collection.remove
856
+ assert_equal 16777216, @client.max_bson_size
857
+ @bulk.find({:a => 1}).update_one({"$inc" => {:x => 1}})
858
+ @bulk.insert({:_id => 1, :a => 1})
859
+ @bulk.insert(generate_sized_doc(@client.max_message_size + 1))
860
+ @bulk.insert({:_id => 3, :a => 3})
861
+ ex = assert_raise BulkWriteError do
862
+ @bulk.execute
863
+ end
864
+ result = ex.result
865
+ assert_match_document(
866
+ {
867
+ "ok" => 1,
868
+ "n" => 1,
869
+ "writeErrors" =>
870
+ [{
871
+ "index" => 2,
872
+ "code" => 22,
873
+ "errmsg" => /too large/
874
+ }],
875
+ "code" => 65,
876
+ "errmsg" => "batch item errors occurred",
877
+ "nInserted" => 1,
878
+ "nMatched" => 0,
879
+ "nModified" => batch_commands?(wire_version) ? 0 : nil
880
+ }, result, "wire_version:#{wire_version}")
881
+ end
882
+ end
883
+
884
+ should "run ordered bulk op - spec Modes of Execution" do
885
+ with_write_commands_and_operations(@db.connection) do |wire_version|
886
+ @collection.remove
887
+ @collection.ensure_index(BSON::OrderedHash[:a, Mongo::ASCENDING], {:unique => true})
888
+ @bulk.insert({:a => 1})
889
+ @bulk.insert({:a => 2})
890
+ @bulk.find({:a => 2}).update({'$set' => {:a => 1}}) # Clashes with unique index
891
+ @bulk.find({:a => 1}).remove
892
+ ex = assert_raise BulkWriteError do
893
+ @bulk.execute
894
+ end
895
+ assert_equal(2, @collection.count)
896
+ end
897
+ end
898
+
899
+ should "handle duplicate key error - spec Merging Results" do
900
+ with_write_commands_and_operations(@db.connection) do |wire_version|
901
+ @collection.remove
902
+ @collection.ensure_index(BSON::OrderedHash[:a, Mongo::ASCENDING], {:unique => true})
903
+ bulk = @collection.initialize_ordered_bulk_op
904
+ bulk.insert({:a => 1})
905
+ bulk.insert({:a => 2})
906
+ bulk.find({:a => 2}).upsert.update({'$set' => {:a => 1}})
907
+ bulk.insert({:a => 3})
908
+ ex = assert_raise BulkWriteError do
909
+ bulk.execute
910
+ end
911
+ result = ex.result
912
+ assert_match_document(
913
+ {
914
+ "ok" => 1,
915
+ "n" => 2,
916
+ "writeErrors" =>
917
+ [{
918
+ "index" => 2,
919
+ "code" => DUPLICATE_KEY_ERROR_CODE_SET,
920
+ "errmsg" => /duplicate key error/
921
+ }],
922
+ "code" => 65,
923
+ "errmsg" => "batch item errors occurred",
924
+ "nInserted" => 2,
925
+ "nMatched" => 0,
926
+ "nModified" => batch_commands?(wire_version) ? 0 : nil
927
+ }, result, "wire_version:#{wire_version}")
928
+ end
929
+ end
930
+
931
+ should "report user index - spec Merging errors" do
932
+ with_write_commands_and_operations(@db.connection) do |wire_version|
933
+ @collection.remove
934
+ @collection.ensure_index(BSON::OrderedHash[:a, Mongo::ASCENDING], {:unique => true})
935
+ bulk = @collection.initialize_ordered_bulk_op
936
+ bulk.insert({:a => 1})
937
+ bulk.insert({:a => 2})
938
+ bulk.find({:a => 2}).update_one({'$set' => {:a => 1}});
939
+ bulk.find({:a => 4}).remove_one();
940
+ ex = assert_raise BulkWriteError do
941
+ bulk.execute({:w => 1})
942
+ end
943
+ result = ex.result
944
+ assert_match_document(
945
+ {
946
+ "ok" => 1,
947
+ "n" => 2,
948
+ "writeErrors" =>
949
+ [{
950
+ "index" => 2,
951
+ "code" => DUPLICATE_KEY_ERROR_CODE_SET,
952
+ "errmsg" => /duplicate key error/
953
+ }],
954
+ "code" => 65,
955
+ "errmsg" => "batch item errors occurred",
956
+ "nInserted" => 2,
957
+ "nMatched" => 0,
958
+ "nModified" => batch_commands?(wire_version) ? 0 : nil
959
+ }, result, "wire_version:#{wire_version}")
960
+ end
961
+ end
962
+
963
+ should "handle multiple upsert - spec Handling upserts" do
964
+ with_write_commands_and_operations(@db.connection) do |wire_version|
965
+ @collection.remove
966
+ @collection.ensure_index(BSON::OrderedHash[:a, Mongo::ASCENDING], {:unique => true})
967
+ bulk = @collection.initialize_ordered_bulk_op
968
+ bulk.find({:a => 1}).upsert.update({'$set' => {:a => 2}})
969
+ bulk.find({:a => 3}).upsert.update({'$set' => {:a => 4}})
970
+ result = bulk.execute
971
+ assert_match_document(
972
+ {
973
+ "ok" => 1,
974
+ "n" => 2,
975
+ "nMatched" => 0,
976
+ "nUpserted" => 2,
977
+ "nModified" => batch_commands?(wire_version) ? 0 : nil,
978
+ "upserted" => [
979
+ {"index" => 0, "_id" => BSON::ObjectId('52a1e37cbb67fbc77e26a338')},
980
+ {"index" => 1, "_id" => BSON::ObjectId('52a1e37cbb67fbc77e26a339')}
981
+ ]
982
+ }, result, "wire_version:#{wire_version}")
983
+ end
984
+ end
985
+
986
+ should "handle replication usage error" do
987
+ with_no_replication(@db.connection) do
988
+ with_write_commands_and_operations(@db.connection) do |wire_version|
989
+ @collection.remove
990
+ @bulk = @collection.initialize_ordered_bulk_op
991
+ @bulk.insert({:_id => 1, :a => 1})
992
+ write_concern = {:w => 5}
993
+ ex = assert_raise BulkWriteError do
994
+ @bulk.execute(write_concern)
995
+ end
996
+ result = ex.result
997
+ if @version >= "2.5.5"
998
+ assert_match_document(
999
+ {
1000
+ "ok" => 0,
1001
+ "n" => 0,
1002
+ "code" => 65,
1003
+ "errmsg" => "batch item errors occurred",
1004
+ "writeErrors" => [
1005
+ {
1006
+ "errmsg" => "cannot use 'w' > 1 when a host is not replicated",
1007
+ "code" => 2,
1008
+ "index" => 0}
1009
+ ],
1010
+ "nInserted" => 0,
1011
+ }, result, "wire_version:#{wire_version}")
1012
+ else
1013
+ assert_match_document(
1014
+ {
1015
+ "ok" => 1,
1016
+ "n" => 1,
1017
+ "code" => 65,
1018
+ "errmsg" => "batch item errors occurred",
1019
+ "writeConcernError" => [
1020
+ {
1021
+ "errmsg" => /no replication has been enabled/,
1022
+ "code" => 64,
1023
+ "index" => 0
1024
+ }
1025
+ ],
1026
+ "nInserted" => 1,
1027
+ }, result, "wire_version:#{wire_version}")
1028
+ end
1029
+ end
1030
+ end
1031
+ end
1032
+
1033
+ # ----- UNORDERED, WITH ERRORS -----
1034
+
1035
+ should "handle error for unordered multiple duplicate key with offset" do
1036
+ with_write_commands_and_operations(@db.connection) do |wire_version|
1037
+ @collection.remove
1038
+ @bulk = @collection.initialize_unordered_bulk_op
1039
+ @bulk.find({:a => 1}).remove
1040
+ @bulk.insert({:_id => 1, :a => 1})
1041
+ @bulk.insert({:_id => 1, :a => 2})
1042
+ @bulk.insert({:_id => 3, :a => 3})
1043
+ @bulk.insert({:_id => 3, :a => 3})
1044
+ ex = assert_raise BulkWriteError do
1045
+ @bulk.execute
1046
+ end
1047
+ result = ex.result
1048
+ assert_true (0 < result["nInserted"] && result["nInserted"] < 3)
1049
+ assert_not_nil(result["writeErrors"], "wire_version:#{wire_version}")
1050
+ end
1051
+ end
1052
+
1053
+ should "run unordered bulk op - spec Modes of Execution" do
1054
+ with_write_commands_and_operations(@db.connection) do |wire_version|
1055
+ @collection.remove
1056
+ @collection.ensure_index(BSON::OrderedHash[:a, Mongo::ASCENDING], {:unique => true})
1057
+ bulk = @collection.initialize_unordered_bulk_op
1058
+ bulk.insert({:a => 1})
1059
+ bulk.insert({:a => 2})
1060
+ bulk.find({:a => 2}).update({'$set' => {:a => 1}}) # Clashes with unique index
1061
+ bulk.find({:a => 3}).remove
1062
+ bulk.find({:a => 2}).update({'$set' => {:a => 1}}) # Clashes with unique index
1063
+ ex = assert_raise BulkWriteError do
1064
+ bulk.execute
1065
+ end
1066
+ result = ex.result
1067
+ assert(result["writeErrors"].size > 1, "wire_version:#{wire_version}")
1068
+ end
1069
+ end
1070
+
1071
+ should "handle unordered errors - spec Merging Results" do
1072
+ with_write_commands_and_operations(@db.connection) do |wire_version|
1073
+ @collection.remove
1074
+ @collection.ensure_index(BSON::OrderedHash[:a, Mongo::ASCENDING], {:unique => true})
1075
+ bulk = @collection.initialize_unordered_bulk_op
1076
+ bulk.insert({:a => 1})
1077
+ bulk.find({:a => 1}).upsert.update({'$set' => {:a => 2}})
1078
+ bulk.insert({:a => 2})
1079
+ ex = assert_raise BulkWriteError do
1080
+ bulk.execute
1081
+ end
1082
+ result = ex.result # unordered varies, don't use assert_bulk_exception
1083
+ assert_equal(1, result['ok'], "wire_version:#{wire_version}")
1084
+ assert_equal(2, result['n'], "wire_version:#{wire_version}")
1085
+ err_details = result['writeErrors']
1086
+ assert_match(/duplicate key error/, err_details.first['errmsg'], "wire_version:#{wire_version}")
1087
+ end
1088
+ end
1089
+
1090
+ should "handle multiple errors for unordered bulk write" do
1091
+ with_write_commands_and_operations(@db.connection) do |wire_version|
1092
+ @collection.remove
1093
+ @bulk = @collection.initialize_unordered_bulk_op
1094
+ @bulk.insert({:_id => 1, :a => 1})
1095
+ @bulk.insert({:_id => 1, :a => 2})
1096
+ @bulk.insert(generate_sized_doc(@client.max_message_size + 1))
1097
+ @bulk.insert({:_id => 3, :a => 3})
1098
+ @bulk.find({:a => 4}).upsert.replace_one({:x => 3})
1099
+ ex = assert_raise BulkWriteError do
1100
+ @bulk.execute
1101
+ end
1102
+ result = ex.result # unordered varies, don't use assert_bulk_exception
1103
+ assert_equal(1, result['ok'], "wire_version:#{wire_version}")
1104
+ assert_equal(3, result['n'], "wire_version:#{wire_version}")
1105
+ err_details = result['writeErrors']
1106
+ assert_match(/duplicate key error/, err_details.find { |e| e['code']==11000 }['errmsg'], "wire_version:#{wire_version}")
1107
+ assert_match(/too large/, err_details.find { |e| e['index']==2 }['errmsg'], "wire_version:#{wire_version}")
1108
+ assert_not_nil(result['upserted'].find { |e| e['index']==4 }, "wire_version:#{wire_version}")
1109
+ end
1110
+ end
1111
+
1112
+ # ----- NO_JOURNAL -----
1113
+
1114
+ should "handle journaling error" do
1115
+ with_no_journaling(@db.connection) do
1116
+ with_write_commands_and_operations(@db.connection) do |wire_version|
1117
+ @collection.remove
1118
+ @bulk = @collection.initialize_ordered_bulk_op
1119
+ @bulk.insert({:_id => 1, :a => 1})
1120
+ write_concern = {:w => 1, :j => 1}
1121
+ ex = assert_raise BulkWriteError do
1122
+ @bulk.execute(write_concern)
1123
+ end
1124
+ result = ex.result
1125
+ if @version >= "2.5.5"
1126
+ assert_match_document(
1127
+ {
1128
+ "ok" => 0,
1129
+ "n" => 0,
1130
+ "writeErrors" => [
1131
+ {
1132
+ "code" => 2,
1133
+ "errmsg" => "cannot use 'j' option when a host does not have journaling enabled", "index" => 0
1134
+ }
1135
+ ],
1136
+ "code" => 65,
1137
+ "errmsg" => "batch item errors occurred",
1138
+ "nInserted" => 0
1139
+ }, result, "wire_version:#{wire_version}")
1140
+ else
1141
+ assert_match_document(
1142
+ {
1143
+ "ok" => 1,
1144
+ "n" => 1,
1145
+ "writeConcernError" => [
1146
+ {
1147
+ "code" => 2,
1148
+ "errmsg" => "journaling not enabled on this server",
1149
+ "index" => 0
1150
+ }
1151
+ ],
1152
+ "code" => 65,
1153
+ "errmsg" => "batch item errors occurred",
1154
+ "nInserted" => 1
1155
+ }, result, "wire_version:#{wire_version}")
1156
+ end
1157
+ end
1158
+ end
1159
+ end
1160
+
1161
+ # ----- W = 0 -----
1162
+
1163
+ should "run ordered big example with w 0" do
1164
+ with_write_commands_and_operations(@db.connection) do |wire_version|
1165
+ @collection.remove
1166
+ big_example(@bulk)
1167
+ result = @bulk.execute({:w => 0})
1168
+ assert_equal(true, result, "wire_version:#{wire_version}")
1169
+ assert_false(@collection.find.to_a.empty?, "wire_version:#{wire_version}")
1170
+ assert_equal [{"a"=>1, "x"=>2}, {"a"=>2, "x"=>4}, {"x"=>3}, {"x"=>3}, {"x"=>4}], sort_docs(@collection.find.to_a.collect { |doc| doc.delete("_id"); doc })
1171
+ end
1172
+ end
1173
+
1174
+ should "running with w 0 should not report write errors" do
1175
+ with_write_commands_and_operations(@db.connection) do
1176
+ @bulk.insert({:_id => 1, :a => 1 })
1177
+ @bulk.insert({:_id => 1, :a => 2 })
1178
+ @bulk.execute({:w => 0}) # should raise no duplicate key error
1179
+ end
1180
+ end
1181
+
1182
+ # ----- W > 0 WITH STANDALONE -----
1183
+
1184
+ should "disallow w > 0 against a standalone" do
1185
+ with_write_commands_and_operations(@db.connection) do |wire_version|
1186
+ @collection.remove
1187
+ @bulk.insert({:_id => 1, :a => 1 })
1188
+ @bulk.insert({:_id => 2, :a => 1 })
1189
+ @bulk.insert({:_id => 3, :a => 1 })
1190
+ assert_raise_error BulkWriteError do
1191
+ @bulk.execute({:w => 2})
1192
+ end
1193
+ assert (@collection.count == batch_commands?(wire_version) ? 0 : 1)
1194
+ end
1195
+ end
1196
+
1197
+ end
1198
+ end