Skip to content

Commit f72120f

Browse files
Workaround and tests for Bulk API edge case with pre-2.6 servers
1 parent 01c0dbe commit f72120f

File tree

2 files changed

+71
-0
lines changed

2 files changed

+71
-0
lines changed

lib/mongo/bulk_write_collection_view.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,13 @@ def merge_result(errors, exchanges)
297297
upserted = [{"_id" => upserted}] if upserted.class != Array # OP_UPDATE non-array
298298
n_upserted = upserted.size
299299
concat(result, "upserted", merge_indexes(upserted, exchange))
300+
elsif (response["updatedExisting"] == false && n == 1)
301+
# workaround for DRIVERS-151 (non-ObjectID _id fields in pre-2.6 servers)
302+
op = exchange[:batch][0]
303+
missing_id = op[:u].fetch(:_id, op[:q][:_id]) # _id in update document takes precedence
304+
upserted = [ { "_id" => missing_id, "index" => 0 } ]
305+
n_upserted = n
306+
concat(result, "upserted", merge_indexes(upserted, exchange))
300307
end
301308
tally(result, "nUpserted", n_upserted) if n_upserted > 0
302309
tally(result, "nMatched", n - n_upserted)

test/functional/bulk_write_collection_view_test.rb

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,27 @@ def nil_tally_responses(responses, key)
501501
end
502502
end
503503

504+
should "count nUpserted correctly when _id is not an ObjectId (upsert-update)" do
505+
with_write_commands_and_operations(@db.connection) do |wire_version|
506+
@collection.remove
507+
508+
bulk = @collection.initialize_unordered_bulk_op
509+
bulk.find({:_id => 3}).upsert.update({"$set" => {:b => 3}})
510+
result = bulk.execute
511+
assert_match_document(
512+
{
513+
"ok" => 1,
514+
"n" => 1,
515+
"nMatched" => 0,
516+
"nUpserted" => 1,
517+
"nModified" => batch_commands?(wire_version) ? 0 : nil,
518+
"upserted" => [
519+
{ "_id" => 3, "index" => 0 }
520+
]
521+
}, result, "wire_version:#{wire_version}")
522+
end
523+
end
524+
504525
# ----- UPSERT-UPDATE_ONE -----
505526

506527
should "#upsert a document without affecting non-upsert update_ones" do
@@ -544,6 +565,27 @@ def nil_tally_responses(responses, key)
544565
end
545566
end
546567

568+
569+
should "count nUpserted correctly when _id is not an ObjectId (upsert-update_one)" do
570+
with_write_commands_and_operations(@db.connection) do |wire_version|
571+
@collection.remove
572+
bulk = @collection.initialize_ordered_bulk_op
573+
bulk.find({:_id => 2}).upsert.update_one({"$set" => {:x => 2}})
574+
result = bulk.execute
575+
assert_match_document(
576+
{
577+
"ok" => 1,
578+
"n" => 1,
579+
"nMatched" => 0,
580+
"nUpserted" => 1,
581+
"nModified" => batch_commands?(wire_version) ? 0 : nil,
582+
"upserted" => [
583+
{"_id" => 2, "index" => 0 }
584+
]
585+
}, result, "wire_version:#{wire_version}")
586+
end
587+
end
588+
547589
# ----- UPSERT-REPLACE_ONE -----
548590

549591
should "not affect non-upsert replace_ones in same batch as #upsert-replace_one" do
@@ -598,6 +640,28 @@ def nil_tally_responses(responses, key)
598640
assert_equal({"nM" => nil}, nil_tally_responses([{"nM" => 1}, {"nM" => nil}, {"nM" => 3}], "nM"))
599641
end
600642

643+
644+
should "count nUpserted correctly when _id is not an ObjectId (upsert-replace_one)" do
645+
with_write_commands_and_operations(@db.connection) do |wire_version|
646+
@collection.remove
647+
bulk = @collection.initialize_unordered_bulk_op
648+
bulk.find({:a => 1}).upsert.replace_one({:_id => 2})
649+
result = bulk.execute
650+
assert_match_document(
651+
{
652+
"ok" => 1,
653+
"n" => 1,
654+
"nMatched" => 0,
655+
"nUpserted" => 1,
656+
"nModified" => batch_commands?(wire_version) ? 0 : nil,
657+
"upserted" => [
658+
{ "_id" => 2, "index" => 0 }
659+
]
660+
}, result, "wire_version:#{wire_version}")
661+
assert_equal 1, @collection.count
662+
end
663+
end
664+
601665
# ----- MIXED OPS, ORDERED -----
602666

603667
should "execute, return result and reset @ops for #execute" do

0 commit comments

Comments
 (0)