@@ -69,3 +69,75 @@ async def test_update_metadata(self, index_host, dimension, target_namespace):
6969 fetched_vec = await asyncio_idx .fetch (ids = ["2" ], namespace = target_namespace )
7070 assert fetched_vec .vectors ["2" ].metadata == {"genre" : "comedy" }
7171 await asyncio_idx .close ()
72+
73+ async def test_update_with_filter_and_dry_run (self , index_host , dimension , target_namespace ):
74+ """Test update with filter and dry_run=True to verify matched_records and updated_records are returned."""
75+ asyncio_idx = build_asyncioindex_client (index_host )
76+
77+ # Upsert vectors with different genres
78+ upsert1 = await asyncio_idx .upsert (
79+ vectors = [
80+ Vector (
81+ id = str (i ),
82+ values = embedding_values (dimension ),
83+ metadata = {"genre" : "comedy" if i % 2 == 0 else "drama" , "status" : "active" },
84+ )
85+ for i in range (10 )
86+ ],
87+ namespace = target_namespace ,
88+ batch_size = 10 ,
89+ show_progress = False ,
90+ )
91+
92+ await poll_until_lsn_reconciled_async (
93+ asyncio_idx , upsert1 ._response_info , namespace = target_namespace
94+ )
95+
96+ # Test dry_run=True - should return matched_records without updating
97+ dry_run_response = await asyncio_idx .update (
98+ filter = {"genre" : {"$eq" : "comedy" }},
99+ set_metadata = {"status" : "updated" },
100+ dry_run = True ,
101+ namespace = target_namespace ,
102+ )
103+
104+ # Verify matched_records is returned and correct (5 comedy vectors)
105+ assert dry_run_response .matched_records is not None
106+ assert dry_run_response .matched_records == 5
107+ # In dry run, updated_records should be 0 or None since no records are actually updated
108+ assert dry_run_response .updated_records is None or dry_run_response .updated_records == 0
109+
110+ # Verify the vectors were NOT actually updated (dry run)
111+ fetched_before = await asyncio_idx .fetch (
112+ ids = ["0" , "2" , "4" , "6" , "8" ], namespace = target_namespace
113+ )
114+ for vec_id in ["0" , "2" , "4" , "6" , "8" ]:
115+ assert fetched_before .vectors [vec_id ].metadata .get ("status" ) == "active"
116+
117+ # Now do the actual update
118+ update_response = await asyncio_idx .update (
119+ filter = {"genre" : {"$eq" : "comedy" }},
120+ set_metadata = {"status" : "updated" },
121+ namespace = target_namespace ,
122+ )
123+
124+ # Verify matched_records and updated_records are returned
125+ assert update_response .matched_records is not None
126+ assert update_response .matched_records == 5
127+ # updated_records should match the number of records actually updated (if returned by API)
128+ if update_response .updated_records is not None :
129+ assert update_response .updated_records == 5
130+
131+ await poll_until_lsn_reconciled_async (
132+ asyncio_idx , update_response ._response_info , namespace = target_namespace
133+ )
134+
135+ # Verify the vectors were actually updated
136+ fetched_after = await asyncio_idx .fetch (
137+ ids = ["0" , "2" , "4" , "6" , "8" ], namespace = target_namespace
138+ )
139+ for vec_id in ["0" , "2" , "4" , "6" , "8" ]:
140+ assert fetched_after .vectors [vec_id ].metadata .get ("status" ) == "updated"
141+ assert fetched_after .vectors [vec_id ].metadata .get ("genre" ) == "comedy"
142+
143+ await asyncio_idx .close ()
0 commit comments