From 0d14522a8fbd7b6bc1cf47eb18efc9e0e29a1bbd Mon Sep 17 00:00:00 2001 From: Frank <25704248+rakuzen25@users.noreply.github.com> Date: Thu, 26 Dec 2024 17:37:25 +0800 Subject: [PATCH] fix(utils/Format): prevent dynamic field overwrite when building dynamic row see #399 Signed-off-by: Frank <25704248+rakuzen25@users.noreply.github.com> --- milvus/utils/Format.ts | 11 +++++++ test/utils/Format.spec.ts | 65 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/milvus/utils/Format.ts b/milvus/utils/Format.ts index 4a0c77e9..a9cb9d02 100644 --- a/milvus/utils/Format.ts +++ b/milvus/utils/Format.ts @@ -453,6 +453,10 @@ export const buildDynamicRow = ( // iterate through each key in the input data object for (let key in originRow) { + if (key === dynamicFieldName) { + // ignore the dynamic field key for now + continue; + } row[dynamicFieldName] = row[dynamicFieldName] || ({} as JSON); // initialize the dynamic field object if (fieldMap.has(key)) { @@ -467,6 +471,13 @@ export const buildDynamicRow = ( } } + // do this last to prevent any non-deterministic behavior with loop order + if (originRow[dynamicFieldName]) { + // extend the dynamic field object with the input dynamic field object + row[dynamicFieldName] = row[dynamicFieldName] || ({} as JSON); + Object.assign(row[dynamicFieldName] as JSON, originRow[dynamicFieldName]); + } + return row; // return the generated dynamic row object }; diff --git a/test/utils/Format.spec.ts b/test/utils/Format.spec.ts index 23395b8f..0dbad4b9 100644 --- a/test/utils/Format.spec.ts +++ b/test/utils/Format.spec.ts @@ -615,6 +615,71 @@ describe('utils/format', () => { }); }); + it('should return an object with dynamicField key when data contains keys not in fieldsDataMap and an empty dynamicField object', () => { + const dynamicField = 'dynamic'; + const data = { key1: 'value1', key2: 'value2', [dynamicField]: {} }; + const fieldsDataMap = new Map([ + [ + 'key1', + { + name: 'key1', + type: 'VarChar', + data: [{ key1: 'value1' }], + } as _Field, + ], + [ + // this mirrors the dynamic field map in grpc.Data._insert method + dynamicField, + { + name: dynamicField, + type: 'JSON', + data: [], + } as _Field, + ], + ]); + const result = buildDynamicRow(data, fieldsDataMap, dynamicField, []); + expect(result).toEqual({ + key1: 'value1', + [dynamicField]: { key2: 'value2' }, + }); + }); + + it('should return an object with dynamicField key when data contains keys not in fieldsDataMap and a non-empty dynamicField object', () => { + const dynamicField = 'dynamic'; + const data = { + key1: 'value1', + key2: 'value2', + [dynamicField]: { + key2: 'value22', + key3: 'value3', + }, + }; + const fieldsDataMap = new Map([ + [ + 'key1', + { + name: 'key1', + type: 'VarChar', + data: [{ key1: 'value1' }], + } as _Field, + ], + [ + // this mirrors the dynamic field map in grpc.Data._insert method + dynamicField, + { + name: dynamicField, + type: 'JSON', + data: [], + } as _Field, + ], + ]); + const result = buildDynamicRow(data, fieldsDataMap, dynamicField, []); + expect(result).toEqual({ + key1: 'value1', + [dynamicField]: { key2: 'value22', key3: 'value3' }, + }); + }); + it('should return an empty string if no credentials are provided', () => { const authString = getAuthString({}); expect(authString).toEqual('');