Browse Source

- support create index on collections page
- code refactor

Signed-off-by: ryjiang <jiangruiyi@gmail.com>

ryjiang 1 year ago
parent
commit
4727d5d1de
73 changed files with 866 additions and 812 deletions
  1. 3 3
      client/src/components/customSelector/Types.ts
  2. 3 3
      client/src/components/layout/Header.tsx
  3. 10 5
      client/src/components/status/Types.ts
  4. 2 2
      client/src/context/Auth.tsx
  5. 5 5
      client/src/context/Data.tsx
  6. 2 2
      client/src/context/Prometheus.tsx
  7. 2 2
      client/src/context/Root.tsx
  8. 3 3
      client/src/context/Types.ts
  9. 6 7
      client/src/context/WebSocket.tsx
  10. 7 7
      client/src/http/BaseModel.ts
  11. 56 133
      client/src/http/Collection.ts
  12. 44 0
      client/src/http/Data.service.ts
  13. 3 9
      client/src/http/Database.ts
  14. 17 31
      client/src/http/Field.ts
  15. 1 11
      client/src/http/Milvus.service.ts
  16. 8 18
      client/src/http/MilvusIndex.ts
  17. 10 18
      client/src/http/Partition.ts
  18. 3 3
      client/src/http/Prometheus.service.ts
  19. 38 0
      client/src/http/Segment.ts
  20. 2 6
      client/src/http/User.ts
  21. 7 2
      client/src/http/index.ts
  22. 5 0
      client/src/i18n/cn/collection.ts
  23. 5 0
      client/src/i18n/en/collection.ts
  24. 1 1
      client/src/i18n/en/index.ts
  25. 2 2
      client/src/pages/collections/Aliases.tsx
  26. 0 1
      client/src/pages/collections/Collection.tsx
  27. 87 120
      client/src/pages/collections/Collections.tsx
  28. 167 0
      client/src/pages/collections/StatusAction.tsx
  29. 6 30
      client/src/pages/collections/Types.ts
  30. 2 2
      client/src/pages/connect/AuthForm.tsx
  31. 6 6
      client/src/pages/database/Database.tsx
  32. 11 7
      client/src/pages/dialogs/CompactDialog.tsx
  33. 2 2
      client/src/pages/dialogs/CreateAliasDialog.tsx
  34. 5 5
      client/src/pages/dialogs/CreateCollectionDialog.tsx
  35. 2 2
      client/src/pages/dialogs/CreatePartitionDialog.tsx
  36. 2 2
      client/src/pages/dialogs/DropCollectionDialog.tsx
  37. 2 2
      client/src/pages/dialogs/DropPartitionDialog.tsx
  38. 4 3
      client/src/pages/dialogs/ImportSampleDialog.tsx
  39. 3 3
      client/src/pages/dialogs/LoadCollectionDialog.tsx
  40. 2 2
      client/src/pages/dialogs/ReleaseCollectionDialog.tsx
  41. 2 2
      client/src/pages/dialogs/RenameCollectionDialog.tsx
  42. 2 2
      client/src/pages/dialogs/Types.ts
  43. 13 14
      client/src/pages/dialogs/insert/Dialog.tsx
  44. 3 2
      client/src/pages/dialogs/insert/Preview.tsx
  45. 3 4
      client/src/pages/dialogs/insert/Types.ts
  46. 6 6
      client/src/pages/overview/Overview.tsx
  47. 13 15
      client/src/pages/overview/collectionCard/CollectionCard.tsx
  48. 2 2
      client/src/pages/overview/collectionCard/Types.ts
  49. 13 16
      client/src/pages/partitions/Partitions.tsx
  50. 5 5
      client/src/pages/partitions/Types.ts
  51. 19 19
      client/src/pages/preview/Preview.tsx
  52. 9 11
      client/src/pages/query/Query.tsx
  53. 62 43
      client/src/pages/schema/IndexTypeElement.tsx
  54. 15 38
      client/src/pages/schema/Schema.tsx
  55. 1 40
      client/src/pages/schema/Types.ts
  56. 3 6
      client/src/pages/search/Types.ts
  57. 31 28
      client/src/pages/search/VectorSearch.tsx
  58. 9 10
      client/src/pages/segments/Segments.tsx
  59. 3 3
      client/src/pages/system/SystemView.tsx
  60. 2 2
      client/src/pages/systemHealthy/SystemHealthyView.tsx
  61. 3 3
      client/src/pages/user/Roles.tsx
  62. 4 4
      client/src/pages/user/UpdateRoleDialog.tsx
  63. 3 3
      client/src/pages/user/UpdateUserRole.tsx
  64. 8 8
      client/src/pages/user/User.tsx
  65. 0 16
      client/src/types/Milvus.ts
  66. 2 2
      client/src/types/SearchTypes.ts
  67. 10 11
      client/src/utils/Format.ts
  68. 3 2
      client/src/utils/Validation.ts
  69. 17 16
      client/src/utils/search.ts
  70. 3 3
      server/src/collections/collections.controller.ts
  71. 17 16
      server/src/collections/collections.service.ts
  72. 20 0
      server/src/types/collections.type.ts
  73. 14 0
      server/src/types/index.ts

+ 3 - 3
client/src/components/customSelector/Types.ts

@@ -1,9 +1,9 @@
 import { FormControlClassKey, SelectProps } from '@material-ui/core';
 import { ClassNameMap } from '@material-ui/core/styles/withStyles';
 
-export interface Option {
-  label: string;
-  value: string | number;
+export interface Option<T = string, U = string | number> {
+  label: T;
+  value: U;
 }
 
 export interface GroupOption {

+ 3 - 3
client/src/components/layout/Header.tsx

@@ -10,7 +10,7 @@ import {
 import AccountCircleIcon from '@material-ui/icons/AccountCircle';
 import { useNavigate } from 'react-router-dom';
 import { navContext, dataContext, authContext } from '@/context';
-import { MilvusHttp } from '@/http';
+import { MilvusService } from '@/http';
 import { MILVUS_ADDRESS, LOGIN_USERNAME } from '@/consts';
 import CustomSelector from '@/components/customSelector/CustomSelector';
 import icons from '../icons/Icons';
@@ -90,7 +90,7 @@ const Header: FC<HeaderType> = props => {
     setAddress('');
     setUsername('');
     setIsAuth(false);
-    await MilvusHttp.closeConnection();
+    await MilvusService.closeConnection();
     window.localStorage.removeItem(MILVUS_ADDRESS);
     window.localStorage.removeItem(LOGIN_USERNAME);
     // make sure we clear state in all pages
@@ -98,7 +98,7 @@ const Header: FC<HeaderType> = props => {
   };
 
   const useDatabase = async (database: string) => {
-    await MilvusHttp.useDatabase({ database });
+    await MilvusService.useDatabase({ database });
   };
 
   const dbOptions = databases.map(d => ({ value: d, label: d }));

+ 10 - 5
client/src/components/status/Types.ts

@@ -1,15 +1,20 @@
 import { LOADING_STATE } from '@/consts';
+import { FieldHttp } from '@/http';
 
-// export enum StatusEnum {
-//   'unloaded',
-//   'loaded',
-//   'error',
-// }
 export type StatusType = {
   status: LOADING_STATE;
   percentage?: string;
 };
 
+export type StatusActionType = {
+  status: LOADING_STATE;
+  percentage?: string;
+  action?: Function;
+  field: FieldHttp;
+  collectionName: string;
+  onIndexCreate: Function;
+};
+
 // @todo need rename
 export enum ChildrenStatusType {
   CREATING = 'creating',

+ 2 - 2
client/src/context/Auth.tsx

@@ -1,6 +1,6 @@
 import { createContext, useEffect, useState } from 'react';
 import { MILVUS_ADDRESS, LOGIN_USERNAME } from '@/consts';
-import { MilvusHttp } from '@/http';
+import { MilvusService } from '@/http';
 import { AuthContextType } from './Types';
 
 export const authContext = createContext<AuthContextType>({
@@ -33,7 +33,7 @@ export const AuthProvider = (props: { children: React.ReactNode }) => {
       if (!milvusAddress) {
         return;
       }
-      const res = await MilvusHttp.check(milvusAddress);
+      const res = await MilvusService.check(milvusAddress);
       setAddress(res.connected ? milvusAddress : '');
       res.connected && setIsAuth(true);
       if (!res.connected) {

+ 5 - 5
client/src/context/Data.tsx

@@ -1,5 +1,5 @@
 import { createContext, useEffect, useState, useContext } from 'react';
-import { DatabaseHttp, UserHttp, MilvusHttp } from '@/http';
+import { Database, User, MilvusService } from '@/http';
 import { parseJson, getNode, getSystemConfigs } from '@/utils';
 import { MILVUS_NODE_TYPE } from '@/consts';
 import { authContext } from '@/context';
@@ -24,10 +24,10 @@ export const DataProvider = (props: { children: React.ReactNode }) => {
     try {
       // fetch all data
       const [databases, metrics, users, roles] = await Promise.all([
-        DatabaseHttp.getDatabases(),
-        MilvusHttp.getMetrics(),
-        UserHttp.getUsers(),
-        UserHttp.getRoles(),
+        Database.getDatabases(),
+        MilvusService.getMetrics(),
+        User.getUsers(),
+        User.getRoles(),
       ]);
 
       // parse data

+ 2 - 2
client/src/context/Prometheus.tsx

@@ -13,7 +13,7 @@ import {
   WITH_PROMETHEUS,
 } from '@/consts';
 import { formatPrometheusAddress } from '@/utils';
-import { PrometheusHttp } from '@/http';
+import { PrometheusService } from '@/http';
 
 export const prometheusContext = createContext<PrometheusContextType>({
   withPrometheus: false,
@@ -59,7 +59,7 @@ export const PrometheusProvider = (props: { children: React.ReactNode }) => {
     if (withPrometheus) {
       const prometheusAddressformat =
         formatPrometheusAddress(prometheusAddress);
-      PrometheusHttp.setPrometheus({
+      PrometheusService.setPrometheus({
         prometheusAddress: prometheusAddressformat,
         prometheusInstance,
         prometheusNamespace,

+ 2 - 2
client/src/context/Root.tsx

@@ -11,7 +11,7 @@ import {
 } from './Types';
 import CustomSnackBar from '@/components/customSnackBar/CustomSnackBar';
 import CustomDialog from '@/components/customDialog/CustomDialog';
-import { MilvusHttp } from '@/http';
+import { MilvusService } from '@/http';
 import { theme } from '../styles/theme';
 
 const DefaultDialogConfigs: DialogType = {
@@ -115,7 +115,7 @@ export const RootProvider = (props: { children: React.ReactNode }) => {
   useEffect(() => {
     if (isAuth) {
       const fetchVersion = async () => {
-        const res = await MilvusHttp.getVersion();
+        const res = await MilvusService.getVersion();
         setVersionInfo(res);
       };
       fetchVersion();

+ 3 - 3
client/src/context/Types.ts

@@ -1,5 +1,5 @@
 import { Dispatch, ReactElement, SetStateAction } from 'react';
-import { CollectionView } from '@/pages/collections/Types';
+import { Collection } from '@/http';
 import { NavInfo } from '@/router/Types';
 
 export type RootContextType = {
@@ -90,6 +90,6 @@ export type NavContextType = {
 };
 
 export type WebSocketType = {
-  collections: CollectionView[];
-  setCollections: (data: CollectionView[]) => void;
+  collections: Collection[];
+  setCollections: (data: Collection[]) => void;
 };

+ 6 - 7
client/src/context/WebSocket.tsx

@@ -1,8 +1,7 @@
 import { createContext, useContext, useEffect, useState, useRef } from 'react';
 import { io, Socket } from 'socket.io-client';
 import { authContext } from '@/context';
-import { url, CollectionHttp, MilvusHttp } from '@/http';
-import { CollectionView } from '@/pages/collections/Types';
+import { url, Collection, MilvusService } from '@/http';
 import { checkIndexBuilding, checkLoading } from '@/utils';
 import { WebSocketType } from './Types';
 import { WS_EVENTS, WS_EVENTS_TYPE } from '@server/utils/Const';
@@ -15,13 +14,13 @@ export const webSocketContext = createContext<WebSocketType>({
 const { Provider } = webSocketContext;
 
 export const WebSocketProvider = (props: { children: React.ReactNode }) => {
-  const [collections, setCollections] = useState<CollectionView[]>([]);
+  const [collections, setCollections] = useState<Collection[]>([]);
   const { isAuth } = useContext(authContext);
   const socket = useRef<Socket | null>(null);
 
   useEffect(() => {
     if (isAuth) {
-      socket.current = io(url);
+      socket.current = io(url as string);
 
       socket.current.on('connect', function () {
         console.log('--- ws connected ---');
@@ -34,8 +33,8 @@ export const WebSocketProvider = (props: { children: React.ReactNode }) => {
        * After all collections are not loading or building index, tell server stop pulling data.
        */
       socket.current.on(WS_EVENTS.COLLECTION, (data: any) => {
-        const collections: CollectionHttp[] = data.map(
-          (v: any) => new CollectionHttp(v)
+        const collections: Collection[] = data.map(
+          (v: any) => new Collection(v)
         );
 
         const hasLoadingOrBuildingCollection = collections.some(
@@ -46,7 +45,7 @@ export const WebSocketProvider = (props: { children: React.ReactNode }) => {
         // If no collection is building index or loading collection
         // stop server cron job
         if (!hasLoadingOrBuildingCollection) {
-          MilvusHttp.triggerCron({
+          MilvusService.triggerCron({
             name: WS_EVENTS.COLLECTION,
             type: WS_EVENTS_TYPE.STOP,
           });

+ 7 - 7
client/src/http/BaseModel.ts

@@ -41,7 +41,7 @@ export default class BaseModel {
     );
   }
 
-  static async search(data: findParamsType) {
+  static async search<T>(data: findParamsType) {
     const { method = 'get', params = {}, path = '', timeout } = data;
     const httpConfig = {
       method,
@@ -50,26 +50,26 @@ export default class BaseModel {
     } as any;
     if (timeout) httpConfig.timeout = timeout;
     const res = await http(httpConfig);
-    return new this(res.data.data || {});
+    return new this(res.data.data || {}) as T;
   }
 
   /**
    * Create instance in database
    */
-  static async create(options: updateParamsType) {
+  static async create<T>(options: updateParamsType) {
     const { path, data } = options;
     const res = await http.post(path, data);
-    return new this(res.data.data || {});
+    return new this(res.data.data || {}) as T;
   }
 
-  static async update(options: updateParamsType) {
+  static async update<T>(options: updateParamsType) {
     const { path, data } = options;
     const res = await http.put(path, data);
 
-    return new this(res.data.data || {});
+    return new this(res.data.data || {}) as T;
   }
 
-  static async delete(options: updateParamsType) {
+  static async delete<T>(options: updateParamsType) {
     const { path, data } = options;
 
     const res = await http.delete(path, { data: data });

+ 56 - 133
client/src/http/Collection.ts

@@ -1,74 +1,58 @@
-import { ChildrenStatusType } from '../components/status/Types';
-import {
-  CollectionView,
-  DeleteEntitiesReq,
-  InsertDataParam,
-  LoadReplicaReq,
-  Replica,
-} from '../pages/collections/Types';
-import { LoadSampleParam } from '../pages/dialogs/Types';
-import { Field } from '@/pages/schema/Types';
-import { VectorSearchParam } from '../types/SearchTypes';
+import dayjs from 'dayjs';
+import { LoadReplicaReq } from '@/pages/collections/Types';
+import { VectorSearchParam } from '@/types/SearchTypes';
 import { QueryParam } from '@/pages/query/Types';
-import { IndexState, ShowCollectionsType } from '../types/Milvus';
-import { formatNumber } from '../utils/Common';
+import { formatNumber } from '@/utils/Common';
 import BaseModel from './BaseModel';
-import { FieldHttp } from './Field';
-import dayjs from 'dayjs';
 import { LOADING_STATE } from '@/consts';
-
-export class CollectionHttp extends BaseModel implements CollectionView {
-  private aliases!: string[];
-  private autoID!: boolean;
-  private collection_name!: string;
-  private description!: string;
-  private consistency_level!: string;
-  private rowCount!: string;
-  private index_status!: string;
-  private id!: string;
-  private loadedPercentage!: string;
-  private createdTime!: string;
-  private schema!: {
-    fields: Field[];
-    autoID: boolean;
-    description: string;
-    enable_dynamic_field: boolean;
-  };
-  private replicas!: Replica[];
+import {
+  IndexDescription,
+  ShowCollectionsType,
+  CollectionSchema,
+  ReplicaInfo,
+  CollectionData,
+} from '@server/types';
+import { MilvusIndex, FieldHttp } from '@/http';
+
+export class Collection extends BaseModel implements CollectionData {
+  public aliases: string[] = [];
+  public autoID!: boolean;
+  public collection_name!: string;
+  public description: string = '';
+  public consistency_level!: string;
+  public rowCount!: string;
+  public id!: string;
+  public loadedPercentage!: string;
+  public createdTime!: number;
+  public index_descriptions!: IndexDescription[];
+  public schema!: CollectionSchema;
+  public replicas!: ReplicaInfo[];
 
   static COLLECTIONS_URL = '/collections';
-  static COLLECTIONS_INDEX_STATUS_URL = '/collections/indexes/status';
   static COLLECTIONS_STATISTICS_URL = '/collections/statistics';
 
-  constructor(props: CollectionView) {
+  constructor(props: Collection) {
     super(props);
     Object.assign(this, props);
   }
 
   static getCollections(data?: {
     type: ShowCollectionsType;
-  }): Promise<CollectionHttp[]> {
+  }): Promise<Collection[]> {
     return super.findAll({ path: this.COLLECTIONS_URL, params: data || {} });
   }
 
   static getCollection(name: string) {
-    return super.search({
+    return super.search<Collection>({
       path: `${this.COLLECTIONS_URL}/${name}`,
       params: {},
-    }) as Promise<CollectionHttp>;
+    });
   }
 
   static createCollection(data: any) {
     return super.create({ path: this.COLLECTIONS_URL, data });
   }
 
-  static getCollectionsIndexState(): Promise<CollectionHttp[]> {
-    return super.findAll({
-      path: this.COLLECTIONS_INDEX_STATUS_URL,
-      params: {},
-    });
-  }
-
   static deleteCollection(collectionName: string) {
     return super.delete({ path: `${this.COLLECTIONS_URL}/${collectionName}` });
   }
@@ -100,52 +84,13 @@ export class CollectionHttp extends BaseModel implements CollectionView {
     return super.search({ path: this.COLLECTIONS_STATISTICS_URL, params: {} });
   }
 
-  static getPSegments(collectionName: string) {
-    return super.search({
-      path: `${this.COLLECTIONS_URL}/${collectionName}/psegments`,
-      params: {},
-    }) as Promise<{
-      infos: any;
-    }>;
-  }
-
   static count(collectionName: string) {
-    return super.search({
+    return super.search<Collection>({
       path: `${this.COLLECTIONS_URL}/${collectionName}/count`,
       params: {},
     });
   }
 
-  static getQSegments(collectionName: string) {
-    return super.search({
-      path: `${this.COLLECTIONS_URL}/${collectionName}/qsegments`,
-      params: {},
-    }) as Promise<{
-      infos: any;
-    }>;
-  }
-
-  static insertData(collectionName: string, param: InsertDataParam) {
-    return super.create({
-      path: `${this.COLLECTIONS_URL}/${collectionName}/insert`,
-      data: param,
-    });
-  }
-
-  static importSample(collectionName: string, param: LoadSampleParam) {
-    return super.create({
-      path: `${this.COLLECTIONS_URL}/${collectionName}/importSample`,
-      data: param,
-    }) as Promise<{ sampleFile: string }>;
-  }
-
-  static deleteEntities(collectionName: string, param: DeleteEntitiesReq) {
-    return super.update({
-      path: `${this.COLLECTIONS_URL}/${collectionName}/entities`,
-      data: param,
-    });
-  }
-
   static vectorSearchData(collectionName: string, params: VectorSearchParam) {
     return super.query({
       path: `${this.COLLECTIONS_URL}/${collectionName}/search`,
@@ -173,86 +118,64 @@ export class CollectionHttp extends BaseModel implements CollectionView {
     });
   }
 
-  static compact(collectionName: string) {
-    return super.update({
-      path: `${this.COLLECTIONS_URL}/${collectionName}/compact`,
-    });
-  }
-
-  get _autoId() {
-    return this.autoID;
-  }
-
-  get _aliases() {
-    return this.aliases || [];
-  }
-
-  get _desc() {
+  get desc() {
     return this.description || '--';
   }
 
-  get _id() {
-    return this.id;
-  }
-
-  get _name() {
+  get collectionName() {
     return this.collection_name;
   }
 
-  get _rowCount() {
+  get entityCount() {
     return formatNumber(Number(this.rowCount));
   }
 
-  get _loadedPercentage() {
-    return this.loadedPercentage;
-  }
   // load status
-  get _status() {
+  get status() {
     // If not load, insight server will return '-1'. Otherwise milvus will return percentage
-    return this._loadedPercentage === '-1'
+    return this.loadedPercentage === '-1'
       ? LOADING_STATE.UNLOADED
-      : this._loadedPercentage === '100'
+      : this.loadedPercentage === '100'
       ? LOADING_STATE.LOADED
       : LOADING_STATE.LOADING;
     // return LOADING_STATE.LOADING
   }
 
-  get _consistencyLevel() {
-    return this.consistency_level;
-  }
-
-  get _fields() {
+  get fields() {
     return this.schema.fields.map(f => new FieldHttp(f));
   }
 
-  get _indexState() {
-    switch (this.index_status) {
-      case IndexState.InProgress:
-        return ChildrenStatusType.CREATING;
-      case IndexState.Failed:
-        return ChildrenStatusType.ERROR;
-      default:
-        return ChildrenStatusType.FINISH;
-    }
+  get indexes() {
+    return this.index_descriptions.map(index => new MilvusIndex(index));
   }
 
   // Befor milvus-2.0-rc3  will return '0'.
   // If milvus is stable, we can remote this condition
-  get _createdTime(): string {
-    return this.createdTime && this.createdTime !== '0'
+  get createdAt(): string {
+    return this.createdTime && this.createdTime !== 0
       ? dayjs(Number(this.createdTime)).format('YYYY-MM-DD HH:mm:ss')
       : '';
   }
 
-  get _replicas(): Replica[] {
+  get replicasInfo(): ReplicaInfo[] {
     return this.replicas || [];
   }
 
-  get _enableDynamicField(): boolean {
+  get enableDynamicField(): boolean {
     return this.schema && this.schema.enable_dynamic_field;
   }
 
-  get _schema() {
-    return this.schema;
+  get fieldWithIndexInfo() {
+    let fields: FieldHttp[] = [];
+    for (const schema of this.fields) {
+      let field: FieldHttp = schema;
+      const index = this.indexes.find(i => i.field_name === schema.name);
+      field.indexParameterPairs = index?.indexParameterPairs || [];
+      field.indexType = index?.indexType || '';
+      field.indexName = index?.index_name || '';
+
+      fields.push(field);
+    }
+    return fields;
   }
 }

+ 44 - 0
client/src/http/Data.service.ts

@@ -0,0 +1,44 @@
+import { LoadSampleParam } from '@/pages/dialogs/Types';
+import { InsertDataParam, DeleteEntitiesReq } from '@/pages/collections/Types';
+import BaseModel from './BaseModel';
+
+export class DataService extends BaseModel {
+  static COLLECTIONS_URL = '/collections';
+  static FLUSH_URL = '/milvus/flush';
+
+  sampleFile!: string;
+
+  constructor(props: DataService) {
+    super(props);
+    Object.assign(this, props);
+  }
+  static importSample(collectionName: string, param: LoadSampleParam) {
+    return super.create<DataService>({
+      path: `${this.COLLECTIONS_URL}/${collectionName}/importSample`,
+      data: param,
+    });
+  }
+
+  static insertData(collectionName: string, param: InsertDataParam) {
+    return super.create({
+      path: `${this.COLLECTIONS_URL}/${collectionName}/insert`,
+      data: param,
+    });
+  }
+
+  static deleteEntities(collectionName: string, param: DeleteEntitiesReq) {
+    return super.update({
+      path: `${this.COLLECTIONS_URL}/${collectionName}/entities`,
+      data: param,
+    });
+  }
+
+  static flush(collectionName: string) {
+    return super.update({
+      path: this.FLUSH_URL,
+      data: {
+        collection_names: [collectionName],
+      },
+    });
+  }
+}

+ 3 - 9
client/src/http/Database.ts

@@ -4,8 +4,8 @@ import {
 } from '../pages/database/Types';
 import BaseModel from './BaseModel';
 
-export class DatabaseHttp extends BaseModel {
-  private names!: string[];
+export class Database extends BaseModel {
+  public db_names!: string[];
 
   constructor(props: {}) {
     super(props);
@@ -15,9 +15,7 @@ export class DatabaseHttp extends BaseModel {
   static DATABASE_URL = `/databases`;
 
   static getDatabases() {
-    return super.search({ path: this.DATABASE_URL, params: {} }) as Promise<{
-      db_names: string[];
-    }>;
+    return super.search({ path: this.DATABASE_URL, params: {} }) as Promise<Database>;
   }
 
   static createDatabase(data: CreateDatabaseParams) {
@@ -27,8 +25,4 @@ export class DatabaseHttp extends BaseModel {
   static dropDatabase(data: DropDatabaseParams) {
     return super.delete({ path: `${this.DATABASE_URL}/${data.db_name}` });
   }
-
-  get _names() {
-    return this.names;
-  }
 }

+ 17 - 31
client/src/http/Field.ts

@@ -1,9 +1,8 @@
-import { DataTypeStringEnum } from '@/consts';
-import { FieldData } from '../pages/schema/Types';
 import BaseModel from './BaseModel';
+import { KeyValuePair } from '@server/types';
+import { DataTypeStringEnum } from '@/consts';
 
-export class FieldHttp extends BaseModel implements FieldData {
-  data_type!: DataTypeStringEnum;
+export class FieldHttp extends BaseModel {
   fieldID!: string;
   type_params!: { key: string; value: string }[];
   is_primary_key!: true;
@@ -11,62 +10,49 @@ export class FieldHttp extends BaseModel implements FieldData {
   name!: string;
   description!: string;
   autoID!: boolean;
+  data_type!: DataTypeStringEnum;
   element_type!: DataTypeStringEnum;
+  index_params!: KeyValuePair[];
+  createIndexDisabled?: boolean = false;
+  indexType: string = '';
+  indexName: string = '';
+  indexParameterPairs: { key: string; value: string }[] = [];
 
   constructor(props: {}) {
     super(props);
     Object.assign(this, props);
   }
 
-  static async getFields(collectionName: string): Promise<FieldHttp[]> {
-    const path = `/collections/${collectionName}`;
-
-    const res = await super.findAll({
-      path,
-      params: {},
-    });
-
-    return res.schema.fields.map((f: any) => new this(f));
-  }
-
-  get _fieldId() {
-    return this.fieldID;
-  }
-
-  get _isPrimaryKey() {
+  get isPrimaryKey() {
     return this.is_primary_key;
   }
 
-  get _isPartitionKey() {
+  get isPartitionKey() {
     return this.is_partition_key;
   }
 
-  get _isAutoId() {
+  get isAutoId() {
     return this.autoID;
   }
 
-  get _fieldName() {
-    return this.name;
-  }
-
-  get _fieldType() {
+  get fieldType() {
     return this.data_type;
   }
 
-  get _desc() {
+  get desc() {
     return this.description || '--';
   }
 
-  get _dimension() {
+  get dimension() {
     return this.type_params.find(item => item.key === 'dim')?.value || '';
   }
 
-  get _maxLength() {
+  get maxLength() {
     return (
       this.type_params.find(item => item.key === 'max_length')?.value || ''
     );
   }
-  get _maxCapacity() {
+  get maxCapacity() {
     return (
       this.type_params.find(item => item.key === 'max_capacity')?.value || ''
     );

+ 1 - 11
client/src/http/Milvus.ts → client/src/http/Milvus.service.ts

@@ -1,11 +1,10 @@
 import { WS_EVENTS, WS_EVENTS_TYPE } from '@server/utils/Const';
 import BaseModel from './BaseModel';
 
-export class MilvusHttp extends BaseModel {
+export class MilvusService extends BaseModel {
   static CONNECT_URL = '/milvus/connect';
   static DISCONNECT_URL = '/milvus/disconnect';
   static CHECK_URL = '/milvus/check';
-  static FLUSH_URL = '/milvus/flush';
   static METRICS_URL = '/milvus/metrics';
   static VERSION_URL = '/milvus/version';
   static USE_DB_URL = '/milvus/usedb';
@@ -43,15 +42,6 @@ export class MilvusHttp extends BaseModel {
     }) as Promise<{ connected: boolean }>;
   }
 
-  static flush(collectionName: string) {
-    return super.update({
-      path: this.FLUSH_URL,
-      data: {
-        collection_names: [collectionName],
-      },
-    });
-  }
-
   static getMetrics() {
     return super.search({
       path: this.METRICS_URL,

+ 8 - 18
client/src/http/MilvusIndex.ts

@@ -1,15 +1,12 @@
-import {
-  IndexCreateParam,
-  IndexManageParam,
-  IndexView,
-} from '../pages/schema/Types';
+import { IndexCreateParam, IndexManageParam } from '../pages/schema/Types';
 import { ManageRequestMethods } from '../types/Common';
-import { IndexDescription, IndexState } from '../types/Milvus';
+import { IndexState } from '../types/Milvus';
 import { findKeyValue } from '../utils/Common';
 import { getKeyValueListFromJsonString } from '../utils/Format';
 import BaseModel from './BaseModel';
+import { IndexDescription } from '@server/types';
 
-export class IndexHttp extends BaseModel implements IndexView {
+export class MilvusIndex extends BaseModel {
   params!: { key: string; value: string }[];
   field_name!: string;
   index_name!: string;
@@ -23,7 +20,7 @@ export class IndexHttp extends BaseModel implements IndexView {
 
   static BASE_URL = `/schema/index`;
 
-  static async getIndexInfo(collectionName: string): Promise<IndexHttp[]> {
+  static async getIndexInfo(collectionName: string): Promise<MilvusIndex[]> {
     const path = this.BASE_URL;
 
     const res = await super.findAll({
@@ -52,14 +49,11 @@ export class IndexHttp extends BaseModel implements IndexView {
     return super.batchDelete({ path, data: { ...param, type } });
   }
 
-  get _indexType() {
+  get indexType() {
     return this.params.find(p => p.key === 'index_type')?.value || '';
   }
-  get _indexName() {
-    return this.index_name;
-  }
 
-  get _indexParameterPairs() {
+  get indexParameterPairs() {
     const metricType = this.params.filter(v => v.key === 'metric_type');
     // parms is json string, so we need parse it to key value array
     const params = findKeyValue(this.params, 'params');
@@ -69,11 +63,7 @@ export class IndexHttp extends BaseModel implements IndexView {
     return metricType;
   }
 
-  get _fieldName() {
-    return this.field_name;
-  }
-
-  get _metricType() {
+  get metricType() {
     return this.params.find(p => p.key === 'metric_type')?.value || '';
   }
 }

+ 10 - 18
client/src/http/Partition.ts

@@ -8,11 +8,11 @@ import {
 import { formatNumber } from '@/utils';
 import BaseModel from './BaseModel';
 
-export class PartitionHttp extends BaseModel implements PartitionData {
-  private id!: string;
-  private name!: string;
-  private rowCount!: string;
-  private createdTime!: string;
+export class Partition extends BaseModel implements PartitionData {
+  public id!: string;
+  public name!: string;
+  public rowCount!: string;
+  public createdTime!: string;
 
   constructor(props: {}) {
     super(props);
@@ -21,7 +21,7 @@ export class PartitionHttp extends BaseModel implements PartitionData {
 
   static URL_BASE = `/partitions`;
 
-  static getPartitions(collectionName: string): Promise<PartitionHttp[]> {
+  static getPartitions(collectionName: string): Promise<Partition[]> {
     const path = this.URL_BASE;
 
     return super.findAll({ path, params: { collection_name: collectionName } });
@@ -64,30 +64,22 @@ export class PartitionHttp extends BaseModel implements PartitionData {
     });
   }
 
-  get _id() {
-    return this.id;
-  }
-
-  get _name() {
-    return this.name;
-  }
-
-  get _formatName() {
+  get partitionName() {
     return this.name === '_default' ? 'Default partition' : this.name;
   }
 
-  get _rowCount() {
+  get entityCount() {
     return formatNumber(Number(this.rowCount));
   }
 
-  get _status() {
+  get status() {
     // @TODO replace mock data
     return LOADING_STATE.UNLOADED;
   }
 
   // Befor milvus-2.0-rc3  will return '0'.
   // If milvus is stable, we can remote this condition/
-  get _createdTime(): string {
+  get createdAt(): string {
     return this.createdTime && this.createdTime !== '0'
       ? dayjs(Number(this.createdTime)).format('YYYY-MM-DD HH:mm:ss')
       : '';

+ 3 - 3
client/src/http/Prometheus.ts → client/src/http/Prometheus.service.ts

@@ -1,6 +1,6 @@
 import BaseModel from './BaseModel';
 
-export class PrometheusHttp extends BaseModel {
+export class PrometheusService extends BaseModel {
   static SET_PROMETHEUS_URL = '/prometheus/setPrometheus';
   static GET_MILVUS_HEALTHY_DATA_URL = '/prometheus/getMilvusHealthyData';
 
@@ -19,7 +19,7 @@ export class PrometheusHttp extends BaseModel {
     prometheusNamespace: string;
   }) {
     return super.search({
-      path: PrometheusHttp.SET_PROMETHEUS_URL,
+      path: PrometheusService.SET_PROMETHEUS_URL,
       params: { prometheusAddress, prometheusInstance, prometheusNamespace },
       timeout: 1000,
     });
@@ -35,7 +35,7 @@ export class PrometheusHttp extends BaseModel {
     step: number;
   }) {
     return super.search({
-      path: PrometheusHttp.GET_MILVUS_HEALTHY_DATA_URL,
+      path: PrometheusService.GET_MILVUS_HEALTHY_DATA_URL,
       params: { start, end, step },
     });
   }

+ 38 - 0
client/src/http/Segment.ts

@@ -0,0 +1,38 @@
+import BaseModel from './BaseModel';
+import {
+  GetQuerySegmentInfoResponse,
+  QuerySegmentInfo,
+  GePersistentSegmentInfoResponse,
+  PersistentSegmentInfo,
+} from '@server/types';
+
+export class Segement extends BaseModel {
+  static COLLECTIONS_URL = '/collections';
+
+  infos!: QuerySegmentInfo[] | PersistentSegmentInfo[];
+
+  constructor(props: {}) {
+    super(props);
+    Object.assign(this, props);
+  }
+
+  static getQSegments(collectionName: string) {
+    return super.search({
+      path: `${this.COLLECTIONS_URL}/${collectionName}/qsegments`,
+      params: {},
+    }) as Promise<GetQuerySegmentInfoResponse>;
+  }
+
+  static getPSegments(collectionName: string) {
+    return super.search({
+      path: `${this.COLLECTIONS_URL}/${collectionName}/psegments`,
+      params: {},
+    }) as Promise<GePersistentSegmentInfoResponse>;
+  }
+
+  static compact(collectionName: string) {
+    return super.update({
+      path: `${this.COLLECTIONS_URL}/${collectionName}/compact`,
+    });
+  }
+}

+ 2 - 6
client/src/http/User.ts

@@ -9,8 +9,8 @@ import {
 } from '../pages/user/Types';
 import BaseModel from './BaseModel';
 
-export class UserHttp extends BaseModel {
-  private names!: string[];
+export class User extends BaseModel {
+  public names!: string[];
 
   constructor(props: {}) {
     super(props);
@@ -96,8 +96,4 @@ export class UserHttp extends BaseModel {
       Privileges: Record<string, unknown>;
     }>;
   }
-
-  get _names() {
-    return this.names;
-  }
 }

+ 7 - 2
client/src/http/index.ts

@@ -1,10 +1,15 @@
+// objects map
 export * from './Axios';
 export * from './BaseModel';
 export * from './Collection';
 export * from './Database';
-export * from './Milvus';
 export * from './MilvusIndex';
 export * from './Field';
 export * from './Partition';
-export * from './Prometheus';
 export * from './User';
+export * from './Segment';
+
+// service
+export * from './Data.service';
+export * from './Milvus.service';
+export * from './Prometheus.service';

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

@@ -131,6 +131,11 @@ const collectionTrans = {
     '它确保在同一会话中所有数据写入可以立即在读取中感知。',
   consistencyEventuallyTooltip:
     '没有保证读写的顺序,副本最终会在没有进一步写操作的情况下收敛到相同的状态。',
+  releaseCollectionFirst: '请先释放collection.',
+
+  clickToLoad: '点击加载collection。',
+  clickToRelease: '点击释放collection。',
+  collectionIsLoading: 'colleciton正在加载...',
 };
 
 export default collectionTrans;

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

@@ -130,6 +130,11 @@ const collectionTrans = {
   consistencyStrongTooltip: `It ensures that users can read the latest version of data.`,
   consistencySessionTooltip: `It ensures that all data writes can be immediately perceived in reads during the same session.`,
   consistencyEventuallyTooltip: `There is no guaranteed order of reads and writes, and replicas eventually converge to the same state given that no further write operations are done.`,
+  releaseCollectionFirst: `Please release your collection first.`,
+
+  clickToLoad: 'Click to load the collection.',
+  clickToRelease: 'Click to release the collection.',
+  collectionIsLoading: 'The collection is loading...',
 };
 
 export default collectionTrans;

+ 1 - 1
client/src/i18n/en/index.ts

@@ -2,7 +2,7 @@ const indexTrans = {
   type: 'Index Type',
   param: 'Index Parameters',
 
-  create: 'Index',
+  create: 'Create Index',
   index: 'Index',
   desc: 'Description',
 

+ 2 - 2
client/src/pages/collections/Aliases.tsx

@@ -7,7 +7,7 @@ import icons from '@/components/icons/Icons';
 import DeleteIcon from '@material-ui/icons/Delete';
 import CreateAliasDialog from '../dialogs/CreateAliasDialog';
 import DeleteTemplate from '@/components/customDialog/DeleteDialogTemplate';
-import { CollectionHttp } from '@/http';
+import { Collection } from '@/http';
 
 const useStyles = makeStyles((theme: Theme) => ({
   wrapper: {
@@ -77,7 +77,7 @@ export default function Aliases(props: AliasesProps) {
     collection: string;
     alias: string;
   }) => {
-    await CollectionHttp.dropAlias(params.collection, { alias: params.alias });
+    await Collection.dropAlias(params.collection, { alias: params.alias });
     openSnackBar(successTrans('delete', { name: collectionTrans('alias') }));
     handleCloseDialog();
     onDelete();

+ 0 - 1
client/src/pages/collections/Collection.tsx

@@ -13,7 +13,6 @@ import Schema from '../schema/Schema';
 import Query from '../query/Query';
 import Preview from '../preview/Preview';
 import Segments from '../segments/Segments';
-
 import { TAB_ENUM } from './Types';
 
 const useStyles = makeStyles((theme: Theme) => ({

+ 87 - 120
client/src/pages/collections/Collections.tsx

@@ -9,20 +9,18 @@ import {
   dataContext,
   webSocketContext,
 } from '@/context';
+import { Collection, MilvusService, DataService } from '@/http';
 import { useNavigationHook, usePaginationHook } from '@/hooks';
 import { ALL_ROUTER_TYPES } from '@/router/Types';
 import AttuGrid from '@/components/grid/Grid';
 import CustomToolBar from '@/components/grid/ToolBar';
-import { CollectionView, InsertDataParam } from './Types';
+import { InsertDataParam } from './Types';
 import { ColDefinitionsType, ToolBarConfig } from '@/components/grid/Types';
 import icons from '@/components/icons/Icons';
 import EmptyCard from '@/components/cards/EmptyCard';
-import Status from '@/components/status/Status';
-import { ChildrenStatusType } from '@/components/status/Types';
-import StatusIcon from '@/components/status/StatusIcon';
+import StatusAction from '@/pages/collections/StatusAction';
 import CustomToolTip from '@/components/customToolTip/CustomToolTip';
 import CreateCollectionDialog from '../dialogs/CreateCollectionDialog';
-import { CollectionHttp, MilvusHttp } from '@/http';
 import LoadCollectionDialog from '../dialogs/LoadCollectionDialog';
 import ReleaseCollectionDialog from '../dialogs/ReleaseCollectionDialog';
 import DropCollectionDialog from '../dialogs/DropCollectionDialog';
@@ -65,6 +63,7 @@ const useStyles = makeStyles((theme: Theme) => ({
   chip: {
     color: theme.palette.text.primary,
     marginRight: theme.spacing(0.5),
+    background: `rgba(0, 0, 0, 0.04)`,
   },
 }));
 
@@ -78,9 +77,9 @@ const Collections = () => {
     (searchParams.get('search') as string) || ''
   );
   const [loading, setLoading] = useState<boolean>(false);
-  const [selectedCollections, setSelectedCollections] = useState<
-    CollectionView[]
-  >([]);
+  const [selectedCollections, setSelectedCollections] = useState<Collection[]>(
+    []
+  );
 
   const { setDialog, openSnackBar } = useContext(rootContext);
   const { collections, setCollections } = useContext(webSocketContext);
@@ -89,8 +88,6 @@ const Collections = () => {
   const { t: successTrans } = useTranslation('success');
   const classes = useStyles();
 
-  const LoadIcon = icons.load;
-  const ReleaseIcon = icons.release;
   const InfoIcon = icons.info;
   const SourceIcon = icons.source;
 
@@ -101,48 +98,59 @@ const Collections = () => {
     Eventually: collectionTrans('consistencyEventuallyTooltip'),
   };
 
+  const checkCollectionStatus = useCallback((collections: Collection[]) => {
+    const hasLoadingOrBuildingCollection = collections.some(
+      v => checkLoading(v) || checkIndexBuilding(v)
+    );
+
+    // if some collection is building index or loading, start pulling data
+    if (hasLoadingOrBuildingCollection) {
+      MilvusService.triggerCron({
+        name: WS_EVENTS.COLLECTION,
+        type: WS_EVENTS_TYPE.START,
+      });
+    }
+  }, []);
+
   const fetchData = useCallback(async () => {
     try {
       setLoading(true);
-      const res = await CollectionHttp.getCollections();
-      const hasLoadingOrBuildingCollection = res.some(
-        v => checkLoading(v) || checkIndexBuilding(v)
-      );
-
-      // if some collection is building index or loading, start pulling data
-      if (hasLoadingOrBuildingCollection) {
-        MilvusHttp.triggerCron({
-          name: WS_EVENTS.COLLECTION,
-          type: WS_EVENTS_TYPE.START,
-        });
-      }
-
-      setCollections(res);
+      const collections = await Collection.getCollections();
+      setCollections(collections);
+      checkCollectionStatus(collections);
     } finally {
       setLoading(false);
     }
-  }, [setCollections]);
+  }, [setCollections, checkCollectionStatus]);
 
   useEffect(() => {
     fetchData();
   }, [fetchData, database]);
 
+  const getVectorField = (collection: Collection) => {
+    return collection.fieldWithIndexInfo!.find(
+      d => d.fieldType === 'FloatVector' || d.fieldType === 'BinaryVector'
+    );
+  };
+
   const formatCollections = useMemo(() => {
     const filteredCollections = search
-      ? collections.filter(collection => collection._name.includes(search))
+      ? collections.filter(collection =>
+          collection.collectionName.includes(search)
+        )
       : collections;
 
     const data = filteredCollections.map(v => {
-      // const indexStatus = statusRes.find(item => item._name === v._name);
+      // const indexStatus = statusRes.find(item => item.collectionName === v.collectionName);
       Object.assign(v, {
         nameElement: (
           <Link
-            to={`/collections/${v._name}`}
+            to={`/collections/${v.collectionName}`}
             className={classes.link}
-            title={v._name}
+            title={v.collectionName}
           >
             <Highlighter
-              textToHighlight={v._name}
+              textToHighlight={v.collectionName}
               searchWords={[search]}
               highlightClassName={classes.highlight}
             />
@@ -150,7 +158,7 @@ const Collections = () => {
         ),
         features: (
           <>
-            {v._autoId ? (
+            {v.autoID ? (
               <Tooltip
                 title={collectionTrans('autoIDTooltip')}
                 placement="top"
@@ -163,7 +171,7 @@ const Collections = () => {
                 />
               </Tooltip>
             ) : null}
-            {v._enableDynamicField ? (
+            {v.enableDynamicField ? (
               <Tooltip
                 title={collectionTrans('dynamicSchemaTooltip')}
                 placement="top"
@@ -177,28 +185,51 @@ const Collections = () => {
               </Tooltip>
             ) : null}
             <Tooltip
-              title={consistencyTooltipsMap[v._consistencyLevel]}
+              title={consistencyTooltipsMap[v.consistency_level]}
               placement="top"
               arrow
             >
               <Chip
                 className={classes.chip}
-                label={v._consistencyLevel}
+                label={v.consistency_level}
                 size="small"
               />
             </Tooltip>
           </>
         ),
         statusElement: (
-          <Status status={v._status} percentage={v._loadedPercentage} />
-        ),
-        indexCreatingElement: (
-          <StatusIcon type={v._indexState || ChildrenStatusType.FINISH} />
+          <StatusAction
+            status={v.status}
+            onIndexCreate={fetchData}
+            percentage={v.loadedPercentage}
+            field={getVectorField(v)!}
+            collectionName={v.collectionName}
+            action={() => {
+              setDialog({
+                open: true,
+                type: 'custom',
+                params: {
+                  component:
+                    v.status === LOADING_STATE.UNLOADED ? (
+                      <LoadCollectionDialog
+                        collection={v.collectionName}
+                        onLoad={onLoad}
+                      />
+                    ) : (
+                      <ReleaseCollectionDialog
+                        collection={v.collectionName}
+                        onRelease={onRelease}
+                      />
+                    ),
+                },
+              });
+            }}
+          />
         ),
         _aliasElement: (
           <Aliases
-            aliases={v._aliases}
-            collectionName={v._name}
+            aliases={v.aliases}
+            collectionName={v.collectionName}
             onCreate={fetchData}
             onDelete={fetchData}
           />
@@ -232,8 +263,8 @@ const Collections = () => {
       fields_data: fieldData,
     };
     try {
-      await CollectionHttp.insertData(collectionName, param);
-      await MilvusHttp.flush(collectionName);
+      await DataService.insertData(collectionName, param);
+      await DataService.flush(collectionName);
       // update collections
       fetchData();
       return { result: true, msg: '' };
@@ -314,7 +345,7 @@ const Collections = () => {
                 collections={formatCollections}
                 defaultSelectedCollection={
                   selectedCollections.length === 1
-                    ? selectedCollections[0]._name
+                    ? selectedCollections[0].collectionName
                     : ''
                 }
                 // user can't select partition on collection page, so default value is ''
@@ -344,7 +375,7 @@ const Collections = () => {
             component: (
               <RenameCollectionDialog
                 cb={onRename}
-                collectionName={selectedCollections[0]._name}
+                collectionName={selectedCollections[0].collectionName}
               />
             ),
           },
@@ -402,25 +433,25 @@ const Collections = () => {
       id: 'nameElement',
       align: 'left',
       disablePadding: true,
-      sortBy: '_name',
+      sortBy: 'collectionName',
       label: collectionTrans('name'),
     },
     {
       id: 'statusElement',
       align: 'left',
       disablePadding: false,
-      sortBy: '_status',
+      sortBy: 'status',
       label: collectionTrans('status'),
     },
     {
       id: 'features',
       align: 'left',
       disablePadding: true,
-      sortBy: '_enableDynamicField',
+      sortBy: 'enableDynamicField',
       label: collectionTrans('features'),
     },
     {
-      id: '_rowCount',
+      id: 'entityCount',
       align: 'left',
       disablePadding: false,
       label: (
@@ -432,84 +463,18 @@ const Collections = () => {
         </span>
       ),
     },
-
-    // {
-    //   id: 'consistency_level',
-    //   align: 'left',
-    //   disablePadding: true,
-    //   label: (
-    //     <span className="flex-center">
-    //       {collectionTrans('consistency')}
-    //       <CustomToolTip title={collectionTrans('consistencyLevelInfo')}>
-    //         <InfoIcon classes={{ root: classes.icon }} />
-    //       </CustomToolTip>
-    //     </span>
-    //   ),
-    // },
-
     {
-      id: '_desc',
+      id: 'desc',
       align: 'left',
       disablePadding: false,
       label: collectionTrans('desc'),
     },
     {
-      id: '_createdTime',
+      id: 'createdAt',
       align: 'left',
       disablePadding: false,
       label: collectionTrans('createdTime'),
     },
-    // {
-    //   id: 'indexCreatingElement',
-    //   align: 'left',
-    //   disablePadding: false,
-    //   label: '',
-    // },
-    {
-      id: 'action',
-      align: 'center',
-      disablePadding: false,
-      label: '',
-      showActionCell: true,
-      isHoverAction: true,
-      actionBarConfigs: [
-        {
-          onClick: (e: React.MouseEvent, row: CollectionView) => {
-            setDialog({
-              open: true,
-              type: 'custom',
-              params: {
-                component:
-                  row._status === LOADING_STATE.UNLOADED ? (
-                    <LoadCollectionDialog
-                      collection={row._name}
-                      onLoad={onLoad}
-                    />
-                  ) : (
-                    <ReleaseCollectionDialog
-                      collection={row._name}
-                      onRelease={onRelease}
-                    />
-                  ),
-              },
-            });
-
-            e.preventDefault();
-          },
-          icon: 'load',
-          label: 'load',
-          showIconMethod: 'renderFn',
-          getLabel: (row: CollectionView) =>
-            row._status === LOADING_STATE.UNLOADED ? 'load' : 'release',
-          renderIconFn: (row: CollectionView) =>
-            row._status === LOADING_STATE.UNLOADED ? (
-              <LoadIcon />
-            ) : (
-              <ReleaseIcon />
-            ),
-        },
-      ],
-    },
     {
       id: 'import',
       align: 'center',
@@ -519,12 +484,14 @@ const Collections = () => {
       isHoverAction: true,
       actionBarConfigs: [
         {
-          onClick: (e: React.MouseEvent, row: CollectionView) => {
+          onClick: (e: React.MouseEvent, row: Collection) => {
             setDialog({
               open: true,
               type: 'custom',
               params: {
-                component: <ImportSampleDialog collection={row._name} />,
+                component: (
+                  <ImportSampleDialog collection={row.collectionName} />
+                ),
               },
             });
           },
@@ -532,7 +499,7 @@ const Collections = () => {
           label: 'Import',
           showIconMethod: 'renderFn',
           getLabel: () => 'Import sample data',
-          renderIconFn: (row: CollectionView) => <SourceIcon />,
+          renderIconFn: (row: Collection) => <SourceIcon />,
         },
       ],
     },
@@ -573,7 +540,7 @@ const Collections = () => {
           colDefinitions={colDefinitions}
           rows={collectionList}
           rowCount={total}
-          primaryKey="_name"
+          primaryKey="collectionName"
           selected={selectedCollections}
           setSelected={handleSelectChange}
           page={currentPage}

+ 167 - 0
client/src/pages/collections/StatusAction.tsx

@@ -0,0 +1,167 @@
+import { FC, useMemo, MouseEvent, forwardRef } from 'react';
+import {
+  ChildrenStatusType,
+  StatusActionType,
+} from '@/components/status/Types';
+import { useTranslation } from 'react-i18next';
+import {
+  makeStyles,
+  Theme,
+  createStyles,
+  Typography,
+  useTheme,
+  Tooltip,
+  Chip,
+} from '@material-ui/core';
+import { LOADING_STATE } from '@/consts';
+import StatusIcon from '@/components/status/StatusIcon';
+import icons from '@/components/icons/Icons';
+import IndexTypeElement from '@/pages/schema/IndexTypeElement';
+
+const useStyles = makeStyles((theme: Theme) =>
+  createStyles({
+    root: {
+      display: 'flex',
+      alignItems: 'center',
+    },
+    chip: {
+      border: 'none',
+      background: `rgba(0, 0, 0, 0.04)`,
+      marginRight: theme.spacing(0.5),
+      paddingLeft: theme.spacing(0.5),
+    },
+    circle: {
+      backgroundColor: (props: any) => props.color,
+      borderRadius: '50%',
+      width: '10px',
+      height: '10px',
+    },
+
+    circleUnload: {
+      backgroundColor: theme.palette.attuGrey.main,
+      borderRadius: '50%',
+      width: '10px',
+      height: '10px',
+    },
+
+    loading: {
+      marginRight: '10px',
+    },
+    icon: {
+      marginTop: theme.spacing(0.5),
+    },
+    flash: {
+      animation: '$bgColorChange 1.5s infinite',
+    },
+
+    '@keyframes bgColorChange': {
+      '0%': {
+        backgroundColor: (props: any) => props.color,
+      },
+      '50%': {
+        backgroundColor: 'transparent',
+      },
+      '100%': {
+        backgroundColor: (props: any) => props.color,
+      },
+    },
+  })
+);
+
+const StatusAction: FC<StatusActionType> = props => {
+  const theme = useTheme();
+  const classes = useStyles({ color: theme.palette.primary.main });
+  const ReleaseIcon = icons.remove;
+  const LoadIcon = icons.addOutline;
+
+  const {
+    status,
+    percentage = 0,
+    collectionName,
+    field,
+    action = () => {},
+    onIndexCreate,
+  } = props;
+  const { t: commonTrans } = useTranslation();
+  const { t: collectionTrans } = useTranslation('collection');
+
+  const statusTrans = commonTrans('status');
+  const {
+    label,
+    tooltip,
+    icon = <span></span>,
+    deleteIcon = <ReleaseIcon />,
+  } = useMemo(() => {
+    switch (status) {
+      case LOADING_STATE.UNLOADED:
+        return {
+          label: statusTrans.unloaded,
+          icon: <div className={`${classes.circleUnload}`}></div>,
+          tooltip: collectionTrans('clickToLoad'),
+          deleteIcon: <LoadIcon />,
+        };
+
+      case LOADING_STATE.LOADED:
+        return {
+          label: statusTrans.loaded,
+          icon: <div className={classes.circle}></div>,
+          tooltip: collectionTrans('clickToRelease'),
+          deleteIcon: <ReleaseIcon />,
+        };
+      case LOADING_STATE.LOADING:
+        return {
+          label: `${percentage}% ${statusTrans.loading}`,
+          tooltip: collectionTrans('collectionIsLoading'),
+          icon: (
+            <StatusIcon
+              type={ChildrenStatusType.CREATING}
+              className={classes.loading}
+            />
+          ),
+        };
+
+      default:
+        return {
+          label: statusTrans.error,
+          icon: <span></span>,
+          tooltip: '',
+          deleteIcon: <ReleaseIcon />,
+        };
+    }
+  }, [status, statusTrans, percentage]);
+
+  // UI state
+  const hasVectorIndex = field?.indexType !== '';
+  const collectionLoaded = status === LOADING_STATE.LOADED;
+
+  return (
+    <div className={classes.root}>
+      {hasVectorIndex && (
+        <Tooltip arrow interactive title={tooltip} placement={'top'}>
+          <Chip
+            className={classes.chip}
+            variant="outlined"
+            label={<Typography>{label}</Typography>}
+            onDelete={() => action()}
+            onClick={(e: MouseEvent<HTMLDivElement>) => {
+              e.stopPropagation();
+              action();
+            }}
+            deleteIcon={deleteIcon}
+            size="small"
+            icon={icon}
+          />
+        </Tooltip>
+      )}
+      <IndexTypeElement
+        data={field!}
+        collectionName={collectionName}
+        cb={() => onIndexCreate()}
+        disabled={collectionLoaded}
+        disabledTooltip={collectionTrans('releaseCollectionFirst')}
+      />
+    </div>
+  );
+};
+
+export default StatusAction;

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

@@ -1,23 +1,5 @@
-import { Dispatch, ReactElement, SetStateAction } from 'react';
-import { ChildrenStatusType } from '@/components/status/Types';
-import { LOADING_STATE, DataTypeEnum } from '@/consts';
-import { FieldData } from '../schema/Types';
-
-export interface CollectionData {
-  _name: string;
-  _id: string;
-  _loadedPercentage: string;
-  _status: LOADING_STATE;
-  _rowCount: string;
-  _desc: string;
-  _indexState: ChildrenStatusType;
-  _fields?: FieldData[];
-  _consistencyLevel: string;
-  _aliases: string[];
-  _replicas: Replica[];
-  _enableDynamicField: boolean;
-  _autoId: boolean;
-}
+import { Dispatch, SetStateAction } from 'react';
+import { DataTypeEnum } from '@/consts';
 
 export interface Replica {
   collectionID: string;
@@ -34,12 +16,6 @@ export interface ShardReplica {
   node_id: string[];
 }
 
-export interface CollectionView extends CollectionData {
-  nameElement?: ReactElement;
-  statusElement?: ReactElement;
-  indexCreatingElement?: ReactElement;
-}
-
 export interface CollectionCreateProps {
   onCreate?: () => void;
 }
@@ -48,11 +24,11 @@ export interface CollectionCreateParam {
   collection_name: string;
   description: string;
   autoID: boolean;
-  fields: Field[];
+  fields: CreateField[];
   consistency_level: string;
 }
 
-export interface Field {
+export interface CreateField {
   name: string | null;
   data_type: DataTypeEnum;
   is_primary_key: boolean;
@@ -79,8 +55,8 @@ export type CreateFieldType =
   | 'number';
 
 export interface CreateFieldsProps {
-  fields: Field[];
-  setFields: Dispatch<SetStateAction<Field[]>>;
+  fields: CreateField[];
+  setFields: Dispatch<SetStateAction<CreateField[]>>;
   setFieldsValidation: Dispatch<
     SetStateAction<{ [x: string]: string | boolean }[]>
   >;

+ 2 - 2
client/src/pages/connect/AuthForm.tsx

@@ -7,7 +7,7 @@ import icons from '@/components/icons/Icons';
 import { ITextfieldConfig } from '@/components/customInput/Types';
 import { useFormValidation } from '@/hooks';
 import { formatForm } from '@/utils';
-import { MilvusHttp } from '@/http';
+import { MilvusService } from '@/http';
 import { useNavigate } from 'react-router-dom';
 import {
   rootContext,
@@ -203,7 +203,7 @@ export const AuthForm = (props: any) => {
 
   const handleConnect = async (event: React.FormEvent) => {
     event.preventDefault();
-    const result = await MilvusHttp.connect(form);
+    const result = await MilvusService.connect(form);
 
     setIsAuth(true);
     setAddress(form.address);

+ 6 - 6
client/src/pages/database/Database.tsx

@@ -1,6 +1,6 @@
 import { useContext, useEffect, useState } from 'react';
 import { useTranslation } from 'react-i18next';
-import { DatabaseHttp } from '@/http';
+import { Database } from '@/http';
 import AttuGrid from '@/components/grid/Grid';
 import { ColDefinitionsType, ToolBarConfig } from '@/components/grid/Types';
 import {
@@ -14,7 +14,7 @@ import { useNavigationHook } from '@/hooks';
 import { ALL_ROUTER_TYPES } from '@/router/Types';
 import CreateUser from './Create';
 
-const Database = () => {
+const DatabasePage = () => {
   useNavigationHook(ALL_ROUTER_TYPES.DATABASES);
   const { setDatabaseList } = useContext(dataContext);
 
@@ -31,7 +31,7 @@ const Database = () => {
 
   const fetchDatabases = async () => {
     try {
-      const res = await DatabaseHttp.getDatabases();
+      const res = await Database.getDatabases();
       setDatabases(res.db_names.map((v: string) => ({ name: v })));
       setDatabaseList(res.db_names);
     } catch (error) {
@@ -40,7 +40,7 @@ const Database = () => {
   };
 
   const handleCreate = async (data: CreateDatabaseParams) => {
-    await DatabaseHttp.createDatabase(data);
+    await Database.createDatabase(data);
     fetchDatabases();
     openSnackBar(successTrans('create', { name: dbTrans('database') }));
     handleCloseDialog();
@@ -51,7 +51,7 @@ const Database = () => {
       const param: DropDatabaseParams = {
         db_name: db.name,
       };
-      await DatabaseHttp.dropDatabase(param);
+      await Database.dropDatabase(param);
     }
 
     openSnackBar(successTrans('delete', { name: dbTrans('database') }));
@@ -150,4 +150,4 @@ const Database = () => {
   );
 };
 
-export default Database;
+export default DatabasePage;

+ 11 - 7
client/src/pages/dialogs/CompactDialog.tsx

@@ -4,12 +4,12 @@ import { useTranslation } from 'react-i18next';
 import { rootContext } from '@/context';
 import DialogTemplate from '@/components/customDialog/DialogTemplate';
 import { CompactDialogProps } from './Types';
-import { CollectionHttp } from '@/http';
+import { Segement } from '@/http';
 
 const useStyles = makeStyles((theme: Theme) => ({
   desc: {
     margin: '8px 0 16px 0',
-    maxWidth: '500px'
+    maxWidth: '500px',
   },
   dialog: {},
 }));
@@ -26,7 +26,7 @@ const CompactDialog: FC<CompactDialogProps> = props => {
   const { t: btnTrans } = useTranslation('btn');
 
   const handleConfirm = async () => {
-    await CollectionHttp.compact(collectionName);
+    await Segement.compact(collectionName);
 
     handleCloseDialog();
     cb && cb();
@@ -43,10 +43,14 @@ const CompactDialog: FC<CompactDialogProps> = props => {
       handleClose={handleCloseDialog}
       children={
         <>
-          <Typography variant="body1" component="p" className={classes.desc} dangerouslySetInnerHTML={{
-                __html: collectionTrans('compactDialogInfo'),
-              }}>
-          </Typography>
+          <Typography
+            variant="body1"
+            component="p"
+            className={classes.desc}
+            dangerouslySetInnerHTML={{
+              __html: collectionTrans('compactDialogInfo'),
+            }}
+          ></Typography>
         </>
       }
       confirmLabel={btnTrans('confirm')}

+ 2 - 2
client/src/pages/dialogs/CreateAliasDialog.tsx

@@ -7,7 +7,7 @@ import CustomInput from '@/components/customInput/CustomInput';
 import { formatForm } from '@/utils';
 import { useFormValidation } from '@/hooks';
 import { ITextfieldConfig } from '@/components/customInput/Types';
-import { CollectionHttp } from '@/http';
+import { Collection } from '@/http';
 import { CreateAliasProps } from './Types';
 
 const useStyles = makeStyles((theme: Theme) => ({
@@ -42,7 +42,7 @@ const CreateAliasDialog: FC<CreateAliasProps> = props => {
   };
 
   const handleConfirm = async () => {
-    await CollectionHttp.createAlias(collectionName, form);
+    await Collection.createAlias(collectionName, form);
     handleCloseDialog();
     cb && cb();
   };

+ 5 - 5
client/src/pages/dialogs/CreateCollectionDialog.tsx

@@ -15,11 +15,11 @@ import { useFormValidation } from '@/hooks';
 import { formatForm, TypeEnum } from '@/utils';
 import { DataTypeEnum, ConsistencyLevelEnum, DEFAULT_ATTU_DIM } from '@/consts';
 import CreateFields from '../collections/CreateFields';
-import { CollectionHttp } from '@/http';
+import { Collection } from '@/http';
 import {
   CollectionCreateParam,
   CollectionCreateProps,
-  Field,
+  CreateField,
 } from '../collections/Types';
 import { CONSISTENCY_LEVEL_OPTIONS } from '../collections/Constants';
 
@@ -74,7 +74,7 @@ const CreateCollectionDialog: FC<CollectionCreateProps> = ({ onCreate }) => {
   const [consistencyLevel, setConsistencyLevel] =
     useState<ConsistencyLevelEnum>(ConsistencyLevelEnum.Bounded); // Bounded is the default value of consistency level
 
-  const [fields, setFields] = useState<Field[]>([
+  const [fields, setFields] = useState<CreateField[]>([
     {
       data_type: DataTypeEnum.Int64,
       is_primary_key: true,
@@ -197,7 +197,7 @@ const CreateCollectionDialog: FC<CollectionCreateProps> = ({ onCreate }) => {
     const param: CollectionCreateParam = {
       ...form,
       fields: fields.map(v => {
-        let data: Field = {
+        let data: CreateField = {
           name: v.name,
           description: v.description,
           is_primary_key: v.is_primary_key,
@@ -242,7 +242,7 @@ const CreateCollectionDialog: FC<CollectionCreateProps> = ({ onCreate }) => {
       consistency_level: consistencyLevel,
     };
 
-    await CollectionHttp.createCollection({
+    await Collection.createCollection({
       ...param,
     });
 

+ 2 - 2
client/src/pages/dialogs/CreatePartitionDialog.tsx

@@ -7,7 +7,7 @@ import CustomInput from '@/components/customInput/CustomInput';
 import { ITextfieldConfig } from '@/components/customInput/Types';
 import { useFormValidation } from '@/hooks';
 import { formatForm } from '@/utils';
-import { PartitionHttp } from '@/http';
+import { Partition } from '@/http';
 import { PartitionCreateProps } from './Types';
 import { PartitionManageParam } from '../partitions/Types';
 import { ManageRequestMethods } from '../../types/Common';
@@ -66,7 +66,7 @@ const CreatePartition: FC<PartitionCreateProps> = ({
       type: ManageRequestMethods.CREATE,
     };
 
-    await PartitionHttp.managePartition(param);
+    await Partition.managePartition(param);
     onCreate && onCreate();
     handleCloseDialog();
   };

+ 2 - 2
client/src/pages/dialogs/DropCollectionDialog.tsx

@@ -2,7 +2,7 @@ import { FC, useContext } from 'react';
 import { useTranslation } from 'react-i18next';
 import { rootContext } from '@/context';
 import DeleteTemplate from '@/components/customDialog/DeleteDialogTemplate';
-import { CollectionHttp } from '@/http';
+import { Collection } from '@/http';
 import { DropCollectionProps } from './Types';
 
 const DropCollectionDialog: FC<DropCollectionProps> = props => {
@@ -14,7 +14,7 @@ const DropCollectionDialog: FC<DropCollectionProps> = props => {
 
   const handleDelete = async () => {
     for (const item of collections) {
-      await CollectionHttp.deleteCollection(item._name);
+      await Collection.deleteCollection(item._name);
     }
 
     handleCloseDialog();

+ 2 - 2
client/src/pages/dialogs/DropPartitionDialog.tsx

@@ -2,7 +2,7 @@ import { FC, useContext } from 'react';
 import { useTranslation } from 'react-i18next';
 import { rootContext } from '@/context';
 import DeleteTemplate from '@/components/customDialog/DeleteDialogTemplate';
-import { PartitionHttp } from '@/http';
+import { Partition } from '@/http';
 import { PartitionManageParam } from '../partitions/Types';
 import { ManageRequestMethods } from '../../types/Common';
 import { DropPartitionProps } from './Types';
@@ -21,7 +21,7 @@ const DropPartitionDialog: FC<DropPartitionProps> = props => {
         collectionName,
         type: ManageRequestMethods.DELETE,
       };
-      await PartitionHttp.managePartition(param);
+      await Partition.managePartition(param);
     }
 
     handleCloseDialog();

+ 4 - 3
client/src/pages/dialogs/ImportSampleDialog.tsx

@@ -6,9 +6,10 @@ import DialogTemplate from '@/components/customDialog/DialogTemplate';
 import CustomSelector from '@/components/customSelector/CustomSelector';
 import { rootContext } from '@/context';
 import { InsertStatusEnum } from './insert/Types';
-import { CollectionHttp, MilvusHttp } from '@/http';
+import { DataService } from '@/http';
 import { LoadSampleParam } from './Types';
 import icons from '@/components/icons/Icons';
+
 const DownloadIcon = icons.download;
 
 const getStyles = makeStyles((theme: Theme) => {
@@ -107,7 +108,7 @@ const ImportSampleDialog: FC<{ collection: string }> = props => {
       format: format,
     };
     try {
-      const res = await CollectionHttp.importSample(collectionName, param);
+      const res = await DataService.importSample(collectionName, param);
       if (download) {
         const fileName = format === 'csv' ? csvFileName : jsonFileName;
         const type =
@@ -116,7 +117,7 @@ const ImportSampleDialog: FC<{ collection: string }> = props => {
         saveAs(blob, fileName);
         return { result: res.sampleFile, msg: '' };
       }
-      await MilvusHttp.flush(collectionName);
+      await DataService.flush(collectionName);
       return { result: true, msg: '' };
     } catch (err: any) {
       const {

+ 3 - 3
client/src/pages/dialogs/LoadCollectionDialog.tsx

@@ -8,7 +8,7 @@ import {
 } from '@material-ui/core';
 import { useTranslation } from 'react-i18next';
 import { authContext, rootContext } from '@/context';
-import { CollectionHttp, MilvusHttp } from '@/http';
+import { Collection, MilvusService } from '@/http';
 import { useFormValidation } from '@/hooks';
 import { formatForm, parseJson, getNode } from '@/utils';
 import { MILVUS_NODE_TYPE, MILVUS_DEPLOY_MODE } from '@/consts';
@@ -56,7 +56,7 @@ const LoadCollectionDialog = (props: any) => {
   // check if it is cluster
   useEffect(() => {
     async function fetchData() {
-      const res = await MilvusHttp.getMetrics();
+      const res = await MilvusService.getMetrics();
       const parsedJson = parseJson(res);
       // get root cord
       const rootCoords = getNode(
@@ -98,7 +98,7 @@ const LoadCollectionDialog = (props: any) => {
     }
 
     // load collection request
-    await CollectionHttp.loadCollection(collection, params);
+    await Collection.loadCollection(collection, params);
 
     // callback
     onLoad && onLoad();

+ 2 - 2
client/src/pages/dialogs/ReleaseCollectionDialog.tsx

@@ -2,7 +2,7 @@ import { useContext, useState } from 'react';
 import { Typography, makeStyles, Theme } from '@material-ui/core';
 import { useTranslation } from 'react-i18next';
 import DialogTemplate from '@/components/customDialog/DialogTemplate';
-import { CollectionHttp } from '@/http';
+import { Collection } from '@/http';
 import { rootContext } from '@/context';
 
 const useStyles = makeStyles((theme: Theme) => ({
@@ -27,7 +27,7 @@ const ReleaseCollectionDialog = (props: any) => {
     setDisabled(true);
     try {
       // release collection
-      await CollectionHttp.releaseCollection(collection);
+      await Collection.releaseCollection(collection);
       // execute callback
       onRelease && onRelease();
       // enable confirm button

+ 2 - 2
client/src/pages/dialogs/RenameCollectionDialog.tsx

@@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next';
 import { rootContext } from '@/context';
 import { formatForm } from '@/utils';
 import { useFormValidation } from '@/hooks';
-import { CollectionHttp } from '@/http';
+import { Collection } from '@/http';
 import DialogTemplate from '@/components/customDialog/DialogTemplate';
 import CustomInput from '@/components/customInput/CustomInput';
 import { ITextfieldConfig } from '@/components/customInput/Types';
@@ -42,7 +42,7 @@ const RenameCollectionDialog: FC<RenameCollectionProps> = props => {
   };
 
   const handleConfirm = async () => {
-    await CollectionHttp.renameCollection(collectionName, form);
+    await Collection.renameCollection(collectionName, form);
     handleCloseDialog();
     cb && cb();
   };

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

@@ -1,8 +1,8 @@
-import { CollectionData } from '../collections/Types';
+import { Collection } from '@/http';
 import { PartitionData } from '../partitions/Types';
 
 export interface DropCollectionProps {
-  collections: CollectionData[];
+  collections: Collection[];
   onDelete: () => void;
 }
 

+ 13 - 14
client/src/pages/dialogs/insert/Dialog.tsx

@@ -13,7 +13,7 @@ import { useTranslation } from 'react-i18next';
 import DialogTemplate from '@/components/customDialog/DialogTemplate';
 import icons from '@/components/icons/Icons';
 import { Option } from '@/components/customSelector/Types';
-import { PartitionHttp } from '@/http';
+import { Partition } from '@/http';
 import { rootContext } from '@/context';
 import { combineHeadsAndData } from '@/utils';
 import { FILE_MIME_TYPE } from '@/consts';
@@ -130,10 +130,10 @@ const InsertContainer: FC<InsertContentProps> = ({
   // every time selected collection value change, partition options and default value will change
   const fetchPartition = useCallback(async () => {
     if (collectionValue) {
-      const partitions = await PartitionHttp.getPartitions(collectionValue);
+      const partitions = await Partition.getPartitions(collectionValue);
       const partitionOptions: Option[] = partitions.map(p => ({
-        label: p._formatName,
-        value: p._name,
+        label: p.partitionName,
+        value: p.name,
       }));
       setPartitionOptions(partitionOptions);
 
@@ -152,8 +152,8 @@ const InsertContainer: FC<InsertContentProps> = ({
     } else {
       const options = partitions
         .map(p => ({
-          label: p._formatName,
-          value: p._name,
+          label: p.partitionName,
+          value: p.name,
         }))
         // when there's single selected partition
         // insert dialog partitions shouldn't selectable
@@ -209,8 +209,8 @@ const InsertContainer: FC<InsertContentProps> = ({
     () =>
       defaultSelectedCollection === ''
         ? collections.map(c => ({
-            label: c._name,
-            value: c._name,
+            label: c.collectionName,
+            value: c.collectionName,
           }))
         : [
             {
@@ -232,21 +232,20 @@ const InsertContainer: FC<InsertContentProps> = ({
     const list =
       schema && schema.length > 0
         ? schema
-        : collections.find(c => c._name === collectionValue)?._fields;
+        : collections.find(c => c.collectionName === collectionValue)?.fields;
 
     const autoIdFieldName =
-      list?.find(item => item._isPrimaryKey && item._isAutoId)?._fieldName ||
-      '';
+      list?.find(item => item.isPrimaryKey && item.isAutoId)?.name || '';
     /**
      * if below conditions all met, this schema shouldn't be selectable as head:
      * 1. this field is primary key
      * 2. this field auto id is true
      */
     const options = (list || [])
-      .filter(s => !s._isAutoId || !s._isPrimaryKey)
+      .filter(s => !s.isAutoId || !s.isPrimaryKey)
       .map(s => ({
-        label: s._fieldName,
-        value: s._fieldId,
+        label: s.name,
+        value: s.fieldID,
       }));
     return {
       schemaOptions: options,

+ 3 - 2
client/src/pages/dialogs/insert/Preview.tsx

@@ -116,8 +116,9 @@ const InsertPreview: FC<InsertPreviewProps> = ({
             menuItems={schemaOptions.map(schema => ({
               label: schema.label,
               callback: () => handleTableHeadChange(index, schema.label),
-              wrapperClass: `${classes.menuItem} ${head === schema.label ? classes.menuActive : ''
-                }`,
+              wrapperClass: `${classes.menuItem} ${
+                head === schema.label ? classes.menuActive : ''
+              }`,
             }))}
             buttonProps={{
               className: classes.menuLabel,

+ 3 - 4
client/src/pages/dialogs/insert/Types.ts

@@ -1,14 +1,13 @@
-import { CollectionData } from '../../collections/Types';
 import { PartitionView } from '../../partitions/Types';
-import { FieldData } from '../../schema/Types';
+import { FieldHttp, Collection } from '@/http';
 import { Option } from '@/components/customSelector/Types';
 import { FILE_MIME_TYPE } from '@/consts';
 
 export interface InsertContentProps {
   // optional on partition page since its collection is fixed
-  collections?: CollectionData[];
+  collections?: Collection[];
   // required on partition page since user can't select collection to get schema
-  schema?: FieldData[];
+  schema?: FieldHttp[];
   // required on partition page
   partitions?: PartitionView[];
 

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

@@ -16,7 +16,7 @@ import icons from '@/components/icons/Icons';
 import { LOADING_STATE, MILVUS_DEPLOY_MODE } from '@/consts';
 import { WS_EVENTS, WS_EVENTS_TYPE } from '@server/utils/Const';
 import { useNavigationHook } from '@/hooks';
-import { CollectionHttp, MilvusHttp } from '@/http';
+import { Collection, MilvusService } from '@/http';
 import { ALL_ROUTER_TYPES } from '@/router/Types';
 import { checkLoading, checkIndexBuilding, formatNumber } from '@/utils';
 import CollectionCard from './collectionCard/CollectionCard';
@@ -130,14 +130,14 @@ const Overview = () => {
   const fetchData = useCallback(async () => {
     setLoading(true);
     setCollections([]);
-    const res = (await CollectionHttp.getStatistics()) as statisticsType;
-    const collections = await CollectionHttp.getCollections();
+    const res = (await Collection.getStatistics()) as statisticsType;
+    const collections = await Collection.getCollections();
     const hasLoadingOrBuildingCollection = collections.some(
       v => checkLoading(v) || checkIndexBuilding(v)
     );
     // if some collection is building index or loading, start pulling data
     if (hasLoadingOrBuildingCollection) {
-      MilvusHttp.triggerCron({
+      MilvusService.triggerCron({
         name: WS_EVENTS.COLLECTION,
         type: WS_EVENTS_TYPE.START,
       });
@@ -152,7 +152,7 @@ const Overview = () => {
   }, [fetchData]);
 
   const loadCollections = collections.filter(
-    c => c._status !== LOADING_STATE.UNLOADED
+    c => c.status !== LOADING_STATE.UNLOADED
   );
 
   const onRelease = () => {
@@ -237,7 +237,7 @@ const Overview = () => {
           <div className={classes.cardsWrapper}>
             {loadCollections.map(collection => (
               <CollectionCard
-                key={collection._id}
+                key={collection.id}
                 data={collection}
                 onRelease={onRelease}
               />

+ 13 - 15
client/src/pages/overview/collectionCard/CollectionCard.tsx

@@ -6,7 +6,7 @@ import {
   Card,
   CardContent,
 } from '@material-ui/core';
-import { FC, useContext, useEffect, useState, useCallback } from 'react';
+import { FC, useContext, useEffect, useState } from 'react';
 import CustomButton from '@/components/customButton/CustomButton';
 import icons from '@/components/icons/Icons';
 import Status from '@/components/status/Status';
@@ -17,8 +17,7 @@ import { LOADING_STATE } from '@/consts';
 import { rootContext, dataContext } from '@/context';
 import ReleaseCollectionDialog from '../../dialogs/ReleaseCollectionDialog';
 import { CollectionCardProps } from './Types';
-import { CollectionHttp } from '@/http';
-import { CollectionData } from '@/pages/collections/Types';
+import { Collection } from '@/http';
 
 const useStyles = makeStyles((theme: Theme) => ({
   wrapper: {
@@ -94,7 +93,7 @@ const CollectionCard: FC<CollectionCardProps> = ({
   const classes = useStyles();
   const { setDialog } = useContext(rootContext);
 
-  const { _name: name, _status: status, _loadedPercentage, _replicas } = data;
+  const { collectionName, status: status, loadedPercentage, replicasInfo } = data;
   const navigate = useNavigate();
   // icons
   const RightArrowIcon = icons.rightArrow;
@@ -110,14 +109,14 @@ const CollectionCard: FC<CollectionCardProps> = ({
       type: 'custom',
       params: {
         component: (
-          <ReleaseCollectionDialog collection={name} onRelease={onRelease} />
+          <ReleaseCollectionDialog collection={collectionName} onRelease={onRelease} />
         ),
       },
     });
   };
 
   const onVectorSearchClick = () => {
-    navigate({ pathname: '/search', search: `?collectionName=${name}` });
+    navigate({ pathname: '/search', search: `?collectionName=${collectionName}` });
   };
 
   useEffect(() => {
@@ -125,8 +124,8 @@ const CollectionCard: FC<CollectionCardProps> = ({
       try {
         setLoading(true);
         if (status === LOADING_STATE.LOADED) {
-          const data = (await CollectionHttp.count(name)) as CollectionData;
-          setCount(data._rowCount);
+          const data = await Collection.count(collectionName);
+          setCount(data.entityCount);
         }
       } catch (e) {
       } finally {
@@ -141,7 +140,6 @@ const CollectionCard: FC<CollectionCardProps> = ({
     }
 
     return () => {
-      console.log('existing', name, database);
       exiting = true;
     };
   }, [status, database]);
@@ -149,23 +147,23 @@ const CollectionCard: FC<CollectionCardProps> = ({
   return (
     <Card
       className={`card-wrapper ${classes.wrapper} ${wrapperClass} ${
-        data._status === LOADING_STATE.LOADING && classes.loading
+        data.status === LOADING_STATE.LOADING && classes.loading
       }`}
     >
       <CardContent>
         <div>
-          <Status status={status} percentage={_loadedPercentage} />
+          <Status status={status} percentage={loadedPercentage} />
         </div>
-        <Link className="link" to={`/collections/${name}`}>
-          {name}
+        <Link className="link" to={`/collections/${collectionName}`}>
+          {collectionName}
           <RightArrowIcon classes={{ root: classes.icon }} />
         </Link>
         <ul className={classes.content}>
-          {_replicas && _replicas.length > 1 ? (
+          {replicasInfo && replicasInfo.length > 1 ? (
             <li>
               <Typography>{collectionTrans('replicaNum')}</Typography>:
               <Typography className={classes.rowCount}>
-                {_replicas.length}
+                {replicasInfo.length}
               </Typography>
             </li>
           ) : null}

+ 2 - 2
client/src/pages/overview/collectionCard/Types.ts

@@ -1,7 +1,7 @@
-import { CollectionData } from '../../collections/Types';
+import { Collection } from '@/http';
 
 export interface CollectionCardProps {
-  data: CollectionData;
+  data: Collection;
   onRelease: () => void;
   wrapperClass?: string;
 }

+ 13 - 16
client/src/pages/partitions/Partitions.tsx

@@ -9,9 +9,8 @@ import { usePaginationHook, useInsertDialogHook } from '@/hooks';
 import icons from '@/components/icons/Icons';
 import CustomToolTip from '@/components/customToolTip/CustomToolTip';
 import { rootContext } from '@/context';
-import { CollectionHttp, PartitionHttp, FieldHttp, MilvusHttp } from '@/http';
+import { Collection, Partition, FieldHttp, DataService } from '@/http';
 import InsertContainer from '../dialogs/insert/Dialog';
-import { Field } from '../schema/Types';
 import { InsertDataParam } from '../collections/Types';
 import CreatePartitionDialog from '../dialogs/CreatePartitionDialog';
 import DropPartitionDialog from '../dialogs/DropPartitionDialog';
@@ -73,7 +72,7 @@ const Partitions: FC<{
 
   const fetchPartitions = async (collectionName: string) => {
     try {
-      const res = await PartitionHttp.getPartitions(collectionName);
+      const res = await Partition.getPartitions(collectionName);
       setLoading(false);
       setPartitions(res);
     } catch (err) {
@@ -82,7 +81,7 @@ const Partitions: FC<{
   };
 
   const fetchCollectionDetail = async (name: string) => {
-    const res = await CollectionHttp.getCollection(name);
+    const res = await Collection.getCollection(name);
     return res;
   };
 
@@ -99,14 +98,14 @@ const Partitions: FC<{
     timer = setTimeout(() => {
       const searchWords = [search];
       const list = search
-        ? partitions.filter(p => p._formatName.includes(search))
+        ? partitions.filter(p => p.partitionName.includes(search))
         : partitions;
 
       const highlightList = list.map(c => {
         Object.assign(c, {
           _nameElement: (
             <Highlighter
-              textToHighlight={c._formatName}
+              textToHighlight={c.partitionName}
               searchWords={searchWords}
               highlightClassName={classes.highlight}
             />
@@ -138,8 +137,8 @@ const Partitions: FC<{
       fields_data: fieldData,
     };
     try {
-      await CollectionHttp.insertData(collectionName, param);
-      await MilvusHttp.flush(collectionName);
+      await DataService.insertData(collectionName, param);
+      await DataService.flush(collectionName);
       // update partitions
       fetchPartitions(collectionName);
 
@@ -177,9 +176,7 @@ const Partitions: FC<{
       label: btnTrans('insert'),
       onClick: async () => {
         const collection = await fetchCollectionDetail(collectionName);
-        const schema = collection._schema.fields.map(
-          (f: Field) => new FieldHttp(f)
-        );
+        const schema = collection.schema.fields.map(f => new FieldHttp(f));
 
         handleInsertDialog(
           <InsertContainer
@@ -187,7 +184,7 @@ const Partitions: FC<{
             defaultSelectedCollection={collectionName}
             defaultSelectedPartition={
               selectedPartitions.length === 1
-                ? selectedPartitions[0]._formatName
+                ? selectedPartitions[0].partitionName
                 : ''
             }
             partitions={partitions}
@@ -225,8 +222,8 @@ const Partitions: FC<{
       // can't delete default partition
       disabled: () =>
         selectedPartitions.length === 0 ||
-        selectedPartitions.some(p => p._name === '_default'),
-      tooltip: selectedPartitions.some(p => p._name === '_default')
+        selectedPartitions.some(p => p.name === '_default'),
+      tooltip: selectedPartitions.some(p => p.name === '_default')
         ? t('deletePartitionError')
         : '',
     },
@@ -248,7 +245,7 @@ const Partitions: FC<{
       label: t('name'),
     },
     {
-      id: '_createdTime',
+      id: 'createdAt',
       align: 'left',
       disablePadding: false,
       label: t('createdTime'),
@@ -260,7 +257,7 @@ const Partitions: FC<{
     //   label: t('status'),
     // },
     {
-      id: '_rowCount',
+      id: 'entityCount',
       align: 'left',
       disablePadding: false,
       label: (

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

@@ -3,11 +3,11 @@ import { LOADING_STATE } from '@/consts';
 import { ManageRequestMethods } from '../../types/Common';
 
 export interface PartitionData {
-  _id: string;
-  _name: string;
-  _status: LOADING_STATE;
-  _rowCount: string;
-  _formatName: string;
+  id: string;
+  name: string;
+  status: LOADING_STATE;
+  entityCount: string;
+  partitionName: string;
 }
 
 export interface PartitionView extends PartitionData {

+ 19 - 19
client/src/pages/preview/Preview.tsx

@@ -1,7 +1,7 @@
 import { FC, useEffect, useState } from 'react';
 import { useTranslation } from 'react-i18next';
 import AttuGrid from '@/components/grid/Grid';
-import { CollectionHttp, IndexHttp } from '@/http';
+import { Collection, MilvusIndex } from '@/http';
 import { usePaginationHook, useSearchResult } from '@/hooks';
 import { generateVector } from '@/utils';
 import {
@@ -45,42 +45,42 @@ const Preview: FC<{
 
   const loadData = async (collectionName: string) => {
     // get schema list
-    const collection = await CollectionHttp.getCollection(collectionName);
+    const collection = await Collection.getCollection(collectionName);
 
-    const schemaList = collection._fields!;
+    const schemaList = collection.fields!;
     let nameList = schemaList.map(v => ({
       name: v.name,
-      type: v.data_type,
+      type: v.fieldType,
     }));
 
     // if the dynamic field is enabled, we add $meta column in the grid
-    if (collection._enableDynamicField) {
+    if (collection.enableDynamicField) {
       nameList.push({
         name: DYNAMIC_FIELD,
         type: DataTypeStringEnum.JSON,
       });
     }
 
-    const id = schemaList.find(v => v._isPrimaryKey === true);
-    const primaryKey = id?._fieldName || '';
-    const delimiter = id?.data_type === 'Int64' ? '' : '"';
+    const id = schemaList.find(v => v.isPrimaryKey === true);
+    const primaryKey = id?.name || '';
+    const delimiter = id?.fieldType === 'Int64' ? '' : '"';
 
     const vectorField = schemaList.find(
-      v => v.data_type === 'FloatVector' || v.data_type === 'BinaryVector'
+      v => v.fieldType === 'FloatVector' || v.fieldType === 'BinaryVector'
     );
-    const anns_field = vectorField?._fieldName!;
-    const dim = Number(vectorField?._dimension);
+    const anns_field = vectorField?.name!;
+    const dim = Number(vectorField?.dimension);
     const vectors = [
-      generateVector(vectorField?.data_type === 'FloatVector' ? dim : dim / 8),
+      generateVector(vectorField?.fieldType === 'FloatVector' ? dim : dim / 8),
     ];
     // get search params
-    const indexesInfo = await IndexHttp.getIndexInfo(collectionName);
-    const vectorIndex = indexesInfo.filter(i => i._fieldName === anns_field)[0];
+    const indexesInfo = await MilvusIndex.getIndexInfo(collectionName);
+    const vectorIndex = indexesInfo.filter(i => i.field_name === anns_field)[0];
 
-    const indexType = indexesInfo.length == 0 ? 'FLAT' : vectorIndex._indexType;
+    const indexType = indexesInfo.length == 0 ? 'FLAT' : vectorIndex.indexType;
     const indexConfig = INDEX_CONFIG[indexType];
     const metric_type =
-      indexesInfo.length === 0 ? 'L2' : vectorIndex._metricType;
+      indexesInfo.length === 0 ? 'L2' : vectorIndex.metricType;
     const searchParamKey = indexConfig.search[0];
     const searchParamValue = DEFAULT_SEARCH_PARAM_VALUE_MAP[searchParamKey];
     const searchParam = { [searchParamKey]: searchParamValue };
@@ -95,7 +95,7 @@ const Preview: FC<{
 
     try {
       // first, search random data to get random id
-      const searchRes = await CollectionHttp.vectorSearchData(collectionName, {
+      const searchRes = await Collection.vectorSearchData(collectionName, {
         search_params: {
           topk: 100,
           anns_field,
@@ -106,7 +106,7 @@ const Preview: FC<{
         vectors,
         output_fields: [primaryKey],
         vector_type:
-          DataTypeEnum[vectorField!._fieldType as keyof typeof DataTypeEnum],
+          DataTypeEnum[vectorField!.fieldType as keyof typeof DataTypeEnum],
       });
 
       // compose random id list expression
@@ -115,7 +115,7 @@ const Preview: FC<{
         .join(',')}]`;
 
       // query by random id
-      const res = await CollectionHttp.queryData(collectionName, {
+      const res = await Collection.queryData(collectionName, {
         expr: expr,
         output_fields: [...nameList.map(i => i.name)],
       });

+ 9 - 11
client/src/pages/query/Query.tsx

@@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next';
 import { saveAs } from 'file-saver';
 import { Parser } from '@json2csv/plainjs';
 import { rootContext } from '@/context';
-import { CollectionHttp } from '@/http';
+import { Collection, DataService } from '@/http';
 import { usePaginationHook, useSearchResult } from '@/hooks';
 import EmptyCard from '@/components/cards/EmptyCard';
 import icons from '@/components/icons/Icons';
@@ -12,10 +12,8 @@ import CustomButton from '@/components/customButton/CustomButton';
 import AttuGrid from '@/components/grid/Grid';
 import { ToolBarConfig } from '@/components/grid/Types';
 import Filter from '@/components/advancedSearch';
-// import { useTimeTravelHook } from '@/hooks';
 import DeleteTemplate from '@/components/customDialog/DeleteDialogTemplate';
 import CustomToolBar from '@/components/grid/ToolBar';
-// import { CustomDatePicker } from '@/components/customDatePicker/CustomDatePicker';
 import { getLabelDisplayedRows } from '../search/Utils';
 import { getQueryStyles } from './Styles';
 import { DYNAMIC_FIELD, DataTypeStringEnum } from '@/consts';
@@ -68,24 +66,24 @@ const Query: FC<{
   };
 
   const getFields = async (collectionName: string) => {
-    const collection = await CollectionHttp.getCollection(collectionName);
-    const schemaList = collection._fields;
+    const collection = await Collection.getCollection(collectionName);
+    const schemaList = collection.fields;
 
     const nameList = schemaList.map(v => ({
       name: v.name,
-      type: v.data_type,
+      type: v.fieldType,
     }));
 
     // if the dynamic field is enabled, we add $meta column in the grid
-    if (collection._enableDynamicField) {
+    if (collection.enableDynamicField) {
       nameList.push({
         name: DYNAMIC_FIELD,
         type: DataTypeStringEnum.JSON,
       });
     }
 
-    const primaryKey = schemaList.find(v => v._isPrimaryKey === true)!;
-    setPrimaryKey({ value: primaryKey['name'], type: primaryKey['data_type'] });
+    const primaryKey = schemaList.find(v => v.isPrimaryKey === true)!;
+    setPrimaryKey({ value: primaryKey['name'], type: primaryKey['fieldType'] });
 
     setFields(nameList);
   };
@@ -118,7 +116,7 @@ const Query: FC<{
       return;
     }
     try {
-      const res = await CollectionHttp.queryData(collectionName, {
+      const res = await Collection.queryData(collectionName, {
         expr: expr,
         output_fields: fields.map(i => i.name),
         offset: 0,
@@ -140,7 +138,7 @@ const Query: FC<{
   };
 
   const handleDelete = async () => {
-    await CollectionHttp.deleteEntities(collectionName, {
+    await DataService.deleteEntities(collectionName, {
       expr: `${primaryKey.value} in [${selectedData
         .map(v =>
           primaryKey.type === DataTypeStringEnum.VarChar

+ 62 - 43
client/src/pages/schema/IndexTypeElement.tsx

@@ -1,28 +1,30 @@
-import { FC, useContext, useEffect, useMemo, useState } from 'react';
+import {
+  FC,
+  useContext,
+  useEffect,
+  useMemo,
+  useState,
+  MouseEvent,
+} from 'react';
 import { useTranslation } from 'react-i18next';
 import Chip from '@material-ui/core/Chip';
-import { makeStyles, Theme } from '@material-ui/core';
-import {
-  FieldView,
-  IndexCreateParam,
-  IndexExtraParam,
-  IndexManageParam,
-} from './Types';
-import { IndexHttp } from '@/http';
+import { makeStyles, Theme, Tooltip } from '@material-ui/core';
+import { IndexCreateParam, IndexExtraParam, IndexManageParam } from './Types';
+import { MilvusIndex, FieldHttp } from '@/http';
 import { rootContext } from '@/context';
 import icons from '@/components/icons/Icons';
 import DeleteTemplate from '@/components/customDialog/DeleteDialogTemplate';
 import StatusIcon from '@/components/status/StatusIcon';
 import { ChildrenStatusType } from '@/components/status/Types';
 import { sleep } from '@/utils';
-import CreateIndex from './Create';
-import { IndexState } from '../../types/Milvus';
+import { IndexState } from '@/types/Milvus';
 import { NONE_INDEXABLE_DATA_TYPES } from '@/consts';
+import CreateIndex from './Create';
 
 const useStyles = makeStyles((theme: Theme) => ({
   wrapper: {
     // give fixed width to prevent table cell stretching
-    width: 150,
+    width: 'auto',
   },
   item: {
     paddingLeft: theme.spacing(1),
@@ -30,10 +32,7 @@ const useStyles = makeStyles((theme: Theme) => ({
   btn: {
     display: 'flex',
     alignItems: 'center',
-    textTransform: 'uppercase',
     whiteSpace: 'nowrap',
-
-    fontSize: '14px',
     color: theme.palette.primary.main,
 
     '&:hover': {
@@ -49,8 +48,7 @@ const useStyles = makeStyles((theme: Theme) => ({
     },
   },
   chip: {
-    height: '24px',
-    backgroundColor: '#e9e9ed',
+    background: `rgba(0, 0, 0, 0.04)`,
     padding: theme.spacing(0.5),
 
     '& .icon': {
@@ -60,7 +58,6 @@ const useStyles = makeStyles((theme: Theme) => ({
   },
   chipLabel: {
     fontSize: '12px',
-    lineHeight: '16px',
   },
   addIcon: {
     width: '20px',
@@ -69,10 +66,12 @@ const useStyles = makeStyles((theme: Theme) => ({
 }));
 
 const IndexTypeElement: FC<{
-  data: FieldView;
+  data: FieldHttp;
   collectionName: string;
+  disabled?: boolean;
+  disabledTooltip?: string;
   cb: (collectionName: string) => void;
-}> = ({ data, collectionName, cb }) => {
+}> = ({ data, collectionName, cb, disabled, disabledTooltip }) => {
   const classes = useStyles();
   // set empty string as default status
   const [status, setStatus] = useState<string>(IndexState.Default);
@@ -104,7 +103,7 @@ const IndexTypeElement: FC<{
       indexName: string
     ) => {
       // get fetch data
-      const index_descriptions = await IndexHttp.getIndexInfo(collectionName);
+      const index_descriptions = await MilvusIndex.getIndexInfo(collectionName);
 
       const indexDescription = index_descriptions.find(
         i => i.field_name === fieldName
@@ -127,14 +126,14 @@ const IndexTypeElement: FC<{
       }
     };
     // prevent delete index trigger fetching index status
-    if (data._indexType !== '' && status !== IndexState.Delete) {
-      fetchStatus(collectionName, data._fieldName, data._indexName);
+    if (data.indexType !== '' && status !== IndexState.Delete) {
+      fetchStatus(collectionName, data.name, data.indexName!);
     }
 
     return () => {
       running = false;
     };
-  }, [collectionName, data._indexType, data._fieldName, data._indexName]);
+  }, [collectionName, data.indexType, data.name, data.indexName]);
 
   const requestCreateIndex = async (
     params: IndexExtraParam,
@@ -142,11 +141,11 @@ const IndexTypeElement: FC<{
   ) => {
     const indexCreateParam: IndexCreateParam = {
       collection_name: collectionName,
-      field_name: data._fieldName,
+      field_name: data.name,
       index_name,
       extra_params: params,
     };
-    await IndexHttp.createIndex(indexCreateParam);
+    await MilvusIndex.createIndex(indexCreateParam);
     // reset status to default empty string
     setStatus(IndexState.Default);
     handleCloseDialog();
@@ -154,7 +153,9 @@ const IndexTypeElement: FC<{
     cb(collectionName);
   };
 
-  const handleCreate = () => {
+  const handleCreate = (e: MouseEvent<HTMLDivElement>) => {
+    e.stopPropagation();
+
     setDialog({
       open: true,
       type: 'custom',
@@ -162,9 +163,9 @@ const IndexTypeElement: FC<{
         component: (
           <CreateIndex
             collectionName={collectionName}
-            fieldName={data._fieldName}
-            fieldType={data._fieldType}
-            dimension={Number(data._dimension)}
+            fieldName={data.name}
+            fieldType={data.fieldType}
+            dimension={Number(data.dimension)}
             handleCancel={handleCloseDialog}
             handleCreate={requestCreateIndex}
           />
@@ -176,11 +177,11 @@ const IndexTypeElement: FC<{
   const requestDeleteIndex = async () => {
     const indexDeleteParam: IndexManageParam = {
       collection_name: collectionName,
-      field_name: data._fieldName,
-      index_name: data._indexName,
+      field_name: data.name,
+      index_name: data.indexName!,
     };
 
-    await IndexHttp.deleteIndex(indexDeleteParam);
+    await MilvusIndex.deleteIndex(indexDeleteParam);
     // use 'delete' as special status for whether fetching index status check
     setStatus(IndexState.Delete);
     cb(collectionName);
@@ -208,20 +209,20 @@ const IndexTypeElement: FC<{
   const generateElement = () => {
     // only vector type field is able to create index
     if (
-      data._isPrimaryKey ||
-      NONE_INDEXABLE_DATA_TYPES.indexOf(data._fieldType) !== -1
+      data.isPrimaryKey ||
+      NONE_INDEXABLE_DATA_TYPES.indexOf(data.fieldType) !== -1
     ) {
       return <div className={classes.item}>--</div>;
     }
-    // _indexType example: FLAT
-    switch (data._indexType) {
+    // indexType example: FLAT
+    switch (data.indexType) {
       case '': {
         return (
           <div
             role="button"
             onClick={handleCreate}
             className={`${classes.btn} ${
-              data._createIndexDisabled ? classes.btnDisabled : ''
+              data.createIndexDisabled ? classes.btnDisabled : ''
             }`}
           >
             <AddIcon classes={{ root: classes.addIcon }} />
@@ -242,17 +243,35 @@ const IndexTypeElement: FC<{
           return <StatusIcon type={ChildrenStatusType.CREATING} />;
         }
 
-        /**
-         * if creating finished, show chip that contains index type
-         */
-        return (
+        const chipComp = () => (
           <Chip
-            label={data._indexType}
+            label={data.indexType}
             classes={{ root: classes.chip, label: classes.chipLabel }}
             deleteIcon={<DeleteIcon classes={{ root: 'icon' }} />}
             onDelete={handleDelete}
+            disabled={disabled}
+            onClick={(e: MouseEvent<HTMLDivElement>) => {
+              e.stopPropagation();
+              handleDelete();
+            }}
+            size="small"
           />
         );
+        /**
+         * if creating finished, show chip that contains index type
+         */
+        return disabled ? (
+          <Tooltip
+            interactive
+            arrow
+            title={disabledTooltip ?? ''}
+            placement={'top'}
+          >
+            <div>{chipComp()}</div>
+          </Tooltip>
+        ) : (
+          <div>{chipComp()}</div>
+        );
       }
     }
   };

+ 15 - 38
client/src/pages/schema/Schema.tsx

@@ -5,9 +5,8 @@ import { ColDefinitionsType } from '@/components/grid/Types';
 import { useTranslation } from 'react-i18next';
 import { usePaginationHook } from '@/hooks';
 import icons from '@/components/icons/Icons';
-import { FieldHttp, IndexHttp } from '@/http';
 import { formatFieldType } from '@/utils';
-import { FieldView } from './Types';
+import { Collection, FieldHttp } from '@/http';
 import IndexTypeElement from './IndexTypeElement';
 
 const useStyles = makeStyles((theme: Theme) => ({
@@ -66,7 +65,7 @@ const Schema: FC<{
   const { t: collectionTrans } = useTranslation('collection');
   const { t: indexTrans } = useTranslation('index');
 
-  const [fields, setFields] = useState<FieldView[]>([]);
+  const [fields, setFields] = useState<FieldHttp[]>([]);
   const [loading, setLoading] = useState<boolean>(true);
 
   const {
@@ -81,40 +80,18 @@ const Schema: FC<{
     handleGridSort,
   } = usePaginationHook(fields);
 
-  const fetchSchemaListWithIndex = async (
-    collectionName: string
-  ): Promise<FieldView[]> => {
-    const indexList = await IndexHttp.getIndexInfo(collectionName);
-    const schemaList = await FieldHttp.getFields(collectionName);
-    let fields: FieldView[] = [];
-    for (const schema of schemaList) {
-      let field: FieldView = Object.assign(schema, {
-        _indexParameterPairs: [],
-        _indexType: '',
-        _indexName: '',
-      });
-      const index = indexList.find(i => i._fieldName === schema.name);
-      field._indexParameterPairs = index?._indexParameterPairs || [];
-      field._indexType = index?._indexType || '';
-      field._indexName = index?._indexName || '';
-
-      fields = [...fields, field];
-    }
-    return fields;
-  };
-
   const fetchFields = useCallback(
     async (collectionName: string) => {
       const KeyIcon = icons.key;
 
       try {
-        const list = await fetchSchemaListWithIndex(collectionName);
-        const fields: FieldView[] = list.map(f =>
+        const collection = await Collection.getCollection(collectionName);
+        const fields = collection.fieldWithIndexInfo.map(f =>
           Object.assign(f, {
             _fieldNameElement: (
               <div className={classes.nameWrapper}>
-                {f._fieldName}
-                {f._isPrimaryKey ? (
+                {f.name}
+                {f.isPrimaryKey ? (
                   <div
                     className={classes.iconTitle}
                     title={collectionTrans('idFieldName')}
@@ -130,7 +107,7 @@ const Schema: FC<{
                     variant="outlined"
                   />
                 ) : null}
-                {f._isAutoId ? (
+                {f.autoID ? (
                   <Chip
                     className={classes.chip}
                     size="small"
@@ -144,11 +121,11 @@ const Schema: FC<{
             _fieldTypeElement: formatFieldType(f),
             _indexParamElement: (
               <div className={classes.paramWrapper}>
-                {f._indexParameterPairs?.length > 0 ? (
-                  f._indexParameterPairs.map(p =>
+                {f.indexParameterPairs?.length > 0 ? (
+                  f.indexParameterPairs.map(p =>
                     p.value ? (
                       <>
-                        <span key={p.key} className="param">
+                        <span key={p.key + p.value} className="param">
                           <Typography variant="body1" className="key">
                             {`${p.key}:`}
                           </Typography>
@@ -196,7 +173,7 @@ const Schema: FC<{
       align: 'left',
       disablePadding: true,
       label: collectionTrans('fieldName'),
-      sortBy: '_fieldName',
+      sortBy: 'name',
     },
     {
       id: '_fieldTypeElement',
@@ -205,7 +182,7 @@ const Schema: FC<{
       label: collectionTrans('fieldType'),
     },
     {
-      id: '_indexName',
+      id: 'indexName',
       align: 'left',
       disablePadding: true,
       label: 'Index name',
@@ -215,7 +192,7 @@ const Schema: FC<{
       align: 'left',
       disablePadding: true,
       label: indexTrans('type'),
-      sortBy: '_indexType',
+      sortBy: 'indexType',
     },
     {
       id: '_indexParamElement',
@@ -225,7 +202,7 @@ const Schema: FC<{
       notSort: true,
     },
     {
-      id: '_desc',
+      id: 'desc',
       align: 'left',
       disablePadding: false,
       label: indexTrans('desc'),
@@ -243,7 +220,7 @@ const Schema: FC<{
         colDefinitions={colDefinitions}
         rows={schemaList}
         rowCount={total}
-        primaryKey="_fieldId"
+        primaryKey="fieldID"
         showHoverStyle={false}
         page={currentPage}
         onPageChange={handlePageChange}

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

@@ -1,48 +1,9 @@
-import { ReactElement } from 'react';
-import { MetricType, INDEX_TYPES_ENUM, DataTypeStringEnum } from '@/consts';
-
-export interface Field {
-  data_type: DataTypeStringEnum;
-  fieldID: string;
-  type_params: { key: string; value: string }[];
-  is_primary_key: true;
-  name: string;
-  description: string;
-}
-
-export interface FieldData {
-  _fieldId: string;
-  _isPrimaryKey: boolean;
-  is_partition_key: boolean;
-  _isAutoId: boolean;
-  _fieldName: string;
-  _fieldNameElement?: ReactElement;
-  _fieldType: DataTypeStringEnum;
-  _dimension: string;
-  _desc: string;
-  _maxLength: string;
-  _maxCapacity: string;
-  element_type: string;
-}
-
-export interface FieldView extends FieldData, IndexView {
-  _createIndexDisabled?: boolean;
-}
+import { INDEX_TYPES_ENUM } from '@/consts';
 
 export interface Index {
   params: { key: string; value: string }[];
 }
 
-export interface IndexView {
-  _fieldName: string;
-  _indexType: string;
-  _indexName: string;
-  _indexTypeElement?: ReactElement;
-  _indexParameterPairs: { key: string; value: string }[];
-  _indexParamElement?: ReactElement;
-  _metricType?: MetricType | string;
-}
-
 export type IndexType =
   | INDEX_TYPES_ENUM.FLAT
   | INDEX_TYPES_ENUM.IVF_FLAT

+ 3 - 6
client/src/pages/search/Types.ts

@@ -1,10 +1,7 @@
 import { Option } from '@/components/customSelector/Types';
 import { searchKeywordsType } from '@/consts';
-import {
-  DataTypeEnum,
-  DataTypeStringEnum,
-} from '../collections/Types';
-import { IndexView } from '../schema/Types';
+import { DataTypeEnum, DataTypeStringEnum } from '@/consts';
+import { MilvusIndex } from '@/http';
 
 export interface SearchParamsProps {
   // if user created index, pass metric type choosed when creating
@@ -37,7 +34,7 @@ export interface FieldOption extends Option {
   fieldType: DataTypeStringEnum;
   // used to get metric type, index type and index params for search params
   // if user doesn't create index, default value is null
-  indexInfo: IndexView | null;
+  indexInfo: MilvusIndex | null;
   // used for check vector input validation
   dimension: number;
 }

+ 31 - 28
client/src/pages/search/VectorSearch.tsx

@@ -15,7 +15,7 @@ import SimpleMenu from '@/components/menu/SimpleMenu';
 import { Option } from '@/components/customSelector/Types';
 import Filter from '@/components/advancedSearch';
 import { Field } from '@/components/advancedSearch/Types';
-import { CollectionHttp, IndexHttp } from '@/http';
+import { Collection, MilvusIndex } from '@/http';
 import {
   parseValue,
   parseLocationSearch,
@@ -36,7 +36,6 @@ import {
 import { getLabelDisplayedRows } from './Utils';
 import SearchParams from './SearchParams';
 import { getVectorSearchStyles } from './Styles';
-import { CollectionData } from '../collections/Types';
 import { TOP_K_OPTIONS } from './Constants';
 import { FieldOption, SearchResultView, VectorSearchParam } from './Types';
 
@@ -53,7 +52,7 @@ const VectorSearch = () => {
 
   // data stored inside the component
   const [tableLoading, setTableLoading] = useState<boolean>(false);
-  const [collections, setCollections] = useState<CollectionData[]>([]);
+  const [collections, setCollections] = useState<Collection[]>([]);
   const [selectedCollection, setSelectedCollection] = useState<string>('');
   const [fieldOptions, setFieldOptions] = useState<FieldOption[]>([]);
   // fields for advanced filter
@@ -95,29 +94,29 @@ const VectorSearch = () => {
   const collectionOptions: Option[] = useMemo(
     () =>
       collections.map(c => ({
-        label: c._name,
-        value: c._name,
+        label: c.collectionName,
+        value: c.collectionName,
       })),
     [collections]
   );
 
   const outputFields: string[] = useMemo(() => {
-    const s = collections.find(c => c._name === selectedCollection);
+    const s = collections.find(c => c.collectionName === selectedCollection);
 
     if (!s) {
       return [];
     }
 
-    const fields = s._fields || [];
+    const fields = s.fields || [];
 
     // vector field can't be output fields
     const invalidTypes = ['BinaryVector', 'FloatVector'];
     const nonVectorFields = fields.filter(
-      field => !invalidTypes.includes(field._fieldType)
+      field => !invalidTypes.includes(field.fieldType)
     );
 
-    const _outputFields = nonVectorFields.map(f => f._fieldName);
-    if (s._enableDynamicField) {
+    const _outputFields = nonVectorFields.map(f => f.name);
+    if (s.enableDynamicField) {
       _outputFields.push(DYNAMIC_FIELD);
     }
     return _outputFields;
@@ -125,10 +124,10 @@ const VectorSearch = () => {
 
   const primaryKeyField = useMemo(() => {
     const selectedCollectionInfo = collections.find(
-      c => c._name === selectedCollection
+      c => c.collectionName === selectedCollection
     );
-    const fields = selectedCollectionInfo?._fields || [];
-    return fields.find(f => f._isPrimaryKey)?._fieldName;
+    const fields = selectedCollectionInfo?.fields || [];
+    return fields.find(f => f.isPrimaryKey)?.name;
   }, [selectedCollection, collections]);
 
   const orderArray = [primaryKeyField, 'id', 'score', ...outputFields];
@@ -178,16 +177,19 @@ const VectorSearch = () => {
       const index = selectedFieldInfo?.indexInfo;
       const embeddingType = getEmbeddingType(selectedFieldInfo!.fieldType);
       const metric =
-        index?._metricType || DEFAULT_METRIC_VALUE_MAP[embeddingType];
-      const indexParams = index?._indexParameterPairs || [];
+        index?.metricType || DEFAULT_METRIC_VALUE_MAP[embeddingType];
+      const indexParams = index?.indexParameterPairs || [];
       const dim = selectedFieldInfo?.dimension || 0;
       setSelectedMetricType(metric);
 
       return {
         metricType: metric,
-        indexType: index?._indexType || getDefaultIndexType(embeddingType),
+        indexType: index?.indexType || getDefaultIndexType(embeddingType),
         indexParams,
-        fieldType: DataTypeEnum[selectedFieldInfo?.fieldType!],
+        fieldType:
+          DataTypeEnum[
+            selectedFieldInfo?.fieldType! as keyof typeof DataTypeEnum
+          ],
         embeddingType,
         selectedFieldDimension: dim,
       };
@@ -245,15 +247,16 @@ const VectorSearch = () => {
 
   // fetch data
   const fetchCollections = useCallback(async () => {
-    const collections = await CollectionHttp.getCollections();
-    setCollections(collections.filter(c => c._status === LOADING_STATE.LOADED));
+    const collections = await Collection.getCollections();
+    setCollections(collections.filter(c => c.status === LOADING_STATE.LOADED));
   }, [database]);
 
   const fetchFieldsWithIndex = useCallback(
-    async (collectionName: string, collections: CollectionData[]) => {
+    async (collectionName: string, collections: Collection[]) => {
       const fields =
-        collections.find(c => c._name === collectionName)?._fields || [];
-      const indexes = await IndexHttp.getIndexInfo(collectionName);
+        collections.find(c => c.collectionName === collectionName)?.fields ||
+        [];
+      const indexes = await MilvusIndex.getIndexInfo(collectionName);
 
       const { vectorFields, nonVectorFields } = classifyFields(fields);
 
@@ -295,7 +298,7 @@ const VectorSearch = () => {
       const { collectionName } = parseLocationSearch(location.search);
       // collection name validation
       const isNameValid = collections
-        .map(c => c._name)
+        .map(c => c.collectionName)
         .includes(collectionName);
       isNameValid && setSelectedCollection(collectionName);
     }
@@ -348,7 +351,7 @@ const VectorSearch = () => {
 
     setTableLoading(true);
     try {
-      const res = await CollectionHttp.vectorSearchData(
+      const res = await Collection.vectorSearchData(
         selectedCollection,
         params
       );
@@ -390,9 +393,9 @@ const VectorSearch = () => {
             disabled={collectionOptions.length === 0}
             value={selectedCollection}
             onChange={(e: { target: { value: unknown } }) => {
-              const collection = e.target.value;
+              const collection = e.target.value as string;
 
-              setSelectedCollection(collection as string);
+              setSelectedCollection(collection);
               // every time selected collection changed, reset field
               setSelectedField('');
               setSearchResult([]);
@@ -407,8 +410,8 @@ const VectorSearch = () => {
             label={searchTrans('field')}
             value={selectedField}
             onChange={(e: { target: { value: unknown } }) => {
-              const field = e.target.value;
-              setSelectedField(field as string);
+              const field = e.target.value as string;
+              setSelectedField(field);
             }}
           />
         </CardContent>

+ 9 - 10
client/src/pages/segments/Segments.tsx

@@ -1,6 +1,6 @@
 import { useEffect, useState, FC, useContext } from 'react';
 import { useTranslation } from 'react-i18next';
-import { CollectionHttp } from '@/http';
+import { Segement } from '@/http';
 import { usePaginationHook } from '@/hooks';
 import { rootContext } from '@/context';
 import AttuGrid from '@/components/grid/Grid';
@@ -24,18 +24,17 @@ const Segments: FC<{
   const fetchSegments = async () => {
     setLoading(true);
 
-    const psegments = await CollectionHttp.getPSegments(collectionName);
-    const qsegments = await CollectionHttp.getQSegments(collectionName);
+    const psegments = (await Segement.getPSegments(collectionName)) || {};
+    const qsegments = (await Segement.getQSegments(collectionName)) || {};
 
-    const combinedArray = psegments.infos.map((p: any) => {
-      const q = qsegments.infos.find((q: any) => q.segmentID === p.segmentID);
+    const combinedArray = psegments.infos.map(p => {
+      const q = qsegments.infos.find(q => q.segmentID === p.segmentID)! as any;
       return {
         ...p,
-        ...(q &&
-          Object.keys(q).reduce((acc: any, key) => {
-            acc[`q_${key}`] = q[key];
-            return acc;
-          }, {})),
+        ...Object.keys(q).reduce((acc, key) => {
+          acc[`q_${key}`] = q[key];
+          return acc;
+        }, {} as any),
       };
     });
 

+ 3 - 3
client/src/pages/system/SystemView.tsx

@@ -4,7 +4,7 @@ import { makeStyles, Theme } from '@material-ui/core';
 import clsx from 'clsx';
 import { useNavigationHook, useInterval } from '@/hooks';
 import { ALL_ROUTER_TYPES } from '@/router/Types';
-import { MilvusHttp } from '@/http';
+import { MilvusService } from '@/http';
 import { parseJson } from '@/utils';
 import Topo from './Topology';
 import NodeListView from './NodeListView';
@@ -83,14 +83,14 @@ const SystemView: any = () => {
 
   useInterval(async () => {
     if (!selectedCord) {
-      const res = await MilvusHttp.getMetrics();
+      const res = await MilvusService.getMetrics();
       setData(parseJson(res));
     }
   }, INTERVAL);
 
   useEffect(() => {
     async function fetchData() {
-      const res = await MilvusHttp.getMetrics();
+      const res = await MilvusService.getMetrics();
       setData(parseJson(res));
     }
     fetchData();

+ 2 - 2
client/src/pages/systemHealthy/SystemHealthyView.tsx

@@ -2,7 +2,7 @@ import { useEffect, useMemo, useState } from 'react';
 import { makeStyles, Theme } from '@material-ui/core';
 import { useTranslation } from 'react-i18next';
 import { useNavigationHook, useInterval } from '@/hooks';
-import { PrometheusHttp } from '@/http';
+import { PrometheusService } from '@/http';
 import { ALL_ROUTER_TYPES } from '@/router/Types';
 import {
   LAST_TIME_HEALTHY_THRESHOLD_CPU,
@@ -109,7 +109,7 @@ const SystemHealthyView = () => {
 
   const updateData = async () => {
     const curT = new Date().getTime();
-    const result = (await PrometheusHttp.getHealthyData({
+    const result = (await PrometheusService.getHealthyData({
       start: curT - timeRange.value,
       end: curT,
       step: timeRange.step,

+ 3 - 3
client/src/pages/user/Roles.tsx

@@ -1,7 +1,7 @@
 import { useContext, useEffect, useState } from 'react';
 import { makeStyles, Theme, Chip } from '@material-ui/core';
 import { useTranslation } from 'react-i18next';
-import { UserHttp } from '@/http';
+import { User } from '@/http';
 import { rootContext, dataContext } from '@/context';
 import { useNavigationHook } from '@/hooks';
 import AttuGrid from '@/components/grid/Grid';
@@ -35,7 +35,7 @@ const Roles = () => {
   const { t: dialogTrans } = useTranslation('dialog');
 
   const fetchRoles = async () => {
-    const roles = (await UserHttp.getRoles()) as RolesType;
+    const roles = (await User.getRoles()) as RolesType;
     setSelectedRole([]);
 
     setRoles(
@@ -81,7 +81,7 @@ const Roles = () => {
         roleName: role.name,
         force,
       };
-      await UserHttp.deleteRole(param);
+      await User.deleteRole(param);
     }
 
     openSnackBar(successTrans('delete', { name: userTrans('role') }));

+ 4 - 4
client/src/pages/user/UpdateRoleDialog.tsx

@@ -6,7 +6,7 @@ import CustomInput from '@/components/customInput/CustomInput';
 import { ITextfieldConfig } from '@/components/customInput/Types';
 import { useFormValidation } from '@/hooks';
 import { formatForm } from '@/utils';
-import { UserHttp } from '@/http';
+import { User } from '@/http';
 import {
   CreateRoleProps,
   CreateRoleParams,
@@ -50,7 +50,7 @@ const UpdateRoleDialog: FC<CreateRoleProps> = ({
   });
 
   const fetchRBAC = async () => {
-    const rbacOptions = await UserHttp.getRBAC();
+    const rbacOptions = await User.getRBAC();
 
     setRbacOptions(rbacOptions);
   };
@@ -108,10 +108,10 @@ const UpdateRoleDialog: FC<CreateRoleProps> = ({
 
   const handleCreateRole = async () => {
     if (!isEditing) {
-      await UserHttp.createRole(form);
+      await User.createRole(form);
     }
 
-    await UserHttp.updateRolePrivileges(form);
+    await User.updateRolePrivileges(form);
 
     onUpdate({ data: form, isEditing: isEditing });
   };

+ 3 - 3
client/src/pages/user/UpdateUserRole.tsx

@@ -9,7 +9,7 @@ import { FC, useState, useEffect } from 'react';
 import { useTranslation } from 'react-i18next';
 import DialogTemplate from '@/components/customDialog/DialogTemplate';
 import { UpdateUserRoleProps, UpdateUserRoleParams } from './Types';
-import { UserHttp } from '@/http';
+import { User } from '@/http';
 
 const useStyles = makeStyles((theme: Theme) => ({
   input: {
@@ -41,12 +41,12 @@ const UpdateUserRole: FC<UpdateUserRoleProps> = ({
   const classes = useStyles();
 
   const handleUpdate = async () => {
-    await UserHttp.updateUserRole(form);
+    await User.updateUserRole(form);
     onUpdate(form);
   };
 
   const fetchAllRoles = async () => {
-    const roles = await UserHttp.getRoles();
+    const roles = await User.getRoles();
 
     setRoleOptions(roles.results.map((r: any) => r.role.name));
   };

+ 8 - 8
client/src/pages/user/User.tsx

@@ -1,7 +1,7 @@
 import React, { useContext, useEffect, useState } from 'react';
 import { makeStyles, Theme } from '@material-ui/core';
 import { useTranslation } from 'react-i18next';
-import { UserHttp } from '@/http';
+import { User } from '@/http';
 import AttuGrid from '@/components/grid/Grid';
 import { ColDefinitionsType, ToolBarConfig } from '@/components/grid/Types';
 import {
@@ -39,8 +39,8 @@ const Users = () => {
   const { t: dialogTrans } = useTranslation('dialog');
 
   const fetchUsers = async () => {
-    const res = await UserHttp.getUsers();
-    const roles = await UserHttp.getRoles();
+    const res = await User.getUsers();
+    const roles = await User.getRoles();
 
     setUsers(
       res.usernames.map((v: string) => {
@@ -60,9 +60,9 @@ const Users = () => {
   };
 
   const handleCreate = async (data: CreateUserParams) => {
-    await UserHttp.createUser(data);
+    await User.createUser(data);
     // assign user role if
-    await UserHttp.updateUserRole({
+    await User.updateUserRole({
       username: data.username,
       roles: data.roles,
     });
@@ -81,7 +81,7 @@ const Users = () => {
   };
 
   const handleUpdate = async (data: UpdateUserParams) => {
-    await UserHttp.updateUser(data);
+    await User.updateUser(data);
     fetchUsers();
     openSnackBar(successTrans('update', { name: userTrans('user') }));
     handleCloseDialog();
@@ -92,7 +92,7 @@ const Users = () => {
       const param: DeleteUserParams = {
         username: user.name,
       };
-      await UserHttp.deleteUser(param);
+      await User.deleteUser(param);
     }
 
     openSnackBar(successTrans('delete', { name: userTrans('user') }));
@@ -104,7 +104,7 @@ const Users = () => {
     {
       label: userTrans('user'),
       onClick: async () => {
-        const roles = await UserHttp.getRoles();
+        const roles = await User.getRoles();
         setDialog({
           open: true,
           type: 'custom',

+ 0 - 16
client/src/types/Milvus.ts

@@ -9,19 +9,3 @@ export enum IndexState {
   Default = '',
   Delete = 'Delete',
 }
-
-export type IndexDescription = {
-  fields_name: string;
-  index_name: string;
-  indexed_rows: string | number;
-  state: IndexState;
-};
-
-export interface DescribeIndexResponse {
-  index_descriptions: IndexDescription[];
-}
-
-export enum ShowCollectionsType {
-  All = 0,
-  InMemory = 1,
-}

+ 2 - 2
client/src/types/SearchTypes.ts

@@ -1,6 +1,6 @@
 import { Option } from '@/components/customSelector/Types';
 import { searchKeywordsType, DataTypeEnum, DataTypeStringEnum } from '@/consts';
-import { IndexView } from '@/pages/schema/Types';
+import { MilvusIndex } from '@/http';
 
 export interface SearchParamsProps {
   // if user created index, pass metric type choosed when creating
@@ -32,7 +32,7 @@ export interface FieldOption extends Option {
   fieldType: DataTypeStringEnum;
   // used to get metric type, index type and index params for search params
   // if user doesn't create index, default value is null
-  indexInfo: IndexView | null;
+  indexInfo: MilvusIndex | null;
   // used for check vector input validation
   dimension: number;
 }

+ 10 - 11
client/src/utils/Format.ts

@@ -4,8 +4,8 @@ import {
   DEFAULT_PROMETHEUS_PORT,
   DataTypeEnum,
 } from '@/consts';
-import { CreateFieldType, Field } from '@/pages/collections/Types';
-import { FieldView } from '@/pages/schema/Types';
+import { CreateFieldType, CreateField } from '@/pages/collections/Types';
+import { FieldHttp } from '@/http';
 
 /**
  * transform large capacity to capacity in b.
@@ -125,7 +125,7 @@ export const checkIsBinarySubstructure = (metricLabel: string): boolean => {
   return metricLabel === 'Superstructure' || metricLabel === 'Substructure';
 };
 
-export const getCreateFieldType = (config: Field): CreateFieldType => {
+export const getCreateFieldType = (config: CreateField): CreateFieldType => {
   if (config.is_primary_key) {
     return 'primaryKey';
   }
@@ -232,17 +232,16 @@ export const formatUtcToMilvus = (bigNumber: number) => {
  * @param bigNumber
  * @returns
  */
-export const formatFieldType = (field: FieldView) => {
-  const { _fieldType, element_type, _maxLength, _maxCapacity, _dimension } =
-    field;
+export const formatFieldType = (field: FieldHttp) => {
+  const { fieldType, element_type, maxLength, maxCapacity, dimension } = field;
 
   const elementType =
     element_type !== 'None'
-      ? `<${element_type}${_maxLength ? `(${_maxLength})` : ''}>`
+      ? `<${element_type}${maxLength ? `(${maxLength})` : ''}>`
       : '';
-  const maxCapacity = _maxCapacity ? `[${_maxCapacity}]` : '';
-  const dimension = _dimension ? `(${_dimension})` : '';
-  const maxLength = _fieldType === 'VarChar' ? `(${_maxLength})` : '';
+  const maxCap = maxCapacity ? `[${maxCapacity}]` : '';
+  const dim = dimension ? `(${dimension})` : '';
+  const maxLn = fieldType === 'VarChar' ? `(${maxLength})` : '';
 
-  return `${_fieldType}${elementType}${maxCapacity}${dimension}${maxLength}`;
+  return `${fieldType}${elementType}${maxCap}${dim}${maxLn}`;
 };

+ 3 - 2
client/src/utils/Validation.ts

@@ -1,5 +1,6 @@
 import { ChildrenStatusType } from '@/components/status/Types';
 import { MetricType, METRIC_TYPES_VALUES } from '@/consts';
+import { Collection } from '@/http';
 
 export type ValidType =
   | 'email'
@@ -248,8 +249,8 @@ export const getCheckResult = (param: ICheckMapParam): boolean => {
 /**
  * Check collection is loading or not
  */
-export const checkLoading = (v: any): boolean =>
-  v._loadedPercentage !== '-1' && v._loadedPercentage !== '100';
+export const checkLoading = (v: Collection): boolean =>
+  v.loadedPercentage !== '-1' && v.loadedPercentage !== '100';
 
 /**
  * Check collection is index building or not.

+ 17 - 16
client/src/utils/search.ts

@@ -1,7 +1,8 @@
 import { Field } from '../components/advancedSearch/Types';
-import { FieldData, IndexType, IndexView } from '../pages/schema/Types';
+import { IndexType } from '../pages/schema/Types';
 import { INDEX_TYPES_ENUM, DataTypeEnum, DataTypeStringEnum } from '@/consts';
 import { FieldOption } from '../types/SearchTypes';
+import { FieldHttp, MilvusIndex } from '@/http';
 
 /**
  * function to get EmbeddingType
@@ -35,15 +36,15 @@ export const getDefaultIndexType = (embeddingType: DataTypeEnum): IndexType => {
  * funtion to divide fields into two categories: vector or nonVector
  */
 export const classifyFields = (
-  fields: FieldData[]
-): { vectorFields: FieldData[]; nonVectorFields: FieldData[] } => {
+  fields: FieldHttp[]
+): { vectorFields: FieldHttp[]; nonVectorFields: FieldHttp[] } => {
   const vectorTypes: DataTypeStringEnum[] = [
     DataTypeStringEnum.BinaryVector,
     DataTypeStringEnum.FloatVector,
   ];
   return fields.reduce(
     (result, cur) => {
-      const changedFieldType = vectorTypes.includes(cur._fieldType)
+      const changedFieldType = vectorTypes.includes(cur.fieldType)
         ? 'vectorFields'
         : 'nonVectorFields';
 
@@ -51,34 +52,34 @@ export const classifyFields = (
 
       return result;
     },
-    { vectorFields: [] as FieldData[], nonVectorFields: [] as FieldData[] }
+    { vectorFields: [] as FieldHttp[], nonVectorFields: [] as FieldHttp[] }
   );
 };
 
 export const getVectorFieldOptions = (
-  fields: FieldData[],
-  indexes: IndexView[]
+  fields: FieldHttp[],
+  indexes: MilvusIndex[]
 ): FieldOption[] => {
   const options: FieldOption[] = fields.map(f => {
-    const embeddingType = getEmbeddingType(f._fieldType);
+    const embeddingType = getEmbeddingType(f.fieldType);
     const defaultIndex = getDefaultIndexType(embeddingType);
-    const index = indexes.find(i => i._fieldName === f._fieldName);
+    const index = indexes.find(i => i.field_name === f.name);
 
     return {
-      label: `${f._fieldName} (${index?._indexType || defaultIndex})`,
-      value: f._fieldName,
-      fieldType: f._fieldType,
+      label: `${f.name} (${index?.indexType || defaultIndex})`,
+      value: f.name,
+      fieldType: f.fieldType,
       indexInfo: index || null,
-      dimension: Number(f._dimension),
+      dimension: Number(f.dimension),
     };
   });
 
   return options;
 };
 
-export const getNonVectorFieldsForFilter = (fields: FieldData[]): Field[] => {
+export const getNonVectorFieldsForFilter = (fields: FieldHttp[]): Field[] => {
   return fields.map(f => ({
-    name: f._fieldName,
-    type: f._fieldType,
+    name: f.name,
+    type: f.fieldType,
   }));
 };

+ 3 - 3
server/src/collections/collections.controller.ts

@@ -155,10 +155,10 @@ export class CollectionController {
   async describeCollection(req: Request, res: Response, next: NextFunction) {
     const name = req.params?.name;
     try {
-      const result = await this.collectionsService.describeCollection({
-        collection_name: name,
+      const result = await this.collectionsService.getAllCollections({
+        data: [{ name }],
       });
-      res.send(result);
+      res.send(result[0]);
     } catch (error) {
       next(error);
     }

+ 17 - 16
server/src/collections/collections.service.ts

@@ -26,6 +26,7 @@ import {
 import { Parser } from '@json2csv/plainjs';
 import { throwErrorFromSDK, findKeyValue, genRows, ROW_COUNT } from '../utils';
 import { QueryDto, ImportSampleDto, GetReplicasDto } from './dto';
+import { CollectionData } from '../types';
 
 export class CollectionsService {
   constructor(private milvusService: MilvusService) {}
@@ -166,49 +167,49 @@ export class CollectionsService {
    * Get all collections meta data
    * @returns {id:string, collection_name:string, schema:Field[], autoID:boolean, rowCount: string, consistency_level:string}
    */
-  async getAllCollections() {
-    const data: any = [];
-    const res = await this.getCollections();
+  async getAllCollections(collections?: {
+    data: { name: string }[];
+  }): Promise<CollectionData[]> {
+    const data: CollectionData[] = [];
+    const res = collections || (await this.getCollections());
     const loadedCollections = await this.getCollections({
       type: ShowCollectionsType.Loaded,
     });
     if (res.data.length > 0) {
       for (const item of res.data) {
         const { name } = item;
+
+        // get collection schema and properties
         const collectionInfo = await this.describeCollection({
           collection_name: name,
         });
 
-        let count: number | string;
-
+        // get collection statistic data
         const collectionStatisticsRes = await this.getCollectionStatistics({
           collection_name: name,
         });
-        count = collectionStatisticsRes.data.row_count;
-        // try {
-        //   const countRes = await this.count({
-        //     collection_name: name,
-        //   });
-        //   count = countRes.data;
-        // } catch (error) {
-        // }
 
+        // get index info for collections
         const indexRes = await this.getIndexInfo({
           collection_name: item.name,
         });
 
+        // extract autoID
         const autoID = collectionInfo.schema.fields.find(
           v => v.is_primary_key === true
         )?.autoID;
 
+        // if it is loaded
         const loadCollection = loadedCollections.data.find(
           v => v.name === name
         );
 
+        // loading info
         const loadedPercentage = !loadCollection
           ? '-1'
           : loadCollection.loadedPercentage;
 
+        // get replica info
         let replicas;
         try {
           replicas = loadCollection
@@ -226,18 +227,18 @@ export class CollectionsService {
           schema: collectionInfo.schema,
           description: collectionInfo.schema.description,
           autoID,
-          rowCount: count,
+          rowCount: collectionStatisticsRes.data.row_count,
           id: collectionInfo.collectionID,
           loadedPercentage,
           createdTime: parseInt(collectionInfo.created_utc_timestamp, 10),
-          index_descriptions: indexRes,
+          index_descriptions: indexRes.index_descriptions,
           consistency_level: collectionInfo.consistency_level,
           replicas: replicas && replicas.replicas,
         });
       }
     }
     // add default sort - Descending order
-    data.sort((a: any, b: any) => b.createdTime - a.createdTime);
+    data.sort((a, b) => b.createdTime - a.createdTime);
     return data;
   }
 

+ 20 - 0
server/src/types/collections.type.ts

@@ -0,0 +1,20 @@
+import {
+  IndexDescription,
+  CollectionSchema,
+  ReplicaInfo,
+} from '@zilliz/milvus2-sdk-node';
+
+export interface CollectionData {
+  collection_name: string;
+  schema: CollectionSchema;
+  rowCount: number | string;
+  createdTime: number;
+  aliases: string[];
+  description: string;
+  autoID: boolean;
+  id: string;
+  loadedPercentage: string;
+  index_descriptions: IndexDescription[];
+  consistency_level: string;
+  replicas: ReplicaInfo[];
+}

+ 14 - 0
server/src/types/index.ts

@@ -0,0 +1,14 @@
+export {
+  IndexDescription,
+  CollectionSchema,
+  ReplicaInfo,
+  FieldSchema,
+  KeyValuePair,
+  ShowCollectionsType,
+  GetQuerySegmentInfoResponse,
+  QuerySegmentInfo,
+  GePersistentSegmentInfoResponse,
+  PersistentSegmentInfo,
+} from '@zilliz/milvus2-sdk-node';
+
+export * from './collections.type';