mongo 1.10.0-java

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