Browse Source

update search validation and reset

tumao 4 years ago
parent
commit
a03666e1e9

+ 7 - 7
client/src/consts/Milvus.tsx

@@ -142,33 +142,33 @@ export const METRIC_OPTIONS_MAP = {
   [EmbeddingTypeEnum.float]: [
     {
       value: METRIC_TYPES_VALUES.L2,
-      label: 'L2',
+      label: METRIC_TYPES_VALUES.L2,
     },
     {
       value: METRIC_TYPES_VALUES.IP,
-      label: 'IP',
+      label: METRIC_TYPES_VALUES.IP,
     },
   ],
   [EmbeddingTypeEnum.binary]: [
     {
       value: METRIC_TYPES_VALUES.SUBSTRUCTURE,
-      label: 'Substructure',
+      label: METRIC_TYPES_VALUES.SUBSTRUCTURE,
     },
     {
       value: METRIC_TYPES_VALUES.SUPERSTRUCTURE,
-      label: 'Superstructure',
+      label: METRIC_TYPES_VALUES.SUPERSTRUCTURE,
     },
     {
       value: METRIC_TYPES_VALUES.HAMMING,
-      label: 'Hamming',
+      label: METRIC_TYPES_VALUES.HAMMING,
     },
     {
       value: METRIC_TYPES_VALUES.JACCARD,
-      label: 'Jaccard',
+      label: METRIC_TYPES_VALUES.JACCARD,
     },
     {
       value: METRIC_TYPES_VALUES.TANIMOTO,
-      label: 'Tanimoto',
+      label: METRIC_TYPES_VALUES.TANIMOTO,
     },
   ],
 };

+ 10 - 7
client/src/pages/schema/Create.tsx

@@ -2,7 +2,6 @@ import { useEffect, useMemo, useState } from 'react';
 import { useTranslation } from 'react-i18next';
 import DialogTemplate from '../../components/customDialog/DialogTemplate';
 import {
-  EmbeddingTypeEnum,
   INDEX_CONFIG,
   INDEX_OPTIONS_MAP,
   MetricType,
@@ -10,6 +9,7 @@ import {
 } from '../../consts/Milvus';
 import { useFormValidation } from '../../hooks/Form';
 import { formatForm, getMetricOptions } from '../../utils/Form';
+import { getEmbeddingType } from '../../utils/search';
 import { DataType } from '../collections/Types';
 import CreateForm from './CreateForm';
 import { IndexType, ParamPair, INDEX_TYPES_ENUM } from './Types';
@@ -26,8 +26,14 @@ const CreateIndex = (props: {
   const { t: dialogTrans } = useTranslation('dialog');
   const { t: btnTrans } = useTranslation('btn');
 
-  const defaultIndexType = fieldType === 'BinaryVector' ? INDEX_TYPES_ENUM.BIN_IVF_FLAT : INDEX_TYPES_ENUM.IVF_FLAT;
-  const defaultMetricType = fieldType === 'BinaryVector' ? METRIC_TYPES_VALUES.HAMMING : METRIC_TYPES_VALUES.L2;
+  const defaultIndexType =
+    fieldType === 'BinaryVector'
+      ? INDEX_TYPES_ENUM.BIN_IVF_FLAT
+      : INDEX_TYPES_ENUM.IVF_FLAT;
+  const defaultMetricType =
+    fieldType === 'BinaryVector'
+      ? METRIC_TYPES_VALUES.HAMMING
+      : METRIC_TYPES_VALUES.L2;
 
   const [indexSetting, setIndexSetting] = useState<{
     index_type: IndexType;
@@ -68,10 +74,7 @@ const CreateIndex = (props: {
   }, [indexCreateParams, indexSetting]);
 
   const indexOptions = useMemo(() => {
-    const type =
-      fieldType === 'BinaryVector'
-        ? EmbeddingTypeEnum.binary
-        : EmbeddingTypeEnum.float;
+    const type = getEmbeddingType(fieldType);
     return INDEX_OPTIONS_MAP[type];
   }, [fieldType]);
 

+ 1 - 1
client/src/pages/seach/SearchParams.tsx

@@ -93,7 +93,7 @@ const SearchParams: FC<SearchParamsProps> = ({
           },
         ],
       };
-      if (!isSearchK && (min || max)) {
+      if (!isSearchK && min && max) {
         config.validations?.push({
           rule: 'range',
           errorText: warningTrans('range', { min, max }),

+ 87 - 47
client/src/pages/seach/VectorSearch.tsx

@@ -23,7 +23,11 @@ import { CollectionData, DataType, DataTypeEnum } from '../collections/Types';
 import { IndexHttp } from '../../http/Index';
 import { getVectorSearchStyles } from './Styles';
 import { parseValue } from '../../utils/Insert';
-import { transferSearchResult } from '../../utils/search';
+import {
+  getDefaultIndexType,
+  getEmbeddingType,
+  transferSearchResult,
+} from '../../utils/search';
 import { ColDefinitionsType } from '../../components/grid/Types';
 
 const VectorSearch = () => {
@@ -58,6 +62,21 @@ const VectorSearch = () => {
     data: result,
   } = usePaginationHook(searchResult);
 
+  const searchDisabled = useMemo(() => {
+    /**
+     * before search, user must:
+     * 1. enter vector value
+     * 2. choose collection and field
+     * 3. set extra search params
+     */
+    const isInvalid =
+      vectors === '' ||
+      selectedCollection === '' ||
+      selectedField === '' ||
+      paramDisabled;
+    return isInvalid;
+  }, [paramDisabled, selectedField, selectedCollection, vectors]);
+
   const collectionOptions: Option[] = useMemo(
     () =>
       collections.map(c => ({
@@ -92,40 +111,36 @@ const VectorSearch = () => {
       : [];
   }, [searchResult]);
 
-  const { metricType, indexType, indexParams, fieldType } = useMemo(() => {
-    if (selectedField !== '') {
-      // field options must contain selected field, so selectedFieldInfo will never undefined
-      const selectedFieldInfo = fieldOptions.find(
-        f => f.value === selectedField
-      );
-      console.log('===== selected field info', selectedFieldInfo);
-      const index = selectedFieldInfo?.indexInfo;
-
-      const embeddingType =
-        selectedFieldInfo!.fieldType === 'BinaryVector'
-          ? EmbeddingTypeEnum.binary
-          : EmbeddingTypeEnum.float;
+  const { metricType, indexType, indexParams, fieldType, embeddingType } =
+    useMemo(() => {
+      if (selectedField !== '') {
+        // field options must contain selected field, so selectedFieldInfo will never undefined
+        const selectedFieldInfo = fieldOptions.find(
+          f => f.value === selectedField
+        );
+        const index = selectedFieldInfo?.indexInfo;
+        const embeddingType = getEmbeddingType(selectedFieldInfo!.fieldType);
+        const metric =
+          index?._metricType || DEFAULT_METRIC_VALUE_MAP[embeddingType];
+        const indexParams = index?._indexParameterPairs || [];
 
-      const metric =
-        index?._metricType || DEFAULT_METRIC_VALUE_MAP[embeddingType];
-
-      const indexParams = index?._indexParameterPairs || [];
+        return {
+          metricType: metric,
+          indexType: index?._indexType || getDefaultIndexType(embeddingType),
+          indexParams,
+          fieldType: DataTypeEnum[selectedFieldInfo?.fieldType!],
+          embeddingType,
+        };
+      }
 
       return {
-        metricType: metric,
-        indexType: index?._indexType || 'FLAT',
-        indexParams,
-        fieldType: DataTypeEnum[selectedFieldInfo?.fieldType!],
+        metricType: '',
+        indexType: '',
+        indexParams: [],
+        fieldType: 0,
+        embeddingType: EmbeddingTypeEnum.float,
       };
-    }
-
-    return {
-      metricType: '',
-      indexType: '',
-      indexParams: [],
-      fieldType: 0,
-    };
-  }, [selectedField, fieldOptions]);
+    }, [selectedField, fieldOptions]);
 
   // fetch data
   const fetchCollections = useCallback(async () => {
@@ -143,11 +158,13 @@ const VectorSearch = () => {
       const fieldOptions = fields
         // only vector type field can be select
         .filter(field => vectorTypes.includes(field._fieldType))
-        // use FLAT as default index type
         .map(f => {
+          const embeddingType = getEmbeddingType(f._fieldType);
+          const defaultIndex = getDefaultIndexType(embeddingType);
           const index = indexes.find(i => i._fieldName === f._fieldName);
+
           return {
-            label: `${f._fieldName} (${index?._indexType || 'FLAT'})`,
+            label: `${f._fieldName} (${index?._indexType || defaultIndex})`,
             value: f._fieldName,
             fieldType: f._fieldType,
             indexInfo: index || null,
@@ -180,8 +197,21 @@ const VectorSearch = () => {
   const handlePageChange = (e: any, page: number) => {
     handleCurrentPage(page);
   };
-  const handleReset = () => {};
-  const handleSearch = async () => {
+  const handleReset = () => {
+    /**
+     * reset search includes:
+     * 1. reset vectors
+     * 2. reset selected collection and field
+     * 3. reset search params
+     * 4. reset advanced filter
+     * 5. clear search result
+     */
+    setVectors('');
+    setSelectedField('');
+    setSelectedCollection('');
+    setSearchResult([]);
+  };
+  const handleSearch = async (topK: number) => {
     const searhParamPairs = [
       // dynamic search params
       {
@@ -211,14 +241,18 @@ const VectorSearch = () => {
     };
 
     setTableLoading(true);
-    const res = await CollectionHttp.vectorSearchData(
-      selectedCollection,
-      params
-    );
-    setTableLoading(false);
+    try {
+      const res = await CollectionHttp.vectorSearchData(
+        selectedCollection,
+        params
+      );
+      setTableLoading(false);
 
-    const result = transferSearchResult(res.results);
-    setSearchResult(result);
+      const result = transferSearchResult(res.results);
+      setSearchResult(result);
+    } catch (err) {
+      setTableLoading(false);
+    }
   };
   const handleFilter = () => {};
   const handleClearFilter = () => {};
@@ -244,7 +278,8 @@ const VectorSearch = () => {
             multiline
             rows={5}
             placeholder={searchTrans('vectorPlaceholder')}
-            onBlur={(e: React.ChangeEvent<{ value: unknown }>) => {
+            value={vectors}
+            onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
               handleVectorChange(e.target.value as string);
             }}
           />
@@ -284,7 +319,7 @@ const VectorSearch = () => {
           <SearchParams
             wrapperClass={classes.paramsWrapper}
             metricType={metricType!}
-            embeddingType={EmbeddingTypeEnum.float}
+            embeddingType={embeddingType}
             indexType={indexType}
             indexParams={indexParams!}
             searchParamsForm={searchParam}
@@ -311,8 +346,9 @@ const VectorSearch = () => {
               label: item.toString(),
               callback: () => {
                 setTopK(item);
-                // TODO: check search validation before search
-                // handleSearch();
+                if (!searchDisabled) {
+                  handleSearch(item);
+                }
               },
               wrapperClass: classes.menuItem,
             }))}
@@ -346,7 +382,11 @@ const VectorSearch = () => {
             <ResetIcon classes={{ root: 'icon' }} />
             {btnTrans('reset')}
           </CustomButton>
-          <CustomButton variant="contained" onClick={handleSearch}>
+          <CustomButton
+            variant="contained"
+            disabled={searchDisabled}
+            onClick={() => handleSearch(topK)}
+          >
             {btnTrans('search')}
           </CustomButton>
         </div>

+ 30 - 3
client/src/utils/search.ts

@@ -1,4 +1,6 @@
-import { IndexType } from '../pages/schema/Types';
+import { EmbeddingTypeEnum } from '../consts/Milvus';
+import { DataType } from '../pages/collections/Types';
+import { IndexType, INDEX_TYPES_ENUM } from '../pages/schema/Types';
 import { SearchResult, SearchResultView } from '../pages/seach/Types';
 
 export const transferSearchResult = (
@@ -15,6 +17,31 @@ export const transferSearchResult = (
   return resultView;
 };
 
-// export const getDefaultIndexType = (fieldType: ): IndexType => {
+/**
+ * function to get EmbeddingType
+ * @param fieldType only vector type fields: 'BinaryVector' or 'FloatVector'
+ * @returns 'FLOAT_INDEX' or 'BINARY_INDEX'
+ */
+export const getEmbeddingType = (fieldType: DataType): EmbeddingTypeEnum => {
+  const type =
+    fieldType === 'BinaryVector'
+      ? EmbeddingTypeEnum.binary
+      : EmbeddingTypeEnum.float;
+  return type;
+};
 
-// }
+/**
+ * function to get default index type according to embedding type
+ * use FLAT as default float index type, BIN_FLAT as default binary index type
+ * @param embeddingType float or binary
+ * @returns index type
+ */
+export const getDefaultIndexType = (
+  embeddingType: EmbeddingTypeEnum
+): IndexType => {
+  const defaultIndexType =
+    embeddingType === EmbeddingTypeEnum.float
+      ? INDEX_TYPES_ENUM.FLAT
+      : INDEX_TYPES_ENUM.BIN_FLAT;
+  return defaultIndexType;
+};