Browse Source

support varchar in create and schema

Signed-off-by: nameczz <zizhao.chen@zilliz.com>
nameczz 3 years ago
parent
commit
1ae21e0a8f

+ 0 - 2
client/src/consts/Milvus.tsx

@@ -141,8 +141,6 @@ export const INDEX_OPTIONS_MAP = {
   })),
   })),
 };
 };
 
 
-export const PRIMARY_KEY_FIELD = 'INT64 (Primary key)';
-
 export const METRIC_OPTIONS_MAP = {
 export const METRIC_OPTIONS_MAP = {
   [DataTypeEnum.FloatVector]: [
   [DataTypeEnum.FloatVector]: [
     {
     {

+ 6 - 0
client/src/http/Field.ts

@@ -54,4 +54,10 @@ export class FieldHttp extends BaseModel implements FieldData {
   get _dimension() {
   get _dimension() {
     return this.type_params.find(item => item.key === 'dim')?.value || '--';
     return this.type_params.find(item => item.key === 'dim')?.value || '--';
   }
   }
+
+  get _maxLength() {
+    return (
+      this.type_params.find(item => item.key === 'max_length')?.value || '--'
+    );
+  }
 }
 }

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

@@ -22,6 +22,7 @@ const collectionTrans = {
   status: 'Status',
   status: 'Status',
   desc: 'Description',
   desc: 'Description',
   createdTime: 'Created Time',
   createdTime: 'Created Time',
+  maxLength: 'Max Length',
 
 
   // create dialog
   // create dialog
   createTitle: 'Create Collection',
   createTitle: 'Create Collection',

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

@@ -22,6 +22,7 @@ const collectionTrans = {
   status: 'Status',
   status: 'Status',
   desc: 'Description',
   desc: 'Description',
   createdTime: 'Created Time',
   createdTime: 'Created Time',
+  maxLength: 'Max Length',
 
 
   // create dialog
   // create dialog
   createTitle: 'Create Collection',
   createTitle: 'Create Collection',

+ 7 - 0
client/src/pages/collections/Collections.tsx

@@ -196,6 +196,13 @@ const Collections = () => {
               dim: v.dimension!,
               dim: v.dimension!,
             },
             },
           }
           }
+        : v.data_type === DataTypeEnum.VarChar
+        ? {
+            ...v,
+            type_params: {
+              max_length: v.max_length!,
+            },
+          }
         : v
         : v
     );
     );
 
 

+ 15 - 0
client/src/pages/collections/Constants.ts

@@ -66,6 +66,10 @@ export const ALL_OPTIONS: KeyValuePair[] = [
     label: 'Boolean',
     label: 'Boolean',
     value: DataTypeEnum.Bool,
     value: DataTypeEnum.Bool,
   },
   },
+  {
+    label: 'VarChar',
+    value: DataTypeEnum.VarChar,
+  },
 ];
 ];
 
 
 export const AUTO_ID_OPTIONS: KeyValuePair[] = [
 export const AUTO_ID_OPTIONS: KeyValuePair[] = [
@@ -78,3 +82,14 @@ export const AUTO_ID_OPTIONS: KeyValuePair[] = [
     value: 'false',
     value: 'false',
   },
   },
 ];
 ];
+
+export const PRIMARY_FIELDS_OPTIONS: KeyValuePair[] = [
+  {
+    label: 'INT64',
+    value: DataTypeEnum.Int64,
+  },
+  {
+    label: 'VARCHAR',
+    value: DataTypeEnum.VarChar,
+  },
+];

+ 3 - 0
client/src/pages/collections/Create.tsx

@@ -74,6 +74,7 @@ const CreateCollection: FC<CollectionCreateProps> = ({ handleCreate }) => {
       name: null, // we need hide helpertext at first time, so we use null to detect user enter input or not.
       name: null, // we need hide helpertext at first time, so we use null to detect user enter input or not.
       description: '',
       description: '',
       isDefault: true,
       isDefault: true,
+      max_length: null,
       id: '1',
       id: '1',
     },
     },
     {
     {
@@ -180,9 +181,11 @@ const CreateCollection: FC<CollectionCreateProps> = ({ handleCreate }) => {
           is_primary_key: v.is_primary_key,
           is_primary_key: v.is_primary_key,
           data_type: v.data_type,
           data_type: v.data_type,
           dimension: vectorType.includes(v.data_type) ? v.dimension : undefined,
           dimension: vectorType.includes(v.data_type) ? v.dimension : undefined,
+          max_length: v.max_length,
         };
         };
 
 
         v.is_primary_key && (data.autoID = form.autoID);
         v.is_primary_key && (data.autoID = form.autoID);
+
         return data;
         return data;
       }),
       }),
       consistency_level: consistencyLevel,
       consistency_level: consistencyLevel,

+ 66 - 14
client/src/pages/collections/CreateFields.tsx

@@ -4,13 +4,17 @@ import { useTranslation } from 'react-i18next';
 import CustomButton from '../../components/customButton/CustomButton';
 import CustomButton from '../../components/customButton/CustomButton';
 import CustomSelector from '../../components/customSelector/CustomSelector';
 import CustomSelector from '../../components/customSelector/CustomSelector';
 import icons from '../../components/icons/Icons';
 import icons from '../../components/icons/Icons';
-import { PRIMARY_KEY_FIELD } from '../../consts/Milvus';
 import { generateId } from '../../utils/Common';
 import { generateId } from '../../utils/Common';
 import { getCreateFieldType } from '../../utils/Format';
 import { getCreateFieldType } from '../../utils/Format';
-import { checkEmptyValid, getCheckResult } from '../../utils/Validation';
+import {
+  checkEmptyValid,
+  checkRange,
+  getCheckResult,
+} from '../../utils/Validation';
 import {
 import {
   ALL_OPTIONS,
   ALL_OPTIONS,
   AUTO_ID_OPTIONS,
   AUTO_ID_OPTIONS,
+  PRIMARY_FIELDS_OPTIONS,
   VECTOR_FIELDS_OPTIONS,
   VECTOR_FIELDS_OPTIONS,
 } from './Constants';
 } from './Constants';
 import {
 import {
@@ -62,7 +66,7 @@ const useStyles = makeStyles((theme: Theme) => ({
     },
     },
   },
   },
   descInput: {
   descInput: {
-    minWidth: '270px',
+    minWidth: '100px',
     flexGrow: 1,
     flexGrow: 1,
   },
   },
   btnTxt: {
   btnTxt: {
@@ -144,12 +148,19 @@ const CreateFields: FC<CreateFieldsProps> = ({
     type: 'all' | 'vector',
     type: 'all' | 'vector',
     label: string,
     label: string,
     value: number,
     value: number,
-    onChange: (value: DataTypeEnum) => void
+    onChange: (value: DataTypeEnum) => void,
+    options?: any[]
   ) => {
   ) => {
     return (
     return (
       <CustomSelector
       <CustomSelector
         wrapperClass={classes.select}
         wrapperClass={classes.select}
-        options={type === 'all' ? ALL_OPTIONS : VECTOR_FIELDS_OPTIONS}
+        options={
+          options
+            ? options
+            : type === 'all'
+            ? ALL_OPTIONS
+            : VECTOR_FIELDS_OPTIONS
+        }
         onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
         onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
           onChange(e.target.value as DataTypeEnum);
           onChange(e.target.value as DataTypeEnum);
         }}
         }}
@@ -285,6 +296,36 @@ const CreateFields: FC<CreateFieldsProps> = ({
     });
     });
   };
   };
 
 
+  const generateMaxLength = (field: Field) => {
+    return getInput({
+      label: 'Max Length',
+      value: field.max_length!,
+      type: 'number',
+      handleChange: (value: string) =>
+        changeFields(field.id!, 'max_length', value),
+      validate: (value: any) => {
+        if (value === null) return ' ';
+        const isEmptyValid = checkEmptyValid(value);
+        const isRangeValid = checkRange({
+          value,
+          min: 1,
+          max: 65535,
+          type: 'number',
+        });
+        return !isEmptyValid
+          ? warningTrans('required', {
+              name: collectionTrans('fieldName'),
+            })
+          : !isRangeValid
+          ? warningTrans('range', {
+              min: 1,
+              max: 65535,
+            })
+          : ' ';
+      },
+    });
+  };
+
   const changeFields = (id: string, key: string, value: any) => {
   const changeFields = (id: string, key: string, value: any) => {
     const newFields = fields.map(f => {
     const newFields = fields.map(f => {
       if (f.id !== id) {
       if (f.id !== id) {
@@ -307,6 +348,7 @@ const CreateFields: FC<CreateFieldsProps> = ({
       description: '',
       description: '',
       isDefault: false,
       isDefault: false,
       dimension: '128',
       dimension: '128',
+      max_length: null,
       id,
       id,
     };
     };
     const newValidation = {
     const newValidation = {
@@ -328,29 +370,38 @@ const CreateFields: FC<CreateFieldsProps> = ({
     field: Field,
     field: Field,
     autoID: boolean
     autoID: boolean
   ): ReactElement => {
   ): ReactElement => {
+    const isVarChar = field.data_type === DataTypeEnum.VarChar;
+    const autoIdOff = isVarChar ? 'false' : autoID ? 'true' : 'false';
     return (
     return (
       <div className={`${classes.rowWrapper}`}>
       <div className={`${classes.rowWrapper}`}>
-        {getInput({
-          label: collectionTrans('fieldType'),
-          value: PRIMARY_KEY_FIELD,
-          className: classes.primaryInput,
-          inputClassName: classes.input,
-          isReadOnly: true,
-        })}
+        {getSelector(
+          'vector',
+          `Primary ${collectionTrans('fieldType')} `,
+          field.data_type,
+          (value: DataTypeEnum) => {
+            changeFields(field.id!, 'data_type', value);
+            if (value === DataTypeEnum.VarChar) {
+              setAutoID(false);
+            }
+          },
+          PRIMARY_FIELDS_OPTIONS
+        )}
 
 
         {generateFieldName(field)}
         {generateFieldName(field)}
 
 
         <CustomSelector
         <CustomSelector
           label={collectionTrans('autoId')}
           label={collectionTrans('autoId')}
           options={AUTO_ID_OPTIONS}
           options={AUTO_ID_OPTIONS}
-          value={autoID ? 'true' : 'false'}
+          value={autoIdOff}
           onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
           onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
             const autoId = e.target.value === 'true';
             const autoId = e.target.value === 'true';
             setAutoID(autoId);
             setAutoID(autoId);
           }}
           }}
           variant="filled"
           variant="filled"
           wrapperClass={classes.select}
           wrapperClass={classes.select}
+          disabled={isVarChar}
         />
         />
+        {isVarChar && generateMaxLength(field)}
 
 
         {generateDesc(field)}
         {generateDesc(field)}
       </div>
       </div>
@@ -384,6 +435,7 @@ const CreateFields: FC<CreateFieldsProps> = ({
   };
   };
 
 
   const generateNumberRow = (field: Field): ReactElement => {
   const generateNumberRow = (field: Field): ReactElement => {
+    const isVarChar = field.data_type === DataTypeEnum.VarChar;
     return (
     return (
       <div className={`${classes.rowWrapper}`}>
       <div className={`${classes.rowWrapper}`}>
         <IconButton
         <IconButton
@@ -400,7 +452,7 @@ const CreateFields: FC<CreateFieldsProps> = ({
           field.data_type,
           field.data_type,
           (value: DataTypeEnum) => changeFields(field.id!, 'data_type', value)
           (value: DataTypeEnum) => changeFields(field.id!, 'data_type', value)
         )}
         )}
-
+        {isVarChar && generateMaxLength(field)}
         {generateDesc(field)}
         {generateDesc(field)}
       </div>
       </div>
     );
     );

+ 12 - 6
client/src/pages/collections/Types.ts

@@ -34,11 +34,11 @@ export interface CollectionCreateParam {
 }
 }
 
 
 export enum ConsistencyLevelEnum {
 export enum ConsistencyLevelEnum {
-  Strong = "Strong",
-  Session = "Session", // default in PyMilvus
-  Bounded = "Bounded",
-  Eventually = "Eventually",
-  Customized = "Customized", // Users pass their own `guarantee_timestamp`.
+  Strong = 'Strong',
+  Session = 'Session', // default in PyMilvus
+  Bounded = 'Bounded',
+  Eventually = 'Eventually',
+  Customized = 'Customized', // Users pass their own `guarantee_timestamp`.
 }
 }
 
 
 export enum DataTypeEnum {
 export enum DataTypeEnum {
@@ -49,6 +49,8 @@ export enum DataTypeEnum {
   Int64 = 5,
   Int64 = 5,
   Float = 10,
   Float = 10,
   Double = 11,
   Double = 11,
+  String = 20,
+  VarChar = 21,
   BinaryVector = 100,
   BinaryVector = 100,
   FloatVector = 101,
   FloatVector = 101,
 }
 }
@@ -60,6 +62,8 @@ export enum DataTypeStringEnum {
   Int64 = 'Int64',
   Int64 = 'Int64',
   Float = 'Float',
   Float = 'Float',
   Double = 'Double',
   Double = 'Double',
+  String = 'String',
+  VarChar = 'VarChar',
   BinaryVector = 'BinaryVector',
   BinaryVector = 'BinaryVector',
   FloatVector = 'FloatVector',
   FloatVector = 'FloatVector',
 }
 }
@@ -73,9 +77,11 @@ export interface Field {
   isDefault?: boolean;
   isDefault?: boolean;
   id?: string;
   id?: string;
   type_params?: {
   type_params?: {
-    dim: string | number;
+    dim?: string | number;
+    max_length?: string;
   };
   };
   createType?: CreateFieldType;
   createType?: CreateFieldType;
+  max_length?: string | null;
 }
 }
 
 
 export type CreateFieldType =
 export type CreateFieldType =

+ 6 - 0
client/src/pages/schema/Schema.tsx

@@ -185,6 +185,12 @@ const Schema: FC<{
         </span>
         </span>
       ),
       ),
     },
     },
+    {
+      id: '_maxLength',
+      align: 'left',
+      disablePadding: true,
+      label: collectionTrans('maxLength'),
+    },
     {
     {
       id: '_indexTypeElement',
       id: '_indexTypeElement',
       align: 'left',
       align: 'left',

+ 1 - 0
client/src/pages/schema/Types.ts

@@ -33,6 +33,7 @@ export interface FieldData {
   _fieldType: DataTypeStringEnum;
   _fieldType: DataTypeStringEnum;
   _dimension: string;
   _dimension: string;
   _desc: string;
   _desc: string;
+  _maxLength: string;
 }
 }
 
 
 export interface FieldView extends FieldData, IndexView {
 export interface FieldView extends FieldData, IndexView {

+ 2 - 2
server/package.json

@@ -12,7 +12,7 @@
     "url": "https://github.com/zilliztech/attu"
     "url": "https://github.com/zilliztech/attu"
   },
   },
   "dependencies": {
   "dependencies": {
-    "@zilliz/milvus2-sdk-node": "^2.0.3",
+    "@zilliz/milvus2-sdk-node": "^2.1.2",
     "chalk": "^4.1.2",
     "chalk": "^4.1.2",
     "class-sanitizer": "^1.0.1",
     "class-sanitizer": "^1.0.1",
     "class-transformer": "^0.4.0",
     "class-transformer": "^0.4.0",
@@ -139,4 +139,4 @@
       ]
       ]
     }
     }
   }
   }
-}
+}

+ 25 - 6
server/yarn.lock

@@ -1137,10 +1137,10 @@
   dependencies:
   dependencies:
     "@types/yargs-parser" "*"
     "@types/yargs-parser" "*"
 
 
-"@zilliz/milvus2-sdk-node@^2.0.3":
-  version "2.0.3"
-  resolved "https://registry.yarnpkg.com/@zilliz/milvus2-sdk-node/-/milvus2-sdk-node-2.0.3.tgz#9007f6e5162ca9b51b522b1d6eff76a31dc6921c"
-  integrity sha512-BWTIRBhJI/z9HWCeJsfaCC6vueGcTVcL7Ai7SASnwFR4RIzfQ2bclo41G6767cHqZNiyOdoEyM+u0XuPpo8vjA==
+"@zilliz/milvus2-sdk-node@^2.1.2":
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/@zilliz/milvus2-sdk-node/-/milvus2-sdk-node-2.1.2.tgz#9db28a4ba4d7f330ed2af86f83146cff53eacecd"
+  integrity sha512-By1p6OLn++DCA+4tL/Z/O7KUcgcMwM75UFafdwozqzH7l4jHTUvb2oJrWMk0zqk7bapt4To34OfTcKd+3EHnYw==
   dependencies:
   dependencies:
     "@grpc/grpc-js" "^1.2.12"
     "@grpc/grpc-js" "^1.2.12"
     "@grpc/proto-loader" "^0.6.0"
     "@grpc/proto-loader" "^0.6.0"
@@ -1148,7 +1148,7 @@
     "@microsoft/api-extractor" "^7.18.5"
     "@microsoft/api-extractor" "^7.18.5"
     json-schema "^0.4.0"
     json-schema "^0.4.0"
     node-forge "^1.0.0"
     node-forge "^1.0.0"
-    protobufjs "^6.11.2"
+    protobufjs "^6.11.3"
 
 
 abab@^2.0.3, abab@^2.0.5:
 abab@^2.0.3, abab@^2.0.5:
   version "2.0.5"
   version "2.0.5"
@@ -4233,7 +4233,7 @@ proto-list@~1.2.1:
   resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849"
   resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849"
   integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=
   integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=
 
 
-protobufjs@^6.10.0, protobufjs@^6.11.2:
+protobufjs@^6.10.0:
   version "6.11.2"
   version "6.11.2"
   resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.2.tgz#de39fabd4ed32beaa08e9bb1e30d08544c1edf8b"
   resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.2.tgz#de39fabd4ed32beaa08e9bb1e30d08544c1edf8b"
   integrity sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw==
   integrity sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw==
@@ -4252,6 +4252,25 @@ protobufjs@^6.10.0, protobufjs@^6.11.2:
     "@types/node" ">=13.7.0"
     "@types/node" ">=13.7.0"
     long "^4.0.0"
     long "^4.0.0"
 
 
+protobufjs@^6.11.3:
+  version "6.11.3"
+  resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.3.tgz#637a527205a35caa4f3e2a9a4a13ddffe0e7af74"
+  integrity sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==
+  dependencies:
+    "@protobufjs/aspromise" "^1.1.2"
+    "@protobufjs/base64" "^1.1.2"
+    "@protobufjs/codegen" "^2.0.4"
+    "@protobufjs/eventemitter" "^1.1.0"
+    "@protobufjs/fetch" "^1.1.0"
+    "@protobufjs/float" "^1.0.2"
+    "@protobufjs/inquire" "^1.1.0"
+    "@protobufjs/path" "^1.1.2"
+    "@protobufjs/pool" "^1.1.0"
+    "@protobufjs/utf8" "^1.1.0"
+    "@types/long" "^4.0.1"
+    "@types/node" ">=13.7.0"
+    long "^4.0.0"
+
 proxy-addr@~2.0.5:
 proxy-addr@~2.0.5:
   version "2.0.7"
   version "2.0.7"
   resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025"
   resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025"