Browse Source

Merge branch 'main' of github.com:zilliztech/attu into main

nameczz 2 years ago
parent
commit
e6176d953f

+ 1 - 1
.gitignore

@@ -32,7 +32,7 @@ server/dist
 server/build
 server/electron-app
 
-
+express/dist
 
 # package.lock.json
 # server/package.lock.json

+ 2 - 0
client/src/i18n/cn/warning.ts

@@ -5,6 +5,8 @@ const warningTrans = {
   range: 'range is {{min}} ~ {{max}}',
   specValueOrRange:
     '{{name}} should be {{specValue}}, or in range {{min}} ~ {{max}}',
+  noSupportIndexType:
+    'Attu has not supported {{type}} yet. Please change another field',
 };
 
 export default warningTrans;

+ 2 - 0
client/src/i18n/en/warning.ts

@@ -5,6 +5,8 @@ const warningTrans = {
   range: 'range is {{min}} ~ {{max}}',
   specValueOrRange:
     '{{name}} should be {{specValue}}, or in range {{min}} ~ {{max}}',
+  noSupportIndexType:
+    'Attu has not supported {{type}} yet. Please change another field',
 };
 
 export default warningTrans;

+ 56 - 52
client/src/pages/schema/IndexTypeElement.tsx

@@ -21,7 +21,7 @@ import icons from '../../components/icons/Icons';
 import { rootContext } from '../../context/Root';
 import CreateIndex from './Create';
 import DeleteTemplate from '../../components/customDialog/DeleteDialogTemplate';
-import CustomLinearProgress from '../../components/customProgress/CustomLinearProgress';
+// import CustomLinearProgress from '../../components/customProgress/CustomLinearProgress';
 import StatusIcon from '../../components/status/StatusIcon';
 import { ChildrenStatusType } from '../../components/status/Types';
 
@@ -90,7 +90,7 @@ const IndexTypeElement: FC<{
   const { t: dialogTrans } = useTranslation('dialog');
   const { t: successTrans } = useTranslation('success');
 
-  const [createProgress, setCreateProgress] = useState<number>(0);
+  // const [createProgress, setCreateProgress] = useState<number>(0);
 
   const { setDialog, handleCloseDialog, openSnackBar } =
     useContext(rootContext);
@@ -104,58 +104,63 @@ const IndexTypeElement: FC<{
   );
 
   const fetchStatus = useCallback(async () => {
-    // prevent delete index trigger fetching index status
-    if (data._indexType !== '' && status !== IndexState.Delete) {
-      const { state: status } = await IndexHttp.getIndexStatus(
-        collectionName,
-        data._fieldName,
-        data._indexName
-      );
-      setStatus(status);
-    }
-  }, [collectionName, data, status]);
-
-  const fetchProgress = useCallback(() => {
     if (timer) {
       clearTimeout(timer);
     }
-    if (data._indexType !== '' && isIndexCreating) {
+    // prevent delete index trigger fetching index status
+    if (data._indexType !== '' && status !== IndexState.Delete) {
       timer = setTimeout(async () => {
-        try {
-          const res = await IndexHttp.getIndexBuildProgress(
-            collectionName,
-            data._fieldName,
-            data._indexName
-          );
-
-          const { indexed_rows, total_rows } = res;
-          const percent = Number(indexed_rows) / Number(total_rows);
-          const value = Math.floor(percent * 100);
-          setCreateProgress(value);
+        const { state: status } = await IndexHttp.getIndexStatus(
+          collectionName,
+          data._fieldName,
+          data._indexName
+        );
 
-          if (value !== 100) {
-            fetchProgress();
-          } else {
-            timer && clearTimeout(timer);
-            // reset build progress
-            setCreateProgress(0);
-            // change index create status
-            setStatus(IndexState.Finished);
-          }
-        } catch (error) {
-          setStatus(IndexState.Finished);
-        }
+        status !== IndexState.Finished
+          ? fetchStatus()
+          : timer && clearTimeout(timer);
+        setStatus(status);
       }, 500);
     }
-  }, [collectionName, data, isIndexCreating]);
+  }, [collectionName, data, status]);
+
+  // const fetchProgress = useCallback(() => {
+  //   if (timer) {
+  //     clearTimeout(timer);
+  //   }
+  //   if (data._indexType !== '' && isIndexCreating) {
+  //     timer = setTimeout(async () => {
+  //       try {
+  //         const res = await IndexHttp.getIndexBuildProgress(
+  //           collectionName,
+  //           data._fieldName,
+  //           data._indexName
+  //         );
+
+  //         const { indexed_rows, total_rows } = res;
+  //         const percent = Number(indexed_rows) / Number(total_rows);
+  //         const value = Math.floor(percent * 100);
+  //         setCreateProgress(value);
+
+  //         if (value !== 100) {
+  //           fetchProgress();
+  //         } else {
+  //           timer && clearTimeout(timer);
+  //           // reset build progress
+  //           setCreateProgress(0);
+  //           // change index create status
+  //           setStatus(IndexState.Finished);
+  //         }
+  //       } catch (error) {
+  //         setStatus(IndexState.Finished);
+  //       }
+  //     }, 500);
+  //   }
+  // }, [collectionName, data, isIndexCreating]);
 
   useEffect(() => {
-    /**
-     * fetch index status, then fetch index build progress
-     */
     fetchStatus();
-    fetchProgress();
-  }, [fetchStatus, fetchProgress]);
+  }, [fetchStatus]);
 
   const requestCreateIndex = async (
     params: IndexExtraParam,
@@ -252,19 +257,18 @@ const IndexTypeElement: FC<{
          * empty string or 'delete' means fetching progress hasn't finished
          * show loading animation for such situations
          */
-        if (status === IndexState.Default || status === IndexState.Delete) {
+        if (
+          status === IndexState.Default ||
+          status === IndexState.Delete ||
+          isIndexCreating
+        ) {
           return <StatusIcon type={ChildrenStatusType.CREATING} />;
         }
+
         /**
-         * if creating not finished, show progress bar
          * if creating finished, show chip that contains index type
          */
-        return isIndexCreating ? (
-          <CustomLinearProgress
-            value={createProgress}
-            tooltip={indexTrans('creating')}
-          />
-        ) : (
+        return (
           <Chip
             label={data._indexType}
             classes={{ root: classes.chip, label: classes.chipLabel }}

+ 18 - 8
client/src/plugins/search/SearchParams.tsx

@@ -1,5 +1,5 @@
 import { makeStyles, Theme } from '@material-ui/core';
-import { FC, useCallback, useEffect, useMemo } from 'react';
+import { FC, useCallback, useContext, useEffect, useMemo } from 'react';
 import { useTranslation } from 'react-i18next';
 import CustomInput from '../../components/customInput/CustomInput';
 import { ITextfieldConfig } from '../../components/customInput/Types';
@@ -12,6 +12,7 @@ import {
   METRIC_OPTIONS_MAP,
   searchKeywordsType,
 } from '../../consts/Milvus';
+import { rootContext } from '../../context/Root';
 import { useFormValidation } from '../../hooks/Form';
 import { formatForm } from '../../utils/Form';
 import { SearchParamInputConfig, SearchParamsProps } from './Types';
@@ -50,17 +51,26 @@ const SearchParams: FC<SearchParamsProps> = ({
   const { t: warningTrans } = useTranslation('warning');
   const classes = getStyles();
 
+  const { openSnackBar } = useContext(rootContext);
   const metricOptions: Option[] = METRIC_OPTIONS_MAP[embeddingType];
 
   // search params key list, depends on index type
   // e.g. ['nprobe']
-  const searchParams = useMemo(
-    (): searchKeywordsType[] =>
-      indexType !== ''
-        ? [...INDEX_CONFIG[indexType].search, 'round_decimal']
-        : ['round_decimal'],
-    [indexType]
-  );
+  const searchParams = useMemo((): searchKeywordsType[] => {
+    const isSupportedType = Object.keys(INDEX_CONFIG).includes(indexType);
+
+    // show warning snackbar for unsupported type
+    if (!isSupportedType) {
+      indexType !== '' &&
+        openSnackBar(
+          warningTrans('noSupportIndexType', { type: indexType }),
+          'warning'
+        );
+    }
+    return indexType !== '' && isSupportedType
+      ? [...INDEX_CONFIG[indexType].search, 'round_decimal']
+      : ['round_decimal'];
+  }, [indexType, openSnackBar, warningTrans]);
 
   const handleInputChange = useCallback(
     (key: string, value: number) => {

+ 2 - 2
client/src/plugins/system/DataCard.tsx

@@ -196,8 +196,8 @@ const DataCard: FC<DataCardProps & React.HTMLAttributes<HTMLDivElement>> =
       });
     }
 
-    const { deploy_mode: mode = '' } = system_info;
-    // systemContent.push({ label: t('thVersion'), value: version });
+    const { deploy_mode: mode = '', build_version: version = '' } = system_info;
+    systemContent.push({ label: t('thVersion'), value: version });
     systemContent.push({ label: t('thDeployMode'), value: mode });
     systemContent.push({
       label: t('thCreateTime'),

+ 1 - 1
client/src/plugins/system/NodeListView.tsx

@@ -37,7 +37,7 @@ const getStyles = makeStyles((theme: Theme) => ({
     width: '100%',
     transition: 'all .25s',
     position: 'absolute',
-    zIndex: 1000,
+    // zIndex: 1000,
     backgroundColor: 'white',
   },
   childCloseBtn: {

+ 19 - 9
client/src/plugins/system/SystemView.tsx

@@ -43,7 +43,7 @@ const getStyles = makeStyles((theme: Theme) => ({
     width: '100%',
     transition: 'all .25s',
     position: 'absolute',
-    zIndex: 1000,
+    // zIndex: 1000,
     backgroundColor: 'white',
   },
   showChildView: {
@@ -75,14 +75,19 @@ const parseJson = (jsonData: any) => {
     memoryUsage: 0,
   };
 
-  jsonData?.response?.nodes_info.forEach((node: any) => {
-    const type = node?.infos?.type;
-    const has_error = node?.infos?.has_error;
+  const workingNodes = jsonData?.response?.nodes_info.filter(
+    (node: any) => node?.infos?.has_error !== true
+  );
 
-    if (has_error) {
-      return;
+  workingNodes.forEach((node: any) => {
+    const type = node?.infos?.type;
+    if (node.connected) {
+      node.connected = node.connected.filter((v: any) =>
+        workingNodes.find(
+          (item: any) => v.connected_identifier === item.identifier
+        )
+      );
     }
-
     // coordinator node
     if (type?.toLowerCase().includes('coord')) {
       nodes.push(node);
@@ -109,7 +114,7 @@ const SystemView: any = () => {
   // const { t } = useTranslation('systemView');
 
   const classes = getStyles();
-  const INTERVAL = 10000;
+  const INTERVAL = 60000;
 
   const [data, setData] = useState<{
     nodes: any;
@@ -162,7 +167,12 @@ const SystemView: any = () => {
         <LineChartCard title={t('latencyTitle')} value={latency} />
       </div> */}
       <div className={classes.contentContainer}>
-        <Topo nodes={nodes} setNode={setNode} setCord={setCord} />
+        <Topo
+          nodes={nodes}
+          childNodes={childNodes}
+          setNode={setNode}
+          setCord={setCord}
+        />
         <DataCard node={selectedNode} extend />
       </div>
 

+ 8 - 13
server/generate-csv.ts

@@ -3,19 +3,14 @@ import { createObjectCsvWriter as createCsvWriter } from 'csv-writer';
 // use to test vector insert
 const csvWriter = createCsvWriter({
   path: './vectors.csv',
-  header: [
-    { id: 'vector', title: 'vector' },
-    { id: 'name', title: 'name' },
-    { id: 'age', title: 'age' },
-    { id: 'job', title: 'job' },
-  ],
+  header: [{ id: 'vector', title: 'vector' }],
 });
 
-const records = [];
+const records: any[] = [];
 
 const generateVector = (dimension: number) => {
   let index = 0;
-  const vectors = [];
+  const vectors: any[] = [];
   while (index < dimension) {
     vectors.push(1 + Math.random());
     index++;
@@ -23,13 +18,13 @@ const generateVector = (dimension: number) => {
   return JSON.stringify(vectors);
 };
 
-while (records.length < 50000) {
-  const value = generateVector(4);
+while (records.length < 1000) {
+  const value = generateVector(960);
   records.push({
     vector: value,
-    name: `${records.length}_id`,
-    age: records.length * 2,
-    job: Math.random() * 1000 > 500 ? 'designer' : 'programer',
+    // name: `${records.length}_id`,
+    // age: records.length * 2,
+    // job: Math.random() * 1000 > 500 ? 'designer' : 'programer',
   });
 }
 

+ 2 - 2
server/src/collections/collections.service.ts

@@ -164,11 +164,11 @@ export class CollectionsService {
         });
 
         const autoID = collectionInfo.schema.fields.find(
-          (v) => v.is_primary_key === true
+          v => v.is_primary_key === true
         )?.autoID;
 
         const loadCollection = loadedCollections.data.find(
-          (v) => v.name === name
+          v => v.name === name
         );
 
         const loadedPercentage = !loadCollection