@@ -77,6 +77,8 @@ import {
7777 GetQuerySegmentInfoResponse ,
7878 SearchData ,
7979 FloatVector ,
80+ FieldPartialUpdateOpType ,
81+ FieldPartialUpdateOp ,
8082} from '../' ;
8183import { Collection } from './Collection' ;
8284
@@ -142,6 +144,9 @@ export class Data extends Collection {
142144 throw new Error ( ERROR_REASONS . INSERT_CHECK_FIELD_DATA_IS_REQUIRED ) ;
143145 }
144146 const { collection_name } = data ;
147+ const fieldOps = upsert
148+ ? this . normalizeFieldOps ( ( data as UpsertReq ) . field_ops )
149+ : [ ] ;
145150
146151 const describeReq = { collection_name, cache : enable_cache } ;
147152 if ( data . db_name ) {
@@ -228,7 +233,8 @@ export class Data extends Collection {
228233
229234 // Track which fields are present in the original row data for partial updates
230235 const isPartialUpdate =
231- 'partial_update' in data && data . partial_update === true ;
236+ ( 'partial_update' in data && data . partial_update === true ) ||
237+ fieldOps . length > 0 ;
232238 const originalRowKeys = new Set < string > ( ) ;
233239
234240 // Loop through each row and set the corresponding field values in the Map.
@@ -293,10 +299,9 @@ export class Data extends Collection {
293299 schema_timestamp :
294300 collectionInfo . update_timestamp_str ||
295301 ( collectionInfo . update_timestamp as string | number | undefined ) ,
296- // Ensure partial_update is passed for upsert operations
297- ...( upsert && ( data as UpsertReq ) . partial_update
298- ? { partial_update : true }
299- : { } ) ,
302+ // Ensure partial_update is passed for explicit partial updates and field_ops.
303+ ...( upsert && isPartialUpdate ? { partial_update : true } : { } ) ,
304+ ...( upsert && fieldOps . length > 0 ? { field_ops : fieldOps } : { } ) ,
300305 } ;
301306 /* istanbul ignore next if */
302307 if ( data . skip_check_schema ) {
@@ -478,6 +483,35 @@ export class Data extends Collection {
478483 return promise ;
479484 }
480485
486+ private normalizeFieldOps (
487+ fieldOps ?: FieldPartialUpdateOp [ ]
488+ ) : { field_name : string ; op : keyof typeof FieldPartialUpdateOpType } [ ] {
489+ if ( ! fieldOps || fieldOps . length === 0 ) {
490+ return [ ] ;
491+ }
492+
493+ return fieldOps . map ( ( { field_name, op } ) => {
494+ if ( ! field_name ) {
495+ throw new Error ( 'field_ops field_name is required' ) ;
496+ }
497+
498+ if ( typeof op === 'number' ) {
499+ const opName = FieldPartialUpdateOpType [ op ] as
500+ | keyof typeof FieldPartialUpdateOpType
501+ | undefined ;
502+ if ( typeof opName === 'undefined' ) {
503+ throw new Error ( `unsupported field partial update op: ${ op } ` ) ;
504+ }
505+ return { field_name, op : opName } ;
506+ }
507+
508+ if ( typeof FieldPartialUpdateOpType [ op ] === 'undefined' ) {
509+ throw new Error ( `unsupported field partial update op: ${ op } ` ) ;
510+ }
511+ return { field_name, op } ;
512+ } ) ;
513+ }
514+
481515 /**
482516 * Delete entities in a Milvus collection.
483517 *
0 commit comments