@@ -781,6 +781,227 @@ async def test_large_inserts_unordered(self):
781781 self .assertEqual (6 , result .inserted_count )
782782 self .assertEqual (6 , await self .coll .count_documents ({}))
783783
784+ async def test_bulk_write_with_comment (self ):
785+ """Test bulk write operations with comment parameter."""
786+ requests = [
787+ InsertOne ({"x" : 1 }),
788+ UpdateOne ({"x" : 1 }, {"$set" : {"y" : 1 }}),
789+ DeleteOne ({"x" : 1 }),
790+ ]
791+ result = await self .coll .bulk_write (requests , comment = "bulk_comment" )
792+ self .assertEqual (1 , result .inserted_count )
793+ self .assertEqual (1 , result .modified_count )
794+ self .assertEqual (1 , result .deleted_count )
795+
796+ async def test_bulk_write_with_let (self ):
797+ """Test bulk write operations with let parameter."""
798+ if not async_client_context .version .at_least (5 , 0 ):
799+ self .skipTest ("let parameter requires MongoDB 5.0+" )
800+
801+ await self .coll .insert_one ({"x" : 1 })
802+ requests = [
803+ UpdateOne ({"$expr" : {"$eq" : ["$x" , "$$targetVal" ]}}, {"$set" : {"updated" : True }}),
804+ ]
805+ result = await self .coll .bulk_write (requests , let = {"targetVal" : 1 })
806+ self .assertEqual (1 , result .modified_count )
807+
808+ async def test_bulk_write_all_operation_types (self ):
809+ """Test bulk write with all operation types combined."""
810+ await self .coll .insert_many ([{"x" : i } for i in range (5 )])
811+
812+ requests = [
813+ InsertOne ({"x" : 100 }),
814+ UpdateOne ({"x" : 0 }, {"$set" : {"updated" : True }}),
815+ UpdateMany ({"x" : {"$lte" : 2 }}, {"$set" : {"batch_updated" : True }}),
816+ ReplaceOne ({"x" : 3 }, {"x" : 3 , "replaced" : True }),
817+ DeleteOne ({"x" : 4 }),
818+ DeleteMany ({"x" : {"$gt" : 50 }}),
819+ ]
820+ result = await self .coll .bulk_write (requests )
821+
822+ self .assertEqual (1 , result .inserted_count )
823+ self .assertGreaterEqual (result .modified_count , 1 )
824+ self .assertGreaterEqual (result .deleted_count , 1 )
825+
826+ async def test_bulk_write_unordered (self ):
827+ """Test unordered bulk write continues after error."""
828+ await self .coll .create_index ([("x" , 1 )], unique = True )
829+ self .addAsyncCleanup (self .coll .drop_index , [("x" , 1 )])
830+
831+ requests = [
832+ InsertOne ({"x" : 1 }),
833+ InsertOne ({"x" : 1 }), # Duplicate - will error
834+ InsertOne ({"x" : 2 }),
835+ InsertOne ({"x" : 3 }),
836+ ]
837+
838+ with self .assertRaises (BulkWriteError ) as ctx :
839+ await self .coll .bulk_write (requests , ordered = False )
840+
841+ # With unordered, should have inserted 3 documents
842+ self .assertEqual (3 , ctx .exception .details ["nInserted" ])
843+
844+ async def test_bulk_write_ordered (self ):
845+ """Test ordered bulk write stops on first error."""
846+ await self .coll .create_index ([("x" , 1 )], unique = True )
847+ self .addAsyncCleanup (self .coll .drop_index , [("x" , 1 )])
848+
849+ requests = [
850+ InsertOne ({"x" : 1 }),
851+ InsertOne ({"x" : 1 }), # Duplicate - will error
852+ InsertOne ({"x" : 2 }),
853+ InsertOne ({"x" : 3 }),
854+ ]
855+
856+ with self .assertRaises (BulkWriteError ) as ctx :
857+ await self .coll .bulk_write (requests , ordered = True )
858+
859+ # With ordered, should have inserted only 1 document
860+ self .assertEqual (1 , ctx .exception .details ["nInserted" ])
861+
862+ async def test_bulk_write_bypass_document_validation (self ):
863+ """Test bulk write with bypass_document_validation."""
864+ if not async_client_context .version .at_least (3 , 2 ):
865+ self .skipTest ("bypass_document_validation requires MongoDB 3.2+" )
866+
867+ # Create collection with validator
868+ await self .coll .drop ()
869+ await self .db .create_collection (
870+ self .coll .name , validator = {"$jsonSchema" : {"required" : ["name" ]}}
871+ )
872+
873+ # Without bypass, should fail
874+ with self .assertRaises (BulkWriteError ):
875+ await self .coll .bulk_write ([InsertOne ({"x" : 1 })])
876+
877+ # With bypass, should succeed
878+ result = await self .coll .bulk_write ([InsertOne ({"x" : 1 })], bypass_document_validation = True )
879+ self .assertEqual (1 , result .inserted_count )
880+
881+ async def test_bulk_write_result_properties (self ):
882+ """Test all BulkWriteResult properties."""
883+ await self .coll .insert_one ({"x" : 1 })
884+
885+ requests = [
886+ InsertOne ({"x" : 2 }),
887+ UpdateOne ({"x" : 1 }, {"$set" : {"updated" : True }}),
888+ ReplaceOne ({"x" : 2 }, {"x" : 2 , "replaced" : True }, upsert = True ),
889+ DeleteOne ({"x" : 1 }),
890+ ]
891+ result = await self .coll .bulk_write (requests )
892+
893+ # Check all properties
894+ self .assertTrue (result .acknowledged )
895+ self .assertEqual (1 , result .inserted_count )
896+ self .assertGreaterEqual (result .matched_count , 0 )
897+ self .assertGreaterEqual (result .modified_count , 0 )
898+ self .assertEqual (1 , result .deleted_count )
899+ self .assertIsInstance (result .upserted_count , int )
900+ self .assertIsInstance (result .upserted_ids , dict )
901+
902+ async def test_bulk_write_with_upsert (self ):
903+ """Test bulk write upsert operations."""
904+ requests = [
905+ UpdateOne ({"x" : 1 }, {"$set" : {"y" : 1 }}, upsert = True ),
906+ UpdateOne ({"x" : 2 }, {"$set" : {"y" : 2 }}, upsert = True ),
907+ ReplaceOne ({"x" : 3 }, {"x" : 3 , "y" : 3 }, upsert = True ),
908+ ]
909+ result = await self .coll .bulk_write (requests )
910+
911+ self .assertEqual (3 , result .upserted_count )
912+ self .assertEqual (3 , len (result .upserted_ids ))
913+
914+ async def test_update_one_with_hint (self ):
915+ """Test UpdateOne with hint parameter."""
916+ await self .coll .create_index ([("x" , 1 )])
917+ self .addAsyncCleanup (self .coll .drop_index , [("x" , 1 )])
918+
919+ await self .coll .insert_one ({"x" : 1 })
920+
921+ requests = [UpdateOne ({"x" : 1 }, {"$set" : {"y" : 1 }}, hint = [("x" , 1 )])]
922+ result = await self .coll .bulk_write (requests )
923+ self .assertEqual (1 , result .modified_count )
924+
925+ async def test_update_many_with_hint (self ):
926+ """Test UpdateMany with hint parameter."""
927+ await self .coll .create_index ([("x" , 1 )])
928+ self .addAsyncCleanup (self .coll .drop_index , [("x" , 1 )])
929+
930+ await self .coll .insert_many ([{"x" : 1 }, {"x" : 1 }])
931+
932+ requests = [UpdateMany ({"x" : 1 }, {"$set" : {"y" : 1 }}, hint = [("x" , 1 )])]
933+ result = await self .coll .bulk_write (requests )
934+ self .assertEqual (2 , result .modified_count )
935+
936+ async def test_delete_one_with_hint (self ):
937+ """Test DeleteOne with hint parameter."""
938+ await self .coll .create_index ([("x" , 1 )])
939+ self .addAsyncCleanup (self .coll .drop_index , [("x" , 1 )])
940+
941+ await self .coll .insert_one ({"x" : 1 })
942+
943+ requests = [DeleteOne ({"x" : 1 }, hint = [("x" , 1 )])]
944+ result = await self .coll .bulk_write (requests )
945+ self .assertEqual (1 , result .deleted_count )
946+
947+ async def test_delete_many_with_hint (self ):
948+ """Test DeleteMany with hint parameter."""
949+ await self .coll .create_index ([("x" , 1 )])
950+ self .addAsyncCleanup (self .coll .drop_index , [("x" , 1 )])
951+
952+ await self .coll .insert_many ([{"x" : 1 }, {"x" : 1 }])
953+
954+ requests = [DeleteMany ({"x" : 1 }, hint = [("x" , 1 )])]
955+ result = await self .coll .bulk_write (requests )
956+ self .assertEqual (2 , result .deleted_count )
957+
958+ async def test_update_one_with_array_filters (self ):
959+ """Test UpdateOne with array_filters parameter."""
960+ await self .coll .insert_one ({"x" : [{"y" : 1 }, {"y" : 2 }, {"y" : 3 }]})
961+
962+ requests = [
963+ UpdateOne ({}, {"$set" : {"x.$[elem].z" : 1 }}, array_filters = [{"elem.y" : {"$gt" : 1 }}])
964+ ]
965+ result = await self .coll .bulk_write (requests )
966+ self .assertEqual (1 , result .modified_count )
967+
968+ doc = await self .coll .find_one ()
969+ # Elements with y > 1 should have z = 1
970+ for elem in doc ["x" ]:
971+ if elem ["y" ] > 1 :
972+ self .assertEqual (1 , elem .get ("z" ))
973+
974+ async def test_replace_one_with_hint (self ):
975+ """Test ReplaceOne with hint parameter."""
976+ await self .coll .create_index ([("x" , 1 )])
977+ self .addAsyncCleanup (self .coll .drop_index , [("x" , 1 )])
978+
979+ await self .coll .insert_one ({"x" : 1 })
980+
981+ requests = [ReplaceOne ({"x" : 1 }, {"x" : 1 , "replaced" : True }, hint = [("x" , 1 )])]
982+ result = await self .coll .bulk_write (requests )
983+ self .assertEqual (1 , result .modified_count )
984+
985+ async def test_update_with_collation (self ):
986+ """Test update operations with collation."""
987+ await self .coll .insert_many (
988+ [
989+ {"name" : "cafe" },
990+ {"name" : "Cafe" },
991+ ]
992+ )
993+
994+ requests = [
995+ UpdateMany (
996+ {"name" : "cafe" },
997+ {"$set" : {"updated" : True }},
998+ collation = {"locale" : "en" , "strength" : 2 },
999+ )
1000+ ]
1001+ result = await self .coll .bulk_write (requests )
1002+ # With case-insensitive collation, both docs should match
1003+ self .assertEqual (2 , result .modified_count )
1004+
7841005
7851006class AsyncBulkAuthorizationTestBase (AsyncBulkTestBase ):
7861007 @async_client_context .require_auth
0 commit comments