Browse Source

refactor types (#411)

* stash

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

* refactor part1

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

* update

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

* finish refactor

Signed-off-by: ruiyi.jiang <ruiyi.jiang@zilliz.com>

* finish refactor

Signed-off-by: ruiyi.jiang <ruiyi.jiang@zilliz.com>

* fix build

Signed-off-by: ruiyi.jiang <ruiyi.jiang@zilliz.com>

* refactor more

Signed-off-by: ruiyi.jiang <ruiyi.jiang@zilliz.com>

---------

Signed-off-by: ryjiang <jiangruiyi@gmail.com>
Signed-off-by: ruiyi.jiang <ruiyi.jiang@zilliz.com>
ryjiang 1 year ago
parent
commit
d3d4327e7c
69 changed files with 1042 additions and 1502 deletions
  1. 7 6
      client/src/components/advancedSearch/Condition.tsx
  2. 7 12
      client/src/components/advancedSearch/Types.ts
  3. 1 1
      client/src/components/grid/TableHead.tsx
  4. 2 2
      client/src/components/status/Types.ts
  5. 3 12
      client/src/consts/Milvus.ts
  6. 5 6
      client/src/context/Data.tsx
  7. 3 3
      client/src/context/Types.ts
  8. 25 38
      client/src/hooks/Query.ts
  9. 6 13
      client/src/http/BaseModel.ts
  10. 100 0
      client/src/http/Collection.service.ts
  11. 0 192
      client/src/http/Collection.ts
  12. 0 5
      client/src/http/Data.service.ts
  13. 0 5
      client/src/http/Database.service.ts
  14. 0 58
      client/src/http/Field.ts
  15. 37 0
      client/src/http/Index.service.ts
  16. 0 75
      client/src/http/MilvusIndex.ts
  17. 0 5
      client/src/http/Partition.service.ts
  18. 0 5
      client/src/http/Prometheus.service.ts
  19. 4 12
      client/src/http/Segment.service.ts
  20. 0 5
      client/src/http/User.service.ts
  21. 2 3
      client/src/http/index.ts
  22. 4 2
      client/src/pages/collections/Aliases.tsx
  23. 62 61
      client/src/pages/collections/Collections.tsx
  24. 6 6
      client/src/pages/collections/Constants.ts
  25. 25 22
      client/src/pages/collections/CreateFields.tsx
  26. 3 4
      client/src/pages/collections/StatusAction.tsx
  27. 21 23
      client/src/pages/collections/Types.ts
  28. 2 2
      client/src/pages/dialogs/CompactDialog.tsx
  29. 2 2
      client/src/pages/dialogs/CreateAliasDialog.tsx
  30. 2 2
      client/src/pages/dialogs/CreateCollectionDialog.tsx
  31. 2 2
      client/src/pages/dialogs/DropCollectionDialog.tsx
  32. 3 3
      client/src/pages/dialogs/DuplicateCollectionDailog.tsx
  33. 0 1
      client/src/pages/dialogs/FlushDialog.tsx
  34. 2 2
      client/src/pages/dialogs/LoadCollectionDialog.tsx
  35. 2 2
      client/src/pages/dialogs/ReleaseCollectionDialog.tsx
  36. 2 2
      client/src/pages/dialogs/RenameCollectionDialog.tsx
  37. 3 4
      client/src/pages/dialogs/Types.ts
  38. 9 9
      client/src/pages/dialogs/insert/Dialog.tsx
  39. 4 4
      client/src/pages/dialogs/insert/Types.ts
  40. 5 9
      client/src/pages/overview/Overview.tsx
  41. 17 11
      client/src/pages/overview/collectionCard/CollectionCard.tsx
  42. 2 2
      client/src/pages/overview/collectionCard/Types.ts
  43. 2 2
      client/src/pages/overview/statisticsCard/Types.ts
  44. 3 3
      client/src/pages/partitions/Partitions.tsx
  45. 0 177
      client/src/pages/preview/Preview.tsx
  46. 139 132
      client/src/pages/query/Query.tsx
  47. 1 1
      client/src/pages/query/Styles.ts
  48. 36 31
      client/src/pages/schema/IndexTypeElement.tsx
  49. 24 28
      client/src/pages/schema/Schema.tsx
  50. 4 9
      client/src/pages/search/Types.ts
  51. 53 79
      client/src/pages/search/VectorSearch.tsx
  52. 7 8
      client/src/pages/segments/Segments.tsx
  53. 0 7
      client/src/pages/user/Types.ts
  54. 1 19
      client/src/types/Common.ts
  55. 2 7
      client/src/types/SearchTypes.ts
  56. 0 11
      client/src/utils/Common.ts
  57. 8 38
      client/src/utils/Format.ts
  58. 4 75
      client/src/utils/search.ts
  59. 3 2
      server/src/app.ts
  60. 32 70
      server/src/collections/collections.controller.ts
  61. 191 105
      server/src/collections/collections.service.ts
  62. 0 15
      server/src/milvus/milvus.controller.ts
  63. 10 7
      server/src/milvus/milvus.service.ts
  64. 4 2
      server/src/schema/schema.controller.ts
  65. 30 22
      server/src/schema/schema.service.ts
  66. 72 4
      server/src/types/collections.type.ts
  67. 0 9
      server/src/types/index.ts
  68. 8 6
      server/src/utils/Const.ts
  69. 28 0
      server/src/utils/Helper.ts

+ 7 - 6
client/src/components/advancedSearch/Condition.tsx

@@ -7,10 +7,11 @@ import {
   TextField,
 } from '@material-ui/core';
 import CloseIcon from '@material-ui/icons/Close';
-import { ConditionProps, Field } from './Types';
+import { ConditionProps } from './Types';
 import CustomSelector from '../customSelector/CustomSelector';
 import { LOGICAL_OPERATORS, DataTypeStringEnum } from '@/consts';
 import { formatValue, checkValue } from './utils';
+import { FieldObject } from '@server/types';
 
 const Condition: FC<ConditionProps> = props => {
   const {
@@ -25,7 +26,7 @@ const Condition: FC<ConditionProps> = props => {
   const [operator, setOperator] = useState(
     initData?.op || LOGICAL_OPERATORS[0].value
   );
-  const [conditionField, setConditionField] = useState<Field>(
+  const [conditionField, setConditionField] = useState<FieldObject>(
     initData?.field || fields[0] || {}
   );
   const [jsonKeyValue, setJsonKeyValue] = useState(initData?.jsonKey || '');
@@ -43,7 +44,7 @@ const Condition: FC<ConditionProps> = props => {
    * Trigger condition change event.
    */
   useEffect(() => {
-    const type = conditionField?.type;
+    const type = conditionField?.data_type;
     const conditionValueWithNoSpace = conditionValue.replaceAll(' ', '');
     let isKeyLegal = false;
     let isLegal = checkValue({
@@ -78,7 +79,7 @@ const Condition: FC<ConditionProps> = props => {
   const classes = useStyles();
 
   const logicalOperators = useMemo(() => {
-    if (conditionField.type === DataTypeStringEnum.Bool) {
+    if (conditionField.data_type === DataTypeStringEnum.Bool) {
       const data = LOGICAL_OPERATORS.filter(v => v.value === '==');
       setOperator(data[0].value);
       // bool only support ==
@@ -114,14 +115,14 @@ const Condition: FC<ConditionProps> = props => {
   return (
     <div className={`${classes.wrapper} ${className}`} {...others}>
       <CustomSelector
-        label={conditionField.type}
+        label={conditionField.data_type}
         value={conditionField?.name}
         onChange={handleFieldNameChange}
         options={fields.map(i => ({ value: i.name, label: i.name }))}
         variant="filled"
         wrapperClass={classes.fieldName}
       />
-      {conditionField?.type === DataTypeStringEnum.JSON ? (
+      {conditionField?.data_type === DataTypeStringEnum.JSON ? (
         <TextField
           className={classes.key}
           label="key"

+ 7 - 12
client/src/components/advancedSearch/Types.ts

@@ -1,22 +1,17 @@
-import { DataTypeStringEnum } from '@/consts';
+import { FieldObject } from '@server/types';
 
 export interface ConditionProps {
   others?: object;
   onDelete: () => void;
   triggerChange: (id: string, data: TriggerChangeData) => void;
-  fields: Field[];
+  fields: FieldObject[];
   id: string;
   initData: any;
   className?: string;
 }
 
-export interface Field {
-  name: string;
-  type: DataTypeStringEnum;
-}
-
 export interface TriggerChangeData {
-  field: Field;
+  field: FieldObject;
   op: string;
   value: string;
   originValue: string;
@@ -27,7 +22,7 @@ export interface TriggerChangeData {
 
 export interface ConditionGroupProps {
   others?: object;
-  fields: Field[];
+  fields: FieldObject[];
   handleConditions: any;
   conditions: any[];
 }
@@ -61,7 +56,7 @@ export interface DialogProps {
   onReset: () => void;
   onCancel: () => void;
   title: string;
-  fields: Field[];
+  fields: FieldObject[];
   handleConditions: any;
   conditions: any[];
   isLegal: boolean;
@@ -77,11 +72,11 @@ export interface FilterProps {
   others?: { [key: string]: any };
   onSubmit: (data: any) => void;
   tooltipPlacement?: 'left' | 'right' | 'bottom' | 'top';
-  fields: Field[];
+  fields: FieldObject[];
 }
 
 export interface ConditionData {
-  field: Field;
+  field: FieldObject;
   op: string;
   value: string;
   isCorrect: boolean;

+ 1 - 1
client/src/components/grid/TableHead.tsx

@@ -75,7 +75,7 @@ const EnhancedTableHead: FC<TableHeadType> = props => {
 
         {colDefinitions.map(headCell => (
           <TableCell
-            key={headCell.id}
+            key={headCell.id + headCell.label}
             align={headCell.align || 'left'}
             padding={headCell.disablePadding ? 'none' : 'normal'}
             sortDirection={

+ 2 - 2
client/src/components/status/Types.ts

@@ -1,5 +1,5 @@
 import { LOADING_STATE } from '@/consts';
-import { FieldHttp } from '@/http';
+import { SchemaObject } from '@server/types';
 
 export type StatusType = {
   status: LOADING_STATE;
@@ -10,7 +10,7 @@ export type StatusActionType = {
   status: LOADING_STATE;
   percentage?: string;
   action?: Function;
-  field: FieldHttp;
+  field: SchemaObject;
   collectionName: string;
   onIndexCreate: Function;
 };

+ 3 - 12
client/src/consts/Milvus.ts

@@ -235,15 +235,6 @@ export const METRIC_OPTIONS_MAP = {
   ],
 };
 
-/**
- * use L2 as float default metric type
- * use Hamming as binary default metric type
- */
-export const DEFAULT_METRIC_VALUE_MAP = {
-  [DataTypeEnum.FloatVector]: METRIC_TYPES_VALUES.L2,
-  [DataTypeEnum.BinaryVector]: METRIC_TYPES_VALUES.HAMMING,
-};
-
 // search params default value map
 export const DEFAULT_SEARCH_PARAM_VALUE_MAP: {
   [key in searchKeywordsType]?: number;
@@ -263,9 +254,9 @@ export const DEFAULT_SEARCH_PARAM_VALUE_MAP: {
 export const DEFAULT_NLIST_VALUE = 1024;
 
 export enum LOADING_STATE {
-  LOADED,
-  LOADING,
-  UNLOADED,
+  LOADED = 'loaded',
+  LOADING = 'loading',
+  UNLOADED = 'unloaded',
 }
 export enum LOAD_STATE {
   LoadStateNotExist = 'LoadStateNotExist',

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

@@ -8,11 +8,12 @@ import {
 } from 'react';
 import { io, Socket } from 'socket.io-client';
 import { authContext } from '@/context';
-import { url, Collection, MilvusService, DatabaseService } from '@/http';
+import { url, CollectionService, MilvusService, DatabaseService } from '@/http';
 import { checkIndexBuilding, checkLoading } from '@/utils';
 import { DataContextType } from './Types';
 import { WS_EVENTS, WS_EVENTS_TYPE } from '@server/utils/Const';
 import { LAST_TIME_DATABASE } from '@/consts';
+import { CollectionObject } from '@server/types';
 
 export const dataContext = createContext<DataContextType>({
   loading: false,
@@ -30,7 +31,7 @@ const { Provider } = dataContext;
 
 export const DataProvider = (props: { children: React.ReactNode }) => {
   // local data state
-  const [collections, setCollections] = useState<Collection[]>([]);
+  const [collections, setCollections] = useState<CollectionObject[]>([]);
   const [connected, setConnected] = useState(false);
   const [loading, setLoading] = useState(false);
   const [database, setDatabase] = useState<string>(
@@ -44,9 +45,7 @@ export const DataProvider = (props: { children: React.ReactNode }) => {
 
   // socket callback
   const socketCallBack = useCallback(
-    (data: any) => {
-      const collections: Collection[] = data.map((v: any) => new Collection(v));
-
+    (collections: CollectionObject[]) => {
       const hasLoadingOrBuildingCollection = collections.some(
         v => checkLoading(v) || checkIndexBuilding(v)
       );
@@ -68,7 +67,7 @@ export const DataProvider = (props: { children: React.ReactNode }) => {
   const fetchCollections = async () => {
     try {
       setLoading(true);
-      const res = await Collection.getCollections();
+      const res = await CollectionService.getCollections();
       setCollections(res);
       setLoading(false);
     } catch (error) {

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

@@ -1,5 +1,5 @@
 import { Dispatch, ReactElement, SetStateAction } from 'react';
-import { Collection } from '@/http';
+import { CollectionObject } from '@server/types';
 import { NavInfo } from '@/router/Types';
 
 export type RootContextType = {
@@ -90,8 +90,8 @@ export type NavContextType = {
 
 export type DataContextType = {
   loading: boolean;
-  collections: Collection[];
-  setCollections: Dispatch<SetStateAction<Collection[]>>;
+  collections: CollectionObject[];
+  setCollections: Dispatch<SetStateAction<CollectionObject[]>>;
   database: string;
   setDatabase: Dispatch<SetStateAction<string>>;
   databases: string[];

+ 25 - 38
client/src/hooks/Query.ts

@@ -1,11 +1,7 @@
 import { useState, useRef, useEffect } from 'react';
-import {
-  DataTypeStringEnum,
-  DYNAMIC_FIELD,
-  LOAD_STATE,
-  MIN_INT64,
-} from '@/consts';
-import { Collection } from '@/http';
+import { DataTypeStringEnum, DYNAMIC_FIELD, MIN_INT64 } from '@/consts';
+import { CollectionService } from '@/http';
+import { CollectionFullObject } from '@server/types';
 
 export const useQuery = (params: {
   onQueryStart: Function;
@@ -14,12 +10,7 @@ export const useQuery = (params: {
   collectionName: string;
 }) => {
   // state
-  const [collection, setCollection] = useState<any>({
-    fields: [],
-    primaryKey: { value: '', type: DataTypeStringEnum.Int64 },
-    loaded: false,
-    data: null,
-  });
+  const [collection, setCollection] = useState<CollectionFullObject>();
   const [consistencyLevel, setConsistencyLevel] = useState<string>('Bounded');
   const [currentPage, setCurrentPage] = useState<number>(0);
   const [pageSize, setPageSize] = useState<number>(0);
@@ -36,17 +27,19 @@ export const useQuery = (params: {
     const cache = pageCache.current.get(
       page > currentPage ? currentPage : page
     );
-    const primaryKey = collection.primaryKey;
+    const primaryKey = collection?.schema?.primaryField;
 
     const formatPKValue = (pkId: string | number) =>
-      primaryKey.type === DataTypeStringEnum.VarChar ? `'${pkId}'` : pkId;
+      primaryKey?.data_type === DataTypeStringEnum.VarChar ? `'${pkId}'` : pkId;
 
     // If cache does not exist, return expression based on primaryKey type
     let condition = '';
     if (!cache) {
       const defaultValue =
-        primaryKey.type === DataTypeStringEnum.VarChar ? "''" : `${MIN_INT64}`;
-      condition = `${primaryKey.value} > ${defaultValue}`;
+        primaryKey?.data_type === DataTypeStringEnum.VarChar
+          ? "''"
+          : `${MIN_INT64}`;
+      condition = `${primaryKey?.name} > ${defaultValue}`;
     } else {
       const { firstPKId, lastPKId } = cache;
       const lastPKValue = formatPKValue(lastPKId);
@@ -54,8 +47,8 @@ export const useQuery = (params: {
 
       condition =
         page > currentPage
-          ? `(${primaryKey.value} > ${lastPKValue})`
-          : `((${primaryKey.value} <= ${lastPKValue}) && (${primaryKey.value} >= ${firstPKValue}))`;
+          ? `(${primaryKey?.name} > ${lastPKValue})`
+          : `((${primaryKey?.name} <= ${lastPKValue}) && (${primaryKey?.name} >= ${firstPKValue}))`;
     }
 
     return expr ? `${expr} && ${condition}` : condition;
@@ -73,7 +66,7 @@ export const useQuery = (params: {
     try {
       const queryParams = {
         expr: _expr,
-        output_fields: collection.fields.map((i: any) => i.name),
+        output_fields: collection!.schema.fields.map(i => i.name),
         limit: pageSize || 10,
         consistency_level,
         // travel_timestamp: timeTravelInfo.timestamp,
@@ -82,7 +75,7 @@ export const useQuery = (params: {
       // cache last query
       lastQuery.current = queryParams;
       // execute query
-      const res = await Collection.queryData(
+      const res = await CollectionService.queryData(
         params.collectionName,
         queryParams
       );
@@ -93,10 +86,10 @@ export const useQuery = (params: {
       const firstItem = res.data[0];
       // get last pk id
       const lastPKId: string | number =
-        lastItem && lastItem[collection.primaryKey.value];
+        lastItem && lastItem[collection!.schema.primaryField.name];
       // get first pk id
       const firstPKId: string | number =
-        firstItem && firstItem[collection.primaryKey.value];
+        firstItem && firstItem[collection!.schema.primaryField.name];
 
       // store pk id in the cache with the page number
       if (lastItem) {
@@ -121,34 +114,28 @@ export const useQuery = (params: {
 
   // get collection info
   const prepare = async (collectionName: string) => {
-    const collection = await Collection.getCollectionInfo(collectionName);
-    const schemaList = collection.fields;
+    const collection = await CollectionService.getCollectionInfo(collectionName);
+    const schemaList = collection.schema.fields;
 
     const nameList = schemaList.map(v => ({
       name: v.name,
-      type: v.fieldType,
+      type: v.data_type,
     }));
 
     // if the dynamic field is enabled, we add $meta column in the grid
-    if (collection.enableDynamicField) {
+    if (collection.schema.enable_dynamic_field) {
       nameList.push({
         name: DYNAMIC_FIELD,
         type: DataTypeStringEnum.JSON,
       });
     }
-    const primaryKey = schemaList.find(v => v.isPrimaryKey === true)!;
     setConsistencyLevel(collection.consistency_level);
-    setCollection({
-      fields: nameList as any[],
-      primaryKey: { value: primaryKey['name'], type: primaryKey['fieldType'] },
-      loaded: collection.state === LOAD_STATE.LoadStateLoaded,
-      data: collection,
-    });
+    setCollection(collection);
   };
 
   const count = async (consistency_level = consistencyLevel) => {
     const count = 'count(*)';
-    const res = await Collection.queryData(params.collectionName, {
+    const res = await CollectionService.queryData(params.collectionName, {
       expr: expr,
       output_fields: [count],
       consistency_level,
@@ -171,7 +158,7 @@ export const useQuery = (params: {
 
   // query if expr is changed
   useEffect(() => {
-    if (!collection.primaryKey.value || !collection.loaded) {
+    if (!collection || !collection!.loaded) {
       // console.info('[skip running query]: no key yet');
       return;
     } // reset
@@ -184,7 +171,7 @@ export const useQuery = (params: {
 
   // query if collection is changed
   useEffect(() => {
-    if (!collection.primaryKey.value || !collection.loaded) {
+    if (!collection || !collection!.loaded) {
       // console.info('[skip running query]: no key yet');
       return;
     }
@@ -198,7 +185,7 @@ export const useQuery = (params: {
 
   // query if page size is changed
   useEffect(() => {
-    if (!collection.primaryKey.value || !collection.loaded) {
+    if (!collection || !collection!.loaded) {
       // console.info('[skip running query]: no key yet');
       return;
     }

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

@@ -14,10 +14,6 @@ type updateParamsType = {
 };
 
 export default class BaseModel {
-  constructor(props: any) {
-    return this;
-  }
-
   static async findAll<T>(data: findParamsType) {
     const { params = {}, path = '', method = 'get' } = data;
     const type = method === 'post' ? 'data' : 'params';
@@ -33,12 +29,9 @@ export default class BaseModel {
       return list as T;
     }
 
-    return Object.assign(
-      list.map(v => new this(v)),
-      {
-        _total: res.data.data.total_count || list.length,
-      } as T
-    );
+    return Object.assign(list, {
+      _total: res.data.data.total_count || list.length,
+    } as T);
   }
 
   static async search<T>(data: findParamsType) {
@@ -52,7 +45,7 @@ export default class BaseModel {
     const res = await http(httpConfig);
     // conflict with collection view data structure, status is useless, so delete here.
     delete res.data.data.status;
-    return new this(res.data.data || {}) as T;
+    return (res.data.data || {}) as T;
   }
 
   /**
@@ -61,14 +54,14 @@ export default class BaseModel {
   static async create<T>(options: updateParamsType) {
     const { path, data } = options;
     const res = await http.post(path, data);
-    return new this(res.data.data || {}) as T;
+    return (res.data.data || {}) as T;
   }
 
   static async update<T>(options: updateParamsType) {
     const { path, data } = options;
     const res = await http.put(path, data);
 
-    return new this(res.data.data || {}) as T;
+    return (res.data.data || {}) as T;
   }
 
   static async delete<T>(options: updateParamsType) {

+ 100 - 0
client/src/http/Collection.service.ts

@@ -0,0 +1,100 @@
+import { LoadReplicaReq } from '@/pages/collections/Types';
+import { QueryParam } from '@/pages/query/Types';
+import BaseModel from './BaseModel';
+import {
+  ShowCollectionsType,
+  CollectionFullObject,
+  CollectionObject,
+  CountObject,
+  StatisticsObject,
+} from '@server/types';
+
+export class CollectionService extends BaseModel {
+  static getCollections(data?: {
+    type: ShowCollectionsType;
+  }): Promise<CollectionObject[]> {
+    return super.findAll({ path: '/collections', params: data || {} });
+  }
+
+  static getCollectionInfo(collectionName: string) {
+    return super.search<CollectionFullObject>({
+      path: `/collections/${collectionName}`,
+      params: {},
+    });
+  }
+
+  static createCollection(data: any) {
+    return super.create({ path: `collections`, data });
+  }
+
+  static deleteCollection(collectionName: string) {
+    return super.delete({ path: `/collections/${collectionName}` });
+  }
+
+  static loadCollection(collectionName: string, param?: LoadReplicaReq) {
+    return super.update({
+      path: `/collections/${collectionName}/load`,
+      data: param,
+    });
+  }
+
+  static releaseCollection(collectionName: string) {
+    return super.update({
+      path: `/collections/${collectionName}/release`,
+    });
+  }
+
+  static renameCollection(
+    collectionName: string,
+    params: { new_collection_name: string }
+  ) {
+    return super.create({
+      path: `/collections/${collectionName}`,
+      data: params,
+    });
+  }
+
+  static duplicate(
+    collectionName: string,
+    params: { new_collection_name: string }
+  ) {
+    return super.create({
+      path: `/collections/${collectionName}/duplicate`,
+      data: params,
+    });
+  }
+
+  static getStatistics() {
+    return super.search<StatisticsObject>({
+      path: `/collections/statistics`,
+      params: {},
+    });
+  }
+
+  static count(collectionName: string) {
+    return super.search<CountObject>({
+      path: `/collections/${collectionName}/count`,
+      params: {},
+    });
+  }
+
+  static createAlias(collectionName: string, params: { alias: string }) {
+    return super.create({
+      path: `/collections/${collectionName}/alias`,
+      data: params,
+    });
+  }
+
+  static dropAlias(collectionName: string, params: { alias: string }) {
+    return super.delete({
+      path: `/collections/${collectionName}/alias/${params.alias}`,
+    });
+  }
+
+  static queryData(collectionName: string, params: QueryParam) {
+    return super.query({
+      path: `/collections/${collectionName}/query`,
+      data: params,
+    });
+  }
+}

+ 0 - 192
client/src/http/Collection.ts

@@ -1,192 +0,0 @@
-import dayjs from 'dayjs';
-import { LoadReplicaReq } from '@/pages/collections/Types';
-import { QueryParam } from '@/pages/query/Types';
-import { formatNumber } from '@/utils/Common';
-import BaseModel from './BaseModel';
-import { LOADING_STATE, LOAD_STATE } from '@/consts';
-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[];
-  public state!: LOAD_STATE;
-
-  static COLLECTIONS_URL = '/collections';
-  static COLLECTIONS_STATISTICS_URL = '/collections/statistics';
-
-  constructor(props: Collection) {
-    super(props);
-    Object.assign(this, props);
-  }
-
-  static getCollections(data?: {
-    type: ShowCollectionsType;
-  }): Promise<Collection[]> {
-    return super.findAll({ path: this.COLLECTIONS_URL, params: data || {} });
-  }
-
-  static getCollectionWithIndexInfo(name: string) {
-    return super.search<Collection>({
-      path: `${this.COLLECTIONS_URL}/${name}`,
-      params: {},
-    });
-  }
-
-  static getCollectionInfo(collectionName: string) {
-    return super.search<Collection>({
-      path: `${this.COLLECTIONS_URL}/${collectionName}/info`,
-      params: {},
-    });
-  }
-
-  static createCollection(data: any) {
-    return super.create({ path: this.COLLECTIONS_URL, data });
-  }
-
-  static deleteCollection(collectionName: string) {
-    return super.delete({ path: `${this.COLLECTIONS_URL}/${collectionName}` });
-  }
-
-  static loadCollection(collectionName: string, param?: LoadReplicaReq) {
-    return super.update({
-      path: `${this.COLLECTIONS_URL}/${collectionName}/load`,
-      data: param,
-    });
-  }
-
-  static releaseCollection(collectionName: string) {
-    return super.update({
-      path: `${this.COLLECTIONS_URL}/${collectionName}/release`,
-    });
-  }
-
-  static renameCollection(
-    collectionName: string,
-    params: { new_collection_name: string }
-  ) {
-    return super.create({
-      path: `${this.COLLECTIONS_URL}/${collectionName}`,
-      data: params,
-    });
-  }
-
-  static duplicate(
-    collectionName: string,
-    params: { new_collection_name: string }
-  ) {
-    return super.create({
-      path: `${this.COLLECTIONS_URL}/${collectionName}/duplicate`,
-      data: params,
-    });
-  }
-
-  static getStatistics() {
-    return super.search({ path: this.COLLECTIONS_STATISTICS_URL, params: {} });
-  }
-
-  static count(collectionName: string) {
-    return super.search<Collection>({
-      path: `${this.COLLECTIONS_URL}/${collectionName}/count`,
-      params: {},
-    });
-  }
-
-
-  static createAlias(collectionName: string, params: { alias: string }) {
-    return super.create({
-      path: `${this.COLLECTIONS_URL}/${collectionName}/alias`,
-      data: params,
-    });
-  }
-
-  static dropAlias(collectionName: string, params: { alias: string }) {
-    return super.delete({
-      path: `${this.COLLECTIONS_URL}/${collectionName}/alias/${params.alias}`,
-    });
-  }
-
-  static queryData(collectionName: string, params: QueryParam) {
-    return super.query({
-      path: `${this.COLLECTIONS_URL}/${collectionName}/query`,
-      data: params,
-    });
-  }
-
-  get desc() {
-    return this.description || '--';
-  }
-
-  get collectionName() {
-    return this.collection_name;
-  }
-
-  get entityCount() {
-    return formatNumber(Number(this.rowCount));
-  }
-
-  // TODO: deprecated
-  get status() {
-    // If not load, insight server will return '-1'. Otherwise milvus will return percentage
-    return this.loadedPercentage === '-1'
-      ? LOADING_STATE.UNLOADED
-      : this.loadedPercentage === '100'
-      ? LOADING_STATE.LOADED
-      : LOADING_STATE.LOADING;
-    // return LOADING_STATE.LOADING
-  }
-
-  get fields() {
-    return this.schema.fields.map(f => new FieldHttp(f));
-  }
-
-  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 createdAt(): string {
-    return this.createdTime && this.createdTime !== 0
-      ? dayjs(Number(this.createdTime)).format('YYYY-MM-DD HH:mm:ss')
-      : '';
-  }
-
-  get replicasInfo(): ReplicaInfo[] {
-    return this.replicas || [];
-  }
-
-  get enableDynamicField(): boolean {
-    return this.schema && this.schema.enable_dynamic_field;
-  }
-
-  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;
-  }
-}

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

@@ -4,11 +4,6 @@ import { VectorSearchParam } from '@/types/SearchTypes';
 import BaseModel from './BaseModel';
 
 export class DataService extends BaseModel {
-  constructor(props: DataService) {
-    super(props);
-    Object.assign(this, props);
-  }
-
   static importSample(collectionName: string, param: LoadSampleParam) {
     return super.create<{ sampleFile: string }>({
       path: `/collections/${collectionName}/importSample`,

+ 0 - 5
client/src/http/Database.service.ts

@@ -5,11 +5,6 @@ import {
 import BaseModel from './BaseModel';
 
 export class DatabaseService extends BaseModel {
-  constructor(props: DatabaseService) {
-    super(props);
-    Object.assign(this, props);
-  }
-
   static getDatabases() {
     return super.search<{ db_names: [] }>({
       path: `/databases`,

+ 0 - 58
client/src/http/Field.ts

@@ -1,58 +0,0 @@
-import { KeyValuePair } from '@server/types';
-import { DataTypeStringEnum } from '@/consts';
-
-export class FieldHttp {
-  fieldID!: string;
-  type_params!: { key: string; value: string }[];
-  is_primary_key!: true;
-  is_partition_key!: false;
-  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: {}) {
-    Object.assign(this, props);
-  }
-
-  get isPrimaryKey() {
-    return this.is_primary_key;
-  }
-
-  get isPartitionKey() {
-    return this.is_partition_key;
-  }
-
-  get isAutoId() {
-    return this.autoID;
-  }
-
-  get fieldType() {
-    return this.data_type;
-  }
-
-  get desc() {
-    return this.description || '--';
-  }
-
-  get dimension() {
-    return this.type_params.find(item => item.key === 'dim')?.value || '';
-  }
-
-  get maxLength() {
-    return (
-      this.type_params.find(item => item.key === 'max_length')?.value || ''
-    );
-  }
-  get maxCapacity() {
-    return (
-      this.type_params.find(item => item.key === 'max_capacity')?.value || ''
-    );
-  }
-}

+ 37 - 0
client/src/http/Index.service.ts

@@ -0,0 +1,37 @@
+import { IndexCreateParam, IndexManageParam } from '../pages/schema/Types';
+import { ManageRequestMethods } from '../types/Common';
+import BaseModel from './BaseModel';
+import { DescribeIndexRes, IndexObject } from '@server/types';
+
+export class IndexService extends BaseModel {
+  static async describeIndex(collectionName: string): Promise<IndexObject[]> {
+    const res = await super.findAll<DescribeIndexRes>({
+      path: `/schema/index`,
+      params: { collection_name: collectionName },
+    });
+    return res.index_descriptions;
+  }
+
+  static async createIndex(param: IndexCreateParam) {
+    const path = `/schema/index`;
+    const type: ManageRequestMethods = ManageRequestMethods.CREATE;
+
+    return super.create({
+      path,
+      data: { ...param, type },
+    });
+  }
+
+  static async deleteIndex(param: IndexManageParam) {
+    const path = `/schema/index`;
+    const type: ManageRequestMethods = ManageRequestMethods.DELETE;
+
+    return super.batchDelete({ path, data: { ...param, type } });
+  }
+
+  static async flush() {
+    const path = `/schema/index/flush`;
+
+    return super.query({ path, data: {} });
+  }
+}

+ 0 - 75
client/src/http/MilvusIndex.ts

@@ -1,75 +0,0 @@
-import { IndexCreateParam, IndexManageParam } from '../pages/schema/Types';
-import { ManageRequestMethods } from '../types/Common';
-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 MilvusIndex extends BaseModel {
-  params!: { key: string; value: string }[];
-  field_name!: string;
-  index_name!: string;
-  indexed_rows!: string | number;
-  state: IndexState = IndexState.Default;
-
-  constructor(props: {}) {
-    super(props);
-    Object.assign(this, props);
-  }
-
-  static BASE_URL = `/schema/index`;
-
-  static async getIndexInfo(collectionName: string): Promise<MilvusIndex[]> {
-    const path = this.BASE_URL;
-
-    const res = await super.findAll({
-      path,
-      params: { collection_name: collectionName },
-    });
-    return res.index_descriptions.map(
-      (index: IndexDescription) => new this(index)
-    );
-  }
-
-  static async createIndex(param: IndexCreateParam) {
-    const path = this.BASE_URL;
-    const type: ManageRequestMethods = ManageRequestMethods.CREATE;
-
-    return super.create({
-      path,
-      data: { ...param, type },
-    });
-  }
-
-  static async deleteIndex(param: IndexManageParam) {
-    const path = this.BASE_URL;
-    const type: ManageRequestMethods = ManageRequestMethods.DELETE;
-
-    return super.batchDelete({ path, data: { ...param, type } });
-  }
-
-  static async flush() {
-    const path = `${this.BASE_URL}/flush`;
-
-    return super.query({ path, data: {} });
-  }
-
-  get indexType() {
-    return this.params.find(p => p.key === 'index_type')?.value || '';
-  }
-
-  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');
-    if (params) {
-      return [...metricType, ...getKeyValueListFromJsonString(params)];
-    }
-    return metricType;
-  }
-
-  get metricType() {
-    return this.params.find(p => p.key === 'metric_type')?.value || '';
-  }
-}

+ 0 - 5
client/src/http/Partition.service.ts

@@ -3,11 +3,6 @@ import BaseModel from './BaseModel';
 import { PartitionData } from '@server/types';
 
 export class PartitionService extends BaseModel {
-  constructor(props: {}) {
-    super(props);
-    Object.assign(this, props);
-  }
-
   static getPartitions(collectionName: string) {
     const path = `/partitions`;
 

+ 0 - 5
client/src/http/Prometheus.service.ts

@@ -4,11 +4,6 @@ export class PrometheusService extends BaseModel {
   static SET_PROMETHEUS_URL = '/prometheus/setPrometheus';
   static GET_MILVUS_HEALTHY_DATA_URL = '/prometheus/getMilvusHealthyData';
 
-  constructor(props: {}) {
-    super(props);
-    Object.assign(this, props);
-  }
-
   static setPrometheus({
     prometheusAddress,
     prometheusInstance,

+ 4 - 12
client/src/http/Segment.service.ts

@@ -1,24 +1,16 @@
 import BaseModel from './BaseModel';
-import {
-  GetQuerySegmentInfoResponse,
-  GePersistentSegmentInfoResponse,
-} from '@server/types';
-
-export class SegementService extends BaseModel {
-  constructor(props: {}) {
-    super(props);
-    Object.assign(this, props);
-  }
+import { QuerySegmentObjects, PersistentSegmentObjects } from '@server/types';
 
+export class SegmentService extends BaseModel {
   static getQSegments(collectionName: string) {
-    return super.search<GetQuerySegmentInfoResponse>({
+    return super.search<QuerySegmentObjects>({
       path: `/collections/${collectionName}/qsegments`,
       params: {},
     });
   }
 
   static getPSegments(collectionName: string) {
-    return super.search<GePersistentSegmentInfoResponse>({
+    return super.search<PersistentSegmentObjects>({
       path: `/collections/${collectionName}/psegments`,
       params: {},
     });

+ 0 - 5
client/src/http/User.service.ts

@@ -11,11 +11,6 @@ import BaseModel from './BaseModel';
 import { Users, UsersWithRoles } from '@server/types';
 
 export class UserService extends BaseModel {
-  constructor(props: {}) {
-    super(props);
-    Object.assign(this, props);
-  }
-
   // get user data
   static getUsers() {
     return super.search<Users>({ path: '/users', params: {} });

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

@@ -1,11 +1,10 @@
 // objects map(deprecating)
 export * from './Axios';
 export * from './BaseModel';
-export * from './Collection';
-export * from './MilvusIndex';
-export * from './Field';
 
 // service
+export * from './Collection.service';
+export * from './Index.service';
 export * from './Partition.service';
 export * from './Data.service';
 export * from './Milvus.service';

+ 4 - 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 { Collection } from '@/http';
+import { CollectionService } from '@/http';
 
 const useStyles = makeStyles((theme: Theme) => ({
   wrapper: {
@@ -77,7 +77,9 @@ export default function Aliases(props: AliasesProps) {
     collection: string;
     alias: string;
   }) => {
-    await Collection.dropAlias(params.collection, { alias: params.alias });
+    await CollectionService.dropAlias(params.collection, {
+      alias: params.alias,
+    });
     openSnackBar(successTrans('delete', { name: collectionTrans('alias') }));
     handleCloseDialog();
     onDelete();

+ 62 - 61
client/src/pages/collections/Collections.tsx

@@ -3,13 +3,8 @@ import { Link, useSearchParams } from 'react-router-dom';
 import { makeStyles, Theme, Chip, Tooltip } from '@material-ui/core';
 import { useTranslation } from 'react-i18next';
 import Highlighter from 'react-highlight-words';
-import {
-  rootContext,
-  authContext,
-  systemContext,
-  dataContext,
-} from '@/context';
-import { Collection, MilvusService, MilvusIndex } from '@/http';
+import { rootContext, authContext, dataContext } from '@/context';
+import { CollectionService, MilvusService, IndexService } from '@/http';
 import { useNavigationHook, usePaginationHook } from '@/hooks';
 import { ALL_ROUTER_TYPES } from '@/router/Types';
 import AttuGrid from '@/components/grid/Grid';
@@ -29,8 +24,9 @@ import InsertDialog from '../dialogs/insert/Dialog';
 import ImportSampleDialog from '../dialogs/ImportSampleDialog';
 import { LOADING_STATE } from '@/consts';
 import { WS_EVENTS, WS_EVENTS_TYPE } from '@server/utils/Const';
-import { checkIndexBuilding, checkLoading } from '@/utils';
+import { checkIndexBuilding, checkLoading, formatNumber } from '@/utils';
 import Aliases from './Aliases';
+import { CollectionObject, CollectionFullObject } from '@server/types';
 
 const useStyles = makeStyles((theme: Theme) => ({
   emptyWrapper: {
@@ -77,9 +73,9 @@ const Collections = () => {
     (searchParams.get('search') as string) || ''
   );
   const [loading, setLoading] = useState<boolean>(false);
-  const [selectedCollections, setSelectedCollections] = useState<Collection[]>(
-    []
-  );
+  const [selectedCollections, setSelectedCollections] = useState<
+    CollectionObject[]
+  >([]);
 
   const { setDialog, openSnackBar } = useContext(rootContext);
   const { collections, setCollections } = useContext(dataContext);
@@ -98,24 +94,27 @@ const Collections = () => {
     Eventually: collectionTrans('consistencyEventuallyTooltip'),
   };
 
-  const checkCollectionStatus = useCallback((collections: Collection[]) => {
-    const hasLoadingOrBuildingCollection = collections.some(
-      v => checkLoading(v) || checkIndexBuilding(v)
-    );
+  const checkCollectionStatus = useCallback(
+    (collections: CollectionObject[]) => {
+      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,
-      });
-    }
-  }, []);
+      // 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 collections = await Collection.getCollections();
+      const collections = await CollectionService.getCollections();
       setCollections(collections);
       checkCollectionStatus(collections);
     } finally {
@@ -124,28 +123,22 @@ const Collections = () => {
   }, [setCollections, checkCollectionStatus]);
 
   const clearIndexCache = useCallback(async () => {
-    await MilvusIndex.flush();
+    await IndexService.flush();
   }, []);
 
   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.collectionName.includes(search)
+          collection.collection_name.includes(search)
         )
       : collections;
 
     const data = filteredCollections.map(v => {
-      // const indexStatus = statusRes.find(item => item.collectionName === v.collectionName);
+      // const indexStatus = statusRes.find(item => item.collectionName === v.collection_name);
       Object.assign(v, {
         features: v, // add `feature` as id to render
       });
@@ -204,7 +197,7 @@ const Collections = () => {
           params: {
             component: (
               <LoadCollectionDialog
-                collection={selectedCollections[0].collectionName}
+                collection={selectedCollections[0].collection_name}
                 onLoad={async () => {
                   openSnackBar(
                     successTrans('load', {
@@ -224,7 +217,7 @@ const Collections = () => {
         return (
           data.length !== 1 ||
           data[0].status !== LOADING_STATE.UNLOADED ||
-          data[0].indexes.length === 0
+          !data[0].schema.hasVectorIndex
         );
       },
       tooltip: btnTrans('loadColTooltip'),
@@ -241,7 +234,7 @@ const Collections = () => {
           params: {
             component: (
               <ReleaseCollectionDialog
-                collection={selectedCollections[0].collectionName}
+                collection={selectedCollections[0].collection_name}
                 onRelease={async () => {
                   openSnackBar(
                     successTrans('release', {
@@ -279,7 +272,7 @@ const Collections = () => {
                 collections={formatCollections}
                 defaultSelectedCollection={
                   selectedCollections.length === 1
-                    ? selectedCollections[0].collectionName
+                    ? selectedCollections[0].collection_name
                     : ''
                 }
                 // user can't select partition on collection page, so default value is ''
@@ -319,7 +312,7 @@ const Collections = () => {
                   await fetchData();
                   setSelectedCollections([]);
                 }}
-                collectionName={selectedCollections[0].collectionName}
+                collectionName={selectedCollections[0].collection_name}
               />
             ),
           },
@@ -349,7 +342,7 @@ const Collections = () => {
                   setSelectedCollections([]);
                   await fetchData();
                 }}
-                collectionName={selectedCollections[0].collectionName}
+                collectionName={selectedCollections[0].collection_name}
                 collections={collections}
               />
             ),
@@ -360,7 +353,6 @@ const Collections = () => {
       tooltip: btnTrans('duplicateTooltip'),
       disabled: data => data.length !== 1,
     },
-
     {
       icon: 'delete',
       type: 'button',
@@ -416,19 +408,19 @@ const Collections = () => {
 
   const colDefinitions: ColDefinitionsType[] = [
     {
-      id: 'collectionName',
+      id: 'collection_name',
       align: 'left',
       disablePadding: true,
-      sortBy: 'collectionName',
-      formatter({ collectionName }) {
+      sortBy: 'collection_name',
+      formatter({ collection_name }) {
         return (
           <Link
-            to={`/collections/${collectionName}/data`}
+            to={`/collections/${collection_name}/data`}
             className={classes.link}
-            title={collectionName}
+            title={collection_name}
           >
             <Highlighter
-              textToHighlight={collectionName}
+              textToHighlight={collection_name}
               searchWords={[search]}
               highlightClassName={classes.highlight}
             />
@@ -449,8 +441,8 @@ const Collections = () => {
             status={v.status}
             onIndexCreate={fetchData}
             percentage={v.loadedPercentage}
-            field={getVectorField(v)!}
-            collectionName={v.collectionName}
+            field={v.schema}
+            collectionName={v.collection_name}
             action={() => {
               setDialog({
                 open: true,
@@ -459,7 +451,7 @@ const Collections = () => {
                   component:
                     v.status === LOADING_STATE.UNLOADED ? (
                       <LoadCollectionDialog
-                        collection={v.collectionName}
+                        collection={v.collection_name}
                         onLoad={async () => {
                           openSnackBar(
                             successTrans('load', {
@@ -471,7 +463,7 @@ const Collections = () => {
                       />
                     ) : (
                       <ReleaseCollectionDialog
-                        collection={v.collectionName}
+                        collection={v.collection_name}
                         onRelease={async () => {
                           openSnackBar(
                             successTrans('release', {
@@ -493,7 +485,7 @@ const Collections = () => {
       id: 'features',
       align: 'left',
       disablePadding: true,
-      sortBy: 'enableDynamicField',
+      notSort: true,
       label: collectionTrans('features'),
       formatter(v) {
         return (
@@ -511,7 +503,7 @@ const Collections = () => {
                 />
               </Tooltip>
             ) : null}
-            {v.enableDynamicField ? (
+            {v.schema.enable_dynamic_field ? (
               <Tooltip
                 title={collectionTrans('dynamicSchemaTooltip')}
                 placement="top"
@@ -540,7 +532,7 @@ const Collections = () => {
       },
     },
     {
-      id: 'entityCount',
+      id: 'rowCount',
       align: 'left',
       disablePadding: false,
       sortBy: 'rowCount',
@@ -552,18 +544,27 @@ const Collections = () => {
           </CustomToolTip>
         </span>
       ),
+      formatter(v) {
+        return formatNumber(Number(v.rowCount));
+      },
     },
     {
-      id: 'desc',
+      id: 'description',
       align: 'left',
       disablePadding: false,
-      label: collectionTrans('desc'),
+      label: collectionTrans('description'),
+      formatter(v) {
+        return v.description || '--';
+      },
     },
     {
-      id: 'createdAt',
+      id: 'createdTime',
       align: 'left',
       disablePadding: false,
       label: collectionTrans('createdTime'),
+      formatter(data) {
+        return new Date(data.createdTime).toLocaleString();
+      },
     },
     {
       id: 'import',
@@ -574,13 +575,13 @@ const Collections = () => {
       isHoverAction: true,
       actionBarConfigs: [
         {
-          onClick: (e: React.MouseEvent, row: Collection) => {
+          onClick: (e: React.MouseEvent, row: CollectionObject) => {
             setDialog({
               open: true,
               type: 'custom',
               params: {
                 component: (
-                  <ImportSampleDialog collection={row.collectionName} />
+                  <ImportSampleDialog collection={row.collection_name} />
                 ),
               },
             });
@@ -589,7 +590,7 @@ const Collections = () => {
           label: 'Import',
           showIconMethod: 'renderFn',
           getLabel: () => 'Import sample data',
-          renderIconFn: (row: Collection) => <SourceIcon />,
+          renderIconFn: () => <SourceIcon />,
         },
       ],
     },
@@ -612,7 +613,7 @@ const Collections = () => {
         return (
           <Aliases
             aliases={v.aliases}
-            collectionName={v.collectionName}
+            collectionName={v.collection_name}
             onCreate={fetchData}
             onDelete={fetchData}
           />
@@ -640,7 +641,7 @@ const Collections = () => {
           colDefinitions={colDefinitions}
           rows={collectionList}
           rowCount={total}
-          primaryKey="collectionName"
+          primaryKey="collection_name"
           selected={selectedCollections}
           setSelected={handleSelectChange}
           page={currentPage}

+ 6 - 6
client/src/pages/collections/Constants.ts

@@ -1,7 +1,7 @@
-import { KeyValuePair } from '../../types/Common';
+import { LabelValuePair } from '../../types/Common';
 import { DataTypeEnum, ConsistencyLevelEnum } from '@/consts';
 
-export const CONSISTENCY_LEVEL_OPTIONS: KeyValuePair[] = [
+export const CONSISTENCY_LEVEL_OPTIONS: LabelValuePair[] = [
   {
     label: 'Strong',
     value: ConsistencyLevelEnum.Strong,
@@ -20,7 +20,7 @@ export const CONSISTENCY_LEVEL_OPTIONS: KeyValuePair[] = [
   },
 ];
 
-export const VECTOR_FIELDS_OPTIONS: KeyValuePair[] = [
+export const VECTOR_FIELDS_OPTIONS: LabelValuePair[] = [
   {
     label: 'Binary Vector',
     value: DataTypeEnum.BinaryVector,
@@ -31,7 +31,7 @@ export const VECTOR_FIELDS_OPTIONS: KeyValuePair[] = [
   },
 ];
 
-export const ALL_OPTIONS: KeyValuePair[] = [
+export const ALL_OPTIONS: LabelValuePair[] = [
   // ...VECTOR_FIELDS_OPTIONS,
   {
     label: 'Int8',
@@ -75,7 +75,7 @@ export const ALL_OPTIONS: KeyValuePair[] = [
   },
 ];
 
-export const AUTO_ID_OPTIONS: KeyValuePair[] = [
+export const AUTO_ID_OPTIONS: LabelValuePair[] = [
   {
     label: 'On',
     value: 'true',
@@ -86,7 +86,7 @@ export const AUTO_ID_OPTIONS: KeyValuePair[] = [
   },
 ];
 
-export const PRIMARY_FIELDS_OPTIONS: KeyValuePair[] = [
+export const PRIMARY_FIELDS_OPTIONS: LabelValuePair[] = [
   {
     label: 'INT64',
     value: DataTypeEnum.Int64,

+ 25 - 22
client/src/pages/collections/CreateFields.tsx

@@ -23,7 +23,7 @@ import {
   PRIMARY_FIELDS_OPTIONS,
   VECTOR_FIELDS_OPTIONS,
 } from './Constants';
-import { CreateFieldsProps, CreateFieldType, Field } from './Types';
+import { CreateFieldsProps, CreateFieldType, FieldType } from './Types';
 import { DataTypeEnum } from '@/consts';
 import {
   DEFAULT_ATTU_DIM,
@@ -102,12 +102,12 @@ const useStyles = makeStyles((theme: Theme) => ({
 
 type inputType = {
   label: string;
-  value: string | number | null;
+  value: string | number;
   handleChange?: (value: string) => void;
   className?: string;
   inputClassName?: string;
   isReadOnly?: boolean;
-  validate?: (value: string | number | null) => string;
+  validate?: (value: string | number) => string;
   type?: 'number' | 'text';
 };
 
@@ -148,8 +148,8 @@ const CreateFields: FC<CreateFieldsProps> = ({
           return acc;
         },
         {
-          requiredFields: [] as Field[],
-          optionalFields: [] as Field[],
+          requiredFields: [] as FieldType[],
+          optionalFields: [] as FieldType[],
         }
       ),
 
@@ -239,7 +239,7 @@ const CreateFields: FC<CreateFieldsProps> = ({
   };
 
   const generateFieldName = (
-    field: Field,
+    field: FieldType,
     label?: string,
     className?: string
   ) => {
@@ -266,7 +266,7 @@ const CreateFields: FC<CreateFieldsProps> = ({
     });
   };
 
-  const generateDesc = (field: Field) => {
+  const generateDesc = (field: FieldType) => {
     return getInput({
       label: collectionTrans('description'),
       value: field.description,
@@ -276,7 +276,7 @@ const CreateFields: FC<CreateFieldsProps> = ({
     });
   };
 
-  const generateDimension = (field: Field) => {
+  const generateDimension = (field: FieldType) => {
     const validateDimension = (value: string) => {
       const isPositive = getCheckResult({
         value,
@@ -330,7 +330,7 @@ const CreateFields: FC<CreateFieldsProps> = ({
     });
   };
 
-  const generateMaxLength = (field: Field) => {
+  const generateMaxLength = (field: FieldType) => {
     // update data if needed
     if (typeof field.max_length === 'undefined') {
       changeFields(field.id!, 'max_length', DEFAULT_ATTU_VARCHAR_MAX_LENGTH);
@@ -363,7 +363,7 @@ const CreateFields: FC<CreateFieldsProps> = ({
     });
   };
 
-  const generateMaxCapacity = (field: Field) => {
+  const generateMaxCapacity = (field: FieldType) => {
     return getInput({
       label: 'Max Capacity',
       value: field.max_capacity || DEFAULT_ATTU_MAX_CAPACITY,
@@ -392,7 +392,10 @@ const CreateFields: FC<CreateFieldsProps> = ({
     });
   };
 
-  const generatePartitionKeyToggle = (field: Field, fields: Field[]) => {
+  const generatePartitionKeyToggle = (
+    field: FieldType,
+    fields: FieldType[]
+  ) => {
     return (
       <FormControlLabel
         control={
@@ -427,7 +430,7 @@ const CreateFields: FC<CreateFieldsProps> = ({
     );
   };
 
-  const changeFields = (id: string, key: keyof Field, value: any) => {
+  const changeFields = (id: string, key: keyof FieldType, value: any) => {
     const newFields = fields.map(f => {
       if (f.id !== id) {
         return f;
@@ -468,8 +471,8 @@ const CreateFields: FC<CreateFieldsProps> = ({
 
   const handleAddNewField = (index: number) => {
     const id = generateId();
-    const newDefaultItem: Field = {
-      name: null,
+    const newDefaultItem: FieldType = {
+      name: '',
       data_type: DataTypeEnum.Int16,
       is_primary_key: false,
       description: '',
@@ -495,7 +498,7 @@ const CreateFields: FC<CreateFieldsProps> = ({
   };
 
   const generatePrimaryKeyRow = (
-    field: Field,
+    field: FieldType,
     autoID: boolean
   ): ReactElement => {
     const isVarChar = field.data_type === DataTypeEnum.VarChar;
@@ -544,7 +547,7 @@ const CreateFields: FC<CreateFieldsProps> = ({
   };
 
   const generateDefaultVectorRow = (
-    field: Field,
+    field: FieldType,
     index: number
   ): ReactElement => {
     return (
@@ -573,9 +576,9 @@ const CreateFields: FC<CreateFieldsProps> = ({
   };
 
   const generateNonRequiredRow = (
-    field: Field,
+    field: FieldType,
     index: number,
-    fields: Field[]
+    fields: FieldType[]
   ): ReactElement => {
     const isVarChar = field.data_type === DataTypeEnum.VarChar;
     const isInt64 = field.data_type === DataTypeEnum.Int64;
@@ -641,7 +644,7 @@ const CreateFields: FC<CreateFieldsProps> = ({
     );
   };
 
-  const generateVectorRow = (field: Field) => {
+  const generateVectorRow = (field: FieldType) => {
     return (
       <div className={`${classes.rowWrapper}`}>
         <IconButton classes={{ root: classes.iconBtn }} aria-label="delete">
@@ -662,7 +665,7 @@ const CreateFields: FC<CreateFieldsProps> = ({
   };
 
   const generateRequiredFieldRow = (
-    field: Field,
+    field: FieldType,
     autoID: boolean,
     index: number
   ) => {
@@ -675,9 +678,9 @@ const CreateFields: FC<CreateFieldsProps> = ({
   };
 
   const generateOptionalFieldRow = (
-    field: Field,
+    field: FieldType,
     index: number,
-    fields: Field[]
+    fields: FieldType[]
   ) => {
     // optional type is vector or number
     if (field.createType === 'vector') {

+ 3 - 4
client/src/pages/collections/StatusAction.tsx

@@ -1,4 +1,4 @@
-import { FC, useMemo, MouseEvent, forwardRef } from 'react';
+import { FC, useMemo, MouseEvent } from 'react';
 import {
   ChildrenStatusType,
   StatusActionType,
@@ -131,12 +131,11 @@ const StatusAction: FC<StatusActionType> = props => {
   }, [status, statusTrans, percentage]);
 
   // UI state
-  const hasVectorIndex = field?.indexType !== '';
   const collectionLoaded = status === LOADING_STATE.LOADED;
 
   return (
     <div className={classes.root}>
-      {hasVectorIndex && (
+      {field.hasVectorIndex && (
         <Tooltip arrow interactive title={tooltip} placement={'top'}>
           <Chip
             className={classes.chip}
@@ -154,7 +153,7 @@ const StatusAction: FC<StatusActionType> = props => {
         </Tooltip>
       )}
       <IndexTypeElement
-        data={field!}
+        data={field.vectorFields[0]!}
         collectionName={collectionName}
         cb={() => onIndexCreate()}
         disabled={collectionLoaded}

+ 21 - 23
client/src/pages/collections/Types.ts

@@ -1,21 +1,6 @@
 import { Dispatch, SetStateAction } from 'react';
 import { DataTypeEnum } from '@/consts';
 
-export interface Replica {
-  collectionID: string;
-  node_ids: string[];
-  partition_ids: string[];
-  replicaID: string;
-  shard_replicas: ShardReplica[];
-}
-
-export interface ShardReplica {
-  dm_channel_name: string;
-  leaderID: string;
-  leader_addr: string;
-  node_id: string[];
-}
-
 export interface CollectionCreateProps {
   onCreate?: () => void;
 }
@@ -29,7 +14,7 @@ export interface CollectionCreateParam {
 }
 
 export interface CreateField {
-  name: string | null;
+  name: string;
   data_type: DataTypeEnum;
   is_primary_key: boolean;
   is_partition_key?: boolean;
@@ -54,6 +39,26 @@ export type CreateFieldType =
   | 'vector'
   | 'number';
 
+export type FieldType = {
+  name: string;
+  data_type: DataTypeEnum;
+  element_type?: DataTypeEnum;
+  is_primary_key: boolean;
+  is_partition_key?: boolean;
+  description: string;
+  dimension?: number | string;
+  isDefault?: boolean;
+  id?: string;
+  type_params?: {
+    dim?: string | number;
+    max_length?: string | number;
+  };
+  createType?: CreateFieldType;
+  max_length?: string | number;
+  max_capacity?: string | number;
+  autoID?: boolean;
+};
+
 export interface CreateFieldsProps {
   fields: CreateField[];
   setFields: Dispatch<SetStateAction<CreateField[]>>;
@@ -85,10 +90,3 @@ export interface AliasesProps {
 export interface LoadReplicaReq {
   replica_number: number;
 }
-
-export enum TAB_ENUM {
-  'schema',
-  'partition',
-  'data-preview',
-  'data-query',
-}

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

@@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next';
 import { rootContext } from '@/context';
 import DialogTemplate from '@/components/customDialog/DialogTemplate';
 import { CompactDialogProps } from './Types';
-import { SegementService } from '@/http';
+import { SegmentService } from '@/http';
 
 const useStyles = makeStyles((theme: Theme) => ({
   desc: {
@@ -25,7 +25,7 @@ const CompactDialog: FC<CompactDialogProps> = props => {
   const { t: btnTrans } = useTranslation('btn');
 
   const handleConfirm = async () => {
-    await SegementService.compact(collectionName);
+    await SegmentService.compact(collectionName);
 
     handleCloseDialog();
     cb && cb();

+ 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 { Collection } from '@/http';
+import { CollectionService } from '@/http';
 import { CreateAliasProps } from './Types';
 
 const useStyles = makeStyles((theme: Theme) => ({
@@ -42,7 +42,7 @@ const CreateAliasDialog: FC<CreateAliasProps> = props => {
   };
 
   const handleConfirm = async () => {
-    await Collection.createAlias(collectionName, form);
+    await CollectionService.createAlias(collectionName, form);
     handleCloseDialog();
     cb && cb();
   };

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

@@ -15,7 +15,7 @@ import { useFormValidation } from '@/hooks';
 import { formatForm, TypeEnum } from '@/utils';
 import { DataTypeEnum, ConsistencyLevelEnum, DEFAULT_ATTU_DIM } from '@/consts';
 import CreateFields from '../collections/CreateFields';
-import { Collection } from '@/http';
+import { CollectionService } from '@/http';
 import {
   CollectionCreateParam,
   CollectionCreateProps,
@@ -242,7 +242,7 @@ const CreateCollectionDialog: FC<CollectionCreateProps> = ({ onCreate }) => {
       consistency_level: consistencyLevel,
     };
 
-    await Collection.createCollection({
+    await CollectionService.createCollection({
       ...param,
     });
 

+ 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 { Collection } from '@/http';
+import { CollectionService } 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 Collection.deleteCollection(item.collectionName);
+      await CollectionService.deleteCollection(item.collection_name);
     }
 
     handleCloseDialog();

+ 3 - 3
client/src/pages/dialogs/DuplicateCollectionDailog.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 { Collection } from '@/http';
+import { CollectionService } from '@/http';
 import { DuplicateCollectionDialogProps } from './Types';
 
 const useStyles = makeStyles((theme: Theme) => ({
@@ -46,7 +46,7 @@ const DuplicateCollectionDialog: FC<DuplicateCollectionDialogProps> = props => {
 
   const handleConfirm = async () => {
     // duplicate
-    await Collection.duplicate(collectionName, {
+    await CollectionService.duplicate(collectionName, {
       new_collection_name: form.duplicate,
     });
     // close dialog
@@ -76,7 +76,7 @@ const DuplicateCollectionDialog: FC<DuplicateCollectionDialogProps> = props => {
         rule: 'custom',
         extraParam: {
           compare: (value) => {
-            return !collections.some(collection => collection.collectionName === value);
+            return !collections.some(collection => collection.collection_name === value);
           },
         },
         errorText: collectionTrans('duplicateNameExist'),

+ 0 - 1
client/src/pages/dialogs/FlushDialog.tsx

@@ -21,7 +21,6 @@ const FlushDialog: FC<FlushDialogProps> = props => {
 
   const { handleCloseDialog } = useContext(rootContext);
   const { t: dialogTrans } = useTranslation('dialog');
-  const { t: collectionTrans } = useTranslation('collection');
   const { t: btnTrans } = useTranslation('btn');
 
   const handleConfirm = async () => {

+ 2 - 2
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 { Collection, MilvusService } from '@/http';
+import { CollectionService, MilvusService } from '@/http';
 import { useFormValidation } from '@/hooks';
 import { formatForm, parseJson, getNode } from '@/utils';
 import { MILVUS_NODE_TYPE, MILVUS_DEPLOY_MODE } from '@/consts';
@@ -98,7 +98,7 @@ const LoadCollectionDialog = (props: any) => {
     }
 
     // load collection request
-    await Collection.loadCollection(collection, params);
+    await CollectionService.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 { Collection } from '@/http';
+import { CollectionService } 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 Collection.releaseCollection(collection);
+      await CollectionService.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 { Collection } from '@/http';
+import { CollectionService } 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 Collection.renameCollection(collectionName, form);
+    await CollectionService.renameCollection(collectionName, form);
     handleCloseDialog();
     cb && cb();
   };

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

@@ -1,8 +1,7 @@
-import { Collection } from '@/http';
-import { PartitionData } from '@server/types';
+import { PartitionData, CollectionObject } from '@server/types';
 
 export interface DropCollectionProps {
-  collections: Collection[];
+  collections: CollectionObject[];
   onDelete: () => void;
 }
 
@@ -36,7 +35,7 @@ export interface EmptyDataProps {
 }
 
 export interface DuplicateCollectionDialogProps extends CreateAliasProps {
-  collections: Collection[];
+  collections: CollectionObject[];
 }
 
 export interface RenameCollectionProps {

+ 9 - 9
client/src/pages/dialogs/insert/Dialog.tsx

@@ -211,8 +211,8 @@ const InsertContainer: FC<InsertContentProps> = ({
     () =>
       defaultSelectedCollection === ''
         ? collections.map(c => ({
-            label: c.collectionName,
-            value: c.collectionName,
+            label: c.collection_name,
+            value: c.collection_name,
           }))
         : [
             {
@@ -231,23 +231,23 @@ const InsertContainer: FC<InsertContentProps> = ({
      * on collection page, we get schema data from collection
      * on partition page, we pass schema as props
      */
-    const list =
-      schema && schema.length > 0
-        ? schema
-        : collections.find(c => c.collectionName === collectionValue)?.fields;
+    const list = schema
+      ? schema.fields
+      : collections.find(c => c.collection_name === collectionValue)?.schema
+          ?.fields;
 
     const autoIdFieldName =
-      list?.find(item => item.isPrimaryKey && item.isAutoId)?.name || '';
+      list?.find(item => item.is_primary_key && item.autoID)?.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.autoID || !s.is_primary_key)
       .map(s => ({
         label: s.name,
-        value: s.fieldID,
+        value: s.name,
       }));
     return {
       schemaOptions: options,

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

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

+ 5 - 9
client/src/pages/overview/Overview.tsx

@@ -15,11 +15,12 @@ import EmptyCard from '@/components/cards/EmptyCard';
 import icons from '@/components/icons/Icons';
 import { LOADING_STATE, MILVUS_DEPLOY_MODE } from '@/consts';
 import { useNavigationHook } from '@/hooks';
-import { Collection } from '@/http';
+import { CollectionService } from '@/http';
 import { ALL_ROUTER_TYPES } from '@/router/Types';
 import { formatNumber } from '@/utils';
 import CollectionCard from './collectionCard/CollectionCard';
 import StatisticsCard from './statisticsCard/StatisticsCard';
+import { StatisticsObject } from '@server/types';
 
 const useStyles = makeStyles((theme: Theme) => ({
   overviewContainer: {
@@ -105,11 +106,6 @@ const SysCard = (data: {
   );
 };
 
-type statisticsType = {
-  collectionCount: number;
-  totalData: number;
-};
-
 const Overview = () => {
   useNavigationHook(ALL_ROUTER_TYPES.OVERVIEW);
   const { database, databases, collections, loading } = useContext(dataContext);
@@ -119,7 +115,7 @@ const Overview = () => {
   const { t: overviewTrans } = useTranslation('overview');
   const { t: collectionTrans } = useTranslation('collection');
   const { t: successTrans } = useTranslation('success');
-  const [statistics, setStatistics] = useState<statisticsType>({
+  const [statistics, setStatistics] = useState<StatisticsObject>({
     collectionCount: 0,
     totalData: 0,
   });
@@ -129,7 +125,7 @@ const Overview = () => {
   const fetchData = useCallback(async () => {
     if (loading) return;
     setLoadingLocal(true);
-    const res = (await Collection.getStatistics()) as any;
+    const res = await CollectionService.getStatistics();
     setStatistics(res);
     setLoadingLocal(false);
   }, [database, collections]);
@@ -139,7 +135,7 @@ const Overview = () => {
   }, [fetchData]);
 
   const loadCollections = collections.filter(
-    c => c.status !== LOADING_STATE.UNLOADED || (c as any).loaded === 2
+    c => c.status !== LOADING_STATE.UNLOADED
   );
 
   const onRelease = () => {

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

@@ -17,7 +17,7 @@ import { LOADING_STATE } from '@/consts';
 import { rootContext, dataContext } from '@/context';
 import ReleaseCollectionDialog from '../../dialogs/ReleaseCollectionDialog';
 import { CollectionCardProps } from './Types';
-import { Collection } from '@/http';
+import { CollectionService } from '@/http';
 
 const useStyles = makeStyles((theme: Theme) => ({
   wrapper: {
@@ -89,11 +89,11 @@ const CollectionCard: FC<CollectionCardProps> = ({
 }) => {
   const { database } = useContext(dataContext);
   const [loading, setLoading] = useState(false);
-  const [count, setCount] = useState<string>('');
+  const [count, setCount] = useState<number>();
   const classes = useStyles();
   const { setDialog } = useContext(rootContext);
 
-  const { collectionName, status: status, loadedPercentage, replicasInfo } = data;
+  const { collection_name, status: status, loadedPercentage, replicas } = data;
   const navigate = useNavigate();
   // icons
   const RightArrowIcon = icons.rightArrow;
@@ -109,14 +109,20 @@ const CollectionCard: FC<CollectionCardProps> = ({
       type: 'custom',
       params: {
         component: (
-          <ReleaseCollectionDialog collection={collectionName} onRelease={onRelease} />
+          <ReleaseCollectionDialog
+            collection={collection_name}
+            onRelease={onRelease}
+          />
         ),
       },
     });
   };
 
   const onVectorSearchClick = () => {
-    navigate({ pathname: '/search', search: `?collectionName=${collectionName}` });
+    navigate({
+      pathname: '/search',
+      search: `?collectionName=${collection_name}`,
+    });
   };
 
   useEffect(() => {
@@ -124,8 +130,8 @@ const CollectionCard: FC<CollectionCardProps> = ({
       try {
         setLoading(true);
         if (status === LOADING_STATE.LOADED) {
-          const data = await Collection.count(collectionName);
-          setCount(data.entityCount);
+          const data = await CollectionService.count(collection_name);
+          setCount(data.rowCount);
         }
       } catch (e) {
       } finally {
@@ -154,16 +160,16 @@ const CollectionCard: FC<CollectionCardProps> = ({
         <div>
           <Status status={status} percentage={loadedPercentage} />
         </div>
-        <Link className="link" to={`/collections/${collectionName}/data`}>
-          {collectionName}
+        <Link className="link" to={`/collections/${collection_name}/data`}>
+          {collection_name}
           <RightArrowIcon classes={{ root: classes.icon }} />
         </Link>
         <ul className={classes.content}>
-          {replicasInfo && replicasInfo.length > 1 ? (
+          {replicas && replicas.length > 1 ? (
             <li>
               <Typography>{collectionTrans('replicaNum')}</Typography>:
               <Typography className={classes.rowCount}>
-                {replicasInfo.length}
+                {replicas.length}
               </Typography>
             </li>
           ) : null}

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

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

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

@@ -1,10 +1,10 @@
-import { KeyValuePair } from '../../../types/Common';
+import { LabelValuePair } from '../../../types/Common';
 
 export interface StatisticsCardProps {
   wrapperClass?: string;
   data: Item[];
 }
 
-export interface Item extends KeyValuePair {
+export interface Item extends LabelValuePair {
   valueColor: string;
 }

+ 3 - 3
client/src/pages/partitions/Partitions.tsx

@@ -9,7 +9,7 @@ import { usePaginationHook, useInsertDialogHook } from '@/hooks';
 import icons from '@/components/icons/Icons';
 import CustomToolTip from '@/components/customToolTip/CustomToolTip';
 import { rootContext } from '@/context';
-import { Collection, PartitionService, FieldHttp } from '@/http';
+import { CollectionService, PartitionService } from '@/http';
 import InsertContainer from '../dialogs/insert/Dialog';
 import CreatePartitionDialog from '../dialogs/CreatePartitionDialog';
 import DropPartitionDialog from '../dialogs/DropPartitionDialog';
@@ -66,7 +66,7 @@ const Partitions = () => {
   };
 
   const fetchCollectionDetail = async (name: string) => {
-    const res = await Collection.getCollectionInfo(name);
+    const res = await CollectionService.getCollectionInfo(name);
     return res;
   };
 
@@ -134,7 +134,7 @@ const Partitions = () => {
       icon: 'uploadFile',
       onClick: async () => {
         const collection = await fetchCollectionDetail(collectionName);
-        const schema = collection.schema.fields.map(f => new FieldHttp(f));
+        const schema = collection.schema;
 
         handleInsertDialog(
           <InsertContainer

+ 0 - 177
client/src/pages/preview/Preview.tsx

@@ -1,177 +0,0 @@
-import { useEffect, useState } from 'react';
-import { useTranslation } from 'react-i18next';
-import AttuGrid from '@/components/grid/Grid';
-import { useParams } from 'react-router-dom';
-import { Collection, MilvusIndex } from '@/http';
-import { usePaginationHook, useSearchResult } from '@/hooks';
-import { generateVector } from '@/utils';
-import {
-  INDEX_CONFIG,
-  DEFAULT_SEARCH_PARAM_VALUE_MAP,
-  DYNAMIC_FIELD,
-  DataTypeEnum,
-  DataTypeStringEnum,
-} from '@/consts';
-import { ToolBarConfig } from '@/components/grid/Types';
-import CustomToolBar from '@/components/grid/ToolBar';
-import { getQueryStyles } from '../query/Styles';
-
-const Preview = () => {
-  const { collectionName } = useParams<{ collectionName: string }>();
-  const [fields, setFields] = useState<any[]>([]);
-  const [tableLoading, setTableLoading] = useState<any>();
-  const [queryResult, setQueryResult] = useState<any>();
-  const [primaryKey, setPrimaryKey] = useState<string>('');
-  const { t: searchTrans } = useTranslation('search');
-  const { t: btnTrans } = useTranslation('btn');
-
-  const classes = getQueryStyles();
-
-  // Format result list
-  const queryResultMemo = useSearchResult(queryResult);
-
-  const {
-    pageSize,
-    handlePageSize,
-    currentPage,
-    handleCurrentPage,
-    total,
-    data: result,
-  } = usePaginationHook(queryResultMemo || []);
-
-  const handlePageChange = (e: any, page: number) => {
-    handleCurrentPage(page);
-  };
-
-  const loadData = async (collectionName: string) => {
-    // get schema list
-    const collection = await Collection.getCollectionInfo(collectionName);
-
-    const schemaList = collection.fields!;
-    let nameList = schemaList.map(v => ({
-      name: v.name,
-      type: v.fieldType,
-    }));
-
-    // if the dynamic field is enabled, we add $meta column in the grid
-    if (collection.enableDynamicField) {
-      nameList.push({
-        name: DYNAMIC_FIELD,
-        type: DataTypeStringEnum.JSON,
-      });
-    }
-
-    const id = schemaList.find(v => v.isPrimaryKey === true);
-    const primaryKey = id?.name || '';
-    const delimiter = id?.fieldType === 'Int64' ? '' : '"';
-
-    const vectorField = schemaList.find(
-      v => v.fieldType === 'FloatVector' || v.fieldType === 'BinaryVector'
-    );
-    const anns_field = vectorField?.name!;
-    const dim = Number(vectorField?.dimension);
-    const vectors = [
-      generateVector(vectorField?.fieldType === 'FloatVector' ? dim : dim / 8),
-    ];
-    // get search params
-    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 indexConfig = INDEX_CONFIG[indexType];
-    const metric_type =
-      indexesInfo.length === 0 ? 'L2' : vectorIndex.metricType;
-    const searchParamKey = indexConfig.search[0];
-    const searchParamValue = DEFAULT_SEARCH_PARAM_VALUE_MAP[searchParamKey];
-    const searchParam = { [searchParamKey]: searchParamValue };
-    const params = `${JSON.stringify(searchParam)}`;
-    setPrimaryKey(primaryKey);
-
-    // set fields
-    setFields(nameList);
-
-    // set loading
-    setTableLoading(true);
-
-    try {
-      // first, search random data to get random id
-      const searchRes = await Collection.vectorSearchData(collectionName, {
-        search_params: {
-          topk: 100,
-          anns_field,
-          metric_type,
-          params,
-        },
-        expr: '',
-        vectors,
-        output_fields: [primaryKey],
-        vector_type:
-          DataTypeEnum[vectorField!.fieldType as keyof typeof DataTypeEnum],
-      });
-
-      // compose random id list expression
-      const expr = `${primaryKey} in [${searchRes.results
-        .map((d: any) => `${delimiter}${d.id}${delimiter}`)
-        .join(',')}]`;
-
-      // query by random id
-      const res = await Collection.queryData(collectionName, {
-        expr: expr,
-        output_fields: [...nameList.map(i => i.name)],
-      });
-
-      const result = res.data;
-      setQueryResult(result);
-    } catch (err) {
-      setQueryResult([]);
-    } finally {
-      setTableLoading(false);
-    }
-  };
-
-  // Get fields at first or collection name changed.
-  useEffect(() => {
-    collectionName && loadData(collectionName);
-  }, [collectionName]);
-
-  const toolbarConfigs: ToolBarConfig[] = [
-    {
-      icon: 'refresh',
-      type: 'button',
-      btnVariant: 'text',
-      onClick: () => {
-        loadData(collectionName!);
-      },
-      label: btnTrans('refresh'),
-    },
-  ];
-
-  return (
-    <div className={classes.root}>
-      <CustomToolBar toolbarConfigs={toolbarConfigs} />
-
-      <AttuGrid
-        toolbarConfigs={[]}
-        colDefinitions={fields.map(i => ({
-          id: i.name,
-          align: 'left',
-          disablePadding: false,
-          needCopy: true,
-          label:
-            i.name === DYNAMIC_FIELD ? searchTrans('dynamicFields') : i.name,
-        }))}
-        primaryKey={primaryKey}
-        openCheckBox={false}
-        isLoading={!!tableLoading}
-        rows={result}
-        rowCount={total}
-        page={currentPage}
-        onPageChange={handlePageChange}
-        rowsPerPage={pageSize}
-        setRowsPerPage={handlePageSize}
-      />
-    </div>
-  );
-};
-
-export default Preview;

+ 139 - 132
client/src/pages/query/Query.tsx

@@ -88,11 +88,12 @@ const Query = () => {
   const handleDelete = async () => {
     // call delete api
     await DataService.deleteEntities(collectionName, {
-      expr: `${collection.primaryKey.value} in [${selectedData
+      expr: `${collection!.schema.primaryField.name} in [${selectedData
         .map(v =>
-          collection.primaryKey.type === DataTypeStringEnum.VarChar
-            ? `"${v[collection.primaryKey.value]}"`
-            : v[collection.primaryKey.value]
+          collection!.schema.primaryField.data_type ===
+          DataTypeStringEnum.VarChar
+            ? `"${v[collection!.schema.primaryField.name]}"`
+            : v[collection!.schema.primaryField.name]
         )
         .join(',')}]`,
     });
@@ -151,7 +152,7 @@ const Query = () => {
                 defaultSelectedCollection={collectionName}
                 // user can't select partition on collection page, so default value is ''
                 defaultSelectedPartition={''}
-                collections={[collection.data]}
+                collections={[collection!]}
                 onInsert={() => {}}
               />
             ),
@@ -275,135 +276,141 @@ const Query = () => {
 
   return (
     <div className={classes.root}>
-      <CustomToolBar toolbarConfigs={toolbarConfigs} hideOnDisable={true} />
-      <div className={classes.toolbar}>
-        <div className="left">
-          <TextField
-            className="textarea"
-            InputProps={{
-              classes: {
-                root: 'textfield',
-                multiline: 'multiline',
-              },
-            }}
-            value={expression}
-            onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
-              setExpression(e.target.value as string);
-            }}
-            disabled={!collection.loaded}
-            InputLabelProps={{ shrink: true }}
-            label={collectionTrans('exprPlaceHolder')}
-            onKeyDown={e => {
-              if (e.key === 'Enter') {
-                // reset page
-                setCurrentPage(0);
-                if (expr !== expression) {
-                  setExpr(expression);
-                } else {
-                  // ensure query
-                  query();
-                }
-                e.preventDefault();
-              }
-            }}
-            inputRef={inputRef}
-          />
-          <Filter
-            ref={filterRef}
-            title="Advanced Filter"
-            fields={collection.fields.filter(
-              (i: any) =>
-                i.type !== DataTypeStringEnum.FloatVector &&
-                i.type !== DataTypeStringEnum.BinaryVector
+      {collection && (
+        <>
+          <CustomToolBar toolbarConfigs={toolbarConfigs} hideOnDisable={true} />
+          <div className={classes.toolbar}>
+            <div className="left">
+              <TextField
+                className="textarea"
+                InputProps={{
+                  classes: {
+                    root: 'textfield',
+                    multiline: 'multiline',
+                  },
+                }}
+                value={expression}
+                onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
+                  setExpression(e.target.value as string);
+                }}
+                disabled={!collection!.loaded}
+                InputLabelProps={{ shrink: true }}
+                label={collectionTrans('exprPlaceHolder')}
+                onKeyDown={e => {
+                  if (e.key === 'Enter') {
+                    // reset page
+                    setCurrentPage(0);
+                    if (expr !== expression) {
+                      setExpr(expression);
+                    } else {
+                      // ensure query
+                      query();
+                    }
+                    e.preventDefault();
+                  }
+                }}
+                inputRef={inputRef}
+              />
+              <Filter
+                ref={filterRef}
+                title="Advanced Filter"
+                fields={collection.schema.fields.filter(
+                  i =>
+                    i.data_type !== DataTypeStringEnum.FloatVector &&
+                    i.data_type !== DataTypeStringEnum.BinaryVector
+                )}
+                filterDisabled={!collection.loaded}
+                onSubmit={handleFilterSubmit}
+                showTitle={false}
+                showTooltip={false}
+              />
+              {/* </div> */}
+              <CustomSelector
+                options={CONSISTENCY_LEVEL_OPTIONS}
+                value={consistencyLevel}
+                label={collectionTrans('consistency')}
+                wrapperClass={classes.selector}
+                disabled={!collection.loaded}
+                variant="filled"
+                onChange={(e: { target: { value: unknown } }) => {
+                  const consistency = e.target.value as string;
+                  setConsistencyLevel(consistency);
+                }}
+              />
+            </div>
+            <div className="right">
+              <CustomButton
+                className="btn"
+                onClick={handleFilterReset}
+                disabled={!collection.loaded}
+              >
+                <ResetIcon classes={{ root: 'icon' }} />
+                {btnTrans('reset')}
+              </CustomButton>
+              <CustomButton
+                variant="contained"
+                onClick={() => {
+                  setCurrentPage(0);
+                  if (expr !== expression) {
+                    setExpr(expression);
+                  } else {
+                    // ensure query
+                    query();
+                  }
+                }}
+                disabled={!collection.loaded}
+              >
+                {btnTrans('query')}
+              </CustomButton>
+            </div>
+          </div>
+          <AttuGrid
+            toolbarConfigs={[]}
+            colDefinitions={collection.schema.fields.map((i: any) => {
+              return {
+                id: i.name,
+                align: 'left',
+                disablePadding: false,
+                needCopy: true,
+                formatter(_: any, cellData: any) {
+                  const itemType = detectItemType(cellData);
+                  switch (itemType) {
+                    case 'json':
+                    case 'array':
+                    case 'bool':
+                      const res = JSON.stringify(cellData);
+                      return <Typography title={res}>{res}</Typography>;
+                    default:
+                      return cellData;
+                  }
+                },
+                label:
+                  i.name === DYNAMIC_FIELD
+                    ? searchTrans('dynamicFields')
+                    : i.name,
+              };
+            })}
+            primaryKey={collection.schema.primaryField.name}
+            openCheckBox={true}
+            isLoading={!!tableLoading}
+            rows={queryResult.data}
+            rowCount={total}
+            rowHeight={43}
+            selected={selectedData}
+            setSelected={onSelectChange}
+            page={currentPage}
+            onPageChange={handlePageChange}
+            setRowsPerPage={setPageSize}
+            rowsPerPage={pageSize}
+            labelDisplayedRows={getLabelDisplayedRows(
+              `(${queryResult.latency || ''} ms)`
+            )}
+            noData={searchTrans(
+              `${collection.loaded ? 'empty' : 'collectionNotLoaded'}`
             )}
-            filterDisabled={!collection.loaded}
-            onSubmit={handleFilterSubmit}
-            showTitle={false}
-            showTooltip={false}
-          />
-          {/* </div> */}
-          <CustomSelector
-            options={CONSISTENCY_LEVEL_OPTIONS}
-            value={consistencyLevel}
-            label={collectionTrans('consistency')}
-            wrapperClass={classes.selector}
-            disabled={!collection.loaded}
-            variant="filled"
-            onChange={(e: { target: { value: unknown } }) => {
-              const consistency = e.target.value as string;
-              setConsistencyLevel(consistency);
-            }}
           />
-        </div>
-        <div className="right">
-          <CustomButton
-            className="btn"
-            onClick={handleFilterReset}
-            disabled={!collection.loaded}
-          >
-            <ResetIcon classes={{ root: 'icon' }} />
-            {btnTrans('reset')}
-          </CustomButton>
-          <CustomButton
-            variant="contained"
-            onClick={() => {
-              setCurrentPage(0);
-              if (expr !== expression) {
-                setExpr(expression);
-              } else {
-                // ensure query
-                query();
-              }
-            }}
-            disabled={!collection.loaded}
-          >
-            {btnTrans('query')}
-          </CustomButton>
-        </div>
-      </div>
-      <AttuGrid
-        toolbarConfigs={[]}
-        colDefinitions={collection.fields.map((i: any) => {
-          return {
-            id: i.name,
-            align: 'left',
-            disablePadding: false,
-            needCopy: true,
-            formatter(_: any, cellData: any) {
-              const itemType = detectItemType(cellData);
-              switch (itemType) {
-                case 'json':
-                case 'array':
-                case 'bool':
-                  const res = JSON.stringify(cellData);
-                  return <Typography title={res}>{res}</Typography>;
-                default:
-                  return cellData;
-              }
-            },
-            label:
-              i.name === DYNAMIC_FIELD ? searchTrans('dynamicFields') : i.name,
-          };
-        })}
-        primaryKey={collection.primaryKey.value}
-        openCheckBox={true}
-        isLoading={!!tableLoading}
-        rows={queryResult.data}
-        rowCount={total}
-        rowHeight={43}
-        selected={selectedData}
-        setSelected={onSelectChange}
-        page={currentPage}
-        onPageChange={handlePageChange}
-        setRowsPerPage={setPageSize}
-        rowsPerPage={pageSize}
-        labelDisplayedRows={getLabelDisplayedRows(
-          `(${queryResult.latency || ''} ms)`
-        )}
-        noData={searchTrans(
-          `${collection.loaded ? 'empty' : 'collectionNotLoaded'}`
-        )}
-      />
+        </>
+      )}
     </div>
   );
 };

+ 1 - 1
client/src/pages/query/Styles.ts

@@ -16,7 +16,7 @@ export const getQueryStyles = makeStyles((theme: Theme) => ({
     justifyContent: 'space-between',
     alignItems: 'center',
     backgroundColor: 'white',
-    padding: theme.spacing(1, 2),
+    padding: theme.spacing(1, 0),
     gap: theme.spacing(2),
     borderRadius: theme.spacing(0.5, 0.5, 0, 0),
 

+ 36 - 31
client/src/pages/schema/IndexTypeElement.tsx

@@ -10,7 +10,7 @@ import { useTranslation } from 'react-i18next';
 import Chip from '@material-ui/core/Chip';
 import { makeStyles, Theme, Tooltip } from '@material-ui/core';
 import { IndexCreateParam, IndexExtraParam, IndexManageParam } from './Types';
-import { MilvusIndex, FieldHttp } from '@/http';
+import { IndexService } from '@/http';
 import { rootContext } from '@/context';
 import icons from '@/components/icons/Icons';
 import DeleteTemplate from '@/components/customDialog/DeleteDialogTemplate';
@@ -18,8 +18,9 @@ import StatusIcon from '@/components/status/StatusIcon';
 import { ChildrenStatusType } from '@/components/status/Types';
 import { sleep } from '@/utils';
 import { IndexState } from '@/types/Milvus';
-import { NONE_INDEXABLE_DATA_TYPES } from '@/consts';
+import { NONE_INDEXABLE_DATA_TYPES, DataTypeStringEnum } from '@/consts';
 import CreateIndex from './Create';
+import { FieldObject } from '@server/types';
 
 const useStyles = makeStyles((theme: Theme) => ({
   wrapper: {
@@ -66,7 +67,7 @@ const useStyles = makeStyles((theme: Theme) => ({
 }));
 
 const IndexTypeElement: FC<{
-  data: FieldHttp;
+  data: FieldObject;
   collectionName: string;
   disabled?: boolean;
   disabledTooltip?: string;
@@ -80,8 +81,6 @@ const IndexTypeElement: FC<{
   const { t: dialogTrans } = useTranslation('dialog');
   const { t: successTrans } = useTranslation('success');
 
-  // const [createProgress, setCreateProgress] = useState<number>(0);
-
   const { setDialog, handleCloseDialog, openSnackBar } =
     useContext(rootContext);
 
@@ -94,6 +93,9 @@ const IndexTypeElement: FC<{
   );
 
   useEffect(() => {
+    if (!data.index) {
+      return;
+    }
     let running = true;
 
     // define async data getter
@@ -103,7 +105,9 @@ const IndexTypeElement: FC<{
       indexName: string
     ) => {
       // get fetch data
-      const index_descriptions = await MilvusIndex.getIndexInfo(collectionName);
+      const index_descriptions = await IndexService.describeIndex(
+        collectionName
+      );
 
       const indexDescription = index_descriptions.find(
         i => i.field_name === fieldName
@@ -126,14 +130,18 @@ const IndexTypeElement: FC<{
       }
     };
     // prevent delete index trigger fetching index status
-    if (data.indexType !== '' && status !== IndexState.Delete) {
-      fetchStatus(collectionName, data.name, data.indexName!);
+    if (data.index.indexType !== '' && status !== IndexState.Delete) {
+      fetchStatus(
+        collectionName,
+        data.name,
+        data.index && data.index.index_name!
+      );
     }
 
     return () => {
       running = false;
     };
-  }, [collectionName, data.indexType, data.name, data.indexName]);
+  }, [collectionName, data.name, data.index && data.index.index_name]);
 
   const requestCreateIndex = async (
     params: IndexExtraParam,
@@ -145,7 +153,7 @@ const IndexTypeElement: FC<{
       index_name,
       extra_params: params,
     };
-    await MilvusIndex.createIndex(indexCreateParam);
+    await IndexService.createIndex(indexCreateParam);
     // reset status to default empty string
     setStatus(IndexState.Default);
     handleCloseDialog();
@@ -164,7 +172,7 @@ const IndexTypeElement: FC<{
           <CreateIndex
             collectionName={collectionName}
             fieldName={data.name}
-            fieldType={data.fieldType}
+            fieldType={data.data_type as DataTypeStringEnum}
             dimension={Number(data.dimension)}
             handleCancel={handleCloseDialog}
             handleCreate={requestCreateIndex}
@@ -178,10 +186,10 @@ const IndexTypeElement: FC<{
     const indexDeleteParam: IndexManageParam = {
       collection_name: collectionName,
       field_name: data.name,
-      index_name: data.indexName!,
+      index_name: data.index.index_name!,
     };
 
-    await MilvusIndex.deleteIndex(indexDeleteParam);
+    await IndexService.deleteIndex(indexDeleteParam);
     // use 'delete' as special status for whether fetching index status check
     setStatus(IndexState.Delete);
     cb(collectionName);
@@ -209,27 +217,24 @@ 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.is_primary_key ||
+      NONE_INDEXABLE_DATA_TYPES.indexOf(
+        data.data_type as DataTypeStringEnum
+      ) !== -1
     ) {
       return <div className={classes.item}>--</div>;
     }
+
+    if (!data.index) {
+      return (
+        <div role="button" onClick={handleCreate} className={`${classes.btn}`}>
+          <AddIcon classes={{ root: classes.addIcon }} />
+          {indexTrans('create')}
+        </div>
+      );
+    }
     // indexType example: FLAT
-    switch (data.indexType) {
-      case '': {
-        return (
-          <div
-            role="button"
-            onClick={handleCreate}
-            className={`${classes.btn} ${
-              data.createIndexDisabled ? classes.btnDisabled : ''
-            }`}
-          >
-            <AddIcon classes={{ root: classes.addIcon }} />
-            {indexTrans('create')}
-          </div>
-        );
-      }
+    switch (data.index.indexType) {
       default: {
         /**
          * empty string or 'delete' means fetching progress hasn't finished
@@ -245,7 +250,7 @@ const IndexTypeElement: FC<{
 
         const chipComp = () => (
           <Chip
-            label={data.indexType}
+            label={data.index.indexType}
             classes={{ root: classes.chip, label: classes.chipLabel }}
             deleteIcon={<DeleteIcon classes={{ root: 'icon' }} />}
             onDelete={handleDelete}

+ 24 - 28
client/src/pages/schema/Schema.tsx

@@ -7,8 +7,9 @@ import { useTranslation } from 'react-i18next';
 import { usePaginationHook } from '@/hooks';
 import icons from '@/components/icons/Icons';
 import { formatFieldType } from '@/utils';
-import { Collection, FieldHttp } from '@/http';
+import { CollectionService } from '@/http';
 import IndexTypeElement from './IndexTypeElement';
+import { FieldObject } from '@server/types';
 
 const useStyles = makeStyles((theme: Theme) => ({
   wrapper: {
@@ -65,7 +66,7 @@ const Schema = () => {
   const { t: collectionTrans } = useTranslation('collection');
   const { t: indexTrans } = useTranslation('index');
 
-  const [fields, setFields] = useState<FieldHttp[]>([]);
+  const [fields, setFields] = useState<FieldObject[]>([]);
   const [loading, setLoading] = useState<boolean>(true);
 
   const KeyIcon = icons.key;
@@ -85,21 +86,11 @@ const Schema = () => {
   const fetchFields = useCallback(
     async (collectionName: string) => {
       try {
-        const collection = await Collection.getCollectionWithIndexInfo(
+        const collection = await CollectionService.getCollectionInfo(
           collectionName
         );
 
-        // add custom fields
-        const fields = collection.fieldWithIndexInfo.map(f =>
-          Object.assign(f, {
-            _fieldNameElement: f,
-            _fieldTypeElement: f,
-            _indexParamElement: f,
-            _indexTypeElement: f,
-          })
-        );
-
-        setFields(fields);
+        setFields(collection.schema.fields);
         setLoading(false);
       } catch (err) {
         setLoading(false);
@@ -115,14 +106,14 @@ const Schema = () => {
 
   const colDefinitions: ColDefinitionsType[] = [
     {
-      id: '_fieldNameElement',
+      id: 'name',
       align: 'left',
       disablePadding: true,
       formatter(f) {
         return (
           <div className={classes.nameWrapper}>
             {f.name}
-            {f.isPrimaryKey ? (
+            {f.is_primary_key ? (
               <div
                 className={classes.iconTitle}
                 title={collectionTrans('idFieldName')}
@@ -153,7 +144,7 @@ const Schema = () => {
       sortBy: 'name',
     },
     {
-      id: '_fieldTypeElement',
+      id: 'data_type',
       align: 'left',
       disablePadding: false,
       formatter(f) {
@@ -162,17 +153,20 @@ const Schema = () => {
       label: collectionTrans('fieldType'),
     },
     {
-      id: 'indexName',
+      id: 'name',
       align: 'left',
       disablePadding: true,
       label: indexTrans('indexName'),
+      formatter(f) {
+        return f.index?.index_name;
+      },
     },
     {
-      id: '_indexTypeElement',
+      id: 'name',
       align: 'left',
       disablePadding: true,
       label: indexTrans('type'),
-      sortBy: 'indexType',
+      notSort: true,
       formatter(f) {
         return (
           <IndexTypeElement
@@ -184,19 +178,19 @@ const Schema = () => {
       },
     },
     {
-      id: '_indexParamElement',
+      id: 'name',
       align: 'left',
       disablePadding: false,
       label: indexTrans('param'),
       notSort: true,
       formatter(f) {
-        return (
+        return f.index ? (
           <div className={classes.paramWrapper}>
-            {f.indexParameterPairs?.length > 0 ? (
-              f.indexParameterPairs.map((p: any) =>
+            {f.index.indexParameterPairs.length > 0 ? (
+              f.index.indexParameterPairs.map((p: any) =>
                 p.value ? (
-                  <>
-                    <span key={p.key + p.value} className="param">
+                  <span key={p.key + p.value}>
+                    <span className="param">
                       <Typography variant="body1" className="key">
                         {`${p.key}:`}
                       </Typography>
@@ -204,7 +198,7 @@ const Schema = () => {
                         {p.value}
                       </Typography>
                     </span>
-                  </>
+                  </span>
                 ) : (
                   ''
                 )
@@ -213,11 +207,13 @@ const Schema = () => {
               <>--</>
             )}
           </div>
+        ) : (
+          <>--</>
         );
       },
     },
     {
-      id: 'desc',
+      id: 'description',
       align: 'left',
       disablePadding: false,
       label: indexTrans('desc'),

+ 4 - 9
client/src/pages/search/Types.ts

@@ -1,13 +1,13 @@
 import { Option } from '@/components/customSelector/Types';
 import { searchKeywordsType } from '@/consts';
-import { DataTypeEnum, DataTypeStringEnum } from '@/consts';
-import { MilvusIndex } from '@/http';
+import { DataTypeEnum } from '@/consts';
+import { FieldObject, KeyValuePair } from '@server/types';
 
 export interface SearchParamsProps {
   // default index type is FLAT
   indexType: string;
   // index extra params, e.g. nlist
-  indexParams: { key: string; value: string }[];
+  indexParams: KeyValuePair[];
   searchParamsForm: {
     [key in string]: number;
   };
@@ -27,12 +27,7 @@ export interface SearchResultView {
 }
 
 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: MilvusIndex | null;
-  // used for check vector input validation
-  dimension: number;
+  field: FieldObject;
 }
 
 export interface SearchParamInputConfig {

+ 53 - 79
client/src/pages/search/VectorSearch.tsx

@@ -15,30 +15,25 @@ import CustomButton from '@/components/customButton/CustomButton';
 import SimpleMenu from '@/components/menu/SimpleMenu';
 import { Option } from '@/components/customSelector/Types';
 import Filter from '@/components/advancedSearch';
-import { Field } from '@/components/advancedSearch/Types';
-import { Collection, DataService } from '@/http';
+import { DataService } from '@/http';
 import {
   parseValue,
   parseLocationSearch,
-  classifyFields,
-  getDefaultIndexType,
-  getEmbeddingType,
-  getNonVectorFieldsForFilter,
   getVectorFieldOptions,
   cloneObj,
   generateVector,
 } from '@/utils';
-import {
-  LOADING_STATE,
-  DEFAULT_METRIC_VALUE_MAP,
-  DYNAMIC_FIELD,
-  DataTypeEnum,
-} from '@/consts';
+import { LOADING_STATE, DYNAMIC_FIELD, DataTypeEnum } from '@/consts';
 import { getLabelDisplayedRows } from './Utils';
 import SearchParams from './SearchParams';
 import { getVectorSearchStyles } from './Styles';
 import { TOP_K_OPTIONS } from './Constants';
 import { FieldOption, SearchResultView, VectorSearchParam } from './Types';
+import {
+  FieldObject,
+  CollectionObject,
+  CollectionFullObject,
+} from '@server/types';
 
 const VectorSearch = () => {
   useNavigationHook(ALL_ROUTER_TYPES.SEARCH);
@@ -56,7 +51,7 @@ const VectorSearch = () => {
   const [selectedCollection, setSelectedCollection] = useState<string>('');
   const [fieldOptions, setFieldOptions] = useState<FieldOption[]>([]);
   // fields for advanced filter
-  const [filterFields, setFilterFields] = useState<Field[]>([]);
+  const [filterFields, setFilterFields] = useState<FieldObject[]>([]);
   const [selectedField, setSelectedField] = useState<string>('');
 
   // search params form
@@ -92,22 +87,24 @@ const VectorSearch = () => {
   } = usePaginationHook(searchResultMemo || []);
 
   const outputFields: string[] = useMemo(() => {
-    const s = collections.find(c => c.collectionName === selectedCollection);
+    const s = collections.find(
+      c => c.collection_name === selectedCollection
+    ) as CollectionFullObject;
 
     if (!s) {
       return [];
     }
 
-    const fields = s.fields || [];
+    const fields = s.schema.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.data_type)
     );
 
     const _outputFields = nonVectorFields.map(f => f.name);
-    if (s.enableDynamicField) {
+    if (s.schema?.enable_dynamic_field) {
       _outputFields.push(DYNAMIC_FIELD);
     }
     return _outputFields;
@@ -115,10 +112,10 @@ const VectorSearch = () => {
 
   const primaryKeyField = useMemo(() => {
     const selectedCollectionInfo = collections.find(
-      c => c.collectionName === selectedCollection
+      c => c.collection_name === selectedCollection
     );
-    const fields = selectedCollectionInfo?.fields || [];
-    return fields.find(f => f.isPrimaryKey)?.name;
+    const fields = selectedCollectionInfo?.schema?.fields || [];
+    return fields.find(f => f.is_primary_key)?.name;
   }, [selectedCollection, collections]);
 
   const orderArray = [primaryKeyField, 'id', 'score', ...outputFields];
@@ -155,47 +152,30 @@ const VectorSearch = () => {
   const [selectedConsistencyLevel, setSelectedConsistencyLevel] =
     useState<string>('');
 
-  const {
-    indexType,
-    indexParams,
-    fieldType,
-    embeddingType,
-    selectedFieldDimension,
-  } = useMemo(() => {
-    if (selectedField !== '') {
-      // field options must contain selected field, so selectedFieldInfo will never undefined
-      const selectedFieldInfo = fieldOptions.find(
-        f => f.value === selectedField
-      );
-      const index = selectedFieldInfo?.indexInfo;
-      const embeddingType = getEmbeddingType(selectedFieldInfo!.fieldType);
-      const metric =
-        index?.metricType || DEFAULT_METRIC_VALUE_MAP[embeddingType];
-      const indexParams = index?.indexParameterPairs || [];
-      const dim = selectedFieldInfo?.dimension || 0;
-      setSelectedMetricType(metric);
+  const { indexType, indexParams, fieldType, selectedFieldDimension } =
+    useMemo(() => {
+      if (selectedField !== '') {
+        // field options must contain selected field, so selectedFieldInfo will never undefined
+        const field = fieldOptions.find(f => f.value === selectedField)?.field;
+        const metric = field?.index.metricType || '';
+        setSelectedMetricType(metric);
+
+        return {
+          metricType: metric,
+          indexType: field?.index.indexType,
+          indexParams: field?.index.indexParameterPairs || [],
+          fieldType: field?.dataType,
+          selectedFieldDimension: field?.dimension || 0,
+        };
+      }
 
       return {
-        metricType: metric,
-        indexType: index?.indexType || getDefaultIndexType(embeddingType),
-        indexParams,
-        fieldType:
-          DataTypeEnum[
-            selectedFieldInfo?.fieldType! as keyof typeof DataTypeEnum
-          ],
-        embeddingType,
-        selectedFieldDimension: dim,
+        indexType: '',
+        indexParams: [],
+        fieldType: DataTypeEnum.FloatVector,
+        selectedFieldDimension: 0,
       };
-    }
-
-    return {
-      indexType: '',
-      indexParams: [],
-      fieldType: 0,
-      embeddingType: DataTypeEnum.FloatVector,
-      selectedFieldDimension: 0,
-    };
-  }, [selectedField, fieldOptions]);
+    }, [selectedField, fieldOptions]);
 
   /**
    * vector value validation
@@ -245,25 +225,18 @@ const VectorSearch = () => {
   const collectionOptions: Option[] = useMemo(
     () =>
       loadedCollections.map(c => ({
-        label: c.collectionName,
-        value: c.collectionName,
+        label: c.collection_name,
+        value: c.collection_name,
       })),
     [loadedCollections]
   );
 
   const fetchFieldsWithIndex = useCallback(
-    async (collectionName: string, collections: Collection[]) => {
-      const col = collections.find(c => c.collectionName === collectionName);
-
-      const fields = col?.fields ?? [];
-
-      const { vectorFields, nonVectorFields } = classifyFields(fields);
+    async (collectionName: string, collections: CollectionFullObject[]) => {
+      const col = collections.find(c => c.collection_name === collectionName);
 
       // only vector type fields can be select
-      const fieldOptions = getVectorFieldOptions(
-        vectorFields,
-        col?.indexes ?? []
-      );
+      const fieldOptions = getVectorFieldOptions(col?.schema.vectorFields!);
       setFieldOptions(fieldOptions);
       if (fieldOptions.length > 0) {
         // set first option value as default field value
@@ -271,9 +244,7 @@ const VectorSearch = () => {
         setSelectedField(defaultFieldValue as string);
       }
 
-      // only non vector type fields can be advanced filter
-      const filterFields = getNonVectorFieldsForFilter(nonVectorFields);
-      setFilterFields(filterFields);
+      setFilterFields(col?.schema.scalarFields!);
     },
     [collections]
   );
@@ -288,9 +259,12 @@ const VectorSearch = () => {
   // get field options with index when selected collection changed
   useEffect(() => {
     if (selectedCollection !== '') {
-      fetchFieldsWithIndex(selectedCollection, collections);
+      fetchFieldsWithIndex(
+        selectedCollection,
+        collections as CollectionFullObject[]
+      );
     }
-    const level = collections.find(c => c.collectionName == selectedCollection)
+    const level = collections.find(c => c.collection_name == selectedCollection)
       ?.consistency_level!;
     setSelectedConsistencyLevel(level);
   }, [selectedCollection, collections, fetchFieldsWithIndex]);
@@ -301,7 +275,7 @@ const VectorSearch = () => {
       const { collectionName } = parseLocationSearch(location.search);
       // collection name validation
       const isNameValid = collections
-        .map(c => c.collectionName)
+        .map(c => c.collection_name)
         .includes(collectionName);
       isNameValid && setSelectedCollection(collectionName);
     }
@@ -350,10 +324,10 @@ const VectorSearch = () => {
       expr,
       search_params: searchParamPairs,
       vectors: [parseValue(vectors)],
-      vector_type: fieldType,
+      vector_type: fieldType as DataTypeEnum,
       consistency_level:
         selectedConsistencyLevel ||
-        collections.find(c => c.collectionName == selectedCollection)
+        collections.find(c => c.collection_name == selectedCollection)
           ?.consistency_level!,
     };
 
@@ -480,7 +454,7 @@ const VectorSearch = () => {
             handleConsistencyChange={(level: string) => {
               setSelectedConsistencyLevel(level);
             }}
-            indexType={indexType}
+            indexType={indexType!}
             indexParams={indexParams!}
             searchParamsForm={searchParam}
             handleFormChange={setSearchParam}

+ 7 - 8
client/src/pages/segments/Segments.tsx

@@ -1,7 +1,7 @@
 import { useEffect, useState, useContext } from 'react';
 import { useTranslation } from 'react-i18next';
 import { useParams } from 'react-router-dom';
-import { SegementService } from '@/http';
+import { SegmentService } from '@/http';
 import { usePaginationHook } from '@/hooks';
 import { rootContext } from '@/context';
 import AttuGrid from '@/components/grid/Grid';
@@ -27,13 +27,12 @@ const Segments = () => {
   const fetchSegments = async () => {
     setLoading(true);
 
-    const psegments =
-      (await SegementService.getPSegments(collectionName)) || {};
-    const qsegments =
-      (await SegementService.getQSegments(collectionName)) || {};
-    const combinedArray = psegments.infos.map(p => {
-      const q: any =
-        qsegments.infos.find(q => q.segmentID === p.segmentID)! || {};
+    const psegments = await SegmentService.getPSegments(collectionName);
+    const qsegments = await SegmentService.getQSegments(collectionName);
+
+    console.log('psegments', psegments);
+    const combinedArray = psegments.map(p => {
+      const q: any = qsegments.find(q => q.segmentID === p.segmentID)! || {};
       return {
         ...p,
         ...Object.keys(q).reduce((acc, key) => {

+ 0 - 7
client/src/pages/user/Types.ts

@@ -81,13 +81,6 @@ export interface AssignRoleParams {
 
 export interface UnassignRoleParams extends AssignRoleParams {}
 
-export enum TAB_ENUM {
-  'schema',
-  'partition',
-  'data-preview',
-  'data-query',
-}
-
 export type RBACObject = 'Global' | 'Collection' | 'User';
 
 export interface PrivilegeOptionsProps {

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

@@ -1,28 +1,10 @@
 import { IValidationItem } from '@/hooks';
 
-export interface KeyValuePair {
+export interface LabelValuePair {
   label: string;
   value: string | number;
 }
 
-export interface IRes {
-  code: number;
-  message: string;
-  data?: any;
-}
-
-export interface IPagination {
-  offset?: number;
-  limit?: number;
-}
-
-export interface IPaginationRes {
-  count: number;
-  limit: number;
-  offset: number;
-  total_count: number;
-}
-
 export enum ManageRequestMethods {
   DELETE = 'delete',
   CREATE = 'create',

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

@@ -1,6 +1,6 @@
 import { Option } from '@/components/customSelector/Types';
 import { searchKeywordsType, DataTypeEnum, DataTypeStringEnum } from '@/consts';
-import { MilvusIndex } from '@/http';
+import { FieldObject } from '@server/types';
 
 export interface SearchParamsProps {
   // if user created index, pass metric type choosed when creating
@@ -29,12 +29,7 @@ export interface SearchResultView {
 }
 
 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: MilvusIndex | null;
-  // used for check vector input validation
-  dimension: number;
+  field: FieldObject;
 }
 
 export interface SearchParamInputConfig {

+ 0 - 11
client/src/utils/Common.ts

@@ -38,17 +38,6 @@ export const throwErrorForDev = (text: string) => {
   throw new Error(text);
 };
 
-/**
- *
- * @param obj key value pair Array
- * @param key the target you want to find.
- * @returns undefined | string
- */
-export const findKeyValue = (
-  obj: { key: string; value: string }[],
-  key: string
-) => obj.find(v => v.key === key)?.value;
-
 export const generateHashCode = (source: string) => {
   let hash = 0,
     i,

+ 8 - 38
client/src/utils/Format.ts

@@ -5,7 +5,7 @@ import {
   DataTypeEnum,
 } from '@/consts';
 import { CreateFieldType, CreateField } from '@/pages/collections/Types';
-import { FieldHttp } from '@/http';
+import { FieldObject } from '@server/types';
 
 /**
  * transform large capacity to capacity in b.
@@ -76,23 +76,6 @@ export const getEnumKeyByValue = (enumObj: any, enumValue: any) => {
   return '--';
 };
 
-/**
- *
- * @param obj e.g. {name: 'test'}
- * @returns key value pair, e.g. [{key: 'name', value: 'test'}]
- */
-export const getKeyValuePairFromObj = (
-  obj: { [key in string]: any }
-): { key: string; value: any }[] => {
-  const pairs: { key: string; value: string }[] = Object.entries(obj).map(
-    ([key, value]) => ({
-      key,
-      value: value as string,
-    })
-  );
-  return pairs;
-};
-
 /**
  * @param pairs e.g. [{key: 'key', value: 'value'}]
  * @returns object, e.g. {key: value}
@@ -107,19 +90,6 @@ export const getObjFromKeyValuePair = (
   return obj;
 };
 
-export const getKeyValueListFromJsonString = (
-  json: string
-): { key: string; value: string }[] => {
-  try {
-    const obj = JSON.parse(json);
-    const pairs = getKeyValuePairFromObj(obj);
-
-    return pairs;
-  } catch (err) {
-    throw err;
-  }
-};
-
 // BinarySubstructure includes Superstructure and Substructure
 export const checkIsBinarySubstructure = (metricLabel: string): boolean => {
   return metricLabel === 'Superstructure' || metricLabel === 'Substructure';
@@ -232,16 +202,16 @@ export const formatUtcToMilvus = (bigNumber: number) => {
  * @param bigNumber
  * @returns
  */
-export const formatFieldType = (field: FieldHttp) => {
-  const { fieldType, element_type, maxLength, maxCapacity, dimension } = field;
+export const formatFieldType = (field: FieldObject) => {
+  const { data_type, element_type, maxLength, maxCapacity, dimension } = field;
 
   const elementType =
     element_type !== 'None'
-      ? `<${element_type}${maxLength ? `(${maxLength})` : ''}>`
+      ? `<${element_type}${maxLength !== -1 ? `(${maxLength})` : ''}>`
       : '';
-  const maxCap = maxCapacity ? `[${maxCapacity}]` : '';
-  const dim = dimension ? `(${dimension})` : '';
-  const maxLn = fieldType === 'VarChar' ? `(${maxLength})` : '';
+  const maxCap = maxCapacity !== -1 ? `[${maxCapacity}]` : '';
+  const dim = dimension !== -1 ? `(${dimension})` : '';
+  const maxLn = data_type === 'VarChar' ? `(${maxLength})` : '';
 
-  return `${fieldType}${elementType}${maxCap}${dim}${maxLn}`;
+  return `${data_type}${elementType}${maxCap}${dim}${maxLn}`;
 };

+ 4 - 75
client/src/utils/search.ts

@@ -1,85 +1,14 @@
-import { Field } from '../components/advancedSearch/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';
+import { FieldObject } from '@server/types';
 
-/**
- * function to get EmbeddingType
- * @param fieldType only vector type fields: 'BinaryVector' or 'FloatVector'
- */
-export const getEmbeddingType = (
-  fieldType: DataTypeStringEnum
-): DataTypeEnum.BinaryVector | DataTypeEnum.FloatVector => {
-  const type =
-    fieldType === 'BinaryVector'
-      ? DataTypeEnum.BinaryVector
-      : DataTypeEnum.FloatVector;
-  return type;
-};
-
-/**
- * function to get default index type according to embedding type
- * use FLAT as default float index type, BIN_FLAT as default binary index type
- * @param embeddingType float or binary
- * @returns index type
- */
-export const getDefaultIndexType = (embeddingType: DataTypeEnum): IndexType => {
-  const defaultIndexType =
-    embeddingType === DataTypeEnum.FloatVector
-      ? INDEX_TYPES_ENUM.FLAT
-      : INDEX_TYPES_ENUM.BIN_FLAT;
-  return defaultIndexType;
-};
-
-/**
- * funtion to divide fields into two categories: vector or nonVector
- */
-export const classifyFields = (
-  fields: FieldHttp[]
-): { vectorFields: FieldHttp[]; nonVectorFields: FieldHttp[] } => {
-  const vectorTypes: DataTypeStringEnum[] = [
-    DataTypeStringEnum.BinaryVector,
-    DataTypeStringEnum.FloatVector,
-  ];
-  return fields.reduce(
-    (result, cur) => {
-      const changedFieldType = vectorTypes.includes(cur.fieldType)
-        ? 'vectorFields'
-        : 'nonVectorFields';
-
-      result[changedFieldType].push(cur);
-
-      return result;
-    },
-    { vectorFields: [] as FieldHttp[], nonVectorFields: [] as FieldHttp[] }
-  );
-};
-
-export const getVectorFieldOptions = (
-  fields: FieldHttp[],
-  indexes: MilvusIndex[]
-): FieldOption[] => {
+export const getVectorFieldOptions = (fields: FieldObject[]): FieldOption[] => {
   const options: FieldOption[] = fields.map(f => {
-    const embeddingType = getEmbeddingType(f.fieldType);
-    const defaultIndex = getDefaultIndexType(embeddingType);
-    const index = indexes.find(i => i.field_name === f.name);
-
     return {
-      label: `${f.name} (${index?.indexType || defaultIndex})`,
+      field: f,
+      label: f.name,
       value: f.name,
-      fieldType: f.fieldType,
-      indexInfo: index || null,
-      dimension: Number(f.dimension),
     };
   });
 
   return options;
 };
-
-export const getNonVectorFieldsForFilter = (fields: FieldHttp[]): Field[] => {
-  return fields.map(f => ({
-    name: f.name,
-    type: f.fieldType,
-  }));
-};

+ 3 - 2
server/src/app.ts

@@ -21,7 +21,7 @@ import {
 } from './middleware';
 import { CLIENT_TTL } from './utils';
 import { getIp } from './utils/Network';
-import { DescribeIndexResponse, MilvusClient } from './types';
+import { DescribeIndexRes, MilvusClient } from './types';
 import { initWebSocket } from './socket';
 
 // initialize express app
@@ -33,7 +33,8 @@ export const clientCache = new LRUCache<
   {
     milvusClient: MilvusClient;
     address: string;
-    indexCache: LRUCache<string, DescribeIndexResponse>;
+    indexCache: LRUCache<string, DescribeIndexRes>;
+    database: string;
   }
 >({
   ttl: CLIENT_TTL,

+ 32 - 70
server/src/collections/collections.controller.ts

@@ -28,49 +28,55 @@ export class CollectionController {
   }
 
   generateRoutes() {
+    // get all collections
     this.router.get('/', this.showCollections.bind(this));
+    // get all collections statistics
+    this.router.get('/statistics', this.getStatistics.bind(this));
+
+    // get collection with index info
+    this.router.get('/:name', this.describeCollection.bind(this));
+    // get count
+    this.router.get('/:name/count', this.count.bind(this));
+    // create collection
     this.router.post(
       '/',
       dtoValidationMiddleware(CreateCollectionDto),
       this.createCollection.bind(this)
     );
-    this.router.get('/statistics', this.getStatistics.bind(this));
-    this.router.get(
-      '/:name/statistics',
-      this.getCollectionStatistics.bind(this)
-    );
-    this.router.get(
-      '/indexes/status',
-      this.getCollectionsIndexStatus.bind(this)
-    );
+    // drop collection
     this.router.delete('/:name', this.dropCollection.bind(this));
+    // rename collection
     this.router.post(
       '/:name',
       dtoValidationMiddleware(RenameCollectionDto),
       this.renameCollection.bind(this)
     );
+    // duplicate collection
     this.router.post(
       '/:name/duplicate',
       dtoValidationMiddleware(DuplicateCollectionDto),
       this.duplicateCollection.bind(this)
     );
-    this.router.delete('/:name/alias/:alias', this.dropAlias.bind(this));
-    // collection with index info
-    this.router.get('/:name', this.describeCollection.bind(this));
-    // just collection info
-    this.router.get('/:name/info', this.getCollectionInfo.bind(this));
-    this.router.get('/:name/count', this.count.bind(this));
+
+    // get collection statistics
+    this.router.get(
+      '/:name/statistics',
+      this.getCollectionStatistics.bind(this)
+    );
 
     // load / release
     this.router.put('/:name/load', this.loadCollection.bind(this));
     this.router.put('/:name/release', this.releaseCollection.bind(this));
     this.router.put('/:name/empty', this.empty.bind(this));
 
+    // insert data
     this.router.post(
       '/:name/insert',
       dtoValidationMiddleware(InsertDataDto),
       this.insert.bind(this)
     );
+
+    // insert sample data
     this.router.post(
       '/:name/importSample',
       dtoValidationMiddleware(ImportSampleDto),
@@ -83,16 +89,20 @@ export class CollectionController {
       dtoValidationMiddleware(VectorSearchDto),
       this.vectorSearch.bind(this)
     );
+    // query
     this.router.post(
       '/:name/query',
       dtoValidationMiddleware(QueryDto),
       this.query.bind(this)
     );
+    // create alias
     this.router.post(
       '/:name/alias',
       dtoValidationMiddleware(CreateAliasDto),
       this.createAlias.bind(this)
     );
+    // drop alias
+    this.router.delete('/:name/alias/:alias', this.dropAlias.bind(this));
 
     // segments
     this.router.get('/:name/psegments', this.getPSegment.bind(this));
@@ -191,9 +201,7 @@ export class CollectionController {
     try {
       const result = await this.collectionsService.getAllCollections(
         req.clientId,
-        {
-          data: [{ name }],
-        }
+        name
       );
       res.send(result[0]);
     } catch (error) {
@@ -201,28 +209,6 @@ export class CollectionController {
     }
   }
 
-  async getCollectionInfo(req: Request, res: Response, next: NextFunction) {
-    const name = req.params?.name;
-    const params = {
-      collection_name: name,
-    };
-    try {
-      const result = await this.collectionsService.describeCollection(
-        req.clientId,
-        params
-      );
-
-      const loadState = await this.collectionsService.getLoadState(
-        req.clientId,
-        params
-      );
-
-      res.send({ ...result, state: loadState.state });
-    } catch (error) {
-      next(error);
-    }
-  }
-
   async getCollectionStatistics(
     req: Request,
     res: Response,
@@ -242,21 +228,6 @@ export class CollectionController {
     }
   }
 
-  async getCollectionsIndexStatus(
-    req: Request,
-    res: Response,
-    next: NextFunction
-  ) {
-    try {
-      const result = await this.collectionsService.getCollectionsIndexStatus(
-        req.clientId
-      );
-      res.send(result);
-    } catch (error) {
-      next(error);
-    }
-  }
-
   async loadCollection(req: Request, res: Response, next: NextFunction) {
     const collection_name = req.params?.name;
     const data = req.body;
@@ -421,7 +392,7 @@ export class CollectionController {
           collectionName: name,
         }
       );
-      res.send(result);
+      res.send(result.infos);
     } catch (error) {
       next(error);
     }
@@ -436,7 +407,7 @@ export class CollectionController {
           collectionName: name,
         }
       );
-      res.send(result);
+      res.send(result.infos);
     } catch (error) {
       next(error);
     }
@@ -457,20 +428,11 @@ export class CollectionController {
   async count(req: Request, res: Response, next: NextFunction) {
     const name = req.params?.name;
     try {
-      const { value } = await this.collectionsService.hasCollection(
-        req.clientId,
-        {
-          collection_name: name,
-        }
-      );
-      let result: any = '';
-      if (value) {
-        result = await this.collectionsService.count(req.clientId, {
-          collection_name: name,
-        });
-      }
+      const result = await this.collectionsService.count(req.clientId, {
+        collection_name: name,
+      });
 
-      res.send({ collection_name: name, rowCount: result });
+      res.send(result);
     } catch (error) {
       next(error);
     }

+ 191 - 105
server/src/collections/collections.service.ts

@@ -20,8 +20,8 @@ import {
   CompactReq,
   HasCollectionReq,
   CountReq,
-  FieldSchema,
   GetLoadStateReq,
+  CollectionData,
 } from '@zilliz/milvus2-sdk-node';
 import { Parser } from '@json2csv/plainjs';
 import {
@@ -30,11 +30,21 @@ import {
   genRows,
   ROW_COUNT,
   convertFieldSchemaToFieldType,
+  LOADING_STATE,
 } from '../utils';
 import { QueryDto, ImportSampleDto, GetReplicasDto } from './dto';
-import { CollectionData } from '../types';
+import {
+  CollectionObject,
+  CollectionLazyObject,
+  FieldObject,
+  IndexObject,
+  DescribeCollectionRes,
+  CountObject,
+  StatisticsObject,
+} from '../types';
 import { SchemaService } from '../schema/schema.service';
 import { clientCache } from '../app';
+import { MIN_INT64 } from '../utils/Const';
 
 export class CollectionsService {
   private schemaService: SchemaService;
@@ -43,7 +53,7 @@ export class CollectionsService {
     this.schemaService = new SchemaService();
   }
 
-  async getCollections(clientId: string, data?: ShowCollectionsReq) {
+  async showCollections(clientId: string, data?: ShowCollectionsReq) {
     const { milvusClient } = clientCache.get(clientId);
     const res = await milvusClient.showCollections(data);
     throwErrorFromSDK(res.status);
@@ -59,8 +69,60 @@ export class CollectionsService {
 
   async describeCollection(clientId: string, data: DescribeCollectionReq) {
     const { milvusClient } = clientCache.get(clientId);
-    const res = await milvusClient.describeCollection(data);
+    const res = (await milvusClient.describeCollection(
+      data
+    )) as DescribeCollectionRes;
+
+    // get index info for collections
+    const indexRes = await this.schemaService.describeIndex(clientId, {
+      collection_name: data.collection_name,
+    });
+
     throwErrorFromSDK(res.status);
+
+    const vectorFields: FieldObject[] = [];
+    const scalarFields: FieldObject[] = [];
+
+    // append index info to each field
+    res.schema.fields.forEach((field: FieldObject) => {
+      // add index
+      field.index = indexRes.index_descriptions.find(
+        index => index.field_name === field.name
+      ) as IndexObject;
+      // add dimension
+      field.dimension =
+        Number(field.type_params.find(item => item.key === 'dim')?.value) || -1;
+      // add max capacity
+      field.maxCapacity =
+        Number(
+          field.type_params.find(item => item.key === 'max_capacity')?.value
+        ) || -1;
+      // add max length
+      field.maxLength =
+        Number(
+          field.type_params.find(item => item.key === 'max_length')?.value
+        ) || -1;
+
+      // classify fields
+      if (
+        field.data_type === 'BinaryVector' ||
+        field.data_type === 'FloatVector'
+      ) {
+        vectorFields.push(field);
+      } else {
+        scalarFields.push(field);
+      }
+
+      if (field.is_primary_key) {
+        res.schema.primaryField = field;
+      }
+    });
+
+    // add extra data to schema
+    res.schema.hasVectorIndex = vectorFields.some(v => v.index);
+    res.schema.scalarFields = scalarFields;
+    res.schema.vectorFields = vectorFields;
+
     return res;
   }
 
@@ -122,7 +184,7 @@ export class CollectionsService {
       );
       count = collectionStatisticsRes.data.row_count;
     }
-    return count;
+    return { rowCount: Number(count) } as CountObject;
   }
 
   async insert(clientId: string, data: InsertReq) {
@@ -194,94 +256,138 @@ export class CollectionsService {
     return res;
   }
 
-  /**
-   * Get all collections meta data
-   * @returns {id:string, collection_name:string, schema:Field[], autoID:boolean, rowCount: number, consistency_level:string}
-   */
-  async getAllCollections(
+  // get single collection details
+  async getCollection(
     clientId: string,
-    collections?: {
-      data: { name: string }[];
+    collection: CollectionData,
+    loadCollection: CollectionData,
+    lazy: boolean = false
+  ) {
+    if (lazy) {
+      return {
+        id: collection.id,
+        collection_name: collection.name,
+        createdTime: Number(collection.timestamp),
+        schema: undefined,
+        rowCount: undefined,
+        aliases: undefined,
+        description: undefined,
+        autoID: undefined,
+        loadedPercentage: undefined,
+        consistency_level: undefined,
+        replicas: undefined,
+        loaded: undefined,
+      } as CollectionLazyObject;
     }
-  ): Promise<CollectionData[]> {
-    const data: CollectionData[] = [];
-    const res = collections || (await this.getCollections(clientId));
-    const loadedCollections = await this.getCollections(clientId, {
-      type: ShowCollectionsType.Loaded,
+    // get collection schema and properties
+    const collectionInfo = await this.describeCollection(clientId, {
+      collection_name: collection.name,
     });
-    if (res.data.length > 0) {
-      for (const item of res.data) {
-        const { name } = item;
 
-        // get collection schema and properties
-        const collectionInfo = await this.describeCollection(clientId, {
-          collection_name: name,
-        });
+    // get collection statistic data
+    const collectionStatisticsRes = await this.getCollectionStatistics(
+      clientId,
+      {
+        collection_name: collection.name,
+      }
+    );
+    // extract autoID
+    const autoID = collectionInfo.schema.fields.find(
+      v => v.is_primary_key === true
+    )?.autoID;
 
-        // get collection statistic data
-        const collectionStatisticsRes = await this.getCollectionStatistics(
-          clientId,
-          {
-            collection_name: name,
-          }
-        );
+    // get replica info
+    let replicas;
+    try {
+      replicas = loadCollection
+        ? await this.getReplicas(clientId, {
+            collectionID: collectionInfo.collectionID,
+          })
+        : replicas;
+    } catch (e) {
+      console.log('ignore getReplica');
+    }
 
-        // get index info for collections
-        const indexRes = await this.schemaService.describeIndex(clientId, {
-          collection_name: item.name,
-        });
+    // loading info
+    const loadedPercentage = !loadCollection
+      ? '-1'
+      : loadCollection.loadedPercentage;
+
+    const status =
+      loadedPercentage === '-1'
+        ? LOADING_STATE.UNLOADED
+        : loadedPercentage === '100'
+        ? LOADING_STATE.LOADED
+        : LOADING_STATE.LOADING;
+
+    return {
+      collection_name: collection.name,
+      schema: collectionInfo.schema,
+      rowCount: Number(collectionStatisticsRes.data.row_count),
+      createdTime: parseInt(collectionInfo.created_utc_timestamp, 10),
+      aliases: collectionInfo.aliases,
+      description: collectionInfo.schema.description,
+      autoID,
+      id: collectionInfo.collectionID,
+      loadedPercentage,
+      consistency_level: collectionInfo.consistency_level,
+      replicas: replicas && replicas.replicas,
+      loaded: status === LOADING_STATE.LOADED,
+      status,
+    };
+  }
 
-        // extract autoID
-        const autoID = collectionInfo.schema.fields.find(
-          v => v.is_primary_key === true
-        )?.autoID;
+  async getAllCollections(
+    clientId: string,
+    collectionName?: string
+  ): Promise<CollectionObject[]> {
+    // get all collections(name, timestamp, id)
+    const allCollections = await this.showCollections(clientId);
+    // get all loaded collection
+    const loadedCollections = await this.showCollections(clientId, {
+      type: ShowCollectionsType.Loaded,
+    });
 
-        // if it is loaded
-        const loadCollection = loadedCollections.data.find(
-          v => v.name === name
-        );
+    // data container
+    const data: CollectionObject[] = [];
+    // sort by created time
+    allCollections.data.sort(
+      (a, b) => Number(b.timestamp) - Number(a.timestamp)
+    );
 
-        // loading info
-        const loadedPercentage = !loadCollection
-          ? '-1'
-          : loadCollection.loadedPercentage;
-
-        // get replica info
-        let replicas;
-        try {
-          replicas = loadCollection
-            ? await this.getReplicas(clientId, {
-                collectionID: collectionInfo.collectionID,
-              })
-            : replicas;
-        } catch (e) {
-          console.log('ignore getReplica');
-        }
+    // get single collection details
+    const targetCollections = allCollections.data.find(
+      d => d.name === collectionName
+    );
+    if (targetCollections) {
+      const res = await this.getCollection(
+        clientId,
+        targetCollections,
+        loadedCollections.data.find(v => v.name === targetCollections.name),
+        false
+      );
+      return [res];
+    }
 
-        data.push({
-          aliases: collectionInfo.aliases,
-          collection_name: name,
-          schema: collectionInfo.schema,
-          description: collectionInfo.schema.description,
-          autoID,
-          rowCount: Number(collectionStatisticsRes.data.row_count),
-          id: collectionInfo.collectionID,
-          loadedPercentage,
-          createdTime: parseInt(collectionInfo.created_utc_timestamp, 10),
-          index_descriptions: indexRes.index_descriptions,
-          consistency_level: collectionInfo.consistency_level,
-          replicas: replicas && replicas.replicas,
-        });
-      }
+    // get all collection details
+    for (let i = 0; i < allCollections.data.length; i++) {
+      const collection = allCollections.data[i];
+      data.push(
+        await this.getCollection(
+          clientId,
+          collection,
+          loadedCollections.data.find(v => v.name === collection.name),
+          false
+        )
+      );
     }
-    // add default sort - Descending order
-    data.sort((a, b) => b.createdTime - a.createdTime);
+
     return data;
   }
 
   async getLoadedCollections(clientId: string) {
     const data = [];
-    const res = await this.getCollections(clientId, {
+    const res = await this.showCollections(clientId, {
       type: ShowCollectionsType.Loaded,
     });
     if (res.data.length > 0) {
@@ -308,8 +414,8 @@ export class CollectionsService {
     const data = {
       collectionCount: 0,
       totalData: 0,
-    };
-    const res = await this.getCollections(clientId);
+    } as StatisticsObject;
+    const res = await this.showCollections(clientId);
     data.collectionCount = res.data.length;
     if (res.data.length > 0) {
       for (const item of res.data) {
@@ -326,27 +432,6 @@ export class CollectionsService {
     return data;
   }
 
-  /**
-   * Get all collection index status
-   * @returns {collection_name:string, index_descriptions: index_descriptions}[]
-   */
-  async getCollectionsIndexStatus(clientId: string) {
-    const data = [];
-    const res = await this.getCollections(clientId);
-    if (res.data.length > 0) {
-      for (const item of res.data) {
-        const indexRes = await this.schemaService.describeIndex(clientId, {
-          collection_name: item.name,
-        });
-        data.push({
-          collection_name: item.name,
-          index_descriptions: indexRes,
-        });
-      }
-    }
-    return data;
-  }
-
   /**
    * Load sample data into collection
    */
@@ -416,19 +501,19 @@ export class CollectionsService {
   }
 
   async duplicateCollection(clientId: string, data: RenameCollectionReq) {
-    const collection: any = await this.describeCollection(clientId, {
+    const collection = await this.describeCollection(clientId, {
       collection_name: data.collection_name,
     });
 
     const createCollectionParams: CreateCollectionReq = {
       collection_name: data.new_collection_name,
       fields: collection.schema.fields.map(convertFieldSchemaToFieldType),
-      consistency_level: collection.consistency_level,
-      enable_dynamic_field: !!collection.enable_dynamic_field,
+      consistency_level: collection.consistency_level as any,
+      enable_dynamic_field: !!collection.schema.enable_dynamic_field,
     };
 
     if (
-      collection.schema.fields.some((f: FieldSchema) => f.is_partition_key) &&
+      collection.schema.fields.some(f => f.is_partition_key) &&
       collection.num_partitions
     ) {
       createCollectionParams.num_partitions = Number(collection.num_partitions);
@@ -444,7 +529,8 @@ export class CollectionsService {
 
     const res = await milvusClient.deleteEntities({
       collection_name: data.collection_name,
-      filter: pkType === 'Int64' ? `${pkField} >= 0` : `${pkField} != ''`,
+      filter:
+        pkType === 'Int64' ? `${pkField} >= ${MIN_INT64}` : `${pkField} != ''`,
     });
 
     return res;

+ 0 - 15
server/src/milvus/milvus.controller.ts

@@ -30,7 +30,6 @@ export class MilvusController {
       this.useDatabase.bind(this)
     );
     this.router.post('/disconnect', this.closeConnection.bind(this));
-    this.router.get('/check', this.checkConnect.bind(this));
     this.router.put(
       '/flush',
       dtoValidationMiddleware(FlushDto),
@@ -58,20 +57,6 @@ export class MilvusController {
     }
   }
 
-  async checkConnect(req: Request, res: Response, next: NextFunction) {
-    const address = '' + req.query?.address;
-
-    try {
-      const result = await this.milvusService.checkConnect(
-        req.clientId,
-        address
-      );
-      res.send(result);
-    } catch (error) {
-      next(error);
-    }
-  }
-
   async flush(req: Request, res: Response, next: NextFunction) {
     const collectionNames = req.body;
     try {

+ 10 - 7
server/src/milvus/milvus.service.ts

@@ -3,13 +3,13 @@ import {
   FlushReq,
   GetMetricsResponse,
   ClientConfig,
-  DescribeIndexResponse,
 } from '@zilliz/milvus2-sdk-node';
 import { LRUCache } from 'lru-cache';
 import { DEFAULT_MILVUS_PORT, INDEX_TTL } from '../utils';
 import { connectivityState } from '@grpc/grpc-js';
 import { DatabasesService } from '../database/databases.service';
 import { clientCache } from '../app';
+import { DescribeIndexRes } from '../types';
 
 export class MilvusService {
   private databaseService: DatabasesService;
@@ -95,10 +95,11 @@ export class MilvusService {
       clientCache.set(milvusClient.clientId, {
         milvusClient,
         address,
-        indexCache: new LRUCache<string, DescribeIndexResponse>({
+        indexCache: new LRUCache<string, DescribeIndexRes>({
           ttl: INDEX_TTL,
           ttlAutopurge: true,
         }),
+        database: database,
       });
 
       await this.databaseService.use(milvusClient.clientId, database);
@@ -116,11 +117,6 @@ export class MilvusService {
     }
   }
 
-  async checkConnect(clientId: string, address: string) {
-    const milvusAddress = MilvusService.formatAddress(address);
-    return { connected: clientCache.has(milvusAddress) };
-  }
-
   async flush(clientId: string, data: FlushReq) {
     const { milvusClient } = clientCache.get(clientId);
 
@@ -141,6 +137,9 @@ export class MilvusService {
     const { milvusClient } = clientCache.get(clientId);
 
     const res = milvusClient.closeConnection();
+    // clear cache on disconnect
+    clientCache.delete(milvusClient.clientId);
+
     return res;
   }
 
@@ -150,6 +149,10 @@ export class MilvusService {
     const res = milvusClient.use({
       db_name: db,
     });
+
+    // update the database in the cache
+    const cache = clientCache.get(clientId);
+    cache.database = db;
     return res;
   }
 }

+ 4 - 2
server/src/schema/schema.controller.ts

@@ -2,6 +2,7 @@ import { NextFunction, Request, Response, Router } from 'express';
 import { dtoValidationMiddleware } from '../middleware/validation';
 import { SchemaService } from './schema.service';
 import { ManageIndexDto } from './dto';
+import { DescribeIndexRes } from '../types';
 
 export class SchemaController {
   private router: Router;
@@ -51,9 +52,10 @@ export class SchemaController {
   async describeIndex(req: Request, res: Response, next: NextFunction) {
     const data = '' + req.query?.collection_name;
     try {
-      const result = await this.schemaService.describeIndex(req.clientId, {
+      const result = (await this.schemaService.describeIndex(req.clientId, {
         collection_name: data,
-      });
+      })) as DescribeIndexRes;
+
       res.send(result);
     } catch (error) {
       next(error);

+ 30 - 22
server/src/schema/schema.service.ts

@@ -2,16 +2,17 @@ import {
   CreateIndexReq,
   DescribeIndexReq,
   DropIndexReq,
-  DescribeIndexResponse,
 } from '@zilliz/milvus2-sdk-node';
 import { throwErrorFromSDK } from '../utils/Error';
 import { clientCache } from '../app';
+import { DescribeIndexRes } from '../types';
+import { getKeyValueListFromJsonString, findKeyValue } from '../utils';
 
 export class SchemaService {
   async createIndex(clientId: string, data: CreateIndexReq) {
-    const { milvusClient, indexCache } = clientCache.get(clientId);
+    const { milvusClient, indexCache, database } = clientCache.get(clientId);
     const res = await milvusClient.createIndex(data);
-    const key = data.collection_name;
+    const key = `${database}/${data.collection_name}`;
 
     // clear cache;
     indexCache.delete(key);
@@ -29,33 +30,40 @@ export class SchemaService {
    * @returns - The response from the Milvus SDK's describeIndex function or the cached index description.
    */
   async describeIndex(clientId: string, data: DescribeIndexReq) {
-    const { milvusClient, indexCache } = clientCache.get(clientId);
+    const { milvusClient, indexCache, database } = clientCache.get(clientId);
 
     // Get the collection name from the request data
-    const key = data.collection_name;
+    const key = `${database}/${data.collection_name}`;
 
     // Try to get the index description from the cache
-    const value: DescribeIndexResponse = indexCache.get(key);
+    const value = indexCache.get(key);
 
     // If the index description is in the cache, return it
     if (value) {
-      return value;
+      return value as DescribeIndexRes;
     } else {
       // If the index description is not in the cache, call the Milvus SDK's describeIndex function
-      const res = await milvusClient.describeIndex(data);
+      const res = (await milvusClient.describeIndex(data)) as DescribeIndexRes;
 
-      // If the index is finished building and there is at least one index description,
-      // cache the index description for future use
-      if (
-        (res.index_descriptions?.length > 0 &&
-          res.index_descriptions.every(i => i.state === 'Finished')) ||
-        res.index_descriptions.length === 0
-      ) {
-        indexCache.set(key, res);
-      } else {
-        // If the index is not finished building, delete any cached value for this index
-        indexCache.delete(key);
-      }
+      res.index_descriptions.map(index => {
+        // get indexType
+        index.indexType = (index.params.find(p => p.key === 'index_type')
+          ?.value || '') as string;
+        // get metricType
+        const metricTypePair =
+          index.params.filter(v => v.key === 'metric_type') || [];
+        index.metricType = findKeyValue(
+          metricTypePair,
+          'metric_type'
+        ) as string;
+        // get index parameter pairs
+        const paramsJSONstring = findKeyValue(index.params, 'params'); // params is a json string
+        const params =
+          (paramsJSONstring &&
+            getKeyValueListFromJsonString(paramsJSONstring as string)) ||
+          [];
+        index.indexParameterPairs = [...metricTypePair, ...params];
+      });
 
       // Return the response from the Milvus SDK's describeIndex function
       return res;
@@ -63,10 +71,10 @@ export class SchemaService {
   }
 
   async dropIndex(clientId: string, data: DropIndexReq) {
-    const { milvusClient, indexCache } = clientCache.get(clientId);
+    const { milvusClient, indexCache, database } = clientCache.get(clientId);
 
     const res = await milvusClient.dropIndex(data);
-    const key = data.collection_name;
+    const key = `${database}/${data.collection_name}`;
 
     // clear cache;
     indexCache.delete(key);

+ 72 - 4
server/src/types/collections.type.ts

@@ -1,12 +1,49 @@
 import {
   IndexDescription,
   CollectionSchema,
+  FieldSchema,
   ReplicaInfo,
+  KeyValuePair,
+  DescribeIndexResponse,
+  DescribeCollectionResponse,
+  QuerySegmentInfo,
+  PersistentSegmentInfo,
 } from '@zilliz/milvus2-sdk-node';
 
-export interface CollectionData {
+import { LOADING_STATE } from '../utils';
+
+export interface IndexObject extends IndexDescription {
+  indexType: string;
+  metricType: string;
+  indexParameterPairs: KeyValuePair[];
+}
+export interface FieldObject extends FieldSchema {
+  index: IndexObject;
+  // field type params
+  dimension: number;
+  maxCapacity: number;
+  maxLength: number;
+}
+
+export interface SchemaObject extends CollectionSchema {
+  fields: FieldObject[];
+  primaryField: FieldObject;
+  vectorFields: FieldObject[];
+  scalarFields: FieldObject[];
+  hasVectorIndex: boolean;
+}
+
+export interface DescribeCollectionRes extends DescribeCollectionResponse {
+  schema: SchemaObject;
+}
+
+export interface DescribeIndexRes extends DescribeIndexResponse {
+  index_descriptions: IndexObject[];
+}
+
+export type CollectionFullObject = {
   collection_name: string;
-  schema: CollectionSchema;
+  schema: SchemaObject;
   rowCount: number | string;
   createdTime: number;
   aliases: string[];
@@ -14,7 +51,38 @@ export interface CollectionData {
   autoID: boolean;
   id: string;
   loadedPercentage: string;
-  index_descriptions: IndexDescription[];
   consistency_level: string;
   replicas: ReplicaInfo[];
-}
+  status: LOADING_STATE;
+  loaded: boolean;
+};
+
+export type CollectionLazyObject = {
+  id: string;
+  collection_name: string;
+  status: LOADING_STATE;
+  schema: undefined;
+  rowCount: undefined;
+  createdTime: number;
+  aliases: undefined;
+  description: undefined;
+  autoID: undefined;
+  loadedPercentage: undefined;
+  consistency_level: undefined;
+  replicas: undefined;
+  loaded: undefined;
+};
+
+export type CollectionObject = CollectionFullObject | CollectionLazyObject;
+
+export type CountObject = {
+  rowCount: number;
+};
+
+export type StatisticsObject = {
+  collectionCount: number;
+  totalData: number;
+};
+
+export type QuerySegmentObjects = QuerySegmentInfo[];
+export type PersistentSegmentObjects = PersistentSegmentInfo[];

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

@@ -1,15 +1,6 @@
 export {
-  IndexDescription,
-  CollectionSchema,
-  ReplicaInfo,
-  FieldSchema,
   KeyValuePair,
   ShowCollectionsType,
-  GetQuerySegmentInfoResponse,
-  QuerySegmentInfo,
-  GePersistentSegmentInfoResponse,
-  PersistentSegmentInfo,
-  DescribeIndexResponse,
   MilvusClient,
 } from '@zilliz/milvus2-sdk-node';
 

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

@@ -10,12 +10,6 @@ export const INDEX_CACHE = 'index_cache';
 export const CLIENT_TTL = 1000 * 60 * 60 * 24;
 export const INDEX_TTL = 1000 * 60 * 60;
 
-export enum LOADING_STATE {
-  LOADED,
-  LOADING,
-  UNLOADED,
-}
-
 export enum WS_EVENTS {
   REGISTER = 'REGISTER',
   COLLECTION = 'COLLECTION',
@@ -153,3 +147,11 @@ export const Privileges = {
   ...UserPrivileges,
   ...GlobalPrivileges,
 };
+
+export enum LOADING_STATE {
+  LOADED = 'loaded',
+  LOADING = 'loading',
+  UNLOADED = 'unloaded',
+}
+
+export const MIN_INT64 = `-9223372036854775807`; // safe int64 min value

+ 28 - 0
server/src/utils/Helper.ts

@@ -127,3 +127,31 @@ export const convertFieldSchemaToFieldType = (fieldSchema: FieldSchema) => {
 
   return fieldType;
 };
+
+/**
+ *
+ * @param obj e.g. {name: 'test'}
+ * @returns key value pair, e.g. [{key: 'name', value: 'test'}]
+ */
+export const getKeyValuePairFromObj = (obj: {
+  [key in string]: any;
+}): KeyValuePair[] => {
+  const pairs: { key: string; value: string }[] = Object.entries(obj).map(
+    ([key, value]) => ({
+      key,
+      value: value as string,
+    })
+  );
+  return pairs;
+};
+
+export const getKeyValueListFromJsonString = (json: string): KeyValuePair[] => {
+  try {
+    const obj = JSON.parse(json);
+    const pairs = getKeyValuePairFromObj(obj);
+
+    return pairs;
+  } catch (err) {
+    throw err;
+  }
+};