Browse Source

support array part1 (#316)

* finish create collection ui

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

* temp

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

* add max capacity

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

* finish create array collection

Signed-off-by: ruiyi.jiang <ruiyi.jiang@zilliz.com>

* refacto

Signed-off-by: ruiyi.jiang <ruiyi.jiang@zilliz.com>

* finish array creation and schema info

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

* remove console

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

* update schema ui

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

* fix binary vector

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

* support import array sample data

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

* update import sample

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

* upgrade node sdk

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

* refector

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

---------

Signed-off-by: ryjiang <jiangruiyi@gmail.com>
Signed-off-by: ruiyi.jiang <ruiyi.jiang@zilliz.com>
ryjiang 1 year ago
parent
commit
6839cd2d6e

+ 1 - 2
client/src/components/advancedSearch/Condition.tsx

@@ -9,8 +9,7 @@ import {
 import CloseIcon from '@material-ui/icons/Close';
 import { ConditionProps, Field } from './Types';
 import CustomSelector from '../customSelector/CustomSelector';
-import { LOGICAL_OPERATORS } from '@/consts';
-import { DataTypeStringEnum } from '@/pages/collections/Types';
+import { LOGICAL_OPERATORS, DataTypeStringEnum } from '@/consts';
 import { formatValue, checkValue } from './utils';
 
 const Condition: FC<ConditionProps> = props => {

+ 1 - 1
client/src/components/advancedSearch/Types.ts

@@ -1,4 +1,4 @@
-import { DataTypeStringEnum } from '../../pages/collections/Types';
+import { DataTypeStringEnum } from '@/consts';
 
 export interface ConditionProps {
   others?: object;

+ 1 - 1
client/src/components/advancedSearch/utils.ts

@@ -1,4 +1,4 @@
-import { DataTypeStringEnum } from '../../pages/collections/Types';
+import { DataTypeStringEnum } from '@/consts';
 
 export const formatValue = (value: string, type: string, operator: string) => {
   let conditionValue: string = ''; //

+ 1 - 1
client/src/components/customInput/Types.ts

@@ -63,7 +63,7 @@ export interface ITextfieldConfig {
   variant: VariantType;
   value?: any;
   label?: string;
-  hiddenLabel?: boolean;
+  hiddenlabel?: boolean;
   size?: SizeType;
   placeholder?: string;
   required?: boolean;

+ 1 - 1
client/src/components/customSelector/Types.ts

@@ -20,7 +20,7 @@ export type CustomSelectorType = SelectProps & {
   variant?: 'filled' | 'outlined' | 'standard';
   labelClass?: string;
   wrapperClass?: string;
-  hiddenLabel?: boolean;
+  hiddenlabel?: boolean;
   size?: 'small' | 'medium' | undefined;
 };
 

+ 27 - 2
client/src/consts/Milvus.ts

@@ -1,10 +1,24 @@
-import { DataTypeEnum } from '@/pages/collections/Types';
-
 export const MILVUS_URL =
   ((window as any)._env_ && (window as any)._env_.MILVUS_URL) || '';
 
 export const DYNAMIC_FIELD = `$meta`;
 
+export enum DataTypeEnum {
+  Bool = 1,
+  Int8 = 2,
+  Int16 = 3,
+  Int32 = 4,
+  Int64 = 5,
+  Float = 10,
+  Double = 11,
+  String = 20,
+  VarChar = 21,
+  JSON = 23,
+  BinaryVector = 100,
+  FloatVector = 101,
+  Array = 22,
+}
+
 export enum INDEX_TYPES_ENUM {
   AUTO_INDEX = 'AUTO_INDEX',
   IVF_FLAT = 'IVF_FLAT',
@@ -272,6 +286,14 @@ export enum MILVUS_DEPLOY_MODE {
   STANDALONE = 'STANDALONE',
 }
 
+export enum ConsistencyLevelEnum {
+  Strong = 'Strong',
+  Session = 'Session', // default in PyMilvus
+  Bounded = 'Bounded',
+  Eventually = 'Eventually',
+  Customized = 'Customized', // Users pass their own `guarantee_timestamp`.
+}
+
 export enum DataTypeStringEnum {
   Bool = 'Bool',
   Int8 = 'Int8',
@@ -285,9 +307,12 @@ export enum DataTypeStringEnum {
   JSON = 'JSON',
   BinaryVector = 'BinaryVector',
   FloatVector = 'FloatVector',
+  Array = 'Array',
+  None = 'None',
 }
 
 export const NONE_INDEXABLE_DATA_TYPES = [
   DataTypeStringEnum.Bool,
   DataTypeStringEnum.JSON,
+  DataTypeStringEnum.Array,
 ];

+ 4 - 0
client/src/consts/default.ts

@@ -0,0 +1,4 @@
+export const DEFAULT_ATTU_MAX_CAPACITY = 64;
+export const DEFAULT_ATTU_VARCHAR_MAX_LENGTH = 32;
+export const DEFAULT_ATTU_ELEMENT_TYPE = 4; // int32
+export const DEFAULT_ATTU_DIM = 128;

+ 1 - 0
client/src/consts/index.ts

@@ -3,3 +3,4 @@ export * from './Localstorage';
 export * from './Milvus';
 export * from './Prometheus';
 export * from './Util';
+export * from './default';

+ 7 - 1
client/src/http/Field.ts

@@ -1,4 +1,4 @@
-import { DataTypeStringEnum } from '../pages/collections/Types';
+import { DataTypeStringEnum } from '@/consts';
 import { FieldData } from '../pages/schema/Types';
 import BaseModel from './BaseModel';
 
@@ -11,6 +11,7 @@ export class FieldHttp extends BaseModel implements FieldData {
   name!: string;
   description!: string;
   autoID!: boolean;
+  element_type!:  DataTypeStringEnum;
 
   constructor(props: {}) {
     super(props);
@@ -65,4 +66,9 @@ export class FieldHttp extends BaseModel implements FieldData {
       this.type_params.find(item => item.key === 'max_length')?.value || ''
     );
   }
+  get _maxCapacity() {
+    return (
+      this.type_params.find(item => item.key === 'max_capacity')?.value || ''
+    );
+  }
 }

+ 1 - 1
client/src/i18n/cn/collection.ts

@@ -53,7 +53,7 @@ const collectionTrans = {
   idType: 'Type',
   dimension: 'Dimension',
   dimensionTooltip: 'Only vector type has dimension',
-  dimensionMutipleWarning: 'Dimension should be 8 multiple',
+  dimensionMultipleWarning: 'Dimension should be 8 multiple',
   dimensionPositiveWarning: 'Positive number only',
   newBtn: 'add new field',
   nameLengthWarning: 'Name length should be less than 256',

+ 2 - 1
client/src/i18n/en/collection.ts

@@ -45,6 +45,7 @@ const collectionTrans = {
   consistencyLevel: 'Consistency Level',
   description: 'Description',
   fieldType: 'Type',
+  elementType: 'Array Type',
   vectorFieldType: 'Vector Field Type',
   fieldName: 'Field',
   idFieldName: 'Primary Key Field',
@@ -56,7 +57,7 @@ const collectionTrans = {
   idType: 'Type',
   dimension: 'Dimension',
   dimensionTooltip: 'Only vector type has dimension',
-  dimensionMutipleWarning: 'Dimension should be 8 multiple',
+  dimensionMultipleWarning: 'Dimension should be 8 multiple',
   dimensionPositiveWarning: 'Positive number only',
   newBtn: 'add new field',
   nameLengthWarning: 'Name length should be less than 256',

+ 5 - 1
client/src/pages/collections/Constants.ts

@@ -1,5 +1,5 @@
 import { KeyValuePair } from '../../types/Common';
-import { DataTypeEnum, ConsistencyLevelEnum } from './Types';
+import { DataTypeEnum, ConsistencyLevelEnum } from '@/consts';
 
 export const CONSISTENCY_LEVEL_OPTIONS: KeyValuePair[] = [
   {
@@ -69,6 +69,10 @@ export const ALL_OPTIONS: KeyValuePair[] = [
     label: 'JSON',
     value: DataTypeEnum.JSON,
   },
+  {
+    label: 'Array',
+    value: DataTypeEnum.Array,
+  },
 ];
 
 export const AUTO_ID_OPTIONS: KeyValuePair[] = [

+ 154 - 58
client/src/pages/collections/CreateFields.tsx

@@ -23,12 +23,14 @@ import {
   PRIMARY_FIELDS_OPTIONS,
   VECTOR_FIELDS_OPTIONS,
 } from './Constants';
+import { CreateFieldsProps, CreateFieldType, Field } from './Types';
+import { DataTypeEnum } from '@/consts';
 import {
-  CreateFieldsProps,
-  CreateFieldType,
-  DataTypeEnum,
-  Field,
-} from './Types';
+  DEFAULT_ATTU_DIM,
+  DEFAULT_ATTU_MAX_CAPACITY,
+  DEFAULT_ATTU_VARCHAR_MAX_LENGTH,
+  DEFAULT_ATTU_ELEMENT_TYPE,
+} from '@/consts';
 
 const useStyles = makeStyles((theme: Theme) => ({
   optionalWrapper: {
@@ -155,22 +157,35 @@ const CreateFields: FC<CreateFieldsProps> = ({
   );
 
   const getSelector = (
-    type: 'all' | 'vector',
+    type: 'all' | 'vector' | 'element' | 'primaryKey',
     label: string,
     value: number,
-    onChange: (value: DataTypeEnum) => void,
-    options?: any[]
+    onChange: (value: DataTypeEnum) => void
   ) => {
+    let _options = ALL_OPTIONS;
+    switch (type) {
+      case 'primaryKey':
+        _options = PRIMARY_FIELDS_OPTIONS;
+        break;
+      case 'all':
+        _options = ALL_OPTIONS;
+        break;
+      case 'vector':
+        _options = VECTOR_FIELDS_OPTIONS;
+        break;
+      case 'element':
+        _options = ALL_OPTIONS.filter(
+          d => d.label !== 'Array' && d.label !== 'JSON'
+        );
+        break;
+      default:
+        break;
+    }
+
     return (
       <CustomSelector
         wrapperClass={classes.select}
-        options={
-          options
-            ? options
-            : type === 'all'
-            ? ALL_OPTIONS
-            : VECTOR_FIELDS_OPTIONS
-        }
+        options={_options}
         size="small"
         onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
           onChange(e.target.value as DataTypeEnum);
@@ -267,7 +282,7 @@ const CreateFields: FC<CreateFieldsProps> = ({
         value,
         rule: 'positiveNumber',
       });
-      const isMutiple = getCheckResult({
+      const isMultiple = getCheckResult({
         value,
         rule: 'multiple',
         extraParam: {
@@ -276,7 +291,7 @@ const CreateFields: FC<CreateFieldsProps> = ({
       });
       if (field.data_type === DataTypeEnum.BinaryVector) {
         return {
-          isMutiple,
+          isMultiple,
           isPositive,
         };
       }
@@ -289,10 +304,10 @@ const CreateFields: FC<CreateFieldsProps> = ({
       value: field.dimension as number,
       inputClassName: classes.numberBox,
       handleChange: (value: string) => {
-        const { isPositive, isMutiple } = validateDimension(value);
+        const { isPositive, isMultiple } = validateDimension(value);
         const isValid =
           field.data_type === DataTypeEnum.BinaryVector
-            ? !!isMutiple && isPositive
+            ? !!isMultiple && isPositive
             : isPositive;
 
         changeFields(field.id!, 'dimension', `${value}`);
@@ -305,9 +320,9 @@ const CreateFields: FC<CreateFieldsProps> = ({
       },
       type: 'number',
       validate: (value: any) => {
-        const { isPositive, isMutiple } = validateDimension(value);
-        if (isMutiple === false) {
-          return collectionTrans('dimensionMutipleWarning');
+        const { isPositive, isMultiple } = validateDimension(value);
+        if (isMultiple === false) {
+          return collectionTrans('dimensionMultipleWarning');
         }
 
         return isPositive ? ' ' : collectionTrans('dimensionPositiveWarning');
@@ -316,9 +331,13 @@ const CreateFields: FC<CreateFieldsProps> = ({
   };
 
   const generateMaxLength = (field: Field) => {
+    // update data if needed
+    if (typeof field.max_length === 'undefined') {
+      changeFields(field.id!, 'max_length', DEFAULT_ATTU_VARCHAR_MAX_LENGTH);
+    }
     return getInput({
       label: 'Max Length',
-      value: field.max_length!,
+      value: field.max_length! || DEFAULT_ATTU_VARCHAR_MAX_LENGTH,
       type: 'number',
       inputClassName: classes.maxLength,
       handleChange: (value: string) =>
@@ -344,7 +363,36 @@ const CreateFields: FC<CreateFieldsProps> = ({
     });
   };
 
-  const generateParitionKeyToggle = (field: Field, fields: Field[]) => {
+  const generateMaxCapacity = (field: Field) => {
+    return getInput({
+      label: 'Max Capacity',
+      value: field.max_capacity || DEFAULT_ATTU_MAX_CAPACITY,
+      type: 'number',
+      inputClassName: classes.maxLength,
+      handleChange: (value: string) =>
+        changeFields(field.id!, 'max_capacity', value),
+      validate: (value: any) => {
+        if (value === null) return ' ';
+        const isEmptyValid = checkEmptyValid(value);
+        const isRangeValid = checkRange({
+          value,
+          min: 1,
+          max: 4096,
+          type: 'number',
+        });
+        return !isEmptyValid
+          ? warningTrans('requiredOnly')
+          : !isRangeValid
+          ? warningTrans('range', {
+              min: 1,
+              max: 4096,
+            })
+          : ' ';
+      },
+    });
+  };
+
+  const generatePartitionKeyToggle = (field: Field, fields: Field[]) => {
     return (
       <FormControlLabel
         control={
@@ -379,16 +427,42 @@ const CreateFields: FC<CreateFieldsProps> = ({
     );
   };
 
-  const changeFields = (id: string, key: string, value: any) => {
+  const changeFields = (id: string, key: keyof Field, value: any) => {
     const newFields = fields.map(f => {
       if (f.id !== id) {
         return f;
       }
-      return {
+
+      const updatedField = {
         ...f,
         [key]: value,
       };
+
+      // remove array params, if not array
+      if (updatedField.data_type !== DataTypeEnum.Array) {
+        delete updatedField.max_capacity;
+        delete updatedField.element_type;
+      }
+
+      // remove varchar params, if not varchar
+      if (
+        updatedField.data_type !== DataTypeEnum.VarChar &&
+        updatedField.element_type !== DataTypeEnum.VarChar
+      ) {
+        delete updatedField.max_length;
+      }
+
+      // remove dimension, if not vector
+      if (
+        updatedField.data_type !== DataTypeEnum.FloatVector &&
+        updatedField.data_type !== DataTypeEnum.BinaryVector
+      ) {
+        delete updatedField.dimension;
+      }
+
+      return updatedField;
     });
+
     setFields(newFields);
   };
 
@@ -400,8 +474,7 @@ const CreateFields: FC<CreateFieldsProps> = ({
       is_primary_key: false,
       description: '',
       isDefault: false,
-      dimension: '128',
-      max_length: null,
+      dimension: DEFAULT_ATTU_DIM,
       id,
     };
     const newValidation = {
@@ -430,7 +503,7 @@ const CreateFields: FC<CreateFieldsProps> = ({
       <div className={`${classes.rowWrapper}`}>
         {generateFieldName(field, collectionTrans('idFieldName'))}
         {getSelector(
-          'vector',
+          'primaryKey',
           `${collectionTrans('idType')} `,
           field.data_type,
           (value: DataTypeEnum) => {
@@ -438,8 +511,7 @@ const CreateFields: FC<CreateFieldsProps> = ({
             if (value === DataTypeEnum.VarChar) {
               setAutoID(false);
             }
-          },
-          PRIMARY_FIELDS_OPTIONS
+          }
         )}
         {generateDesc(field)}
 
@@ -476,39 +548,48 @@ const CreateFields: FC<CreateFieldsProps> = ({
     index: number
   ): ReactElement => {
     return (
-      <>
-        <div className={`${classes.rowWrapper}`}>
-          {generateFieldName(field, collectionTrans('vectorFieldName'))}
-
-          {getSelector(
-            'vector',
-            `${collectionTrans('vectorType')} `,
-            field.data_type,
-            (value: DataTypeEnum) => changeFields(field.id!, 'data_type', value)
-          )}
-          {generateDesc(field)}
-
-          {generateDimension(field)}
-
-          <IconButton
-            onClick={() => handleAddNewField(index)}
-            classes={{ root: classes.iconBtn }}
-            aria-label="add"
-          >
-            <AddIcon />
-          </IconButton>
-        </div>
-      </>
+      <div className={`${classes.rowWrapper}`}>
+        {generateFieldName(field, collectionTrans('vectorFieldName'))}
+
+        {getSelector(
+          'vector',
+          `${collectionTrans('vectorType')} `,
+          field.data_type,
+          (value: DataTypeEnum) => changeFields(field.id!, 'data_type', value)
+        )}
+        {generateDesc(field)}
+
+        {generateDimension(field)}
+
+        <IconButton
+          onClick={() => handleAddNewField(index)}
+          classes={{ root: classes.iconBtn }}
+          aria-label="add"
+        >
+          <AddIcon />
+        </IconButton>
+      </div>
     );
   };
 
-  const generateNumberRow = (
+  const generateNonRequiredRow = (
     field: Field,
     index: number,
     fields: Field[]
   ): ReactElement => {
     const isVarChar = field.data_type === DataTypeEnum.VarChar;
     const isInt64 = field.data_type === DataTypeEnum.Int64;
+    const isArray = field.data_type === DataTypeEnum.Array;
+    const isElementVarChar = field.element_type === DataTypeEnum.VarChar;
+
+    // handle default values
+    if (isArray && typeof field.element_type === 'undefined') {
+      changeFields(field.id!, 'element_type', DEFAULT_ATTU_ELEMENT_TYPE);
+    }
+    if (isArray && typeof field.max_capacity === 'undefined') {
+      changeFields(field.id!, 'max_capacity', DEFAULT_ATTU_MAX_CAPACITY);
+    }
+
     return (
       <div className={`${classes.rowWrapper}`}>
         {generateFieldName(field)}
@@ -518,10 +599,25 @@ const CreateFields: FC<CreateFieldsProps> = ({
           field.data_type,
           (value: DataTypeEnum) => changeFields(field.id!, 'data_type', value)
         )}
+
+        {isArray
+          ? getSelector(
+              'element',
+              collectionTrans('elementType'),
+              field.element_type || DEFAULT_ATTU_ELEMENT_TYPE,
+              (value: DataTypeEnum) =>
+                changeFields(field.id!, 'element_type', value)
+            )
+          : null}
+
+        {isArray ? generateMaxCapacity(field) : null}
+        {isVarChar || isElementVarChar ? generateMaxLength(field) : null}
+
         {generateDesc(field)}
 
-        {isVarChar && generateMaxLength(field)}
-        {(isVarChar || isInt64) && generateParitionKeyToggle(field, fields)}
+        {isVarChar || isInt64
+          ? generatePartitionKeyToggle(field, fields)
+          : null}
         <IconButton
           onClick={() => {
             handleAddNewField(index);
@@ -589,7 +685,7 @@ const CreateFields: FC<CreateFieldsProps> = ({
     }
 
     // use number as default createType
-    return generateNumberRow(field, index, fields);
+    return generateNonRequiredRow(field, index, fields);
   };
 
   return (

+ 4 - 39
client/src/pages/collections/Types.ts

@@ -1,6 +1,6 @@
 import { Dispatch, ReactElement, SetStateAction } from 'react';
 import { ChildrenStatusType } from '@/components/status/Types';
-import { LOADING_STATE } from '@/consts';
+import { LOADING_STATE, DataTypeEnum } from '@/consts';
 import { FieldData } from '../schema/Types';
 
 export interface CollectionData {
@@ -52,43 +52,6 @@ export interface CollectionCreateParam {
   consistency_level: string;
 }
 
-export enum ConsistencyLevelEnum {
-  Strong = 'Strong',
-  Session = 'Session', // default in PyMilvus
-  Bounded = 'Bounded',
-  Eventually = 'Eventually',
-  Customized = 'Customized', // Users pass their own `guarantee_timestamp`.
-}
-
-export enum DataTypeEnum {
-  Bool = 1,
-  Int8 = 2,
-  Int16 = 3,
-  Int32 = 4,
-  Int64 = 5,
-  Float = 10,
-  Double = 11,
-  String = 20,
-  VarChar = 21,
-  JSON = 23,
-  BinaryVector = 100,
-  FloatVector = 101,
-}
-export enum DataTypeStringEnum {
-  Bool = 'Bool',
-  Int8 = 'Int8',
-  Int16 = 'Int16',
-  Int32 = 'Int32',
-  Int64 = 'Int64',
-  Float = 'Float',
-  Double = 'Double',
-  String = 'String',
-  VarChar = 'VarChar',
-  JSON = 'JSON',
-  BinaryVector = 'BinaryVector',
-  FloatVector = 'FloatVector',
-}
-
 export interface Field {
   name: string | null;
   data_type: DataTypeEnum;
@@ -103,7 +66,9 @@ export interface Field {
     max_length?: string | number;
   };
   createType?: CreateFieldType;
-  max_length?: string | number | null;
+  element_type?: DataTypeEnum;
+  max_length?: string | number;
+  max_capacity?: string | number;
   autoID?: boolean;
 }
 

+ 14 - 9
client/src/pages/dialogs/CreateCollectionDialog.tsx

@@ -13,13 +13,12 @@ import { ITextfieldConfig } from '@/components/customInput/Types';
 import { rootContext } from '@/context';
 import { useFormValidation } from '@/hooks';
 import { formatForm, TypeEnum } from '@/utils';
+import { DataTypeEnum, ConsistencyLevelEnum, DEFAULT_ATTU_DIM } from '@/consts';
 import CreateFields from '../collections/CreateFields';
 import { CollectionHttp } from '@/http';
 import {
   CollectionCreateParam,
   CollectionCreateProps,
-  DataTypeEnum,
-  ConsistencyLevelEnum,
   Field,
 } from '../collections/Types';
 import { CONSISTENCY_LEVEL_OPTIONS } from '../collections/Constants';
@@ -82,14 +81,13 @@ const CreateCollectionDialog: FC<CollectionCreateProps> = ({ onCreate }) => {
       name: null, // we need hide helpertext at first time, so we use null to detect user enter input or not.
       description: '',
       isDefault: true,
-      max_length: null,
       id: '1',
     },
     {
       data_type: DataTypeEnum.FloatVector,
       is_primary_key: false,
       name: null,
-      dimension: '128',
+      dimension: DEFAULT_ATTU_DIM,
       description: '',
       isDefault: true,
       id: '2',
@@ -205,15 +203,21 @@ const CreateCollectionDialog: FC<CollectionCreateProps> = ({ onCreate }) => {
           is_primary_key: v.is_primary_key,
           is_partition_key: v.is_partition_key,
           data_type: v.data_type,
-          dimension: vectorType.includes(v.data_type)
-            ? Number(v.dimension)
-            : undefined,
         };
 
         // if we need
+        if (typeof v.dimension !== undefined) {
+          data.dimension = Number(v.dimension);
+        }
         if (typeof v.max_length === 'number') {
           data.max_length = Number(v.max_length);
         }
+        if (typeof v.element_type !== 'undefined') {
+          data.element_type = Number(v.element_type);
+        }
+        if (typeof v.max_capacity !== 'undefined') {
+          data.max_capacity = Number(v.max_capacity);
+        }
 
         v.is_primary_key && (data.autoID = form.autoID);
 
@@ -225,7 +229,8 @@ const CreateCollectionDialog: FC<CollectionCreateProps> = ({ onCreate }) => {
                 dim: Number(data.dimension!),
               },
             }
-          : v.data_type === DataTypeEnum.VarChar
+          : v.data_type === DataTypeEnum.VarChar ||
+            v.element_type === DataTypeEnum.VarChar
           ? {
               ...v,
               type_params: {
@@ -297,7 +302,7 @@ const CreateCollectionDialog: FC<CollectionCreateProps> = ({ onCreate }) => {
             onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
               setConsistencyLevel(e.target.value as ConsistencyLevelEnum);
             }}
-            hiddenLabel={true}
+            hiddenlabel={true}
             value={consistencyLevel}
             variant="filled"
           />

+ 2 - 1
client/src/pages/preview/Preview.tsx

@@ -8,10 +8,11 @@ import {
   INDEX_CONFIG,
   DEFAULT_SEARCH_PARAM_VALUE_MAP,
   DYNAMIC_FIELD,
+  DataTypeEnum,
+  DataTypeStringEnum,
 } from '@/consts';
 import { ToolBarConfig } from '@/components/grid/Types';
 import CustomToolBar from '@/components/grid/ToolBar';
-import { DataTypeEnum, DataTypeStringEnum } from '@/pages/collections/Types';
 import { getQueryStyles } from '../query/Styles';
 
 const Preview: FC<{

+ 1 - 2
client/src/pages/query/Query.tsx

@@ -16,10 +16,9 @@ import Filter from '@/components/advancedSearch';
 import DeleteTemplate from '@/components/customDialog/DeleteDialogTemplate';
 import CustomToolBar from '@/components/grid/ToolBar';
 // import { CustomDatePicker } from '@/components/customDatePicker/CustomDatePicker';
-import { DataTypeStringEnum } from '../collections/Types';
 import { getLabelDisplayedRows } from '../search/Utils';
 import { getQueryStyles } from './Styles';
-import { DYNAMIC_FIELD } from '@/consts';
+import { DYNAMIC_FIELD, DataTypeStringEnum } from '@/consts';
 
 const Query: FC<{
   collectionName: string;

+ 2 - 1
client/src/pages/schema/Create.tsx

@@ -10,6 +10,8 @@ import {
   INDEX_OPTIONS_MAP,
   METRIC_TYPES_VALUES,
   INDEX_TYPES_ENUM,
+  DataTypeEnum,
+  DataTypeStringEnum,
 } from '@/consts';
 import { useFormValidation } from '@/hooks';
 import { getCreateIndexJSCode } from '@/utils/code/Js';
@@ -21,7 +23,6 @@ import {
   computMilvusRecommonds,
   formatSize,
 } from '@/utils';
-import { DataTypeEnum, DataTypeStringEnum } from '../collections/Types';
 import CreateForm from './CreateForm';
 import SizingInfo from './SizingInfo';
 import { IndexType, IndexExtraParam } from './Types';

+ 3 - 40
client/src/pages/schema/Schema.tsx

@@ -6,6 +6,7 @@ import { useTranslation } from 'react-i18next';
 import { usePaginationHook } from '@/hooks';
 import icons from '@/components/icons/Icons';
 import { FieldHttp, IndexHttp } from '@/http';
+import { formatFieldType } from '@/utils';
 import { FieldView } from './Types';
 import IndexTypeElement from './IndexTypeElement';
 
@@ -139,27 +140,8 @@ const Schema: FC<{
                 ) : null}
               </div>
             ),
-            _fieldTypeElement: (
-              <div className={classes.nameWrapper}>
-                {f._fieldType}
-                {f._dimension ? (
-                  <Chip
-                    className={classes.chip}
-                    size="small"
-                    label={`dim: ${f._dimension}`}
-                    variant="outlined"
-                  />
-                ) : null}
-                {f._maxLength && f._maxLength !== 'null' ? (
-                  <Chip
-                    className={classes.chip}
-                    size="small"
-                    label={`max: ${f._maxLength}`}
-                    variant="outlined"
-                  />
-                ) : null}
-              </div>
-            ),
+            // Array<VarChar(64)>[Capacity]
+            _fieldTypeElement: formatFieldType(f),
             _indexParamElement: (
               <div className={classes.paramWrapper}>
                 {f._indexParameterPairs?.length > 0 ? (
@@ -222,25 +204,6 @@ const Schema: FC<{
       disablePadding: false,
       label: collectionTrans('fieldType'),
     },
-    // {
-    //   id: '_dimension',
-    //   align: 'left',
-    //   disablePadding: false,
-    //   label: (
-    //     <span className="flex-center">
-    //       {collectionTrans('dimension')}
-    //       <CustomToolTip title={collectionTrans('dimensionTooltip')}>
-    //         <InfoIcon classes={{ root: classes.icon }} />
-    //       </CustomToolTip>
-    //     </span>
-    //   ),
-    // },
-    // {
-    //   id: '_maxLength',
-    //   align: 'left',
-    //   disablePadding: true,
-    //   label: collectionTrans('maxLength'),
-    // },
     {
       id: '_indexName',
       align: 'left',

+ 3 - 2
client/src/pages/schema/Types.ts

@@ -1,6 +1,5 @@
 import { ReactElement } from 'react';
-import { MetricType, INDEX_TYPES_ENUM } from '@/consts';
-import { DataTypeStringEnum } from '../collections/Types';
+import { MetricType, INDEX_TYPES_ENUM, DataTypeStringEnum } from '@/consts';
 
 export interface Field {
   data_type: DataTypeStringEnum;
@@ -22,6 +21,8 @@ export interface FieldData {
   _dimension: string;
   _desc: string;
   _maxLength: string;
+  _maxCapacity: string;
+  element_type: string;
 }
 
 export interface FieldView extends FieldData, IndexView {

+ 2 - 1
client/src/pages/search/VectorSearch.tsx

@@ -31,11 +31,12 @@ import {
   LOADING_STATE,
   DEFAULT_METRIC_VALUE_MAP,
   DYNAMIC_FIELD,
+  DataTypeEnum,
 } from '@/consts';
 import { getLabelDisplayedRows } from './Utils';
 import SearchParams from './SearchParams';
 import { getVectorSearchStyles } from './Styles';
-import { CollectionData, DataTypeEnum } from '../collections/Types';
+import { CollectionData } from '../collections/Types';
 import { TOP_K_OPTIONS } from './Constants';
 import { FieldOption, SearchResultView, VectorSearchParam } from './Types';
 

+ 1 - 2
client/src/utils/Form.ts

@@ -1,7 +1,6 @@
 import { Option } from '@/components/customSelector/Types';
-import { METRIC_TYPES_VALUES } from '@/consts';
+import { METRIC_TYPES_VALUES, DataTypeStringEnum } from '@/consts';
 import { IForm } from '@/hooks';
-import { DataTypeStringEnum } from '@/pages/collections/Types';
 import { IndexType } from '@/pages/schema/Types';
 
 interface IInfo {

+ 23 - 5
client/src/utils/Format.ts

@@ -2,12 +2,10 @@ import {
   BYTE_UNITS,
   DEFAULT_MILVUS_PORT,
   DEFAULT_PROMETHEUS_PORT,
-} from '@/consts';
-import {
-  CreateFieldType,
   DataTypeEnum,
-  Field,
-} from '@/pages/collections/Types';
+} from '@/consts';
+import { CreateFieldType, Field } from '@/pages/collections/Types';
+import { FieldView } from '@/pages/schema/Types';
 
 /**
  * transform large capacity to capacity in b.
@@ -228,3 +226,23 @@ export const formatUtcToMilvus = (bigNumber: number) => {
   const milvusTimeStamp = BigInt(bigNumber) << BigInt(18);
   return milvusTimeStamp.toString();
 };
+
+/**
+ * Format field
+ * @param bigNumber
+ * @returns
+ */
+export const formatFieldType = (field: FieldView) => {
+  const { _fieldType, element_type, _maxLength, _maxCapacity, _dimension } =
+    field;
+
+  const elementType =
+    element_type !== 'None'
+      ? `<${element_type}${_maxLength ? `(${_maxLength})` : ''}>`
+      : '';
+  const maxCapacity = _maxCapacity ? `[${_maxCapacity}]` : '';
+  const dimension = _dimension ? `(${_dimension})` : '';
+  const maxLength = _fieldType === 'VarChar' ? `(${_maxLength})` : '';
+
+  return `${_fieldType}${elementType}${maxCapacity}${dimension}${maxLength}`;
+};

+ 2 - 2
client/src/utils/Validation.ts

@@ -45,8 +45,8 @@ export enum TypeEnum {
   'number' = 'number',
 }
 
-export const checkEmptyValid = (value: string): boolean => {
-  return value.trim() !== '';
+export const checkEmptyValid = (value: string | number): boolean => {
+  return String(value).trim() !== '';
 };
 
 export const checkEmail = (value: string): boolean => {

+ 1 - 2
client/src/utils/search.ts

@@ -1,7 +1,6 @@
 import { Field } from '../components/advancedSearch/Types';
-import { DataTypeEnum, DataTypeStringEnum } from '../pages/collections/Types';
 import { FieldData, IndexType, IndexView } from '../pages/schema/Types';
-import { INDEX_TYPES_ENUM } from '@/consts';
+import { INDEX_TYPES_ENUM, DataTypeEnum, DataTypeStringEnum } from '@/consts';
 import { FieldOption } from '../types/SearchTypes';
 
 /**

+ 1 - 1
server/package.json

@@ -12,7 +12,7 @@
     "url": "https://github.com/zilliztech/attu"
   },
   "dependencies": {
-    "@zilliz/milvus2-sdk-node": "2.3.3",
+    "@zilliz/milvus2-sdk-node": "2.3.4",
     "axios": "^1.4.0",
     "chalk": "^4.1.2",
     "class-sanitizer": "^1.0.1",

+ 1 - 0
server/src/collections/collections.controller.ts

@@ -288,6 +288,7 @@ export class CollectionController {
         collection_name: name,
         ...data,
       });
+
       // const queryResultList = result.data;
       const queryResultLength = result.data.length;
       // const startNum = page * limit;

+ 1 - 0
server/src/collections/collections.service.ts

@@ -140,6 +140,7 @@ export class CollectionsService {
   ) {
     const now = Date.now();
     const res = await this.milvusService.client.query(data);
+
     const after = Date.now();
 
     throwErrorFromSDK(res.status);

+ 49 - 57
server/src/utils/Helper.ts

@@ -1,44 +1,73 @@
-import {
-  KeyValuePair,
-  FieldSchema,
-} from '@zilliz/milvus2-sdk-node/dist/milvus/types';
+import { KeyValuePair, FieldSchema } from '@zilliz/milvus2-sdk-node';
 
 export const findKeyValue = (obj: KeyValuePair[], key: string) =>
   obj.find(v => v.key === key)?.value;
 
+const MAX_INT8 = 127;
+const MAX_INT16 = 32767;
+const MAX_INT32 = 214748364;
+const MAX_INT64 = 214748364;
+const CHARACTERS =
+  'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
+const CHARACTERS_LENGTH = CHARACTERS.length;
+const MAX_KEYS = 10;
+
+export const makeRandomId = (length: number): string =>
+  Array.from({ length })
+    .map(() => CHARACTERS.charAt(makeRandomInt(CHARACTERS_LENGTH)))
+    .join('');
+
 export const makeDynamicBool = () => Math.random() > 0.5;
-export const makeRandomInt = () => Math.floor(Math.random() * 127);
+export const makeRandomInt = (max: number) => Math.floor(Math.random() * max);
 export const makeFloat = () => Math.random();
 
-export const genDataByType = ({ data_type, type_params }: FieldSchema) => {
+export const genDataByType = (field: FieldSchema): any => {
+  const { data_type, type_params, element_type } = field;
   switch (data_type) {
     case 'Bool':
       return makeDynamicBool();
     case 'Int8':
-      return makeRandomInt();
+      return makeRandomInt(MAX_INT8);
     case 'Int16':
-      return Math.floor(Math.random() * 32767);
+      return makeRandomInt(MAX_INT16);
     case 'Int32':
-      return Math.floor(Math.random() * 214748364);
+      return makeRandomInt(MAX_INT32);
     case 'Int64':
-      return Math.floor(Math.random() * 214748364);
+      return makeRandomInt(MAX_INT64);
     case 'Float':
-      return makeFloat();
     case 'Double':
       return makeFloat();
     case 'FloatVector':
-      return Array.from({ length: (type_params as any)[0].value }).map(() =>
-        Math.random()
-      );
     case 'BinaryVector':
-      return Array.from({ length: (type_params as any)[0].value / 8 }).map(
-        () => (Math.random() > 0.5 ? 1 : 0)
-      );
+      return Array.from({
+        length:
+          Number(type_params[0].value) / (data_type === 'BinaryVector' ? 8 : 1),
+      }).map(makeFloat);
     case 'VarChar':
-      return makeRandomId((type_params as any)[0].value);
+      return makeRandomId(Number(findKeyValue(type_params, 'max_length')));
     case 'JSON':
       return makeRandomJSON();
+    case 'Array':
+      return Array.from({
+        length: Number(findKeyValue(type_params, 'max_capacity')),
+      }).map(() => genDataByType({ ...field, data_type: element_type }));
+  }
+};
+
+export const makeRandomJSON = () => {
+  const obj: any = {};
+  const numKeys = makeRandomInt(MAX_KEYS) + 1;
+  for (let i = 0; i < numKeys; i++) {
+    const key = `key${i}`;
+    const value = Math.random() < 0.5 ? makeRandomInt(100) : `value${i}`;
+    obj[key] = value;
   }
+
+  const arrayKey = 'containsKey';
+  const arrayLength = makeRandomInt(MAX_KEYS) + 1;
+  obj[arrayKey] = Array.from({ length: arrayLength }, () => makeRandomInt(100));
+
+  return obj;
 };
 
 export const genRow = (
@@ -54,7 +83,7 @@ export const genRow = (
 
   if (enableDynamicField) {
     result.dynamicBool = makeDynamicBool();
-    result.dynamicInt = makeRandomInt();
+    result.dynamicInt = makeRandomInt(MAX_INT8);
     result.dynamicJSON = makeRandomJSON();
   }
   return result;
@@ -64,41 +93,4 @@ export const genRows = (
   fields: FieldSchema[],
   size: number,
   enableDynamicField: boolean = false
-) => {
-  const result = [];
-  for (let i = 0; i < size; i++) {
-    result[i] = genRow(fields, enableDynamicField);
-  }
-  return result;
-};
-
-export const makeRandomId = (length: number): string => {
-  let result = '';
-  const characters =
-    'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
-  const charactersLength = characters.length;
-  for (let i = 0; i < length; i++) {
-    result += characters.charAt(Math.floor(Math.random() * charactersLength));
-  }
-  return result;
-};
-
-export const makeRandomJSON = () => {
-  const obj: any = {};
-  const numKeys = Math.floor(Math.random() * 10) + 1; // generate a random number of keys between 1 and 10
-  for (let i = 0; i < numKeys; i++) {
-    const key = `key${i}`;
-    const value =
-      Math.random() < 0.5 ? Math.floor(Math.random() * 100) : `value${i}`; // randomly choose between a number or a string value
-    obj[key] = value;
-  }
-
-  const arrayKey = 'containsKey';
-  const arrayLength = Math.floor(Math.random() * 10) + 1; // generate a random length for the array between 1 and 10
-  const randomArray = Array.from({ length: arrayLength }, () =>
-    Math.floor(Math.random() * 100)
-  );
-  obj[arrayKey] = randomArray;
-
-  return obj;
-};
+) => Array.from({ length: size }, () => genRow(fields, enableDynamicField));

+ 4 - 14
server/yarn.lock

@@ -1208,14 +1208,13 @@
   resolved "https://registry.yarnpkg.com/@xmldom/xmldom/-/xmldom-0.8.9.tgz#b6ef7457e826be8049667ae673eda7876eb049be"
   integrity sha512-4VSbbcMoxc4KLjb1gs96SRmi7w4h1SF+fCoiK0XaQX62buCc1G5d0DC5bJ9xJBNPDSVCmIrcl8BiYxzjrqaaJA==
 
-"@zilliz/milvus2-sdk-node@2.3.3":
-  version "2.3.3"
-  resolved "https://registry.yarnpkg.com/@zilliz/milvus2-sdk-node/-/milvus2-sdk-node-2.3.3.tgz#79ed286652b396c95693c4703bd67363ff855f0e"
-  integrity sha512-fUu1/5aENWem7XqoZ5LBOMoi0B7KPOoVD9Ga0gqHzbtoq8No0BRJDLkdv4xMubNnL3xPjBoNrYE5A615MG0tEw==
+"@zilliz/milvus2-sdk-node@2.3.4":
+  version "2.3.4"
+  resolved "https://registry.yarnpkg.com/@zilliz/milvus2-sdk-node/-/milvus2-sdk-node-2.3.4.tgz#b9bec5e3fcf944342e713bce4274d030cdeb9482"
+  integrity sha512-KdUkNBn/kDc0UThtGGCbf36ku89FhIfFaPQvL3xVjF5yT1WHxrZC+8zisOZgv7VI/MrZyYLIVwdoq4I5x6DfcA==
   dependencies:
     "@grpc/grpc-js" "1.8.17"
     "@grpc/proto-loader" "0.7.7"
-    axios "^1.5.1"
     dayjs "^1.11.7"
     lru-cache "^9.1.2"
     protobufjs "7.2.4"
@@ -1420,15 +1419,6 @@ axios@^1.4.0:
     form-data "^4.0.0"
     proxy-from-env "^1.1.0"
 
-axios@^1.5.1:
-  version "1.6.0"
-  resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.0.tgz#f1e5292f26b2fd5c2e66876adc5b06cdbd7d2102"
-  integrity sha512-EZ1DYihju9pwVB+jg67ogm+Tmqc6JmhamRN6I4Zt8DfZu5lbcQGw3ozH9lFejSJgs/ibaef3A9PMXPLeefFGJg==
-  dependencies:
-    follow-redirects "^1.15.0"
-    form-data "^4.0.0"
-    proxy-from-env "^1.1.0"
-
 babel-jest@^29.6.1:
   version "29.6.1"
   resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.6.1.tgz#a7141ad1ed5ec50238f3cd36127636823111233a"