Browse Source

fix index form validation problem (#548)

Signed-off-by: shanghaikid <jiangruiyi@gmail.com>
ryjiang 1 year ago
parent
commit
c475282058

+ 0 - 8
client/src/consts/Milvus.ts

@@ -168,14 +168,6 @@ export const FLOAT_INDEX_CONFIG: indexConfigType = {
     create: [],
     search: ['search_list'],
   },
-  SPARSE_WAND: {
-    create: ['drop_ratio_build'],
-    search: ['drop_ratio_search'],
-  },
-  SPARSE_INVERTED_INDEX: {
-    create: ['drop_ratio_build'],
-    search: ['drop_ratio_search'],
-  },
 };
 
 export const BINARY_INDEX_CONFIG: indexConfigType = {

+ 16 - 8
client/src/hooks/Form.ts

@@ -33,6 +33,7 @@ export interface ICheckValidParam {
   rules: IValidation[];
 }
 
+// form and element ref
 export const useFormValidation = (form: IForm[]): IValidationInfo => {
   const initValidation = form
     .filter(f => f.needCheck)
@@ -57,6 +58,7 @@ export const useFormValidation = (form: IForm[]): IValidationInfo => {
   const [disabled, setDisabled] = useState<boolean>(!isOverallValid);
 
   const checkIsValid = (param: ICheckValidParam): IValidationItem => {
+    console.log(param)
     const { value, key, rules } = param;
 
     let validDetail = {
@@ -106,14 +108,20 @@ export const useFormValidation = (form: IForm[]): IValidationInfo => {
     return validDetail;
   };
 
-  const checkFormValid = (form: IForm[]): boolean => {
-    const requireCheckItems = form.filter(f => f.needCheck);
-    if (requireCheckItems.some(item => !checkEmptyValid(item.value))) {
-      return false;
-    }
-
-    const validations = Object.values(validation);
-    return validations.every(v => !(v as IValidationItem).result);
+  const checkFormValid = (elementClass: string) => {
+    // find dom element to check
+    const elements = document.querySelectorAll(
+      elementClass
+    ) as NodeListOf<HTMLElement>;
+    // Trigger blur
+    elements.forEach(element => {
+      // Using setTimeout to trigger blur event after render
+      setTimeout(() => {
+        // Using HTMLElement.prototype.blur method to trigger blur event
+        element.focus();
+        element.blur();
+      }, 0);
+    });
   };
 
   const resetValidation = (form: IForm[]) => {

+ 15 - 4
client/src/pages/databases/collections/overview/Create.tsx

@@ -209,8 +209,14 @@ const CreateIndex = (props: {
     fieldType,
   ]);
 
-  const { validation, checkIsValid, disabled, setDisabled, resetValidation } =
-    useFormValidation(checkedForm);
+  const {
+    validation,
+    checkIsValid,
+    disabled,
+    setDisabled,
+    resetValidation,
+    checkFormValid,
+  } = useFormValidation(checkedForm);
   // reset index params
   useEffect(() => {
     // no need
@@ -231,13 +237,14 @@ const CreateIndex = (props: {
       knng: '',
       drop_ratio_build: '0.5',
     }));
-  }, [indexCreateParams, setDisabled, defaultMetricType]);
+  }, [setDisabled, defaultMetricType]);
 
   const updateStepTwoForm = (type: string, value: string) => {
     setIndexSetting(v => ({ ...v, [type]: value }));
   };
 
   const onIndexTypeChange = (type: string) => {
+    // reset index params
     let paramsForm: { [key in string]: string } = {};
     // m is selector not input
     (INDEX_CONFIG[type].create || [])
@@ -246,9 +253,12 @@ const CreateIndex = (props: {
         paramsForm[item] = '';
       });
     // if no other params, the form should be valid.
-    setDisabled((INDEX_CONFIG[type].create || []).length === 0 ? false : true);
     const form = formatForm(paramsForm);
     resetValidation(form);
+    // trigger validation check after the render
+    setTimeout(() => {
+      checkFormValid('.index-form .MuiInputBase-input');
+    }, 0);
   };
 
   const handleCreateIndex = () => {
@@ -284,6 +294,7 @@ const CreateIndex = (props: {
           validation={validation}
           indexParams={indexCreateParams}
           indexTypeChange={onIndexTypeChange}
+          wrapperClass="index-form"
         />
       </>
     </DialogTemplate>

+ 50 - 103
client/src/pages/databases/collections/overview/CreateForm.tsx

@@ -5,7 +5,6 @@ import { ITextfieldConfig } from '@/components/customInput/Types';
 import CustomInput from '@/components/customInput/CustomInput';
 import CustomSelector from '@/components/customSelector/CustomSelector';
 import { Option } from '@/components/customSelector/Types';
-import { m_OPTIONS } from '@/consts';
 import { FormHelperType } from '../../../../types/Common';
 
 const useStyles = makeStyles((theme: Theme) => ({
@@ -42,6 +41,7 @@ const CreateForm = (
     indexTypeChange,
     indexOptions,
     metricOptions,
+    wrapperClass,
   } = props;
 
   const { t: commonTrans } = useTranslation();
@@ -49,7 +49,6 @@ const CreateForm = (
   const { t: warningTrans } = useTranslation('warning');
 
   const paramsConfig: ITextfieldConfig[] = useMemo(() => {
-    const result = [];
     const generateNumberConfig = (
       label: string,
       key: string,
@@ -85,79 +84,46 @@ const CreateForm = (
       return config;
     };
 
-    const nlist = generateNumberConfig('nlist', 'nlist', 1, 65536);
-    const nbits = generateNumberConfig('nbits', 'nbits', 1, 16);
-    const nTrees = generateNumberConfig('nTrees', 'n_trees', 1, 1024);
-
-    const M = generateNumberConfig('M', 'M', 1, 2048);
-    const efConstruction = generateNumberConfig(
-      'Ef Construction',
-      'efConstruction',
-      1,
-      2147483647
-    );
-
-    const outDegree = generateNumberConfig('out_degree', 'out_degree', 5, 300);
-    const candidatePoolSize = generateNumberConfig(
-      'candidate_pool_size',
-      'candidate_pool_size',
-      50,
-      1000
-    );
-    const searchLength = generateNumberConfig(
-      'search_length',
-      'search_length',
-      10,
-      300
-    );
-    const knng = generateNumberConfig('knng', 'knng', 5, 300);
-
-    const drop_ratio_build = generateNumberConfig(
-      'drop_ratio_build',
-      'drop_ratio_build',
-      0,
-      1
-    );
-
-    if (indexParams.includes('nlist')) {
-      result.push(nlist);
-    }
-
-    if (indexParams.includes('nbits')) {
-      result.push(nbits);
-    }
-
-    if (indexParams.includes('M')) {
-      result.push(M);
-    }
-
-    if (indexParams.includes('efConstruction')) {
-      result.push(efConstruction);
-    }
-
-    if (indexParams.includes('n_trees')) {
-      result.push(nTrees);
-    }
-
-    if (indexParams.includes('out_degree')) {
-      result.push(outDegree);
-    }
-
-    if (indexParams.includes('candidate_pool_size')) {
-      result.push(candidatePoolSize);
-    }
-
-    if (indexParams.includes('search_length')) {
-      result.push(searchLength);
-    }
+    const paramsMap = {
+      nlist: generateNumberConfig('nlist', 'nlist', 1, 65536),
+      nbits: generateNumberConfig('nbits', 'nbits', 1, 16),
+      M: generateNumberConfig('M', 'M', 1, 2048),
+      efConstruction: generateNumberConfig(
+        'Ef Construction',
+        'efConstruction',
+        1,
+        2147483647
+      ),
+      n_trees: generateNumberConfig('nTrees', 'n_trees', 1, 1024),
+      out_degree: generateNumberConfig('out_degree', 'out_degree', 5, 300),
+      candidate_pool_size: generateNumberConfig(
+        'candidate_pool_size',
+        'candidate_pool_size',
+        50,
+        1000
+      ),
+      search_length: generateNumberConfig(
+        'search_length',
+        'search_length',
+        10,
+        300
+      ),
+      knng: generateNumberConfig('knng', 'knng', 5, 300),
+      drop_ratio_build: generateNumberConfig(
+        'drop_ratio_build',
+        'drop_ratio_build',
+        0,
+        1
+      ),
+    };
 
-    if (indexParams.includes('knng')) {
-      result.push(knng);
-    }
+    const result: ITextfieldConfig[] = [];
 
-    if (indexParams.includes('drop_ratio_build')) {
-      result.push(drop_ratio_build);
-    }
+    indexParams.forEach(param => {
+      if (paramsMap.hasOwnProperty(param)) {
+        result.push(paramsMap[param as keyof typeof paramsMap]);
+      }
+    });
 
     return result;
   }, [updateForm, warningTrans, indexParams, formValue]);
@@ -167,26 +133,14 @@ const CreateForm = (
     key: 'index_name',
     onChange: (value: string) => updateForm('index_name', value),
     variant: 'filled',
-    placeholder: 'Index name',
     fullWidth: true,
-    validations: [
-      {
-        rule: 'require',
-        errorText: warningTrans('required', {
-          name: 'Index Name',
-        }),
-      },
-    ],
+    validations: [],
     defaultValue: '',
+    value: formValue.index_name,
   };
+
   return (
-    <div className={classes.wrapper}>
-      <CustomInput
-        type="text"
-        textConfig={indexNameConfig}
-        checkValid={checkIsValid}
-        validInfo={validation}
-      />
+    <div className={`${classes.wrapper} ${wrapperClass}`}>
       <CustomSelector
         label={indexTrans('type')}
         value={formValue.index_type}
@@ -195,12 +149,18 @@ const CreateForm = (
           const type = e.target.value;
           updateForm('index_type', type as string);
           // reset metric type value
-          updateForm('metric_type', 'L2');
+          updateForm('metric_type', metricOptions[0].value as string);
           indexTypeChange && indexTypeChange(type as string);
         }}
         variant="filled"
         wrapperClass={classes.select}
       />
+      <CustomInput
+        type="text"
+        textConfig={indexNameConfig}
+        checkValid={checkIsValid}
+        validInfo={validation}
+      />
       {metricOptions.length ? (
         <Typography className={classes.paramTitle}>
           {commonTrans('param')}
@@ -221,19 +181,6 @@ const CreateForm = (
         />
       ) : null}
 
-      {indexParams.includes('m') && (
-        <CustomSelector
-          label="m"
-          value={Number(formValue.m)}
-          options={m_OPTIONS}
-          onChange={(e: { target: { value: unknown } }) =>
-            updateForm('m', e.target.value as string)
-          }
-          variant="filled"
-          wrapperClass={classes.select}
-        />
-      )}
-
       {paramsConfig.length
         ? paramsConfig.map(v => (
             <CustomInput

+ 1 - 0
client/src/types/Common.ts

@@ -15,4 +15,5 @@ export type FormHelperType = {
   updateForm: (type: string, value: string) => void;
   validation: { [key: string]: IValidationItem };
   checkIsValid: Function;
+  wrapperClass?: string;
 };

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

@@ -1,9 +1,5 @@
 import { Option } from '@/components/customSelector/Types';
-import {
-  METRIC_TYPES_VALUES,
-  DataTypeStringEnum,
-  DataTypeEnum,
-} from '@/consts';
+import { METRIC_TYPES_VALUES, DataTypeEnum } from '@/consts';
 import { IForm } from '@/hooks';
 import { IndexType } from '@/pages/databases/collections/overview/Types';
 

+ 0 - 7
client/src/utils/Metric.ts

@@ -19,13 +19,6 @@ export const parseJson = (jsonData: any) => {
 
   const allNodes = jsonData?.response?.nodes_info;
 
-  console.log(workingNodes,
-    // workingNodes.map((d: any) => ({
-    //   name: d.infos.name,
-    //   ip: d.infos.hardware_infos.ip,
-    // }))
-  );
-
   workingNodes.forEach((node: any) => {
     const type = node?.infos?.type;
     if (node.connected) {