Browse Source

Merge pull request #222 from nameczz/dev

Add loading status
ryjiang 3 years ago
parent
commit
e4febb8b5e

+ 10 - 2
client/src/components/cards/EmptyCard.tsx

@@ -1,5 +1,7 @@
+import React, { FC } from 'react';
 import { makeStyles, Theme, Typography } from '@material-ui/core';
-import { FC } from 'react';
+import StatusIcon from '../status/StatusIcon';
+import { ChildrenStatusType } from '../status/Types';
 import { EmptyCardProps } from './Types';
 
 const useStyles = makeStyles((theme: Theme) => ({
@@ -17,13 +19,19 @@ const useStyles = makeStyles((theme: Theme) => ({
   },
 }));
 
-const EmptyCard: FC<EmptyCardProps> = ({ icon, text, wrapperClass = '' }) => {
+const EmptyCard: FC<EmptyCardProps> = ({
+  icon,
+  text,
+  wrapperClass = '',
+  loading = false,
+}) => {
   const classes = useStyles();
 
   return (
     <section
       className={`flex-center card-wrapper ${classes.wrapper} ${wrapperClass}`}
     >
+      {loading && <StatusIcon type={ChildrenStatusType.CREATING} size={40} />}
       {icon}
       <Typography className={classes.text}>{text}</Typography>
     </section>

+ 2 - 1
client/src/components/cards/Types.ts

@@ -1,7 +1,8 @@
 import { ReactElement } from 'react';
 
 export interface EmptyCardProps {
-  icon: ReactElement;
   text: string;
+  icon?: ReactElement;
   wrapperClass?: string;
+  loading?: boolean;
 }

+ 23 - 8
client/src/components/status/Status.tsx

@@ -1,7 +1,9 @@
 import { FC, useMemo } from 'react';
-import { StatusType, StatusEnum } from './Types';
+import { ChildrenStatusType, StatusType } from './Types';
 import { useTranslation } from 'react-i18next';
 import { makeStyles, Theme, createStyles, Typography } from '@material-ui/core';
+import { LOADING_STATE } from '../../consts/Milvus';
+import StatusIcon from './StatusIcon';
 
 const useStyles = makeStyles((theme: Theme) =>
   createStyles({
@@ -21,6 +23,10 @@ const useStyles = makeStyles((theme: Theme) =>
       marginRight: theme.spacing(0.5),
     },
 
+    loading: {
+      marginRight: '10px',
+    },
+
     flash: {
       animation: '$bgColorChange 1.5s infinite',
     },
@@ -40,25 +46,25 @@ const useStyles = makeStyles((theme: Theme) =>
 );
 
 const Status: FC<StatusType> = props => {
-  const { status } = props;
+  const { status, percentage = 0 } = props;
   const { t: commonTrans } = useTranslation();
   const statusTrans = commonTrans('status');
   const { label, color } = useMemo(() => {
     switch (status) {
-      case StatusEnum.unloaded:
+      case LOADING_STATE.UNLOADED:
         return {
           label: statusTrans.unloaded,
           color: '#06aff2',
         };
 
-      case StatusEnum.loaded:
+      case LOADING_STATE.LOADED:
         return {
           label: statusTrans.loaded,
           color: '#06f3af',
         };
-      case StatusEnum.error:
+      case LOADING_STATE.LOADING:
         return {
-          label: statusTrans.error,
+          label: `${percentage}% ${statusTrans.loading}`,
           color: '#f25c06',
         };
 
@@ -68,13 +74,22 @@ const Status: FC<StatusType> = props => {
           color: '#f25c06',
         };
     }
-  }, [status, statusTrans]);
+  }, [status, statusTrans, percentage]);
 
   const classes = useStyles({ color });
 
   return (
     <div className={classes.root}>
-      {status === StatusEnum.loaded && <div className={classes.circle}></div>}
+      {status === LOADING_STATE.LOADED && (
+        <div className={classes.circle}></div>
+      )}
+      {status === LOADING_STATE.LOADING && (
+        <StatusIcon
+          type={ChildrenStatusType.CREATING}
+          className={classes.loading}
+        />
+      )}
+
       <Typography variant="body2" className={classes.label}>
         {label}
       </Typography>

+ 5 - 3
client/src/components/status/StatusIcon.tsx

@@ -16,14 +16,14 @@ const useStyles = makeStyles((theme: Theme) => ({
 
 const StatusIcon: FC<StatusIconType> = props => {
   const classes = useStyles();
-  const { type } = props;
+  const { type, className = '', size = 20 } = props;
 
   const getElement = (type: ChildrenStatusType): ReactElement => {
     switch (type) {
       case 'creating':
         return (
           <CircularProgress
-            size={20}
+            size={size}
             thickness={8}
             classes={{ svg: classes.svg }}
           />
@@ -35,7 +35,9 @@ const StatusIcon: FC<StatusIconType> = props => {
     }
   };
 
-  return <div className={classes.wrapper}>{getElement(type)}</div>;
+  return (
+    <div className={`${classes.wrapper} ${className}`}>{getElement(type)}</div>
+  );
 };
 
 export default StatusIcon;

+ 11 - 6
client/src/components/status/Types.ts

@@ -1,10 +1,13 @@
-export enum StatusEnum {
-  'unloaded',
-  'loaded',
-  'error',
-}
+import { LOADING_STATE } from '../../consts/Milvus';
+
+// export enum StatusEnum {
+//   'unloaded',
+//   'loaded',
+//   'error',
+// }
 export type StatusType = {
-  status: StatusEnum;
+  status: LOADING_STATE;
+  percentage?: string;
 };
 
 // @todo need rename
@@ -16,4 +19,6 @@ export enum ChildrenStatusType {
 
 export type StatusIconType = {
   type: ChildrenStatusType;
+  className?: string;
+  size?: number;
 };

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

@@ -194,3 +194,9 @@ export const DEFAULT_SEARCH_PARAM_VALUE_MAP: {
 };
 
 export const DEFAULT_NLIST_VALUE = 1024;
+
+export enum LOADING_STATE {
+  LOADED,
+  LOADING,
+  UNLOADED,
+}

+ 3 - 4
client/src/hooks/Dialog.tsx

@@ -2,10 +2,9 @@ import { ReactElement, useContext } from 'react';
 import { useTranslation } from 'react-i18next';
 import { Typography } from '@material-ui/core';
 import { rootContext } from '../context/Root';
-import { CollectionView } from '../pages/collections/Types';
+import { CollectionData, CollectionView } from '../pages/collections/Types';
 import { PartitionView } from '../pages/partitions/Types';
-import { StatusEnum } from '../components/status/Types';
-import { CollectionData } from '../pages/overview/collectionCard/Types';
+import { LOADING_STATE } from '../consts/Milvus';
 
 // handle release and load dialog
 export interface LoadAndReleaseDialogHookProps {
@@ -53,7 +52,7 @@ export const useLoadAndReleaseDialogHook = (
     cb: (data: any) => Promise<any>
   ) => {
     const actionType: 'release' | 'load' =
-      data._status === StatusEnum.loaded ? 'release' : 'load';
+      data._status === LOADING_STATE.UNLOADED ? 'load' : 'release';
     const { title, component, confirmLabel } = actionsMap[actionType];
 
     setDialog({

+ 13 - 6
client/src/http/Collection.ts

@@ -1,4 +1,4 @@
-import { ChildrenStatusType, StatusEnum } from '../components/status/Types';
+import { ChildrenStatusType } from '../components/status/Types';
 import { CollectionView, InsertDataParam } from '../pages/collections/Types';
 import { Field } from '../pages/schema/Types';
 import { VectorSearchParam } from '../pages/seach/Types';
@@ -7,6 +7,7 @@ import { formatNumber } from '../utils/Common';
 import BaseModel from './BaseModel';
 import { FieldHttp } from './Field';
 import dayjs from 'dayjs';
+import { LOADING_STATE } from '../consts/Milvus';
 
 export class CollectionHttp extends BaseModel implements CollectionView {
   private autoID!: string;
@@ -15,7 +16,7 @@ export class CollectionHttp extends BaseModel implements CollectionView {
   private rowCount!: string;
   private index_status!: string;
   private id!: string;
-  private isLoaded!: boolean;
+  private loadedPercentage!: string;
   private createdTime!: string;
   private schema!: {
     fields: Field[];
@@ -110,12 +111,18 @@ export class CollectionHttp extends BaseModel implements CollectionView {
     return formatNumber(Number(this.rowCount));
   }
 
-  get _isLoaded() {
-    return this.isLoaded;
+  get _loadedPercentage() {
+    return this.loadedPercentage;
   }
-
+  // load status
   get _status() {
-    return this.isLoaded === true ? StatusEnum.loaded : StatusEnum.unloaded;
+    // If not load, insight server will return '-1'. Otherwise milvus will return percentage
+    return this._loadedPercentage === '-1'
+      ? LOADING_STATE.UNLOADED
+      : this._loadedPercentage === '100'
+      ? LOADING_STATE.LOADED
+      : LOADING_STATE.LOADING;
+    // return LOADING_STATE.LOADING
   }
 
   get _fields() {

+ 2 - 2
client/src/http/Partition.ts

@@ -1,5 +1,5 @@
 import dayjs from 'dayjs';
-import { StatusEnum } from '../components/status/Types';
+import { LOADING_STATE } from '../consts/Milvus';
 import {
   PartitionManageParam,
   PartitionParam,
@@ -82,7 +82,7 @@ export class PartitionHttp extends BaseModel implements PartitionData {
 
   get _status() {
     // @TODO replace mock data
-    return StatusEnum.unloaded;
+    return LOADING_STATE.UNLOADED
   }
 
   // Befor milvus-2.0-rc3  will return '0'.

+ 2 - 1
client/src/i18n/cn/common.ts

@@ -8,6 +8,7 @@ const commonTrans = {
     unloaded: 'unloaded',
     error: 'error',
     running: 'running',
+    loading: 'is loaded',
   },
   grid: {
     action: 'action',
@@ -35,7 +36,7 @@ const commonTrans = {
     qr: 'Scan WeChat QR code to get access',
     more: 'More Channels to Explore',
     join: 'Join our growing social community today',
-    get: 'Get insight, tips and share ideas'
+    get: 'Get insight, tips and share ideas',
   },
 };
 

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

@@ -3,6 +3,7 @@ const overviewTrans = {
   all: 'All Collections',
   data: 'Data',
   rows: '{{number}} Entities',
+  loading: 'Loading Collections',
 };
 
 export default overviewTrans;

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

@@ -8,6 +8,7 @@ const commonTrans = {
     unloaded: 'unloaded',
     error: 'error',
     running: 'running',
+    loading: 'is loaded',
   },
   grid: {
     action: 'action',
@@ -35,7 +36,7 @@ const commonTrans = {
     qr: 'Scan WeChat QR code to get access',
     more: 'More Channels to Explore',
     join: 'Join our growing social community today',
-    get: 'Get insight, tips and share ideas'
+    get: 'Get insight, tips and share ideas',
   },
 };
 

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

@@ -3,6 +3,7 @@ const overviewTrans = {
   all: 'All Collections',
   data: 'Data',
   rows: '{{number}} Entities',
+  loading: 'Loading Collections',
 };
 
 export default overviewTrans;

+ 14 - 5
client/src/pages/collections/Collections.tsx

@@ -16,7 +16,7 @@ import icons from '../../components/icons/Icons';
 import EmptyCard from '../../components/cards/EmptyCard';
 import Status from '../../components/status/Status';
 import { useTranslation } from 'react-i18next';
-import { ChildrenStatusType, StatusEnum } from '../../components/status/Types';
+import { ChildrenStatusType } from '../../components/status/Types';
 import { makeStyles, Theme } from '@material-ui/core';
 import StatusIcon from '../../components/status/StatusIcon';
 import CustomToolTip from '../../components/customToolTip/CustomToolTip';
@@ -32,6 +32,7 @@ import Highlighter from 'react-highlight-words';
 import { parseLocationSearch } from '../../utils/Format';
 import InsertContainer from '../../components/insert/Container';
 import { MilvusHttp } from '../../http/Milvus';
+import { LOADING_STATE } from '../../consts/Milvus';
 
 const useStyles = makeStyles((theme: Theme) => ({
   emptyWrapper: {
@@ -112,7 +113,9 @@ const Collections = () => {
               />
             </Link>
           ),
-          statusElement: <Status status={v._status} />,
+          statusElement: (
+            <Status status={v._status} percentage={v._loadedPercentage} />
+          ),
           indexCreatingElement: (
             <StatusIcon
               type={indexStatus?._indexState || ChildrenStatusType.FINISH}
@@ -382,16 +385,22 @@ const Collections = () => {
         {
           onClick: (e: React.MouseEvent, row: CollectionView) => {
             const cb =
-              row._status === StatusEnum.unloaded ? handleLoad : handleRelease;
+              row._status === LOADING_STATE.UNLOADED
+                ? handleLoad
+                : handleRelease;
             handleAction(row, cb);
           },
           icon: 'load',
           label: 'load',
           showIconMethod: 'renderFn',
           getLabel: (row: CollectionView) =>
-            row._status === StatusEnum.loaded ? 'release' : 'load',
+            row._status === LOADING_STATE.UNLOADED ? 'load' : 'release',
           renderIconFn: (row: CollectionView) =>
-            row._status === StatusEnum.loaded ? <ReleaseIcon /> : <LoadIcon />,
+            row._status === LOADING_STATE.UNLOADED ? (
+              <LoadIcon />
+            ) : (
+              <ReleaseIcon />
+            ),
         },
       ],
     },

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

@@ -1,16 +1,17 @@
 import { Dispatch, ReactElement, SetStateAction } from 'react';
-import { ChildrenStatusType, StatusEnum } from '../../components/status/Types';
+import { ChildrenStatusType } from '../../components/status/Types';
+import { LOADING_STATE } from '../../consts/Milvus';
 import { FieldData } from '../schema/Types';
 
 export interface CollectionData {
   _name: string;
   _id: string;
-  _status: StatusEnum;
+  _loadedPercentage:string;
+  _status: LOADING_STATE;
   _rowCount: string;
   _desc: string;
   _indexState: ChildrenStatusType;
   _fields?: FieldData[];
-  _isLoaded: boolean;
 }
 
 export interface CollectionView extends CollectionData {
@@ -75,7 +76,7 @@ export interface CreateFieldsProps {
   fields: Field[];
   setFields: Dispatch<SetStateAction<Field[]>>;
   setFieldsValidation: Dispatch<
-    SetStateAction<{ [x: string]: string | boolean }[]>
+    SetStateAction<{ [x: string]: string | boolean; }[]>
   >;
   autoID: boolean;
   setAutoID: (value: boolean) => void;

+ 16 - 16
client/src/pages/overview/Overview.tsx

@@ -3,15 +3,15 @@ import { useCallback, useContext, useEffect, useMemo, useState } from 'react';
 import { useTranslation } from 'react-i18next';
 import EmptyCard from '../../components/cards/EmptyCard';
 import icons from '../../components/icons/Icons';
-import { StatusEnum } from '../../components/status/Types';
+import { LOADING_STATE } from '../../consts/Milvus';
 import { rootContext } from '../../context/Root';
 import { useLoadAndReleaseDialogHook } from '../../hooks/Dialog';
 import { useNavigationHook } from '../../hooks/Navigation';
 import { CollectionHttp } from '../../http/Collection';
 import { ALL_ROUTER_TYPES } from '../../router/Types';
 import { formatNumber } from '../../utils/Common';
+import { CollectionData } from '../collections/Types';
 import CollectionCard from './collectionCard/CollectionCard';
-import { CollectionData } from './collectionCard/Types';
 import StatisticsCard from './statisticsCard/StatisticsCard';
 
 const useStyles = makeStyles((theme: Theme) => ({
@@ -42,16 +42,21 @@ const Overview = () => {
     collectionCount: 0,
     totalData: 0,
   });
+  const [loading, setLoading] = useState(false);
 
   const [loadCollections, setLoadCollections] = useState<CollectionHttp[]>([]);
   const { openSnackBar } = useContext(rootContext);
 
   const fetchData = useCallback(async () => {
+    setLoading(true);
     const res = await CollectionHttp.getStatistics();
     const collections = await CollectionHttp.getCollections();
-    const loadCollections = collections.filter(c => c._isLoaded);
+    const loadCollections = collections.filter(
+      c => c._status !== LOADING_STATE.UNLOADED
+    );
     setStatistics(res);
     setLoadCollections(loadCollections);
+    setLoading(false);
   }, []);
 
   useEffect(() => {
@@ -96,15 +101,6 @@ const Overview = () => {
     };
   }, [overviewTrans, statistics, loadCollections]);
 
-  const loadCollectionsData: CollectionData[] = useMemo(() => {
-    return loadCollections.map(v => ({
-      _id: v._id,
-      _name: v._name,
-      _status: StatusEnum.loaded,
-      _rowCount: v._rowCount,
-    }));
-  }, [loadCollections]);
-
   const CollectionIcon = icons.navCollection;
 
   return (
@@ -113,9 +109,10 @@ const Overview = () => {
       <Typography className={classes.collectionTitle}>
         {overviewTrans('load')}
       </Typography>
-      {loadCollectionsData.length > 0 ? (
+
+      {loadCollections.length > 0 ? (
         <div className={classes.cardsWrapper}>
-          {loadCollectionsData.map(collection => (
+          {loadCollections.map(collection => (
             <CollectionCard
               key={collection._id}
               data={collection}
@@ -125,9 +122,12 @@ const Overview = () => {
         </div>
       ) : (
         <EmptyCard
+          loading={loading}
           wrapperClass="page-empty-card"
-          icon={<CollectionIcon />}
-          text={collectionTrans('noLoadData')}
+          icon={!loading ? <CollectionIcon /> : undefined}
+          text={
+            loading ? overviewTrans('loading') : collectionTrans('noLoadData')
+          }
         />
       )}
     </section>

+ 17 - 3
client/src/pages/overview/collectionCard/CollectionCard.tsx

@@ -14,12 +14,17 @@ import { CollectionCardProps } from './Types';
 import { useTranslation } from 'react-i18next';
 import CustomIconButton from '../../../components/customButton/CustomIconButton';
 import { useHistory } from 'react-router-dom';
+import { LOADING_STATE } from '../../../consts/Milvus';
 
 const useStyles = makeStyles((theme: Theme) => ({
   wrapper: {
     padding: theme.spacing(2),
     textAlign: 'end',
   },
+  loading: {
+    background: '#F0F4F9',
+    border: `1px dashed #06AFF2`,
+  },
   link: {
     display: 'flex',
     alignItems: 'center',
@@ -77,7 +82,12 @@ const CollectionCard: FC<CollectionCardProps> = ({
   wrapperClass = '',
 }) => {
   const classes = useStyles();
-  const { _name: name, _status: status, _rowCount: rowCount } = data;
+  const {
+    _name: name,
+    _status: status,
+    _rowCount: rowCount,
+    _loadedPercentage,
+  } = data;
   const history = useHistory();
   // icons
   const RightArrowIcon = icons.rightArrow;
@@ -97,9 +107,13 @@ const CollectionCard: FC<CollectionCardProps> = ({
   };
 
   return (
-    <div className={`card-wrapper ${classes.wrapper} ${wrapperClass}`}>
+    <div
+      className={`card-wrapper ${classes.wrapper} ${wrapperClass} ${
+        data._status === LOADING_STATE.LOADING && classes.loading
+      }`}
+    >
       <div>
-        <Status status={status} />
+        <Status status={status} percentage={_loadedPercentage} />
       </div>
       <Link
         classes={{ root: classes.link }}

+ 1 - 8
client/src/pages/overview/collectionCard/Types.ts

@@ -1,14 +1,7 @@
-import { StatusEnum } from '../../../components/status/Types';
+import { CollectionData } from '../../collections/Types';
 
 export interface CollectionCardProps {
   data: CollectionData;
   handleRelease: (data: CollectionData) => void;
   wrapperClass?: string;
 }
-
-export interface CollectionData {
-  _name: string;
-  _status: StatusEnum;
-  _id: string;
-  _rowCount: string;
-}

+ 2 - 2
client/src/pages/partitions/Types.ts

@@ -1,11 +1,11 @@
 import { ReactElement } from 'react';
-import { StatusEnum } from '../../components/status/Types';
+import { LOADING_STATE } from '../../consts/Milvus';
 import { ManageRequestMethods } from '../../types/Common';
 
 export interface PartitionData {
   _id: string;
   _name: string;
-  _status: StatusEnum;
+  _status: LOADING_STATE;
   _rowCount: string;
   _formatName: string;
 }

+ 2 - 2
server/generate-csv.ts

@@ -18,8 +18,8 @@ const generateVector = (dimension) => {
   return JSON.stringify(vectors);
 };
 
-while (records.length < 5000) {
-  const value = generateVector(128);
+while (records.length < 100000) {
+  const value = generateVector(4);
   records.push({ vector: value });
 }
 

+ 8 - 4
server/src/collections/collections.service.ts

@@ -13,7 +13,7 @@ import {
 } from '@zilliz/milvus2-sdk-node/dist/milvus/types';
 import { throwErrorFromSDK } from '../utils/Error';
 import { findKeyValue } from '../utils/Helper';
-import { ROW_COUNT } from '../utils/Const';
+import { LOADING_STATE, ROW_COUNT } from '../utils/Const';
 import {
   ShowCollectionsReq,
   ShowCollectionsType,
@@ -122,7 +122,13 @@ export class CollectionsService {
         const autoID = collectionInfo.schema.fields.find(
           (v) => v.is_primary_key === true,
         )?.autoID;
+        const loadCollection = loadedCollections.data.find(
+          (v) => v.name === name,
+        );
 
+        const loadedPercentage = !loadCollection
+          ? '-1'
+          : loadCollection.loadedPercentage;
         data.push({
           collection_name: name,
           schema: collectionInfo.schema,
@@ -130,9 +136,7 @@ export class CollectionsService {
           autoID,
           rowCount: findKeyValue(collectionStatistics.stats, ROW_COUNT),
           id: collectionInfo.collectionID,
-          isLoaded: !!loadedCollections.data.find(
-            (v) => v.name === name && Number(v.loadedPercentage) === 100,
-          ),
+          loadedPercentage,
           createdTime: collectionInfo.created_utc_timestamp,
         });
       }

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

@@ -21,6 +21,7 @@ enum VectorTypes {
   Binary = DataType.BinaryVector,
   Float = DataType.FloatVector,
 }
+
 export class CreateCollection {
   @ApiProperty({
     description: 'Milvus collection name',

+ 6 - 0
server/src/utils/Const.ts

@@ -1 +1,7 @@
 export const ROW_COUNT = 'row_count';
+
+export enum LOADING_STATE {
+  LOADED,
+  LOADING,
+  UNLOADED,
+}