From 0a0578b520515aff2130f5778720dd4b91b7360d Mon Sep 17 00:00:00 2001 From: shanghaikid Date: Thu, 31 Aug 2023 18:14:41 +0800 Subject: [PATCH 1/2] segments view Signed-off-by: shanghaikid --- client/src/http/Collection.ts | 14 ++ client/src/i18n/en/collection.ts | 13 ++ client/src/pages/collections/Collection.tsx | 6 + client/src/pages/segments/Segments.tsx | 142 ++++++++++++++++++ client/src/pages/segments/Types.ts | 29 ++++ .../src/collections/collections.controller.ts | 30 ++++ server/src/collections/collections.service.ts | 21 +++ 7 files changed, 255 insertions(+) create mode 100644 client/src/pages/segments/Segments.tsx create mode 100644 client/src/pages/segments/Types.ts diff --git a/client/src/http/Collection.ts b/client/src/http/Collection.ts index 1633d8aa..50bdb916 100644 --- a/client/src/http/Collection.ts +++ b/client/src/http/Collection.ts @@ -97,6 +97,20 @@ export class CollectionHttp extends BaseModel implements CollectionView { return super.search({ path: this.COLLECTIONS_STATISTICS_URL, params: {} }); } + static getPSegments(collectionName: string) { + return super.search({ + path: `${this.COLLECTIONS_URL}/${collectionName}/psegments`, + params: {}, + }); + } + + static getQSegments(collectionName: string) { + return super.search({ + path: `${this.COLLECTIONS_URL}/${collectionName}/qsegments`, + params: {}, + }); + } + static insertData(collectionName: string, param: InsertDataParam) { return super.create({ path: `${this.COLLECTIONS_URL}/${collectionName}/insert`, diff --git a/client/src/i18n/en/collection.ts b/client/src/i18n/en/collection.ts index 2001d452..1f938303 100644 --- a/client/src/i18n/en/collection.ts +++ b/client/src/i18n/en/collection.ts @@ -89,6 +89,7 @@ const collectionTrans = { schemaTab: 'Schema', queryTab: 'Data Query', previewTab: 'Data Preview', + segmentsTab: 'Segments', startTip: 'Start your data query', dataQuerylimits: ' Please note that the maximum number of results for your data query is 16384.', @@ -100,6 +101,18 @@ const collectionTrans = { // rename dialog newColNamePlaceholder: 'New Collection Name', newNameInfo: 'Only numbers, letters, and underscores are allowed.', + + // segement + segements: 'Segments', + segPState: 'Persistent Segment State', + partitionID: 'Partition ID', + segmentID: 'Segment ID', + num_rows: 'Row Count', + q_nodeIds: 'Query Node IDs', + q_index_name: 'Index Name', + q_indexID: 'Index ID', + q_state: 'Query Segment State', + q_mem_size: 'Memory Size', }; export default collectionTrans; diff --git a/client/src/pages/collections/Collection.tsx b/client/src/pages/collections/Collection.tsx index 08c30216..03991b61 100644 --- a/client/src/pages/collections/Collection.tsx +++ b/client/src/pages/collections/Collection.tsx @@ -12,6 +12,8 @@ import { parseLocationSearch } from '@/utils'; import Schema from '../schema/Schema'; import Query from '../query/Query'; import Preview from '../preview/Preview'; +import Segments from '../segments/Segments'; + import { TAB_ENUM } from './Types'; const useStyles = makeStyles((theme: Theme) => ({ @@ -77,6 +79,10 @@ const Collection = () => { label: collectionTrans('queryTab'), component: , }, + { + label: collectionTrans('segmentsTab'), + component: , + }, ]; // exclude parititon on cloud diff --git a/client/src/pages/segments/Segments.tsx b/client/src/pages/segments/Segments.tsx new file mode 100644 index 00000000..55b3c389 --- /dev/null +++ b/client/src/pages/segments/Segments.tsx @@ -0,0 +1,142 @@ +import { useEffect, useState, FC } from 'react'; +import { useTranslation } from 'react-i18next'; +import { CollectionHttp } from '@/http'; +import { usePaginationHook } from '@/hooks'; +import AttuGrid from '@/components/grid/Grid'; +import { ColDefinitionsType } from '@/components/grid/Types'; +import { ToolBarConfig } from '@/components/grid/Types'; +import CustomToolBar from '@/components/grid/ToolBar'; +import { getQueryStyles } from '../query/Styles'; + +const Segments: FC<{ + collectionName: string; +}> = ({ collectionName }) => { + const classes = getQueryStyles(); + const [segments, setSegements] = useState([]); + const { t: collectionTrans } = useTranslation('collection'); + const [loading, setLoading] = useState(true); + + const fetchSegments = async () => { + setLoading(true); + + const psegments = await CollectionHttp.getPSegments(collectionName); + const qsegments = await CollectionHttp.getQSegments(collectionName); + + const combinedArray = psegments.infos.map((p: any) => { + const q = qsegments.infos.find((q: any) => q.segmentID === p.segmentID); + return { + ...p, + ...(q && + Object.keys(q).reduce((acc: any, key) => { + acc[`q_${key}`] = q[key]; + return acc; + }, {})), + }; + }); + + setSegements(combinedArray); + setLoading(false); + }; + + const toolbarConfigs: ToolBarConfig[] = [ + { + type: 'iconBtn', + onClick: () => { + fetchSegments(); + }, + label: collectionTrans('refresh'), + icon: 'refresh', + }, + ]; + + const colDefinitions: ColDefinitionsType[] = [ + { + id: 'segmentID', + align: 'left', + disablePadding: false, + label: collectionTrans('segmentID'), + }, + { + id: 'partitionID', + align: 'left', + disablePadding: false, + label: collectionTrans('partitionID'), + }, + { + id: 'state', + align: 'left', + disablePadding: false, + label: collectionTrans('segPState'), + }, + { + id: 'num_rows', + align: 'left', + disablePadding: false, + label: collectionTrans('num_rows'), + }, + { + id: 'q_nodeIds', + align: 'left', + disablePadding: false, + label: collectionTrans('q_nodeIds'), + }, + { + id: 'q_state', + align: 'left', + disablePadding: false, + label: collectionTrans('q_state'), + }, + { + id: 'q_index_name', + align: 'left', + disablePadding: false, + label: collectionTrans('q_index_name'), + }, + ]; + + useEffect(() => { + fetchSegments(); + }, []); + + const { + pageSize, + handlePageSize, + currentPage, + handleCurrentPage, + total, + data, + order, + orderBy, + handleGridSort, + } = usePaginationHook(segments); + + const handlePageChange = (e: any, page: number) => { + handleCurrentPage(page); + }; + + return ( +
+ + + +
+ ); +}; + +export default Segments; diff --git a/client/src/pages/segments/Types.ts b/client/src/pages/segments/Types.ts new file mode 100644 index 00000000..be1a365b --- /dev/null +++ b/client/src/pages/segments/Types.ts @@ -0,0 +1,29 @@ +import { ReactElement } from 'react'; +import { LOADING_STATE } from '@/consts'; +import { ManageRequestMethods } from '../../types/Common'; + +export interface PartitionData { + _id: string; + _name: string; + _status: LOADING_STATE; + _rowCount: string; + _formatName: string; +} + +export interface PartitionView extends PartitionData { + _nameElement?: ReactElement; + _statusElement?: ReactElement; +} + +// delete and create +export interface PartitionManageParam { + collectionName: string; + partitionName: string; + type: ManageRequestMethods; +} + +// load and release +export interface PartitionParam { + collectionName: string; + partitionNames: string[]; +} diff --git a/server/src/collections/collections.controller.ts b/server/src/collections/collections.controller.ts index 8e5fe43b..78341b2a 100644 --- a/server/src/collections/collections.controller.ts +++ b/server/src/collections/collections.controller.ts @@ -51,6 +51,8 @@ export class CollectionController { ); this.router.delete('/:name/alias/:alias', this.dropAlias.bind(this)); this.router.get('/:name', this.describeCollection.bind(this)); + + // load / release this.router.put('/:name/load', this.loadCollection.bind(this)); this.router.put('/:name/release', this.releaseCollection.bind(this)); this.router.post( @@ -81,6 +83,10 @@ export class CollectionController { this.createAlias.bind(this) ); + // segements + this.router.get('/:name/psegments', this.getPSegement.bind(this)); + this.router.get('/:name/qsegments', this.getQSegement.bind(this)); + return this.router; } @@ -327,4 +333,28 @@ export class CollectionController { next(error); } } + + async getPSegement(req: Request, res: Response, next: NextFunction) { + const name = req.params?.name; + try { + const result = await this.collectionsService.getPersistentSegmentInfo({ + collectionName: name, + }); + res.send(result); + } catch (error) { + next(error); + } + } + + async getQSegement(req: Request, res: Response, next: NextFunction) { + const name = req.params?.name; + try { + const result = await this.collectionsService.getQuerySegmentInfo({ + collectionName: name, + }); + res.send(result); + } catch (error) { + next(error); + } + } } diff --git a/server/src/collections/collections.service.ts b/server/src/collections/collections.service.ts index 863a2878..eb136978 100644 --- a/server/src/collections/collections.service.ts +++ b/server/src/collections/collections.service.ts @@ -16,6 +16,9 @@ import { ShowCollectionsReq, ShowCollectionsType, DeleteEntitiesReq, + GetCompactionStateReq, + GetQuerySegmentInfoReq, + GePersistentSegmentInfoReq, } from '@zilliz/milvus2-sdk-node'; import { throwErrorFromSDK } from '../utils/Error'; import { findKeyValue, genRows } from '../utils/Helper'; @@ -290,4 +293,22 @@ export class CollectionsService { return await this.insert({ collection_name, fields_data }); } + + async getCompactionState(data: GetCompactionStateReq) { + const res = await this.milvusService.client.getCompactionState(data); + throwErrorFromSDK(res.status); + return res; + } + + async getQuerySegmentInfo(data: GetQuerySegmentInfoReq) { + const res = await this.milvusService.client.getQuerySegmentInfo(data); + throwErrorFromSDK(res.status); + return res; + } + + async getPersistentSegmentInfo(data: GePersistentSegmentInfoReq) { + const res = await this.milvusService.client.getPersistentSegmentInfo(data); + throwErrorFromSDK(res.status); + return res; + } } From a34de1a85336ca99d4b54308d5a24c303c32b530 Mon Sep 17 00:00:00 2001 From: shanghaikid Date: Thu, 31 Aug 2023 18:33:18 +0800 Subject: [PATCH 2/2] add missing key Signed-off-by: shanghaikid --- client/src/pages/segments/Segments.tsx | 3 +- client/src/pages/segments/Types.ts | 46 ++++++++++---------------- 2 files changed, 19 insertions(+), 30 deletions(-) diff --git a/client/src/pages/segments/Segments.tsx b/client/src/pages/segments/Segments.tsx index 55b3c389..610bd434 100644 --- a/client/src/pages/segments/Segments.tsx +++ b/client/src/pages/segments/Segments.tsx @@ -7,12 +7,13 @@ import { ColDefinitionsType } from '@/components/grid/Types'; import { ToolBarConfig } from '@/components/grid/Types'; import CustomToolBar from '@/components/grid/ToolBar'; import { getQueryStyles } from '../query/Styles'; +import { Segment } from './Types'; const Segments: FC<{ collectionName: string; }> = ({ collectionName }) => { const classes = getQueryStyles(); - const [segments, setSegements] = useState([]); + const [segments, setSegements] = useState([]); const { t: collectionTrans } = useTranslation('collection'); const [loading, setLoading] = useState(true); diff --git a/client/src/pages/segments/Types.ts b/client/src/pages/segments/Types.ts index be1a365b..573bb4f4 100644 --- a/client/src/pages/segments/Types.ts +++ b/client/src/pages/segments/Types.ts @@ -1,29 +1,17 @@ -import { ReactElement } from 'react'; -import { LOADING_STATE } from '@/consts'; -import { ManageRequestMethods } from '../../types/Common'; - -export interface PartitionData { - _id: string; - _name: string; - _status: LOADING_STATE; - _rowCount: string; - _formatName: string; -} - -export interface PartitionView extends PartitionData { - _nameElement?: ReactElement; - _statusElement?: ReactElement; -} - -// delete and create -export interface PartitionManageParam { - collectionName: string; - partitionName: string; - type: ManageRequestMethods; -} - -// load and release -export interface PartitionParam { - collectionName: string; - partitionNames: string[]; -} +export type Segment = { + collectionID: string; + num_rows: string; + partitionID: string; + q_collectionID: string; + q_indexID: string; + q_index_name: string; + q_mem_size: string; + q_nodeID: string; + q_nodeIds: string[]; + q_num_rows: string; + q_partitionID: string; + q_segmentID: string; + q_state: string; + segmentID: string; + state: string; +};