@@ -79,6 +79,8 @@ import {
7979 GetQuerySegmentInfoResponse ,
8080 SearchData ,
8181 FloatVector ,
82+ FieldPartialUpdateOpType ,
83+ FieldPartialUpdateOp ,
8284} from '../' ;
8385import { Collection } from './Collection' ;
8486
@@ -144,6 +146,9 @@ export class Data extends Collection {
144146 throw new Error ( ERROR_REASONS . INSERT_CHECK_FIELD_DATA_IS_REQUIRED ) ;
145147 }
146148 const { collection_name } = data ;
149+ const fieldOps = upsert
150+ ? this . normalizeFieldOps ( ( data as UpsertReq ) . field_ops )
151+ : [ ] ;
147152
148153 const describeReq = { collection_name, cache : enable_cache } ;
149154 if ( data . db_name ) {
@@ -234,7 +239,8 @@ export class Data extends Collection {
234239
235240 // Track which fields are present in the original row data for partial updates
236241 const isPartialUpdate =
237- 'partial_update' in data && data . partial_update === true ;
242+ ( 'partial_update' in data && data . partial_update === true ) ||
243+ fieldOps . length > 0 ;
238244 const originalRowKeys = new Set < string > ( ) ;
239245
240246 // Loop through each row and set the corresponding field values in the Map.
@@ -297,10 +303,11 @@ export class Data extends Collection {
297303 schema_timestamp :
298304 collectionInfo . update_timestamp_str ||
299305 ( collectionInfo . update_timestamp as string | number | undefined ) ,
300- // Ensure partial_update is passed for upsert operations
301- ...( upsert && ( data as UpsertReq ) . partial_update
302- ? { partial_update : true }
303- : { } ) ,
306+ // Ensure partial_update is passed for explicit partial updates and
307+ // non-REPLACE field_ops. Milvus server also auto-promotes non-REPLACE
308+ // ops, but setting the flag keeps the request self-descriptive.
309+ ...( upsert && isPartialUpdate ? { partial_update : true } : { } ) ,
310+ ...( upsert && fieldOps . length > 0 ? { field_ops : fieldOps } : { } ) ,
304311 } ;
305312 /* istanbul ignore next if */
306313 if ( data . skip_check_schema ) {
@@ -529,6 +536,35 @@ export class Data extends Collection {
529536 return promise ;
530537 }
531538
539+ private normalizeFieldOps (
540+ fieldOps ?: FieldPartialUpdateOp [ ]
541+ ) : { field_name : string ; op : keyof typeof FieldPartialUpdateOpType } [ ] {
542+ if ( ! fieldOps || fieldOps . length === 0 ) {
543+ return [ ] ;
544+ }
545+
546+ return fieldOps . map ( ( { field_name, op } ) => {
547+ if ( ! field_name ) {
548+ throw new Error ( 'field_ops field_name is required' ) ;
549+ }
550+
551+ if ( typeof op === 'number' ) {
552+ const opName = FieldPartialUpdateOpType [ op ] as
553+ | keyof typeof FieldPartialUpdateOpType
554+ | undefined ;
555+ if ( typeof opName === 'undefined' ) {
556+ throw new Error ( `unsupported field partial update op: ${ op } ` ) ;
557+ }
558+ return { field_name, op : opName } ;
559+ }
560+
561+ if ( typeof FieldPartialUpdateOpType [ op ] === 'undefined' ) {
562+ throw new Error ( `unsupported field partial update op: ${ op } ` ) ;
563+ }
564+ return { field_name, op } ;
565+ } ) ;
566+ }
567+
532568 /**
533569 * Delete entities in a Milvus collection.
534570 *
0 commit comments