From d58aa44596695a4aba4db047bb57321fd0534375 Mon Sep 17 00:00:00 2001 From: Crayonn Date: Fri, 29 Apr 2022 23:12:54 +0800 Subject: [PATCH] feat:supplementary algorithms UI --- .../graph-management/GraphManagementList.tsx | 22 +- .../GraphManagementSidebar.tsx | 11 +- .../async-tasks/AsyncTaskList.less | 3 +- .../async-tasks/AsyncTaskList.tsx | 17 +- .../data-analyze/DataAnalyze.less | 92 +- .../data-analyze/DataAnalyze.tsx | 7 +- .../data-analyze/DataAnalyzeInfoDrawer.tsx | 2 +- .../ExecLogAndQueryCollections.tsx | 438 ++++- .../data-analyze/QueryAndAlgorithmLibrary.tsx | 608 +------ .../data-analyze/algorithm/AllPath.tsx | 118 +- .../data-analyze/algorithm/CustomPath.tsx | 1098 ++++++++++++ .../data-analyze/algorithm/FocusDetection.tsx | 86 +- .../data-analyze/algorithm/Jaccard.tsx | 261 +++ .../data-analyze/algorithm/KHop.tsx | 391 +++++ .../data-analyze/algorithm/KStepNeighbor.tsx | 331 ++++ .../data-analyze/algorithm/LoopDetection.tsx | 59 +- .../algorithm/ModelSimilarity.tsx | 1536 ++++++++--------- .../data-analyze/algorithm/NeighborRank.tsx | 196 +-- .../data-analyze/algorithm/PersonalRank.tsx | 434 +++++ .../algorithm/RadiographicInspection.tsx | 405 +++++ .../data-analyze/algorithm/SameNeighbor.tsx | 301 ++++ .../data-analyze/algorithm/ShortestPath.tsx | 437 +++++ .../algorithm/ShortestPathAll.tsx | 70 +- .../SingleSourceWeightedShortestPath.tsx | 505 ++++++ .../algorithm/WeightedShortestPath.tsx | 483 ++++++ .../query-result/GraphPopOver.tsx | 52 +- .../query-result/QueryFilterOptions.tsx | 2 +- .../data-analyze/query-result/QueryResult.tsx | 4 - .../query-result/TableQueryResult.tsx | 4 + .../data-import/import-tasks/ImportTasks.less | 5 - .../import-tasks/datamap-configs/EdgeMap.tsx | 26 +- .../datamap-configs/FileConfigs.tsx | 3 + .../datamap-configs/VertexMap.tsx | 40 +- .../metadata-configs/MetadataConfigs.tsx | 19 +- .../edge-type/EdgeTypeList.tsx | 202 ++- .../edge-type/ReuseEdgeTypes.tsx | 70 +- .../graph-view/CheckAndEditEdge.tsx | 114 +- .../graph-view/CheckAndEditVertex.tsx | 108 +- .../graph-view/CheckProperty.tsx | 1 + .../graph-view/CreateVertex.tsx | 109 +- .../graph-view/GraphView.less | 7 +- .../metadata-configs/graph-view/GraphView.tsx | 11 +- .../property/ReuseProperties.tsx | 48 +- .../vertex-type/ReuseVertexTypes.tsx | 63 +- .../vertex-type/VertexTypeList.tsx | 174 +- .../en-US/graph-managment/addition.json | 3 +- .../zh-CN/graph-managment/addition.json | 3 +- .../data-import/import-tasks/ImportTasks.json | 3 +- .../zh-CN/graph-managment/dataAnalyze.json | 449 ++++- hugegraph-hubble/hubble-fe/src/index.less | 9 +- .../algorithmAnalyzerStore.ts | 1480 +++++++++++++--- .../dataAnalyzeStore/dataAnalyzeStore.ts | 612 +++++-- .../dataImportStore/dataMapStore.ts | 42 +- .../graphManagementStore.ts | 44 +- .../metadataConfigsStore/edgeTypeStore.ts | 295 ++-- .../metadataConfigsStore/graphViewStore.ts | 9 +- .../metadataConfigsStore.ts | 70 +- .../metadataConfigsStore/vertexTypeStore.ts | 244 +-- .../dataAnalyzeStore/algorithmStore.ts | 284 ++- .../dataAnalyzeStore/dataAnalyzeStore.ts | 5 + .../GraphManagementStore/dataAnalyzeStore.ts | 134 +- .../hubble-fe/src/stores/utils/index.ts | 18 + .../src/utils/calcAlgorithmFormWidth.ts | 7 + .../src/utils/filterEmptyAlgorightmParams.ts | 19 + .../src/utils/formatAlgorithmStatement.ts | 254 +++ hugegraph-hubble/hubble-fe/src/utils/index.ts | 21 +- .../hubble-fe/src/utils/isDataTypeNumeric.ts | 10 + 67 files changed, 10126 insertions(+), 2862 deletions(-) create mode 100644 hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/CustomPath.tsx create mode 100644 hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/Jaccard.tsx create mode 100644 hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/KHop.tsx create mode 100644 hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/KStepNeighbor.tsx create mode 100644 hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/PersonalRank.tsx create mode 100644 hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/RadiographicInspection.tsx create mode 100644 hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/SameNeighbor.tsx create mode 100644 hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/ShortestPath.tsx create mode 100644 hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/SingleSourceWeightedShortestPath.tsx create mode 100644 hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/WeightedShortestPath.tsx create mode 100644 hugegraph-hubble/hubble-fe/src/utils/calcAlgorithmFormWidth.ts create mode 100644 hugegraph-hubble/hubble-fe/src/utils/filterEmptyAlgorightmParams.ts create mode 100644 hugegraph-hubble/hubble-fe/src/utils/formatAlgorithmStatement.ts create mode 100644 hugegraph-hubble/hubble-fe/src/utils/isDataTypeNumeric.ts diff --git a/hugegraph-hubble/hubble-fe/src/components/graph-management/GraphManagementList.tsx b/hugegraph-hubble/hubble-fe/src/components/graph-management/GraphManagementList.tsx index 73b578984..3b645c13c 100644 --- a/hugegraph-hubble/hubble-fe/src/components/graph-management/GraphManagementList.tsx +++ b/hugegraph-hubble/hubble-fe/src/components/graph-management/GraphManagementList.tsx @@ -26,7 +26,6 @@ import HintIcon from '../../assets/imgs/ic_question_mark.svg'; import { GraphData } from '../../stores/types/GraphManagementStore/graphManagementStore'; import { useTranslation } from 'react-i18next'; - const commonInputProps = { size: 'medium', width: 420 @@ -234,7 +233,7 @@ const GraphManagementListItem: React.FC< value: 'delete' } ]; - + const disabledDropdownList = [ { label: t('addition.common.edit'), @@ -338,6 +337,9 @@ const GraphManagementListItem: React.FC< 'password', 'edit' )} + originInputProps={{ + type: 'password' + }} />
@@ -368,7 +370,9 @@ const GraphManagementListItem: React.FC< ) : (
-
{t('addition.graphManagementList.id')}
+
+ {t('addition.graphManagementList.id')} +
-
{t('addition.graphManagementList.name')}
+
+ {t('addition.graphManagementList.name')} +
-
{t('addition.graphManagementList.host')}
+
+ {t('addition.graphManagementList.host')} +
{host}
@@ -408,7 +416,9 @@ const GraphManagementListItem: React.FC<
-
{t('addition.graphManagementList.creation-time')}
+
+ {t('addition.graphManagementList.creation-time')} +
{create_time}
diff --git a/hugegraph-hubble/hubble-fe/src/components/graph-management/GraphManagementSidebar.tsx b/hugegraph-hubble/hubble-fe/src/components/graph-management/GraphManagementSidebar.tsx index 7363ea6b4..eb4850139 100644 --- a/hugegraph-hubble/hubble-fe/src/components/graph-management/GraphManagementSidebar.tsx +++ b/hugegraph-hubble/hubble-fe/src/components/graph-management/GraphManagementSidebar.tsx @@ -10,7 +10,6 @@ import { ImportManagerStoreContext } from '../../stores'; -import BackIcon from '../../assets/imgs/ic_topback.svg'; import ArrowIcon from '../../assets/imgs/ic_arrow_white.svg'; import DataAnalyzeIconNormal from '../../assets/imgs/ic_shuju_normal.svg'; import DataAnalyzeIconPressed from '../../assets/imgs/ic_shuju_pressed.svg'; @@ -43,11 +42,6 @@ const GraphManagementSidebar: React.FC = observer(() => { expand: graphManagementStore.isExpanded }); - const sidebarGoBackClassName = classnames({ - 'data-analyze-sidebar-go-back': true, - expand: graphManagementStore.isExpanded - }); - const sidebarGraphSelectionClassName = classnames({ 'data-analyze-sidebar-graph-selection': true, expand: graphManagementStore.isExpanded @@ -91,9 +85,8 @@ const GraphManagementSidebar: React.FC = observer(() => { const handleSelectId = useCallback( (value: string) => { - const id = graphManagementStore.idList.find( - ({ name }) => name === value - )!.id; + const id = graphManagementStore.idList.find(({ name }) => name === value)! + .id; dataAnalyzeStore.resetIdState(); setLocation(`/graph-management/${id}/data-analyze`); diff --git a/hugegraph-hubble/hubble-fe/src/components/graph-management/async-tasks/AsyncTaskList.less b/hugegraph-hubble/hubble-fe/src/components/graph-management/async-tasks/AsyncTaskList.less index 7fa8e6e44..b33bbe7f6 100644 --- a/hugegraph-hubble/hubble-fe/src/components/graph-management/async-tasks/AsyncTaskList.less +++ b/hugegraph-hubble/hubble-fe/src/components/graph-management/async-tasks/AsyncTaskList.less @@ -59,7 +59,8 @@ line-height: 22px; text-align: center; - &.new, + &.scheduling, + &.scheduled, &.queued, &.restoring, &.running { diff --git a/hugegraph-hubble/hubble-fe/src/components/graph-management/async-tasks/AsyncTaskList.tsx b/hugegraph-hubble/hubble-fe/src/components/graph-management/async-tasks/AsyncTaskList.tsx index 6a3f9950c..315dd300e 100644 --- a/hugegraph-hubble/hubble-fe/src/components/graph-management/async-tasks/AsyncTaskList.tsx +++ b/hugegraph-hubble/hubble-fe/src/components/graph-management/async-tasks/AsyncTaskList.tsx @@ -10,14 +10,7 @@ import classnames from 'classnames'; import { useRoute } from 'wouter'; import { useTranslation } from 'react-i18next'; import { isEmpty, size, intersection, without } from 'lodash-es'; -import { - Breadcrumb, - Input, - Button, - Message, - Table, - Loading -} from 'hubble-ui'; +import { Breadcrumb, Input, Button, Message, Table, Loading } from 'hubble-ui'; import { GraphManagementStoreContext, @@ -151,7 +144,7 @@ const AsyncTaskList: React.FC = observer(() => { {t('async-tasks.table-filters.task-type.gremlin')}
- {t('async-tasks.table-filters.task-type.algorithm')}{' '} + {t('async-tasks.table-filters.task-type.algorithm')}
{t('async-tasks.table-filters.task-type.remove-schema')} @@ -231,6 +224,10 @@ const AsyncTaskList: React.FC = observer(() => { timeString += ' ' + restTime + 'ms'; } + if (restTime <= 0) { + timeString = '0s'; + } + return
{timeString}
; } }, @@ -412,7 +409,7 @@ const AsyncTaskList: React.FC = observer(() => {
img { margin-bottom: 10px; } + + & > span { + display: block; + width: 80%; + text-align: center; + max-height: 200px; + overflow: auto; + } } } @@ -830,8 +881,12 @@ } & > div { - flex-basis: 50%; + // flex-basis: 50%; line-height: 32px; + + &:first-child { + width: 162px; + } } } } @@ -1131,3 +1186,12 @@ div.vis-tooltip { pointer-events: none; z-index: 5; } + +// refer to index.less, this should be fixed in the future version +.data-analyze-dynamic-add-options .new-fc-one-input-all-container { + position: relative; +} + +.data-analyze-graph-node-info .new-fc-one-input-all-container { + position: relative; +} diff --git a/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/DataAnalyze.tsx b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/DataAnalyze.tsx index cc65fdfe0..efa3cd3b0 100644 --- a/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/DataAnalyze.tsx +++ b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/DataAnalyze.tsx @@ -44,6 +44,7 @@ const DataAnalyze: React.FC = observer(() => { dataAnalyzeStore.setCurrentId(Number(params.id)); dataAnalyzeStore.fetchValueTypes(); dataAnalyzeStore.fetchVertexTypes(); + dataAnalyzeStore.fetchAllPropertyIndexes('vertex'); dataAnalyzeStore.fetchEdgeTypes(); dataAnalyzeStore.fetchAllNodeStyle(); dataAnalyzeStore.fetchAllEdgeStyle(); @@ -70,9 +71,9 @@ const DataAnalyze: React.FC = observer(() => { {t('addition.dataAnalyze.return-home')} ]} - visible={Object.values(dataAnalyzeStore.errorInfo) - .map(({ code }) => code) - .includes(401)} + visible={graphManagementStore.graphData.some( + ({ id, enabled }) => dataAnalyzeStore.currentId === id && !enabled + )} destroyOnClose needCloseIcon={false} > diff --git a/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/DataAnalyzeInfoDrawer.tsx b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/DataAnalyzeInfoDrawer.tsx index eef6d439f..541b97dfb 100644 --- a/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/DataAnalyzeInfoDrawer.tsx +++ b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/DataAnalyzeInfoDrawer.tsx @@ -299,7 +299,7 @@ const DataAnalyzeInfoDrawer: React.FC = observer(() => { marginTop: !isEdit ? 0 : nullableIndex === 0 ? 8 : 32 }} > -
{key}:
+
{key}:
{!isEdit ? (
{convertArrayToString(value)}
) : ( diff --git a/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/ExecLogAndQueryCollections.tsx b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/ExecLogAndQueryCollections.tsx index 23ed9233d..10aa40253 100644 --- a/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/ExecLogAndQueryCollections.tsx +++ b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/ExecLogAndQueryCollections.tsx @@ -1,10 +1,12 @@ import React, { useState, useContext, useEffect, useCallback } from 'react'; import { observer } from 'mobx-react'; import classnames from 'classnames'; -import { isUndefined } from 'lodash-es'; +import { isUndefined, cloneDeep, isEmpty, isNull } from 'lodash-es'; import Highlighter from 'react-highlight-words'; import { useRoute, useLocation } from 'wouter'; import { useTranslation } from 'react-i18next'; + +import { v4 } from 'uuid'; import { Table, Input, Button, Message } from 'hubble-ui'; import { Tooltip } from '../../common'; @@ -13,16 +15,53 @@ import { DataAnalyzeStoreContext, AsyncTasksStoreContext } from '../../../stores'; +import { formatAlgorithmStatement } from '../../../utils'; -import type { TFunction } from 'i18next'; import type { ExecutionLogs, - FavoriteQuery + FavoriteQuery, + LoopDetectionParams, + FocusDetectionParams, + ShortestPathAlgorithmParams, + ShortestPathAllAlgorithmParams, + AllPathAlgorithmParams, + KStepNeighbor, + KHop, + RadiographicInspection, + SameNeighbor, + Jaccard, + WeightedShortestPath, + SingleSourceWeightedShortestPath, + PersonalRank, + ModelSimilarityParams, + NeighborRankParams, + CustomPathParams, + CustomPathRule } from '../../../stores/types/GraphManagementStore/dataAnalyzeStore'; import { Algorithm } from '../../../stores/factory/dataAnalyzeStore/algorithmStore'; import ArrowIcon from '../../../assets/imgs/ic_arrow_16.svg'; import EmptyIcon from '../../../assets/imgs/ic_sousuo_empty.svg'; +import { toJS } from 'mobx'; + +export const AlgorithmInternalNameMapping: Record = { + rings: 'loop-detection', + crosspoints: 'focus-detection', + shortpath: 'shortest-path', + allshortpath: 'shortest-path-all', + paths: 'all-path', + fsimilarity: 'model-similarity', + neighborrank: 'neighbor-rank', + kneighbor: 'k-step-neighbor', + kout: 'k-hop', + customizedpaths: 'custom-path', + rays: 'radiographic-inspection', + sameneighbors: 'same-neighbor', + weightedshortpath: 'weighted-shortest-path', + singleshortpath: 'single-source-weighted-shortest-path', + jaccardsimilarity: 'jaccard', + personalrank: 'personal-rank' +}; const styles = { tableCenter: { @@ -39,6 +78,7 @@ const styles = { const ExecLogAndQueryCollections: React.FC = observer(() => { const asyncTasksStore = useContext(AsyncTasksStoreContext); const dataAnalyzeStore = useContext(DataAnalyzeStoreContext); + const { algorithmAnalyzerStore } = dataAnalyzeStore; const [tabIndex, setTabIndex] = useState(0); const [, params] = useRoute('/graph-management/:id/data-analyze'); const { t } = useTranslation(); @@ -80,6 +120,7 @@ const ExecLogAndQueryCollections: React.FC = observer(() => { ); @@ -208,7 +249,12 @@ const ExecLogAndQueryCollections: React.FC = observer(() => { className="exec-log-manipulation" onClick={loadStatements( rowData.content, - rowData.type === 'GREMLIN_ASYNC' ? 'task' : 'query' + rowData.type === 'GREMLIN_ASYNC' + ? 'task' + : rowData.type === 'GREMLIN' + ? 'query' + : 'algorithm', + rowData.algorithm_name )} > {t('addition.operate.load-statement')} @@ -383,8 +429,356 @@ const ExecLogAndQueryCollections: React.FC = observer(() => { ); const loadStatements = useCallback( - (content: string, type?: 'query' | 'task') => () => { + ( + content: string, + type?: 'query' | 'task' | 'algorithm', + algorithmName?: string + ) => () => { if (!dataAnalyzeStore.isLoadingGraph) { + if (type === 'algorithm') { + const params: Record = JSON.parse(content); + dataAnalyzeStore.setCurrentTab('algorithm-analyze'); + + if (params.hasOwnProperty('direction')) { + params.direction = params.direction.toUpperCase(); + } + + if (params.hasOwnProperty('label')) { + params['label'] === null && (params['label'] = '__all__'); + } + + window.scrollTo(0, 0); + + switch (algorithmName) { + case 'rings': + algorithmAnalyzerStore.changeCurrentAlgorithm( + Algorithm.loopDetection + ); + + Object.keys(params).forEach((key) => { + algorithmAnalyzerStore.mutateLoopDetectionParams( + key as keyof LoopDetectionParams, + params[key] + ); + }); + + return; + case 'crosspoints': + algorithmAnalyzerStore.changeCurrentAlgorithm( + Algorithm.focusDetection + ); + + Object.keys(params).forEach((key) => { + algorithmAnalyzerStore.mutateFocusDetectionParams( + key as keyof FocusDetectionParams, + params[key] + ); + }); + + return; + case 'shortpath': + algorithmAnalyzerStore.changeCurrentAlgorithm( + Algorithm.shortestPath + ); + + Object.keys(params).forEach((key) => { + algorithmAnalyzerStore.mutateShortestPathParams( + key as keyof ShortestPathAlgorithmParams, + params[key] + ); + }); + + return; + case 'allshortpath': + algorithmAnalyzerStore.changeCurrentAlgorithm( + Algorithm.shortestPathAll + ); + + Object.keys(params).forEach((key) => { + algorithmAnalyzerStore.mutateShortestPathAllParams( + key as keyof ShortestPathAllAlgorithmParams, + params[key] + ); + }); + + return; + case 'paths': + algorithmAnalyzerStore.changeCurrentAlgorithm(Algorithm.allPath); + + Object.keys(params).forEach((key) => { + algorithmAnalyzerStore.mutateAllPathParams( + key as keyof AllPathAlgorithmParams, + params[key] + ); + }); + + return; + case 'fsimilarity': { + const clonedParams = cloneDeep(params); + + if (isEmpty(params.sources.ids)) { + clonedParams.method = 'property'; + } else { + clonedParams.method = 'id'; + } + + clonedParams.source = clonedParams.sources.ids.join(','); + clonedParams.vertexType = clonedParams.sources.label; + clonedParams.vertexProperty = Object.entries( + clonedParams.sources.properties + ); + + if (isEmpty(clonedParams.vertexProperty)) { + clonedParams.vertexProperty = [['', '']]; + } else { + clonedParams.vertexProperty = clonedParams.vertexProperty.map( + ([key, value]: [string, string[]]) => [key, value.join(',')] + ); + } + + clonedParams.least_neighbor = clonedParams.min_neighbors; + clonedParams.similarity = clonedParams.alpha; + clonedParams.least_similar = clonedParams.min_similars; + clonedParams.max_similar = clonedParams.top; + clonedParams.property_filter = clonedParams.group_property; + clonedParams.least_property_number = clonedParams.min_groups; + clonedParams.return_common_connection = + clonedParams.with_intermediary; + clonedParams.return_complete_info = clonedParams.with_vertex; + + delete clonedParams.sources; + delete clonedParams.min_neighbors; + delete clonedParams.alpha; + delete clonedParams.min_similars; + delete clonedParams.top; + delete clonedParams.group_property; + delete clonedParams.min_groups; + delete clonedParams.with_intermediary; + delete clonedParams.with_vertex; + + Object.keys(clonedParams).forEach((key) => { + algorithmAnalyzerStore.mutateModelSimilarityParams( + key as keyof ModelSimilarityParams, + clonedParams[key] + ); + }); + + algorithmAnalyzerStore.changeCurrentAlgorithm( + Algorithm.modelSimilarity + ); + + return; + } + case 'neighborrank': { + algorithmAnalyzerStore.changeCurrentAlgorithm( + Algorithm.neighborRank + ); + + const clonedParams = cloneDeep(params) as NeighborRankParams; + + // remove neighborrank validate rules first; + algorithmAnalyzerStore.removeValidateNeighborRankRule(0); + clonedParams.steps.forEach(({ labels }, index) => { + clonedParams.steps[index].uuid = v4(); + // add neighborrank rule validation per each + algorithmAnalyzerStore.addValidateNeighborRankRule(); + + if (isEmpty(labels)) { + clonedParams.steps[index].labels = ['__all__']; + } + }); + + Object.keys(clonedParams).forEach((key) => { + algorithmAnalyzerStore.mutateNeighborRankParams( + key as keyof NeighborRankParams, + // @ts-ignore + clonedParams[key] + ); + }); + + return; + } + case 'kneighbor': + algorithmAnalyzerStore.changeCurrentAlgorithm( + Algorithm.kStepNeighbor + ); + + Object.keys(params).forEach((key) => { + algorithmAnalyzerStore.mutateKStepNeighborParams( + key as keyof KStepNeighbor, + params[key] + ); + }); + + return; + case 'kout': + algorithmAnalyzerStore.changeCurrentAlgorithm(Algorithm.kHop); + + Object.keys(params).forEach((key) => { + algorithmAnalyzerStore.mutateKHopParams( + key as keyof KHop, + params[key] + ); + }); + + return; + case 'customizedpaths': + const clonedParams = cloneDeep(params); + + if (isEmpty(params.sources.ids)) { + clonedParams.method = 'property'; + } else { + clonedParams.method = 'id'; + } + clonedParams.source = clonedParams.sources.ids.join(','); + clonedParams.vertexType = clonedParams.sources.label; + clonedParams.vertexProperty = Object.entries( + clonedParams.sources.properties + ); + + if (isEmpty(clonedParams.vertexProperty)) { + clonedParams.vertexProperty = [['', '']]; + } else { + clonedParams.vertexProperty = clonedParams.vertexProperty.map( + ([key, value]: [string, string[]]) => [key, value.join(',')] + ); + + clonedParams.vertexProperty = Object.keys( + clonedParams.vertexProperty + ).map((key) => [ + key, + clonedParams.vertexProperty[key].join(',') + ]); + } + + clonedParams.steps.forEach((step: any, index: number) => { + const { labels, properties, weight_by, default_weight } = step; + clonedParams.steps[index].uuid = v4(); + + if (isEmpty(labels)) { + step.labels = dataAnalyzeStore.edgeTypes.map( + ({ name }) => name + ); + } + + if (isEmpty(properties)) { + step.properties = [['', '']]; + } else { + step.properties = Object.keys(step.properties).map((key) => [ + key, + step.properties[key].join(',') + ]); + } + + if (clonedParams.sort_by === 'NONE') { + step.weight_by = ''; + step.default_weight = ''; + } else { + if (!isNull(weight_by)) { + step.default_weight = ''; + } else { + // custom weight + step.weight_by = '__CUSTOM_WEIGHT__'; + step.default_weight = default_weight; + } + } + }); + + delete clonedParams.sources; + + Object.keys(clonedParams).forEach((key) => { + algorithmAnalyzerStore.mutateCustomPathParams( + key as keyof CustomPathParams, + clonedParams[key] + ); + }); + + algorithmAnalyzerStore.changeCurrentAlgorithm( + Algorithm.customPath + ); + + return; + case 'rays': + algorithmAnalyzerStore.changeCurrentAlgorithm( + Algorithm.radiographicInspection + ); + + Object.keys(params).forEach((key) => { + algorithmAnalyzerStore.mutateRadiographicInspectionParams( + key as keyof RadiographicInspection, + params[key] + ); + }); + + return; + case 'sameneighbors': + algorithmAnalyzerStore.changeCurrentAlgorithm( + Algorithm.sameNeighbor + ); + + Object.keys(params).forEach((key) => { + algorithmAnalyzerStore.mutateSameNeighborParams( + key as keyof SameNeighbor, + params[key] + ); + }); + + return; + case 'weightedshortpath': + algorithmAnalyzerStore.changeCurrentAlgorithm( + Algorithm.weightedShortestPath + ); + + Object.keys(params).forEach((key) => { + algorithmAnalyzerStore.mutateWeightedShortestPathParams( + key as keyof WeightedShortestPath, + params[key] + ); + }); + + return; + case 'singleshortpath': + algorithmAnalyzerStore.changeCurrentAlgorithm( + Algorithm.singleSourceWeightedShortestPath + ); + + Object.keys(params).forEach((key) => { + algorithmAnalyzerStore.mutateSingleSourceWeightedShortestPathParams( + key as keyof SingleSourceWeightedShortestPath, + params[key] + ); + }); + + return; + case 'jaccardsimilarity': + algorithmAnalyzerStore.changeCurrentAlgorithm(Algorithm.jaccard); + + Object.keys(params).forEach((key) => { + algorithmAnalyzerStore.mutateJaccardParams( + key as keyof Jaccard, + params[key] + ); + }); + + return; + case 'personalrank': + algorithmAnalyzerStore.changeCurrentAlgorithm( + Algorithm.personalRankRecommendation + ); + + Object.keys(params).forEach((key) => { + algorithmAnalyzerStore.mutatePersonalRankParams( + key as keyof PersonalRank, + params[key] + ); + }); + + return; + default: + return; + } + } + if (!isUndefined(type)) { type === 'task' ? dataAnalyzeStore.setQueryMode('task') @@ -556,13 +950,15 @@ const ExecutionContent: React.FC<{ type: string; content: string; highlightText: string; -}> = observer(({ type, content, highlightText }) => { + algorithmName?: string; +}> = observer(({ type, content, highlightText, algorithmName }) => { const dataAnalyzeStore = useContext(DataAnalyzeStoreContext); - const { t } = useTranslation(); + const { t, i18n } = useTranslation(); const [isExpand, switchExpand] = useState(dataAnalyzeStore.isSearched.status); + const statements = type === 'ALGORITHM' - ? formatAlgorithmStatement(content, Algorithm.shortestPath, t) + ? formatAlgorithmStatement(content, algorithmName, t, i18n) : content.split('\n').filter((statement) => statement !== ''); const arrowIconClassName = classnames({ @@ -670,30 +1066,4 @@ export const DeleteConfirm: React.FC = observer( } ); -function formatAlgorithmStatement( - content: string, - algorithmType: string, - translator: TFunction -) { - const convertedString = content - .replace(/^.*\(/, '') - .replace(/\)$/, '') - .replace(/ /g, ''); - const statements: string[] = [ - translator(`data-analyze.algorithm-list.${algorithmType}`) - ]; - - convertedString.split(',').forEach((item) => { - const [key, value] = item.split('='); - - statements.push( - `${translator( - `data-analyze.algorithm-forms.shortest-path.options.${key}` - )} ${value}` - ); - }); - - return statements; -} - export default ExecLogAndQueryCollections; diff --git a/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/QueryAndAlgorithmLibrary.tsx b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/QueryAndAlgorithmLibrary.tsx index 0bfb56e21..0fa932a44 100644 --- a/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/QueryAndAlgorithmLibrary.tsx +++ b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/QueryAndAlgorithmLibrary.tsx @@ -10,15 +10,7 @@ import { observer } from 'mobx-react'; import CodeMirror from 'codemirror'; import { useTranslation } from 'react-i18next'; import classnames from 'classnames'; -import { - Button, - Tooltip, - Alert, - Dropdown, - Input, - Radio, - Select -} from 'hubble-ui'; +import { Button, Tooltip, Alert, Dropdown } from 'hubble-ui'; import 'codemirror/lib/codemirror.css'; import 'react-popper-tooltip/dist/styles.css'; @@ -31,10 +23,20 @@ import { useMultiKeyPress } from '../../../hooks'; import LoopDetection from './algorithm/LoopDetection'; import FocusDetection from './algorithm/FocusDetection'; +import ShortestPath from './algorithm/ShortestPath'; import ShortestPathAll from './algorithm/ShortestPathAll'; import AllPath from './algorithm/AllPath'; import ModelSimilarity from './algorithm/ModelSimilarity'; import NeighborRank from './algorithm/NeighborRank'; +import KStepNeighbor from './algorithm/KStepNeighbor'; +import KHop from './algorithm/KHop'; +import CustomPath from './algorithm/CustomPath'; +import RadiographicInspection from './algorithm/RadiographicInspection'; +import SameNeighbor from './algorithm/SameNeighbor'; +import WeightedShortestPath from './algorithm/WeightedShortestPath'; +import SingleSourceWeightedShortestPath from './algorithm/SingleSourceWeightedShortestPath'; +import Jaccard from './algorithm/Jaccard'; +import PersonalRank from './algorithm/PersonalRank'; import ArrowIcon from '../../../assets/imgs/ic_arrow_16.svg'; import QuestionMarkIcon from '../../../assets/imgs/ic_question_mark.svg'; @@ -52,28 +54,32 @@ export const styles = { const codeRegexp = /[A-Za-z0-9]+/; -const algorithmWhiteList: string[] = [ - Algorithm.shortestPath, - Algorithm.loopDetection, - Algorithm.focusDetection, - Algorithm.shortestPathAll, - Algorithm.allPath, - Algorithm.modelSimilarity, - Algorithm.neighborRankRecommendation -]; - const QueryAndAlgorithmLibrary: React.FC = observer(() => { const dataAnalyzeStore = useContext(DataAnalyzeStoreContext); + const { algorithmAnalyzerStore } = dataAnalyzeStore; const { t } = useTranslation(); const handleTabChange = (tab: string) => () => { dataAnalyzeStore.resetSwitchTabState(); if (tab !== dataAnalyzeStore.currentTab) { + // reset codeEditor value dataAnalyzeStore.mutateCodeEditorText(''); + + // reset default selection of edge labels + algorithmAnalyzerStore.mutateCustomPathRuleParams( + 'labels', + dataAnalyzeStore.edgeTypes.map(({ name }) => name), + 0 + ); } dataAnalyzeStore.setCurrentTab(tab); + // need manually set codeMirror text value to empty here + dataAnalyzeStore.codeEditorInstance?.setValue(''); + // reset algorithm tab to list + algorithmAnalyzerStore.changeCurrentAlgorithm(''); + algorithmAnalyzerStore.switchCollapse(false); }; return ( @@ -202,6 +208,7 @@ export const GremlinQuery: React.FC = observer(() => { ); }; + dataAnalyzeStore.assignCodeEditorInstance(codeEditor.current); codeEditor.current.on('change', handleCodeEditorChange); reaction( @@ -211,15 +218,6 @@ export const GremlinQuery: React.FC = observer(() => { } ); - reaction( - () => dataAnalyzeStore.pulse, - () => { - (codeEditor.current as CodeMirror.Editor).setValue( - dataAnalyzeStore.codeEditorText - ); - } - ); - return () => { (codeEditor.current as CodeMirror.Editor).off( 'change', @@ -229,6 +227,17 @@ export const GremlinQuery: React.FC = observer(() => { } }, [dataAnalyzeStore]); + // weird, mobx@reaction is not working when puluse changed, setValue() + // has no influence + useEffect(() => { + if (codeEditor?.current && dataAnalyzeStore.codeEditorText !== '') { + (codeEditor.current as CodeMirror.Editor).setValue( + dataAnalyzeStore.codeEditorText + ); + } + }, [dataAnalyzeStore.pulse]); + // }, [dataAnalyzeStore.pulse, codeEditor?.current]); + useEffect(() => { if ( keyPressed.has('Tab') && @@ -429,465 +438,47 @@ export const AlgorithmQuery: React.FC = observer(() => { algorithmAnalyzerStore.shortestPathAlgorithmParams.max_depth !== ''; const handleChangeAlgorithm = (algorithm: string) => () => { - // disable other algorithm now - if (algorithmWhiteList.includes(algorithm)) { - algorithmAnalyzerStore.changeCurrentAlgorithm(algorithm); - } + algorithmAnalyzerStore.changeCurrentAlgorithm(algorithm); + }; + + const handleExpandClick = () => { + algorithmAnalyzerStore.switchCollapse(!algorithmAnalyzerStore.isCollapse); }; const renderForms = () => { switch (algorithmAnalyzerStore.currentAlgorithm) { - case Algorithm.shortestPath: - return ( -
-
-
-
- * - - {t( - 'data-analyze.algorithm-forms.shortest-path.options.source' - )} - -
- { - algorithmAnalyzerStore.mutateShortestPathParams( - 'source', - e.value as string - ); - - algorithmAnalyzerStore.validateShortestPathParams('source'); - }} - originInputProps={{ - onBlur() { - algorithmAnalyzerStore.validateShortestPathParams( - 'source' - ); - } - }} - /> -
-
-
- - {t( - 'data-analyze.algorithm-forms.shortest-path.options.label' - )} - -
- -
-
-
-
-
- * - - {t( - 'data-analyze.algorithm-forms.shortest-path.options.target' - )} - -
- { - algorithmAnalyzerStore.mutateShortestPathParams( - 'target', - e.value as string - ); - - algorithmAnalyzerStore.validateShortestPathParams('target'); - }} - originInputProps={{ - onBlur() { - algorithmAnalyzerStore.validateShortestPathParams( - 'target' - ); - } - }} - /> -
-
-
- - {t( - 'data-analyze.algorithm-forms.shortest-path.options.max_degree' - )} - -
- { - algorithmAnalyzerStore.mutateShortestPathParams( - 'max_degree', - e.value as string - ); - - algorithmAnalyzerStore.validateShortestPathParams( - 'max_degree' - ); - }} - originInputProps={{ - onBlur() { - algorithmAnalyzerStore.validateShortestPathParams( - 'max_degree' - ); - } - }} - /> -
-
-
-
-
- * - - {t( - 'data-analyze.algorithm-forms.shortest-path.options.direction' - )} - -
- ) => { - algorithmAnalyzerStore.mutateShortestPathParams( - 'direction', - e.target.value - ); - }} - > - both - out - in - -
-
-
- - {t( - 'data-analyze.algorithm-forms.shortest-path.options.skip_degree' - )} - - -
- { - algorithmAnalyzerStore.mutateShortestPathParams( - 'skip_degree', - e.value as string - ); - - algorithmAnalyzerStore.validateShortestPathParams( - 'skip_degree' - ); - }} - originInputProps={{ - onBlur() { - algorithmAnalyzerStore.validateShortestPathParams( - 'skip_degree' - ); - } - }} - /> -
-
-
-
-
- * - - {t( - 'data-analyze.algorithm-forms.shortest-path.options.max_depth' - )} - - -
- { - algorithmAnalyzerStore.mutateShortestPathParams( - 'max_depth', - e.value as string - ); - - algorithmAnalyzerStore.validateShortestPathParams( - 'max_depth' - ); - }} - originInputProps={{ - onBlur() { - algorithmAnalyzerStore.validateShortestPathParams( - 'max_depth' - ); - } - }} - /> -
-
-
- - {t( - 'data-analyze.algorithm-forms.shortest-path.options.capacity' - )} - -
- { - algorithmAnalyzerStore.mutateShortestPathParams( - 'capacity', - e.value as string - ); - - algorithmAnalyzerStore.validateShortestPathParams( - 'capacity' - ); - }} - originInputProps={{ - onBlur() { - algorithmAnalyzerStore.validateShortestPathParams( - 'capacity' - ); - } - }} - /> -
-
-
- - -
-
- ); case Algorithm.loopDetection: return ; case Algorithm.focusDetection: return ; + case Algorithm.shortestPath: + return ; case Algorithm.shortestPathAll: return ; case Algorithm.allPath: return ; case Algorithm.modelSimilarity: return ; - case Algorithm.neighborRankRecommendation: + case Algorithm.neighborRank: return ; + case Algorithm.kStepNeighbor: + return ; + case Algorithm.kHop: + return ; + case Algorithm.customPath: + return ; + case Algorithm.radiographicInspection: + return ; + case Algorithm.sameNeighbor: + return ; + case Algorithm.weightedShortestPath: + return ; + case Algorithm.singleSourceWeightedShortestPath: + return ; + case Algorithm.jaccard: + return ; + case Algorithm.personalRankRecommendation: + return ; } }; @@ -898,7 +489,7 @@ export const AlgorithmQuery: React.FC = observer(() => { }, []); return ( -
+
{algorithmAnalyzerStore.currentAlgorithm === '' ? ( {t('data-analyze.algorithm-list.title')} @@ -912,16 +503,21 @@ export const AlgorithmQuery: React.FC = observer(() => { marginRight: 12 }} onClick={() => { - algorithmAnalyzerStore.dispose(); + algorithmAnalyzerStore.switchCollapse(false); + algorithmAnalyzerStore.changeCurrentAlgorithm(''); }} /> - + {t( `data-analyze.algorithm-list.${algorithmAnalyzerStore.currentAlgorithm}` )}
)} +
expand-collpase { ? 'rotate(0deg)' : 'rotate(180deg)' }} - onClick={() => { - algorithmAnalyzerStore.switchCollapse( - !algorithmAnalyzerStore.isCollapse - ); - }} + onClick={handleExpandClick} />
{algorithmAnalyzerStore.isCollapse ? null : algorithmAnalyzerStore.currentAlgorithm === @@ -948,14 +540,7 @@ export const AlgorithmQuery: React.FC = observer(() => { Algorithm.shortestPathAll, Algorithm.allPath ].map((algorithm) => ( - + {t(`data-analyze.algorithm-list.${algorithm}`)} ))} @@ -963,57 +548,32 @@ export const AlgorithmQuery: React.FC = observer(() => {
{[ Algorithm.modelSimilarity, - Algorithm.neighborRankRecommendation, - Algorithm.realTimeRecommendation, + Algorithm.neighborRank, Algorithm.kStepNeighbor, - Algorithm.kHop + Algorithm.kHop, + Algorithm.customPath ].map((algorithm) => ( - + {t(`data-analyze.algorithm-list.${algorithm}`)} ))}
{[ - Algorithm.customPath, - Algorithm.customIntersectionDetection, Algorithm.radiographicInspection, - Algorithm.commonNeighbor, - Algorithm.weightedShortestPath + Algorithm.sameNeighbor, + Algorithm.weightedShortestPath, + Algorithm.singleSourceWeightedShortestPath, + Algorithm.jaccard ].map((algorithm) => ( - + {t(`data-analyze.algorithm-list.${algorithm}`)} ))}
- {[ - Algorithm.singleSourceWeightedPath, - Algorithm.jaccardSimilarity, - Algorithm.personalRankRecommendation - ].map((algorithm) => ( - {}} - > + {[Algorithm.personalRankRecommendation].map((algorithm) => ( + {t(`data-analyze.algorithm-list.${algorithm}`)} ))} diff --git a/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/AllPath.tsx b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/AllPath.tsx index aab733e92..cfc7f6d41 100644 --- a/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/AllPath.tsx +++ b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/AllPath.tsx @@ -1,23 +1,37 @@ -import React, { useContext, createContext } from 'react'; +import React, { useContext } from 'react'; import { observer } from 'mobx-react'; -import { Button, Radio, Input, Select, Switch } from 'hubble-ui'; +import { Button, Radio, Input, Select } from 'hubble-ui'; import { useTranslation } from 'react-i18next'; + import { styles } from '../QueryAndAlgorithmLibrary'; import { Tooltip as CustomTooltip } from '../../../common'; import DataAnalyzeStore from '../../../../stores/GraphManagementStore/dataAnalyzeStore/dataAnalyzeStore'; +import { GraphManagementStoreContext } from '../../../../stores'; +import { calcAlgorithmFormWidth } from '../../../../utils'; import QuestionMarkIcon from '../../../../assets/imgs/ic_question_mark.svg'; import { Algorithm } from '../../../../stores/factory/dataAnalyzeStore/algorithmStore'; const AllPath = observer(() => { - const { t } = useTranslation(); + const graphManagementStore = useContext(GraphManagementStoreContext); const dataAnalyzeStore = useContext(DataAnalyzeStore); const algorithmAnalyzerStore = dataAnalyzeStore.algorithmAnalyzerStore; + const { t } = useTranslation(); - const isValidExec = !Object.values(algorithmAnalyzerStore.allPathParams).some( - (value) => value === '' + const formWidth = calcAlgorithmFormWidth( + graphManagementStore.isExpanded, + 340, + 400 ); + const isValidExec = + Object.values( + algorithmAnalyzerStore.validateAllPathParamsErrorMessage + ).every((value) => value === '') && + algorithmAnalyzerStore.allPathParams.source !== '' && + algorithmAnalyzerStore.allPathParams.target !== '' && + algorithmAnalyzerStore.allPathParams.max_depth !== ''; + return (
@@ -25,15 +39,15 @@ const AllPath = observer(() => {
* - {t('data-analyze.algorithm-forms.focus-detection.options.source')} + {t('data-analyze.algorithm-forms.all-path.options.source')}
{
- {t('data-analyze.algorithm-forms.focus-detection.options.label')} + {t('data-analyze.algorithm-forms.all-path.options.label')}
{
- {t( - 'data-analyze.algorithm-forms.focus-detection.options.max_degree' - )} + {t('data-analyze.algorithm-forms.all-path.options.max_degree')} +
{
* - {t( - 'data-analyze.algorithm-forms.focus-detection.options.direction' - )} + {t('data-analyze.algorithm-forms.all-path.options.direction')}
{
- {t( - 'data-analyze.algorithm-forms.focus-detection.options.capacity' - )} + {t('data-analyze.algorithm-forms.all-path.options.capacity')}
{
* - {t( - 'data-analyze.algorithm-forms.focus-detection.options.max_depth' - )} + {t('data-analyze.algorithm-forms.all-path.options.max_depth')} { } }} tooltipWrapper={t( - 'data-analyze.algorithm-forms.focus-detection.hint.max-depth' + 'data-analyze.algorithm-forms.all-path.hint.max-depth' )} childrenProps={{ src: QuestionMarkIcon, @@ -255,11 +287,11 @@ const AllPath = observer(() => { />
{
- {t( - 'data-analyze.algorithm-forms.focus-detection.options.capacity' - )} + {t('data-analyze.algorithm-forms.all-path.options.limit')}
{ algorithmAnalyzerStore.mutateAllPathParams( - 'capacity', + 'limit', e.value as string ); - algorithmAnalyzerStore.validateAllPathParams('capacity'); + algorithmAnalyzerStore.validateAllPathParams('limit'); }} originInputProps={{ onBlur() { - algorithmAnalyzerStore.validateAllPathParams('capacity'); + algorithmAnalyzerStore.validateAllPathParams('limit'); } }} /> diff --git a/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/CustomPath.tsx b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/CustomPath.tsx new file mode 100644 index 000000000..92c373301 --- /dev/null +++ b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/CustomPath.tsx @@ -0,0 +1,1098 @@ +import React, { useContext } from 'react'; +import { observer } from 'mobx-react'; +import { + size, + flatten, + flattenDeep, + uniq, + isEmpty, + cloneDeep, + fromPairs +} from 'lodash-es'; +import { Button, Radio, Input, Select } from 'hubble-ui'; +import { useTranslation } from 'react-i18next'; + +import { styles } from '../QueryAndAlgorithmLibrary'; +import { GraphManagementStoreContext } from '../../../../stores'; +import DataAnalyzeStore from '../../../../stores/GraphManagementStore/dataAnalyzeStore/dataAnalyzeStore'; +import { Algorithm } from '../../../../stores/factory/dataAnalyzeStore/algorithmStore'; +import { isDataTypeNumeric, calcAlgorithmFormWidth } from '../../../../utils'; + +const CustomPath = observer(() => { + const graphManagementStore = useContext(GraphManagementStoreContext); + const dataAnalyzeStore = useContext(DataAnalyzeStore); + const { t } = useTranslation(); + const algorithmAnalyzerStore = dataAnalyzeStore.algorithmAnalyzerStore; + + const formWidth = calcAlgorithmFormWidth( + graphManagementStore.isExpanded, + 340, + 390 + ); + + const formWidthInStep = calcAlgorithmFormWidth( + graphManagementStore.isExpanded, + 310, + 380 + ); + + const formSmallWidthInStep = calcAlgorithmFormWidth( + graphManagementStore.isExpanded, + 150, + 160 + ); + + const sourceType = algorithmAnalyzerStore.customPathParams.method; + + const allowAddNewProperty = !flatten( + algorithmAnalyzerStore.customPathParams.vertexProperty + ).some((value) => value === ''); + + const addNewPropertyClassName = + allowAddNewProperty && + dataAnalyzeStore.requestStatus.fetchGraphs !== 'pending' + ? 'query-tab-content-internal-expand-manipulation-enable' + : 'query-tab-content-internal-expand-manipulation-disabled'; + + const allowAddNewRuleProperty = !flattenDeep( + algorithmAnalyzerStore.customPathParams.steps.map( + ({ properties }) => properties + ) + ).some((value) => value === ''); + + const addNewRulePropertyClassName = + allowAddNewRuleProperty && + dataAnalyzeStore.requestStatus.fetchGraphs !== 'pending' + ? 'query-tab-content-internal-expand-manipulation-enable' + : 'query-tab-content-internal-expand-manipulation-disabled'; + + const isValidSourceId = + algorithmAnalyzerStore.customPathParams.method === 'id' && + algorithmAnalyzerStore.customPathParams.source !== ''; + + const isValidProperty = + algorithmAnalyzerStore.customPathParams.method === 'property' && + !( + isEmpty(algorithmAnalyzerStore.customPathParams.vertexType) && + flatten( + algorithmAnalyzerStore.customPathParams.vertexProperty + ).every((value) => isEmpty(value)) + ) && + algorithmAnalyzerStore.customPathParams.vertexProperty.every( + ([key, value]) => (!isEmpty(key) ? !isEmpty(value) : true) + ); + + const isValidateRuleProperties = algorithmAnalyzerStore.customPathParams.steps.every( + ({ properties }) => { + if (size(properties) === 1) { + return ( + (isEmpty(properties[0][0]) && isEmpty(properties[0][1])) || + (!isEmpty(properties[0][0]) && !isEmpty(properties[0][1])) + ); + } else { + return properties.every(([, value]) => !isEmpty(value)); + } + } + ); + + const isValidRuleWeight = algorithmAnalyzerStore.customPathParams.steps.every( + ({ weight_by, default_weight }) => + algorithmAnalyzerStore.customPathParams.sort_by === 'NONE' || + (weight_by === '__CUSTOM_WEIGHT__' && default_weight !== '') || + (weight_by !== '__CUSTOM_WEIGHT__' && !isEmpty(weight_by)) + ); + + const isValidExec = + Object.values( + algorithmAnalyzerStore.validateCustomPathParmasErrorMessage + ).every((value) => Array.isArray(value) || value === '') && + algorithmAnalyzerStore.validateCustomPathParmasErrorMessage.steps.every( + (step) => Object.values(step).every((value) => value === '') + ) && + (algorithmAnalyzerStore.customPathParams.method === 'id' + ? algorithmAnalyzerStore.customPathParams.source !== '' + : true) && + (isValidSourceId || isValidProperty) && + isValidateRuleProperties && + isValidRuleWeight; + + const isValidAddRule = algorithmAnalyzerStore.validateCustomPathParmasErrorMessage.steps.every( + (step) => Object.values(step).every((value) => value === '') + ); + + return ( +
+
+
+
+
+ * + + {t('data-analyze.algorithm-forms.custom-path.options.method')} + +
+ ) => { + algorithmAnalyzerStore.switchCustomPathMethod(e.target.value); + }} + > + + {t( + 'data-analyze.algorithm-forms.custom-path.radio-value.specific-id' + )} + + + {t( + 'data-analyze.algorithm-forms.custom-path.radio-value.filtered-type-property' + )} + + +
+
+ + {sourceType === 'id' && ( +
+
+
+ * + + {t('data-analyze.algorithm-forms.custom-path.options.source')} + +
+ + { + algorithmAnalyzerStore.mutateCustomPathParams( + 'source', + e.value as string + ); + + algorithmAnalyzerStore.validateCustomPathParams('source'); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateCustomPathParams('source'); + } + }} + /> +
+
+ )} + + {sourceType !== 'id' && ( + <> +
+
+
+ + {t( + 'data-analyze.algorithm-forms.custom-path.options.vertex-type' + )} + +
+ +
+
+ +
+
+
+ + {t( + 'data-analyze.algorithm-forms.custom-path.options.vertex-property' + )} + +
+ +
+ {algorithmAnalyzerStore.customPathParams.vertexProperty.map( + ([key, value], propertyIndex) => { + const currentVertexType = dataAnalyzeStore.vertexTypes.find( + ({ name }) => + name === + algorithmAnalyzerStore.customPathParams.vertexType + ); + + return ( +
+ +
+ { + const vertexProperty = cloneDeep( + algorithmAnalyzerStore.customPathParams + .vertexProperty + ); + + vertexProperty[propertyIndex][1] = e.value; + + algorithmAnalyzerStore.mutateCustomPathParams( + 'vertexProperty', + vertexProperty + ); + + algorithmAnalyzerStore.validateCustomPathParams( + 'vertexProperty' + ); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateCustomPathParams( + 'vertexProperty' + ); + } + }} + /> +
+ {size( + algorithmAnalyzerStore.customPathParams + .vertexProperty + ) > 1 && ( +
{ + algorithmAnalyzerStore.removeCustomPathVertexProperty( + propertyIndex + ); + }} + > + {t( + 'data-analyze.algorithm-forms.custom-path.delete' + )} +
+ )} +
+ ); + } + )} +
+
+
+ {algorithmAnalyzerStore.customPathParams.vertexType === '' && + !allowAddNewProperty && + size(algorithmAnalyzerStore.customPathParams.vertexProperty) === + 1 ? ( +
+ + {t( + 'data-analyze.algorithm-forms.custom-path.hint.vertex_type_or_property' + )} + +
+ ) : ( +
+ { + if ( + allowAddNewProperty && + dataAnalyzeStore.requestStatus.fetchGraphs !== 'pending' + ) { + algorithmAnalyzerStore.addCustomPathVertexProperty(); + } + }} + > + {t('data-analyze.algorithm-forms.custom-path.add')} + +
+ )} + + )} + +
+
+
+ * + + {t('data-analyze.algorithm-forms.custom-path.options.sort_by')} + +
+ ) => { + algorithmAnalyzerStore.mutateCustomPathParams( + 'sort_by', + e.target.value + ); + + if (e.target.value === 'NONE') { + algorithmAnalyzerStore.customPathParams.steps.forEach( + (_, ruleIndex) => { + algorithmAnalyzerStore.mutateCustomPathRuleParams( + 'weight_by', + '', + ruleIndex + ); + + algorithmAnalyzerStore.mutateCustomPathRuleParams( + 'default_weight', + '', + ruleIndex + ); + } + ); + } + }} + > + + {t('data-analyze.algorithm-forms.custom-path.radio-value.none')} + + + {t( + 'data-analyze.algorithm-forms.custom-path.radio-value.ascend' + )} + + + {t( + 'data-analyze.algorithm-forms.custom-path.radio-value.descend' + )} + + +
+
+
+
+
+ + {t('data-analyze.algorithm-forms.custom-path.options.capacity')} + +
+ { + algorithmAnalyzerStore.mutateCustomPathParams( + 'capacity', + e.value as string + ); + + algorithmAnalyzerStore.validateCustomPathParams('capacity'); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateCustomPathParams('capacity'); + } + }} + /> +
+
+
+
+
+ + {t('data-analyze.algorithm-forms.custom-path.options.limit')} + +
+ { + algorithmAnalyzerStore.mutateCustomPathParams( + 'limit', + e.value as string + ); + + algorithmAnalyzerStore.validateCustomPathParams('limit'); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateCustomPathParams('limit'); + } + }} + /> +
+
+
+ + +
+
+ +
+ {algorithmAnalyzerStore.customPathParams.steps.map( + ( + { + uuid, + direction, + labels, + degree, + sample, + properties, + weight_by, + default_weight + }, + ruleIndex + ) => { + return ( +
+
+
+ * + + {t( + 'data-analyze.algorithm-forms.custom-path.options.direction' + )} + +
+ ) => { + algorithmAnalyzerStore.mutateCustomPathRuleParams( + 'direction', + e.target.value, + ruleIndex + ); + }} + > + both + out + in + + {size(algorithmAnalyzerStore.customPathParams.steps) > 1 && ( +
{ + algorithmAnalyzerStore.removeCustomPathRule(ruleIndex); + }} + > + 删除 +
+ )} +
+
+
+ + {t( + 'data-analyze.algorithm-forms.custom-path.options.labels' + )} + +
+ +
+ +
+
+ + {t( + 'data-analyze.algorithm-forms.custom-path.options.properties' + )} + +
+
+ <> + {properties.map(([key, value], propertyIndex) => { + return ( +
+
+ +
+ +
+ { + const clonedRuleProperties = cloneDeep( + properties + ); + + clonedRuleProperties[propertyIndex][1] = + e.value; + + algorithmAnalyzerStore.mutateCustomPathRuleParams( + 'properties', + clonedRuleProperties, + ruleIndex + ); + }} + /> +
+ + {size(properties) > 1 && ( +
{ + algorithmAnalyzerStore.removeCustomPathRuleProperty( + ruleIndex, + propertyIndex + ); + }} + > + {t( + 'data-analyze.algorithm-forms.custom-path.delete' + )} +
+ )} +
+ ); + })} + +
+ { + if ( + allowAddNewRuleProperty && + dataAnalyzeStore.requestStatus.fetchGraphs !== + 'pending' + ) { + algorithmAnalyzerStore.addCustomPathRuleProperty( + ruleIndex + ); + } + }} + > + {t('data-analyze.algorithm-forms.custom-path.add')} + +
+ +
+
+ + {algorithmAnalyzerStore.customPathParams.sort_by !== 'NONE' && ( +
+
+ * + + {t( + 'data-analyze.algorithm-forms.custom-path.options.weight_by' + )} + +
+ +
+ )} + {algorithmAnalyzerStore.customPathParams.steps[ruleIndex] + .weight_by === '__CUSTOM_WEIGHT__' && ( +
+
+ +
+ { + algorithmAnalyzerStore.mutateCustomPathRuleParams( + 'default_weight', + e.value as string, + ruleIndex + ); + + algorithmAnalyzerStore.validateCustomPathRules( + 'default_weight', + ruleIndex + ); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateCustomPathRules( + 'default_weight', + ruleIndex + ); + } + }} + /> +
+ )} +
+
+ + {t( + 'data-analyze.algorithm-forms.custom-path.options.degree' + )} + +
+ { + algorithmAnalyzerStore.mutateCustomPathRuleParams( + 'degree', + e.value as string, + ruleIndex + ); + + algorithmAnalyzerStore.validateCustomPathRules( + 'degree', + ruleIndex + ); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateCustomPathRules( + 'degree', + ruleIndex + ); + } + }} + /> +
+
+
+ + {t( + 'data-analyze.algorithm-forms.custom-path.options.sample' + )} + +
+ { + algorithmAnalyzerStore.mutateCustomPathRuleParams( + 'sample', + e.value as string, + ruleIndex + ); + + algorithmAnalyzerStore.validateCustomPathRules( + 'sample', + ruleIndex + ); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateCustomPathRules( + 'sample', + ruleIndex + ); + } + }} + /> +
+
+ ); + } + )} +
+ { + if (isValidAddRule) { + algorithmAnalyzerStore.addCustomPathRule(); + } + }} + > + {t('data-analyze.algorithm-forms.custom-path.add-new-rule')} + +
+
+
+ ); +}); + +export default CustomPath; diff --git a/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/FocusDetection.tsx b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/FocusDetection.tsx index f73f59212..cbf4e5767 100644 --- a/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/FocusDetection.tsx +++ b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/FocusDetection.tsx @@ -2,16 +2,26 @@ import React, { useContext, createContext } from 'react'; import { observer } from 'mobx-react'; import { Button, Radio, Input, Select, Switch } from 'hubble-ui'; import { useTranslation } from 'react-i18next'; + import { styles } from '../QueryAndAlgorithmLibrary'; import { Tooltip as CustomTooltip } from '../../../common'; import DataAnalyzeStore from '../../../../stores/GraphManagementStore/dataAnalyzeStore/dataAnalyzeStore'; +import { GraphManagementStoreContext } from '../../../../stores'; import QuestionMarkIcon from '../../../../assets/imgs/ic_question_mark.svg'; +import { calcAlgorithmFormWidth } from '../../../../utils'; const FocusDetection = observer(() => { - const { t } = useTranslation(); + const graphManagementStore = useContext(GraphManagementStoreContext); const dataAnalyzeStore = useContext(DataAnalyzeStore); const algorithmAnalyzerStore = dataAnalyzeStore.algorithmAnalyzerStore; + const { t } = useTranslation(); + + const formWidth = calcAlgorithmFormWidth( + graphManagementStore.isExpanded, + 340, + 400 + ); const isValidExec = Object.values( @@ -32,7 +42,7 @@ const FocusDetection = observer(() => {
{ 'data-analyze.algorithm-forms.focus-detection.placeholder.no-edge-types' )} disabled={dataAnalyzeStore.requestStatus.fetchGraphs === 'pending'} - width={400} + width={formWidth} onChange={(value: string) => { algorithmAnalyzerStore.mutateFocusDetectionParams('label', value); }} @@ -98,7 +108,7 @@ const FocusDetection = observer(() => {
{ 'data-analyze.algorithm-forms.focus-detection.options.max_degree' )} +
{
- {t('data-analyze.algorithm-forms.focus-detection.options.limit')} + {t( + 'data-analyze.algorithm-forms.focus-detection.options.capacity' + )}
{ algorithmAnalyzerStore.mutateFocusDetectionParams( - 'limit', + 'capacity', e.value as string ); - algorithmAnalyzerStore.validateFocusDetectionParams('limit'); + algorithmAnalyzerStore.validateFocusDetectionParams('capacity'); }} originInputProps={{ onBlur() { - algorithmAnalyzerStore.validateFocusDetectionParams('limit'); + algorithmAnalyzerStore.validateFocusDetectionParams('capacity'); } }} /> @@ -261,7 +299,7 @@ const FocusDetection = observer(() => { />
{
- {t( - 'data-analyze.algorithm-forms.focus-detection.options.capacity' - )} + {t('data-analyze.algorithm-forms.focus-detection.options.limit')}
{ algorithmAnalyzerStore.mutateFocusDetectionParams( - 'capacity', + 'limit', e.value as string ); - algorithmAnalyzerStore.validateFocusDetectionParams('capacity'); + algorithmAnalyzerStore.validateFocusDetectionParams('limit'); }} originInputProps={{ onBlur() { - algorithmAnalyzerStore.validateFocusDetectionParams('capacity'); + algorithmAnalyzerStore.validateFocusDetectionParams('limit'); } }} /> diff --git a/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/Jaccard.tsx b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/Jaccard.tsx new file mode 100644 index 000000000..12764d537 --- /dev/null +++ b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/Jaccard.tsx @@ -0,0 +1,261 @@ +import React, { useContext } from 'react'; +import { observer } from 'mobx-react'; +import { Button, Radio, Input, Select } from 'hubble-ui'; +import { useTranslation } from 'react-i18next'; + +import { styles } from '../QueryAndAlgorithmLibrary'; +import { Tooltip as CustomTooltip } from '../../../common'; +import { GraphManagementStoreContext } from '../../../../stores'; +import DataAnalyzeStore from '../../../../stores/GraphManagementStore/dataAnalyzeStore/dataAnalyzeStore'; +import { Algorithm } from '../../../../stores/factory/dataAnalyzeStore/algorithmStore'; +import { calcAlgorithmFormWidth } from '../../../../utils'; + +import QuestionMarkIcon from '../../../../assets/imgs/ic_question_mark.svg'; + +const Jaccard = observer(() => { + const graphManagementStore = useContext(GraphManagementStoreContext); + const dataAnalyzeStore = useContext(DataAnalyzeStore); + const { t } = useTranslation(); + const algorithmAnalyzerStore = dataAnalyzeStore.algorithmAnalyzerStore; + + const isValidExec = + Object.values( + algorithmAnalyzerStore.validateJaccardParamsErrorMessage + ).every((value) => value === '') && + algorithmAnalyzerStore.jaccardParams.vertex !== '' && + algorithmAnalyzerStore.jaccardParams.other !== ''; + + const formWidth = calcAlgorithmFormWidth( + graphManagementStore.isExpanded, + 340, + 400 + ); + + return ( +
+
+
+
+ * + + {t('data-analyze.algorithm-forms.jaccard.options.vertex')} + +
+ { + algorithmAnalyzerStore.mutateJaccardParams( + 'vertex', + e.value as string + ); + + algorithmAnalyzerStore.validateJaccardParams('vertex'); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateJaccardParams('vertex'); + } + }} + /> +
+
+
+ + {t('data-analyze.algorithm-forms.jaccard.options.label')} + +
+ +
+
+
+
+
+ * + + {t('data-analyze.algorithm-forms.jaccard.options.other')} + +
+ { + algorithmAnalyzerStore.mutateJaccardParams( + 'other', + e.value as string + ); + + algorithmAnalyzerStore.validateJaccardParams('other'); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateJaccardParams('other'); + } + }} + /> +
+
+
+ + {t('data-analyze.algorithm-forms.jaccard.options.max_degree')} + + +
+ { + algorithmAnalyzerStore.mutateJaccardParams( + 'max_degree', + e.value as string + ); + + algorithmAnalyzerStore.validateJaccardParams('max_degree'); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateJaccardParams('max_degree'); + } + }} + /> +
+
+
+
+
+ * + + {t('data-analyze.algorithm-forms.jaccard.options.direction')} + +
+ ) => { + algorithmAnalyzerStore.mutateJaccardParams( + 'direction', + e.target.value + ); + }} + > + both + out + in + +
+
+
+ + +
+
+ ); +}); + +export default Jaccard; diff --git a/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/KHop.tsx b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/KHop.tsx new file mode 100644 index 000000000..20d434e50 --- /dev/null +++ b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/KHop.tsx @@ -0,0 +1,391 @@ +import React, { useContext } from 'react'; +import { observer } from 'mobx-react'; +import { Button, Radio, Input, Select, Switch } from 'hubble-ui'; +import { useTranslation } from 'react-i18next'; +import { styles } from '../QueryAndAlgorithmLibrary'; + +import { Tooltip as CustomTooltip } from '../../../common'; +import { GraphManagementStoreContext } from '../../../../stores'; +import DataAnalyzeStore from '../../../../stores/GraphManagementStore/dataAnalyzeStore/dataAnalyzeStore'; +import { Algorithm } from '../../../../stores/factory/dataAnalyzeStore/algorithmStore'; +import { calcAlgorithmFormWidth } from '../../../../utils'; + +import QuestionMarkIcon from '../../../../assets/imgs/ic_question_mark.svg'; + +const KHop = observer(() => { + const graphManagementStore = useContext(GraphManagementStoreContext); + const dataAnalyzeStore = useContext(DataAnalyzeStore); + const algorithmAnalyzerStore = dataAnalyzeStore.algorithmAnalyzerStore; + const { t } = useTranslation(); + + const formWidth = calcAlgorithmFormWidth( + graphManagementStore.isExpanded, + 340, + 400 + ); + + const isValidExec = + Object.values(algorithmAnalyzerStore.validateKHopParamsErrorMessage).every( + (value) => value === '' + ) && + algorithmAnalyzerStore.kHopParams.source !== '' && + algorithmAnalyzerStore.kHopParams.max_depth !== ''; + + return ( +
+
+
+
+ * + + {t('data-analyze.algorithm-forms.k-hop.options.source')} + +
+ { + algorithmAnalyzerStore.mutateKHopParams( + 'source', + e.value as string + ); + + algorithmAnalyzerStore.validateKHopParams('source'); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateKHopParams('source'); + } + }} + /> +
+
+
+ {t('data-analyze.algorithm-forms.k-hop.options.label')} +
+ +
+
+
+
+
+ * + + {t('data-analyze.algorithm-forms.k-hop.options.direction')} + +
+ ) => { + algorithmAnalyzerStore.mutateKHopParams( + 'direction', + e.target.value + ); + }} + > + both + out + in + +
+
+
+ + {t('data-analyze.algorithm-forms.k-hop.options.max_degree')} + + +
+ { + algorithmAnalyzerStore.mutateKHopParams( + 'max_degree', + e.value as string + ); + + algorithmAnalyzerStore.validateKHopParams('max_degree'); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateKHopParams('max_degree'); + } + }} + /> +
+
+
+
+
+ * + + {t('data-analyze.algorithm-forms.k-hop.options.max_depth')} + + +
+ { + algorithmAnalyzerStore.mutateKHopParams( + 'max_depth', + e.value as string + ); + + algorithmAnalyzerStore.validateKHopParams('max_depth'); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateKHopParams('max_depth'); + } + }} + /> +
+
+
+ + {t('data-analyze.algorithm-forms.k-hop.options.capacity')} + +
+ { + algorithmAnalyzerStore.mutateKHopParams( + 'capacity', + e.value as string + ); + + algorithmAnalyzerStore.validateKHopParams('capacity'); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateKHopParams('capacity'); + } + }} + /> +
+
+
+
+
+ + {t('data-analyze.algorithm-forms.k-hop.options.nearest')} + + +
+ { + algorithmAnalyzerStore.mutateKHopParams('nearest', checked); + }} + /> +
+
+
+ {t('data-analyze.algorithm-forms.k-hop.options.limit')} +
+ { + algorithmAnalyzerStore.mutateKHopParams( + 'limit', + e.value as string + ); + + algorithmAnalyzerStore.validateKHopParams('limit'); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateKHopParams('limit'); + } + }} + /> +
+
+
+ + +
+
+ ); +}); + +export default KHop; diff --git a/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/KStepNeighbor.tsx b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/KStepNeighbor.tsx new file mode 100644 index 000000000..4f22cdbc3 --- /dev/null +++ b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/KStepNeighbor.tsx @@ -0,0 +1,331 @@ +import React, { useContext } from 'react'; +import { observer } from 'mobx-react'; +import { Button, Radio, Input, Select } from 'hubble-ui'; +import { useTranslation } from 'react-i18next'; + +import { styles } from '../QueryAndAlgorithmLibrary'; +import { Tooltip as CustomTooltip } from '../../../common'; +import { GraphManagementStoreContext } from '../../../../stores'; +import DataAnalyzeStore from '../../../../stores/GraphManagementStore/dataAnalyzeStore/dataAnalyzeStore'; +import { Algorithm } from '../../../../stores/factory/dataAnalyzeStore/algorithmStore'; +import { calcAlgorithmFormWidth } from '../../../../utils'; + +import QuestionMarkIcon from '../../../../assets/imgs/ic_question_mark.svg'; + +const KStepNeighbor = observer(() => { + const graphManagementStore = useContext(GraphManagementStoreContext); + const dataAnalyzeStore = useContext(DataAnalyzeStore); + const algorithmAnalyzerStore = dataAnalyzeStore.algorithmAnalyzerStore; + const { t } = useTranslation(); + + const formWidth = calcAlgorithmFormWidth( + graphManagementStore.isExpanded, + 340, + 400 + ); + + const isValidExec = + Object.values( + algorithmAnalyzerStore.validateKStepNeighborParamsErrorMessage + ).every((value) => value === '') && + algorithmAnalyzerStore.kStepNeighborParams.source !== '' && + algorithmAnalyzerStore.kStepNeighborParams.max_depth !== ''; + + return ( +
+
+
+
+ * + + {t('data-analyze.algorithm-forms.k-step-neighbor.options.source')} + +
+ { + algorithmAnalyzerStore.mutateKStepNeighborParams( + 'source', + e.value as string + ); + + algorithmAnalyzerStore.validateKStepNeighborParams('source'); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateKStepNeighborParams('source'); + } + }} + /> +
+
+
+ + {t('data-analyze.algorithm-forms.k-step-neighbor.options.label')} + +
+ +
+
+
+
+
+ * + + {t( + 'data-analyze.algorithm-forms.k-step-neighbor.options.direction' + )} + +
+ ) => { + algorithmAnalyzerStore.mutateKStepNeighborParams( + 'direction', + e.target.value + ); + }} + > + both + out + in + +
+
+
+ + {t( + 'data-analyze.algorithm-forms.k-step-neighbor.options.max_degree' + )} + + +
+ { + algorithmAnalyzerStore.mutateKStepNeighborParams( + 'max_degree', + e.value as string + ); + + algorithmAnalyzerStore.validateKStepNeighborParams('max_degree'); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateKStepNeighborParams( + 'max_degree' + ); + } + }} + /> +
+
+
+
+
+ * + + {t( + 'data-analyze.algorithm-forms.k-step-neighbor.options.max_depth' + )} + + +
+ { + algorithmAnalyzerStore.mutateKStepNeighborParams( + 'max_depth', + e.value as string + ); + + algorithmAnalyzerStore.validateKStepNeighborParams('max_depth'); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateKStepNeighborParams('max_depth'); + } + }} + /> +
+
+
+ + {t('data-analyze.algorithm-forms.k-step-neighbor.options.limit')} + +
+ { + algorithmAnalyzerStore.mutateKStepNeighborParams( + 'limit', + e.value as string + ); + + algorithmAnalyzerStore.validateKStepNeighborParams('limit'); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateKStepNeighborParams('limit'); + } + }} + /> +
+
+
+ + +
+
+ ); +}); + +export default KStepNeighbor; diff --git a/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/LoopDetection.tsx b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/LoopDetection.tsx index 50f62ebb7..0e24bd92e 100644 --- a/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/LoopDetection.tsx +++ b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/LoopDetection.tsx @@ -1,18 +1,28 @@ -import React, { useContext, createContext } from 'react'; +import React, { useContext } from 'react'; import { observer } from 'mobx-react'; import { Button, Radio, Input, Select, Switch } from 'hubble-ui'; import { useTranslation } from 'react-i18next'; + import { styles } from '../QueryAndAlgorithmLibrary'; import { Tooltip as CustomTooltip } from '../../../common'; import DataAnalyzeStore from '../../../../stores/GraphManagementStore/dataAnalyzeStore/dataAnalyzeStore'; import { Algorithm } from '../../../../stores/factory/dataAnalyzeStore/algorithmStore'; +import { GraphManagementStoreContext } from '../../../../stores'; +import { calcAlgorithmFormWidth } from '../../../../utils'; import QuestionMarkIcon from '../../../../assets/imgs/ic_question_mark.svg'; const LoopDetection = observer(() => { - const { t } = useTranslation(); + const graphManagementStore = useContext(GraphManagementStoreContext); const dataAnalyzeStore = useContext(DataAnalyzeStore); const algorithmAnalyzerStore = dataAnalyzeStore.algorithmAnalyzerStore; + const { t } = useTranslation(); + + const formWidth = calcAlgorithmFormWidth( + graphManagementStore.isExpanded, + 340, + 400 + ); const isValidExec = Object.values( @@ -32,7 +42,7 @@ const LoopDetection = observer(() => {
{ 'data-analyze.algorithm-forms.loop-detection.placeholder.no-edge-types' )} disabled={dataAnalyzeStore.requestStatus.fetchGraphs === 'pending'} - width={400} + width={formWidth} onChange={(value: string) => { algorithmAnalyzerStore.mutateLoopDetectionParams('label', value); }} @@ -121,13 +131,39 @@ const LoopDetection = observer(() => { 'data-analyze.algorithm-forms.loop-detection.options.max_degree' )} +
{ />
{
{
{
{ - const { t } = useTranslation(); + const graphManagementStore = useContext(GraphManagementStoreContext); const dataAnalyzeStore = useContext(DataAnalyzeStore); const algorithmAnalyzerStore = dataAnalyzeStore.algorithmAnalyzerStore; + const { t } = useTranslation(); + + const formWidth = calcAlgorithmFormWidth( + graphManagementStore.isExpanded, + 340, + 390 + ); const sourceType = algorithmAnalyzerStore.modelSimilarityParams.method; + const allowAddNewProperty = !flatten( + algorithmAnalyzerStore.modelSimilarityParams.vertexProperty + ).some((value) => value === ''); + + const addNewPropertyClassName = + allowAddNewProperty && + dataAnalyzeStore.requestStatus.fetchGraphs !== 'pending' + ? 'query-tab-content-internal-expand-manipulation-enable' + : 'query-tab-content-internal-expand-manipulation-disabled'; + + const isValidSourceId = + algorithmAnalyzerStore.modelSimilarityParams.method === 'id' && + algorithmAnalyzerStore.modelSimilarityParams.source !== ''; + + const isValidProperty = + algorithmAnalyzerStore.modelSimilarityParams.method === 'property' && + !( + isEmpty(algorithmAnalyzerStore.modelSimilarityParams.vertexType) && + flatten( + algorithmAnalyzerStore.modelSimilarityParams.vertexProperty + ).every((value) => isEmpty(value)) + ) && + algorithmAnalyzerStore.modelSimilarityParams.vertexProperty.every( + ([key, value]) => (!isEmpty(key) ? !isEmpty(value) : true) + ); + const isValidExec = Object.values( algorithmAnalyzerStore.validateModelSimilartiyParamsErrorMessage ).every((value) => value === '') && algorithmAnalyzerStore.modelSimilarityParams.least_neighbor !== '' && algorithmAnalyzerStore.modelSimilarityParams.similarity !== '' && - (algorithmAnalyzerStore.modelSimilarityParams.property_filter !== '' + (!isEmpty(algorithmAnalyzerStore.modelSimilarityParams.property_filter) ? algorithmAnalyzerStore.modelSimilarityParams.least_property_number !== '' : true) && - ((algorithmAnalyzerStore.modelSimilarityParams.method === 'id' && - algorithmAnalyzerStore.modelSimilarityParams.source !== '') || - algorithmAnalyzerStore.modelSimilarityParams.method === 'property'); + (isValidSourceId || isValidProperty); return ( -
-
-
-
- * - - {t( - 'data-analyze.algorithm-forms.model-similarity.options.method' - )} - -
- ) => { - algorithmAnalyzerStore.switchModelSimilarityMethod( - e.target.value - ); - }} - > - - {t( - 'data-analyze.algorithm-forms.model-similarity.radio-value.specific-id' - )} - - - {t( - 'data-analyze.algorithm-forms.model-similarity.radio-value.filtered-type-property' - )} - - -
-
-
- - {t( - 'data-analyze.algorithm-forms.model-similarity.options.property_filter' - )} - -
- -
-
- -
-
-
- {sourceType === 'id' && *} - - {sourceType === 'id' - ? t( - 'data-analyze.algorithm-forms.model-similarity.options.source' - ) - : t( - 'data-analyze.algorithm-forms.model-similarity.options.vertex-type' - )} - -
- {sourceType === 'id' ? ( - { - algorithmAnalyzerStore.mutateModelSimilarityParams( - 'source', - e.value as string - ); - - algorithmAnalyzerStore.validateModelSimilarityParams('source'); - }} - originInputProps={{ - onBlur() { - algorithmAnalyzerStore.validateModelSimilarityParams( - 'source' - ); - } - }} - /> - ) : ( - - )} + + {t( + 'data-analyze.algorithm-forms.model-similarity.radio-value.specific-id' + )} + + + {t( + 'data-analyze.algorithm-forms.model-similarity.radio-value.filtered-type-property' + )} + + +
-
-
- - {t( - 'data-analyze.algorithm-forms.model-similarity.options.least_property_number' - )} - - +
+
+ * + + {t( + 'data-analyze.algorithm-forms.model-similarity.options.source' + )} + +
+ + + value={algorithmAnalyzerStore.modelSimilarityParams.source} + onChange={(e: any) => { + algorithmAnalyzerStore.mutateModelSimilarityParams( + 'source', + e.value as string + ); + + algorithmAnalyzerStore.validateModelSimilarityParams( + 'source' + ); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateModelSimilarityParams( + 'source' + ); + } + }} + /> +
- { - algorithmAnalyzerStore.mutateModelSimilarityParams( - 'least_property_number', - e.value as string - ); + )} - algorithmAnalyzerStore.validateModelSimilarityParams( - 'least_property_number' - ); - }} - originInputProps={{ - onBlur() { - algorithmAnalyzerStore.validateModelSimilarityParams( - 'least_property_number' - ); - } - }} - /> -
-
+ {sourceType !== 'id' && ( + <> +
+
+
+ + {t( + 'data-analyze.algorithm-forms.model-similarity.options.vertex-type' + )} + +
+ +
+
+ +
+
+
+ + {t( + 'data-analyze.algorithm-forms.model-similarity.options.vertex-property' + )} + +
+ +
+ {algorithmAnalyzerStore.modelSimilarityParams.vertexProperty.map( + ([key, value], propertyIndex) => { + const currentVertexType = dataAnalyzeStore.vertexTypes.find( + ({ name }) => + name === + algorithmAnalyzerStore.modelSimilarityParams + .vertexType + ); -
-
-
- {sourceType === 'id' && *} - - {sourceType === 'id' - ? t( - 'data-analyze.algorithm-forms.model-similarity.options.direction' - ) - : t( - 'data-analyze.algorithm-forms.model-similarity.options.vertex-property' + return ( +
+ +
+ { + const vertexProperty = cloneDeep( + algorithmAnalyzerStore.modelSimilarityParams + .vertexProperty + ); + + vertexProperty[propertyIndex][1] = e.value; + + algorithmAnalyzerStore.mutateModelSimilarityParams( + 'vertexProperty', + vertexProperty + ); + + algorithmAnalyzerStore.validateModelSimilarityParams( + 'vertexProperty' + ); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateModelSimilarityParams( + 'vertexProperty' + ); + } + }} + /> +
+ {size( + algorithmAnalyzerStore.modelSimilarityParams + .vertexProperty + ) > 1 && ( +
{ + algorithmAnalyzerStore.removeModelSimilarityVertexProperty( + propertyIndex + ); + }} + > + {t( + 'data-analyze.algorithm-forms.model-similarity.delete' + )} +
+ )} +
+ ); + } )} -
-
- {sourceType === 'id' ? ( +
+
+
+ {algorithmAnalyzerStore.modelSimilarityParams.vertexType === '' && + !allowAddNewProperty && + size( + algorithmAnalyzerStore.modelSimilarityParams.vertexProperty + ) === 1 ? ( +
+ + {t( + 'data-analyze.algorithm-forms.model-similarity.hint.vertex_type_or_property' + )} + +
+ ) : ( +
+ { + if ( + allowAddNewProperty && + dataAnalyzeStore.requestStatus.fetchGraphs !== 'pending' + ) { + algorithmAnalyzerStore.addModelSimilarityVertexProperty(); + } + }} + > + {t('data-analyze.algorithm-forms.model-similarity.add')} + +
+ )} + + )} + +
+
+
+ * + + {t( + 'data-analyze.algorithm-forms.model-similarity.options.direction' + )} + +
{ out in - ) : ( - - )} -
-
-
- - {t( - 'data-analyze.algorithm-forms.model-similarity.options.max_degree' - )} -
- { - algorithmAnalyzerStore.mutateModelSimilarityParams( - 'max_degree', - e.value as string - ); - - algorithmAnalyzerStore.validateModelSimilarityParams( - 'max_degree' - ); - }} - originInputProps={{ - onBlur() { - algorithmAnalyzerStore.validateModelSimilarityParams( - 'max_degree' - ); - } - }} - />
-
-
-
-
- * - - {sourceType === 'id' - ? t( - 'data-analyze.algorithm-forms.model-similarity.options.least_neighbor' - ) - : t( - 'data-analyze.algorithm-forms.model-similarity.options.direction' - )} - - {sourceType === 'id' && ( +
+
+
+ * + + {t( + 'data-analyze.algorithm-forms.model-similarity.options.least_neighbor' + )} + { }} childrenWrapperElement="img" /> - )} -
- {sourceType === 'id' ? ( +
{ } }} /> - ) : ( - ) => { - algorithmAnalyzerStore.mutateModelSimilarityParams( - 'direction', - e.target.value - ); - }} - > - both - out - in - - )} -
-
-
- - {t( - 'data-analyze.algorithm-forms.model-similarity.options.capacity' - )} -
- { - algorithmAnalyzerStore.mutateModelSimilarityParams( - 'capacity', - e.value as string - ); - - algorithmAnalyzerStore.validateModelSimilarityParams('capacity'); - }} - originInputProps={{ - onBlur() { - algorithmAnalyzerStore.validateModelSimilarityParams( - 'capacity' - ); - } - }} - />
-
-
-
-
- * - - {sourceType === 'id' - ? t( - 'data-analyze.algorithm-forms.model-similarity.options.similarity' - ) - : t( - 'data-analyze.algorithm-forms.model-similarity.options.least_neighbor' - )} - - -
- {sourceType === 'id' ? ( +
+
+
+ * + + {t( + 'data-analyze.algorithm-forms.model-similarity.options.similarity' + )} + + +
+ { } }} /> - ) : ( - +
+ +
+
+
+ + {t( + 'data-analyze.algorithm-forms.model-similarity.options.label' + )} + +
+
- { - algorithmAnalyzerStore.mutateModelSimilarityParams( - 'skip_degree', - e.value as string - ); - - algorithmAnalyzerStore.validateModelSimilarityParams( - 'skip_degree' - ); - }} - originInputProps={{ - onBlur() { - algorithmAnalyzerStore.validateModelSimilarityParams( - 'skip_degree' - ); - } - }} - />
-
-
-
-
- {sourceType !== 'id' && *} - - {sourceType === 'id' - ? t( - 'data-analyze.algorithm-forms.model-similarity.options.label' - ) - : t( - 'data-analyze.algorithm-forms.model-similarity.options.similarity' - )} - - {sourceType !== 'id' && ( +
+
+
+ + {t( + 'data-analyze.algorithm-forms.model-similarity.options.max_similar' + )} + { } }} tooltipWrapper={t( - 'data-analyze.algorithm-forms.model-similarity.hint.similarity' + 'data-analyze.algorithm-forms.model-similarity.hint.max_similar' )} childrenProps={{ src: QuestionMarkIcon, @@ -695,122 +661,176 @@ const ModelSimilarity = observer(() => { }} childrenWrapperElement="img" /> - )} -
- {sourceType === 'id' ? ( - { + placeholder={t( + 'data-analyze.algorithm-forms.model-similarity.placeholder.input-integer' + )} + errorLocation="layer" + errorMessage={ + algorithmAnalyzerStore.validateModelSimilartiyParamsErrorMessage + .max_similar + } + value={algorithmAnalyzerStore.modelSimilarityParams.max_similar} + onChange={(e: any) => { algorithmAnalyzerStore.mutateModelSimilarityParams( - 'label', - value + 'max_similar', + e.value as string + ); + + algorithmAnalyzerStore.validateModelSimilarityParams( + 'max_similar' ); }} - > - - {t('data-analyze.algorithm-forms.model-similarity.pre-value')} - - {dataAnalyzeStore.edgeTypes.map(({ name }) => ( - - {name} - - ))} - - ) : ( + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateModelSimilarityParams( + 'max_similar' + ); + } + }} + /> +
+
+ +
+
+
+ + {t( + 'data-analyze.algorithm-forms.model-similarity.options.least_similar' + )} + +
{ algorithmAnalyzerStore.mutateModelSimilarityParams( - 'similarity', + 'least_similar', e.value as string ); algorithmAnalyzerStore.validateModelSimilarityParams( - 'similarity' + 'least_similar' ); }} originInputProps={{ onBlur() { algorithmAnalyzerStore.validateModelSimilarityParams( - 'similarity' + 'least_similar' ); } }} /> - )} -
-
-
- - {t('data-analyze.algorithm-forms.model-similarity.options.limit')} -
- + +
+ +
-
-
-
- {sourceType === 'id' && *} - - {sourceType === 'id' - ? t( - 'data-analyze.algorithm-forms.model-similarity.options.max_similar' - ) - : t( - 'data-analyze.algorithm-forms.model-similarity.options.label' - )} - - {sourceType === 'id' && ( +
+
+
+
+ + {t( + 'data-analyze.algorithm-forms.model-similarity.options.property_filter' + )} + +
+ +
+
+ +
+
+
+ + {t( + 'data-analyze.algorithm-forms.model-similarity.options.least_property_number' + )} + { } }} tooltipWrapper={t( - 'data-analyze.algorithm-forms.model-similarity.hint.max_similar' + 'data-analyze.algorithm-forms.model-similarity.hint.least_property_number' )} childrenProps={{ src: QuestionMarkIcon, @@ -837,142 +857,54 @@ const ModelSimilarity = observer(() => { }} childrenWrapperElement="img" /> - )} -
- {sourceType === 'id' ? ( +
{ algorithmAnalyzerStore.mutateModelSimilarityParams( - 'max_similar', + 'least_property_number', e.value as string ); algorithmAnalyzerStore.validateModelSimilarityParams( - 'max_similar' + 'least_property_number' ); }} originInputProps={{ onBlur() { algorithmAnalyzerStore.validateModelSimilarityParams( - 'max_similar' + 'least_property_number' ); } }} /> - ) : ( - - )} -
-
-
- - {t( - 'data-analyze.algorithm-forms.model-similarity.options.return_common_connection' - )} - - -
-
- { - algorithmAnalyzerStore.mutateModelSimilarityParams( - 'return_common_connection', - checked - ); - }} - />
-
-
-
-
- {sourceType !== 'id' && *} - - {sourceType === 'id' - ? t( - 'data-analyze.algorithm-forms.model-similarity.options.least_similar' - ) - : t( - 'data-analyze.algorithm-forms.model-similarity.options.max_similar' - )} - - {sourceType !== 'id' && ( +
+
+
+ + {t( + 'data-analyze.algorithm-forms.model-similarity.options.max_degree' + )} + { } }} tooltipWrapper={t( - 'data-analyze.algorithm-forms.model-similarity.hint.max_similar' + 'data-analyze.algorithm-forms.model-similarity.hint.max-degree' )} childrenProps={{ src: QuestionMarkIcon, @@ -999,190 +931,216 @@ const ModelSimilarity = observer(() => { }} childrenWrapperElement="img" /> - )} -
- {sourceType === 'id' ? ( +
{ algorithmAnalyzerStore.mutateModelSimilarityParams( - 'least_similar', + 'max_degree', e.value as string ); algorithmAnalyzerStore.validateModelSimilarityParams( - 'least_similar' + 'max_degree' ); }} originInputProps={{ onBlur() { algorithmAnalyzerStore.validateModelSimilarityParams( - 'least_similar' + 'max_degree' ); } }} /> - ) : ( +
+
+ +
+
+
+ + {t( + 'data-analyze.algorithm-forms.model-similarity.options.capacity' + )} + +
{ algorithmAnalyzerStore.mutateModelSimilarityParams( - 'max_similar', + 'capacity', e.value as string ); algorithmAnalyzerStore.validateModelSimilarityParams( - 'max_similar' + 'capacity' ); }} originInputProps={{ onBlur() { algorithmAnalyzerStore.validateModelSimilarityParams( - 'max_similar' + 'capacity' ); } }} /> - )} -
-
-
- - {t( - 'data-analyze.algorithm-forms.model-similarity.options.return_complete_info' - )} - -
-
- { - algorithmAnalyzerStore.mutateModelSimilarityParams( - 'return_complete_info', - checked - ); - }} - />
-
- {sourceType !== 'id' && (
{t( - 'data-analyze.algorithm-forms.model-similarity.options.least_similar' + 'data-analyze.algorithm-forms.model-similarity.options.limit' )}
{ algorithmAnalyzerStore.mutateModelSimilarityParams( - 'least_similar', + 'limit', e.value as string ); - algorithmAnalyzerStore.validateModelSimilarityParams( - 'least_similar' - ); + algorithmAnalyzerStore.validateModelSimilarityParams('limit'); }} originInputProps={{ onBlur() { - algorithmAnalyzerStore.validateModelSimilarityParams( - 'least_similar' - ); + algorithmAnalyzerStore.validateModelSimilarityParams('limit'); } }} />
- )} -
- - +
+
+
+ + {t( + 'data-analyze.algorithm-forms.model-similarity.options.return_complete_info' + )} + +
+
+ { + algorithmAnalyzerStore.mutateModelSimilarityParams( + 'return_complete_info', + checked + ); + }} + /> +
+
+
); diff --git a/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/NeighborRank.tsx b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/NeighborRank.tsx index 534157134..472919d72 100644 --- a/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/NeighborRank.tsx +++ b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/NeighborRank.tsx @@ -1,22 +1,35 @@ import React, { useContext } from 'react'; import { observer } from 'mobx-react'; -import { size, last } from 'lodash-es'; +import { size } from 'lodash-es'; import { useTranslation } from 'react-i18next'; -import classnames from 'classnames'; import { styles } from '../QueryAndAlgorithmLibrary'; import { Button, Radio, Input, Select } from 'hubble-ui'; import { Tooltip as CustomTooltip } from '../../../common'; +import { GraphManagementStoreContext } from '../../../../stores'; import DataAnalyzeStore from '../../../../stores/GraphManagementStore/dataAnalyzeStore/dataAnalyzeStore'; +import { calcAlgorithmFormWidth } from '../../../../utils'; import QuestionMarkIcon from '../../../../assets/imgs/ic_question_mark.svg'; import { Algorithm } from '../../../../stores/factory/dataAnalyzeStore/algorithmStore'; -import { NeighborRankRule } from '../../../../stores/types/GraphManagementStore/dataAnalyzeStore'; const NeighborRank = observer(() => { - const { t } = useTranslation(); + const graphManagementStore = useContext(GraphManagementStoreContext); const dataAnalyzeStore = useContext(DataAnalyzeStore); const algorithmAnalyzerStore = dataAnalyzeStore.algorithmAnalyzerStore; + const { t } = useTranslation(); + + const formWidth = calcAlgorithmFormWidth( + graphManagementStore.isExpanded, + 340, + 400 + ); + + const formWidthInStep = calcAlgorithmFormWidth( + graphManagementStore.isExpanded, + 310, + 380 + ); const isValidExec = Object.values( @@ -28,17 +41,9 @@ const NeighborRank = observer(() => { algorithmAnalyzerStore.neighborRankParams.source !== '' && algorithmAnalyzerStore.neighborRankParams.alpha !== ''; - const isValidAddRule = - algorithmAnalyzerStore.validateNeighborRankParamsParamsErrorMessage.steps.every( - (step) => Object.values(step).every((value) => value === '') - ) && algorithmAnalyzerStore.duplicateNeighborRankRuleSet.size === 0; - - const invalidExtendFormClassname = (flag: boolean) => { - return classnames({ - 'query-tab-content-form-expand-items': true, - 'query-tab-content-form-expand-items-invalid': flag - }); - }; + const isValidAddRule = algorithmAnalyzerStore.validateNeighborRankParamsParamsErrorMessage.steps.every( + (step) => Object.values(step).every((value) => value === '') + ); return (
@@ -55,7 +60,7 @@ const NeighborRank = observer(() => {
{
{ />
-
-
-
- * - - {t( - 'data-analyze.algorithm-forms.neighbor-rank.options.direction' - )} - -
- ) => { - algorithmAnalyzerStore.mutateNeighborRankParams( - 'direction', - e.target.value - ); - }} - > - both - out - in - -
-
{
{ const timerId = dataAnalyzeStore.addTempExecLog(); await dataAnalyzeStore.fetchGraphs({ url: 'neighborrank', - type: Algorithm.neighborRankRecommendation + type: Algorithm.neighborRank }); await dataAnalyzeStore.fetchExecutionLogs(); window.clearTimeout(timerId); @@ -231,7 +205,7 @@ const NeighborRank = observer(() => { style={styles.primaryButton} disabled={dataAnalyzeStore.requestStatus.fetchGraphs === 'pending'} onClick={() => { - algorithmAnalyzerStore.resetShortestPathAllParams(); + algorithmAnalyzerStore.resetNeighborRankParams(); }} > {t('data-analyze.manipulations.reset')} @@ -244,13 +218,9 @@ const NeighborRank = observer(() => { style={{ width: '50%' }} > {algorithmAnalyzerStore.neighborRankParams.steps.map( - ({ uuid, direction, label, degree, top }, ruleIndex) => { + ({ uuid, direction, labels, degree, top }, ruleIndex) => { return ( -
+
* @@ -271,10 +241,6 @@ const NeighborRank = observer(() => { e.target.value, ruleIndex ); - - algorithmAnalyzerStore.validateDuplicateNeighborRankRules( - uuid - ); }} > both @@ -285,7 +251,7 @@ const NeighborRank = observer(() => { 1 && (
{ algorithmAnalyzerStore.removeNeighborRankRule( ruleIndex ); - - algorithmAnalyzerStore.validateDuplicateNeighborRankRules( - uuid - ); }} > {t('addition.common.del')} @@ -316,24 +278,20 @@ const NeighborRank = observer(() => { { 'degree', ruleIndex ); - - algorithmAnalyzerStore.validateDuplicateNeighborRankRules( - uuid - ); }} originInputProps={{ onBlur() { @@ -434,13 +414,13 @@ const NeighborRank = observer(() => { />
{ 'top', ruleIndex ); - - algorithmAnalyzerStore.validateDuplicateNeighborRankRules( - uuid - ); }} originInputProps={{ onBlur() { @@ -488,42 +464,16 @@ const NeighborRank = observer(() => { marginTop: 8 }} > - {algorithmAnalyzerStore.duplicateNeighborRankRuleSet.size === 0 ? ( - { - if (isValidAddRule) { - algorithmAnalyzerStore.addNeighborRankRule(); - - algorithmAnalyzerStore.validateDuplicateNeighborRankRules( - ( - last( - algorithmAnalyzerStore.neighborRankParams.steps - ) as NeighborRankRule - ).uuid - ); - } - }} - > - {t('data-analyze.algorithm-forms.neighbor-rank.add-new-rule')} - - ) : ( -
- {t( - 'data-analyze.algorithm-forms.neighbor-rank.validations.input-chars' - )} -
- )} + { + if (isValidAddRule) { + algorithmAnalyzerStore.addNeighborRankRule(); + } + }} + > + {t('data-analyze.algorithm-forms.neighbor-rank.add-new-rule')} +
diff --git a/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/PersonalRank.tsx b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/PersonalRank.tsx new file mode 100644 index 000000000..a83ba4a42 --- /dev/null +++ b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/PersonalRank.tsx @@ -0,0 +1,434 @@ +import React, { useContext } from 'react'; +import { observer } from 'mobx-react'; +import { Button, Radio, Input, Select, Switch } from 'hubble-ui'; +import { useTranslation } from 'react-i18next'; + +import { styles } from '../QueryAndAlgorithmLibrary'; +import { Tooltip as CustomTooltip } from '../../../common'; +import { GraphManagementStoreContext } from '../../../../stores'; +import DataAnalyzeStore from '../../../../stores/GraphManagementStore/dataAnalyzeStore/dataAnalyzeStore'; +import { Algorithm } from '../../../../stores/factory/dataAnalyzeStore/algorithmStore'; +import { calcAlgorithmFormWidth } from '../../../../utils'; + +import QuestionMarkIcon from '../../../../assets/imgs/ic_question_mark.svg'; + +const PersonalRank = observer(() => { + const graphManagementStore = useContext(GraphManagementStoreContext); + const dataAnalyzeStore = useContext(DataAnalyzeStore); + const algorithmAnalyzerStore = dataAnalyzeStore.algorithmAnalyzerStore; + const { t } = useTranslation(); + + const formWidth = calcAlgorithmFormWidth( + graphManagementStore.isExpanded, + 340, + 400 + ); + + const isValidExec = + Object.values( + algorithmAnalyzerStore.validatePersonalRankErrorMessage + ).every((value) => value === '') && + algorithmAnalyzerStore.personalRankParams.source !== '' && + algorithmAnalyzerStore.personalRankParams.alpha !== '' && + algorithmAnalyzerStore.personalRankParams.label !== '' && + algorithmAnalyzerStore.personalRankParams.max_depth !== ''; + + return ( +
+
+
+
+ * + + {t('data-analyze.algorithm-forms.personal-rank.options.source')} + +
+ { + algorithmAnalyzerStore.mutatePersonalRankParams( + 'source', + e.value as string + ); + + algorithmAnalyzerStore.validatePersonalRankParams('source'); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validatePersonalRankParams('source'); + } + }} + /> +
+
+
+ * + + {t('data-analyze.algorithm-forms.personal-rank.options.label')} + +
+ +
+
+
+
+
+ * + + {t('data-analyze.algorithm-forms.personal-rank.options.alpha')} + +
+ { + algorithmAnalyzerStore.mutatePersonalRankParams( + 'alpha', + e.value as string + ); + + algorithmAnalyzerStore.validatePersonalRankParams('alpha'); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validatePersonalRankParams('alpha'); + } + }} + /> +
+
+
+ + {t('data-analyze.algorithm-forms.personal-rank.options.degree')} + + +
+ { + algorithmAnalyzerStore.mutatePersonalRankParams( + 'degree', + e.value as string + ); + + algorithmAnalyzerStore.validatePersonalRankParams('degree'); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validatePersonalRankParams('degree'); + } + }} + /> +
+
+
+
+
+ * + + {t( + 'data-analyze.algorithm-forms.personal-rank.options.max_depth' + )} + +
+ { + algorithmAnalyzerStore.mutatePersonalRankParams( + 'max_depth', + e.value as string + ); + + algorithmAnalyzerStore.validatePersonalRankParams('max_depth'); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validatePersonalRankParams('max_depth'); + } + }} + /> +
+
+
+ + {t('data-analyze.algorithm-forms.personal-rank.options.limit')} + +
+ { + algorithmAnalyzerStore.mutatePersonalRankParams( + 'limit', + e.value as string + ); + + algorithmAnalyzerStore.validatePersonalRankParams('limit'); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validatePersonalRankParams('limit'); + } + }} + /> +
+
+
+
+
+ * + + {t( + 'data-analyze.algorithm-forms.personal-rank.options.with_label' + )} + + +
+ ) => { + algorithmAnalyzerStore.mutatePersonalRankParams( + 'with_label', + e.target.value + ); + }} + > + + {t( + 'data-analyze.algorithm-forms.personal-rank.with-label-radio-value.same_label' + )} + + + {t( + 'data-analyze.algorithm-forms.personal-rank.with-label-radio-value.other_label' + )} + + + {t( + 'data-analyze.algorithm-forms.personal-rank.with-label-radio-value.both_label' + )} + + +
+
+
+ + {t('data-analyze.algorithm-forms.personal-rank.options.sorted')} + + +
+
+ { + algorithmAnalyzerStore.mutatePersonalRankParams( + 'sorted', + checked + ); + }} + /> +
+
+
+
+ + +
+
+ ); +}); + +export default PersonalRank; diff --git a/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/RadiographicInspection.tsx b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/RadiographicInspection.tsx new file mode 100644 index 000000000..6eec09604 --- /dev/null +++ b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/RadiographicInspection.tsx @@ -0,0 +1,405 @@ +import React, { useContext } from 'react'; +import { observer } from 'mobx-react'; +import { Button, Radio, Input, Select } from 'hubble-ui'; +import { useTranslation } from 'react-i18next'; + +import { styles } from '../QueryAndAlgorithmLibrary'; +import { Tooltip as CustomTooltip } from '../../../common'; +import { GraphManagementStoreContext } from '../../../../stores'; +import DataAnalyzeStore from '../../../../stores/GraphManagementStore/dataAnalyzeStore/dataAnalyzeStore'; +import { Algorithm } from '../../../../stores/factory/dataAnalyzeStore/algorithmStore'; +import { calcAlgorithmFormWidth } from '../../../../utils'; + +import QuestionMarkIcon from '../../../../assets/imgs/ic_question_mark.svg'; + +const RadiographicInspection = observer(() => { + const graphManagementStore = useContext(GraphManagementStoreContext); + const dataAnalyzeStore = useContext(DataAnalyzeStore); + const algorithmAnalyzerStore = dataAnalyzeStore.algorithmAnalyzerStore; + const { t } = useTranslation(); + + const formWidth = calcAlgorithmFormWidth( + graphManagementStore.isExpanded, + 340, + 400 + ); + + const isValidExec = + Object.values( + algorithmAnalyzerStore.validateRadiographicInspectionParamsErrorMessage + ).every((value) => value === '') && + algorithmAnalyzerStore.radiographicInspectionParams.source !== '' && + algorithmAnalyzerStore.radiographicInspectionParams.max_depth !== ''; + + return ( +
+
+
+
+ * + + {t( + 'data-analyze.algorithm-forms.radiographic-inspection.options.source' + )} + +
+ { + algorithmAnalyzerStore.mutateRadiographicInspectionParams( + 'source', + e.value as string + ); + + algorithmAnalyzerStore.validateRadiographicInspectionParams( + 'source' + ); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateRadiographicInspectionParams( + 'source' + ); + } + }} + /> +
+
+
+ + {t( + 'data-analyze.algorithm-forms.radiographic-inspection.options.label' + )} + +
+ +
+
+
+
+
+ * + + {t( + 'data-analyze.algorithm-forms.radiographic-inspection.options.direction' + )} + +
+ ) => { + algorithmAnalyzerStore.mutateRadiographicInspectionParams( + 'direction', + e.target.value + ); + }} + > + both + out + in + +
+
+
+ + {t( + 'data-analyze.algorithm-forms.radiographic-inspection.options.max_degree' + )} + + +
+ { + algorithmAnalyzerStore.mutateRadiographicInspectionParams( + 'max_degree', + e.value as string + ); + + algorithmAnalyzerStore.validateRadiographicInspectionParams( + 'max_degree' + ); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateRadiographicInspectionParams( + 'max_degree' + ); + } + }} + /> +
+
+
+
+
+ * + + {t( + 'data-analyze.algorithm-forms.radiographic-inspection.options.max_depth' + )} + + +
+ { + algorithmAnalyzerStore.mutateRadiographicInspectionParams( + 'max_depth', + e.value as string + ); + + algorithmAnalyzerStore.validateRadiographicInspectionParams( + 'max_depth' + ); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateRadiographicInspectionParams( + 'max_depth' + ); + } + }} + /> +
+
+
+ + {t( + 'data-analyze.algorithm-forms.radiographic-inspection.options.capacity' + )} + +
+ { + algorithmAnalyzerStore.mutateRadiographicInspectionParams( + 'capacity', + e.value as string + ); + + algorithmAnalyzerStore.validateRadiographicInspectionParams( + 'capacity' + ); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateRadiographicInspectionParams( + 'capacity' + ); + } + }} + /> +
+
+
+
+
+
+ + {t( + 'data-analyze.algorithm-forms.radiographic-inspection.options.limit' + )} + +
+ { + algorithmAnalyzerStore.mutateRadiographicInspectionParams( + 'limit', + e.value as string + ); + + algorithmAnalyzerStore.validateRadiographicInspectionParams( + 'limit' + ); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateRadiographicInspectionParams( + 'limit' + ); + } + }} + /> +
+
+
+ + +
+
+ ); +}); + +export default RadiographicInspection; diff --git a/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/SameNeighbor.tsx b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/SameNeighbor.tsx new file mode 100644 index 000000000..d7afc7097 --- /dev/null +++ b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/SameNeighbor.tsx @@ -0,0 +1,301 @@ +import React, { useContext } from 'react'; +import { observer } from 'mobx-react'; +import { Button, Radio, Input, Select } from 'hubble-ui'; +import { useTranslation } from 'react-i18next'; + +import { styles } from '../QueryAndAlgorithmLibrary'; +import { Tooltip as CustomTooltip } from '../../../common'; +import { GraphManagementStoreContext } from '../../../../stores'; +import DataAnalyzeStore from '../../../../stores/GraphManagementStore/dataAnalyzeStore/dataAnalyzeStore'; +import { Algorithm } from '../../../../stores/factory/dataAnalyzeStore/algorithmStore'; +import { calcAlgorithmFormWidth } from '../../../../utils'; + +import QuestionMarkIcon from '../../../../assets/imgs/ic_question_mark.svg'; + +const SameNeighbor = observer(() => { + const graphManagementStore = useContext(GraphManagementStoreContext); + const dataAnalyzeStore = useContext(DataAnalyzeStore); + const algorithmAnalyzerStore = dataAnalyzeStore.algorithmAnalyzerStore; + const { t } = useTranslation(); + + const formWidth = calcAlgorithmFormWidth( + graphManagementStore.isExpanded, + 340, + 400 + ); + + const isValidExec = + Object.values( + algorithmAnalyzerStore.validateSameNeighborParamsErrorMessage + ).every((value) => value === '') && + algorithmAnalyzerStore.sameNeighborParams.vertex !== '' && + algorithmAnalyzerStore.sameNeighborParams.other !== ''; + + return ( +
+
+
+
+ * + + {t('data-analyze.algorithm-forms.same-neighbor.options.vertex')} + +
+ { + algorithmAnalyzerStore.mutateSameNeighborParams( + 'vertex', + e.value as string + ); + + algorithmAnalyzerStore.validateSameNeighborParams('vertex'); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateSameNeighborParams('vertex'); + } + }} + /> +
+
+
+ + {t('data-analyze.algorithm-forms.same-neighbor.options.label')} + +
+ +
+
+
+
+
+ * + + {t('data-analyze.algorithm-forms.same-neighbor.options.other')} + +
+ { + algorithmAnalyzerStore.mutateSameNeighborParams( + 'other', + e.value as string + ); + + algorithmAnalyzerStore.validateSameNeighborParams('other'); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateSameNeighborParams('other'); + } + }} + /> +
+
+
+ + {t( + 'data-analyze.algorithm-forms.same-neighbor.options.max_degree' + )} + + +
+ { + algorithmAnalyzerStore.mutateSameNeighborParams( + 'max_degree', + e.value as string + ); + + algorithmAnalyzerStore.validateSameNeighborParams('max_degree'); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateSameNeighborParams('max_degree'); + } + }} + /> +
+
+
+
+
+ * + + {t( + 'data-analyze.algorithm-forms.same-neighbor.options.direction' + )} + +
+ ) => { + algorithmAnalyzerStore.mutateSameNeighborParams( + 'direction', + e.target.value + ); + }} + > + both + out + in + +
+
+
+ + {t('data-analyze.algorithm-forms.same-neighbor.options.limit')} + +
+ { + algorithmAnalyzerStore.mutateSameNeighborParams( + 'limit', + e.value as string + ); + + algorithmAnalyzerStore.validateSameNeighborParams('limit'); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateSameNeighborParams('limit'); + } + }} + /> +
+
+
+ + +
+
+ ); +}); + +export default SameNeighbor; diff --git a/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/ShortestPath.tsx b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/ShortestPath.tsx new file mode 100644 index 000000000..3e1507b63 --- /dev/null +++ b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/ShortestPath.tsx @@ -0,0 +1,437 @@ +import React, { useContext } from 'react'; +import { observer } from 'mobx-react'; +import { Button, Radio, Input, Select } from 'hubble-ui'; +import { useTranslation } from 'react-i18next'; +import { styles } from '../QueryAndAlgorithmLibrary'; +import { Tooltip as CustomTooltip } from '../../../common'; + +import { GraphManagementStoreContext } from '../../../../stores'; +import DataAnalyzeStore from '../../../../stores/GraphManagementStore/dataAnalyzeStore/dataAnalyzeStore'; + +import QuestionMarkIcon from '../../../../assets/imgs/ic_question_mark.svg'; +import { Algorithm } from '../../../../stores/factory/dataAnalyzeStore/algorithmStore'; +import { calcAlgorithmFormWidth } from '../../../../utils'; + +const ShortestPath = observer(() => { + const graphManagementStore = useContext(GraphManagementStoreContext); + const dataAnalyzeStore = useContext(DataAnalyzeStore); + const algorithmAnalyzerStore = dataAnalyzeStore.algorithmAnalyzerStore; + const { t } = useTranslation(); + + const formWidth = calcAlgorithmFormWidth( + graphManagementStore.isExpanded, + 340, + 400 + ); + + const isValidExec = + Object.values( + algorithmAnalyzerStore.validateShortestPathParamsErrorMessage + ).every((value) => value === '') && + algorithmAnalyzerStore.shortestPathAlgorithmParams.source !== '' && + algorithmAnalyzerStore.shortestPathAlgorithmParams.target !== '' && + algorithmAnalyzerStore.shortestPathAlgorithmParams.max_depth !== ''; + + return ( +
+
+
+
+ * + + {t('data-analyze.algorithm-forms.shortest-path.options.source')} + +
+ { + algorithmAnalyzerStore.mutateShortestPathParams( + 'source', + e.value as string + ); + + algorithmAnalyzerStore.validateShortestPathParams('source'); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateShortestPathParams('source'); + } + }} + /> +
+
+
+ + {t('data-analyze.algorithm-forms.shortest-path.options.label')} + +
+ +
+
+
+
+
+ * + + {t('data-analyze.algorithm-forms.shortest-path.options.target')} + +
+ { + algorithmAnalyzerStore.mutateShortestPathParams( + 'target', + e.value as string + ); + + algorithmAnalyzerStore.validateShortestPathParams('target'); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateShortestPathParams('target'); + } + }} + /> +
+
+
+ + {t( + 'data-analyze.algorithm-forms.shortest-path.options.max_degree' + )} + + +
+ { + algorithmAnalyzerStore.mutateShortestPathParams( + 'max_degree', + e.value as string + ); + + algorithmAnalyzerStore.validateShortestPathParams('max_degree'); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateShortestPathParams('max_degree'); + } + }} + /> +
+
+
+
+
+ * + + {t( + 'data-analyze.algorithm-forms.shortest-path.options.direction' + )} + +
+ ) => { + algorithmAnalyzerStore.mutateShortestPathParams( + 'direction', + e.target.value + ); + }} + > + both + out + in + +
+
+
+ + {t( + 'data-analyze.algorithm-forms.shortest-path.options.skip_degree' + )} + + +
+ { + algorithmAnalyzerStore.mutateShortestPathParams( + 'skip_degree', + e.value as string + ); + + algorithmAnalyzerStore.validateShortestPathParams('skip_degree'); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateShortestPathParams( + 'skip_degree' + ); + } + }} + /> +
+
+
+
+
+ * + + {t( + 'data-analyze.algorithm-forms.shortest-path.options.max_depth' + )} + + +
+ { + algorithmAnalyzerStore.mutateShortestPathParams( + 'max_depth', + e.value as string + ); + + algorithmAnalyzerStore.validateShortestPathParams('max_depth'); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateShortestPathParams('max_depth'); + } + }} + /> +
+
+
+ + {t('data-analyze.algorithm-forms.shortest-path.options.capacity')} + +
+ { + algorithmAnalyzerStore.mutateShortestPathParams( + 'capacity', + e.value as string + ); + + algorithmAnalyzerStore.validateShortestPathParams('capacity'); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateShortestPathParams('capacity'); + } + }} + /> +
+
+
+ + +
+
+ ); +}); + +export default ShortestPath; diff --git a/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/ShortestPathAll.tsx b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/ShortestPathAll.tsx index 82ec79ab3..ff2caedce 100644 --- a/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/ShortestPathAll.tsx +++ b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/ShortestPathAll.tsx @@ -2,21 +2,35 @@ import React, { useContext, createContext } from 'react'; import { observer } from 'mobx-react'; import { Button, Radio, Input, Select, Switch } from 'hubble-ui'; import { useTranslation } from 'react-i18next'; + import { styles } from '../QueryAndAlgorithmLibrary'; import { Tooltip as CustomTooltip } from '../../../common'; +import { GraphManagementStoreContext } from '../../../../stores'; import DataAnalyzeStore from '../../../../stores/GraphManagementStore/dataAnalyzeStore/dataAnalyzeStore'; +import { calcAlgorithmFormWidth } from '../../../../utils'; import QuestionMarkIcon from '../../../../assets/imgs/ic_question_mark.svg'; import { Algorithm } from '../../../../stores/factory/dataAnalyzeStore/algorithmStore'; const ShortestPathAll = observer(() => { - const { t } = useTranslation(); + const graphManagementStore = useContext(GraphManagementStoreContext); const dataAnalyzeStore = useContext(DataAnalyzeStore); const algorithmAnalyzerStore = dataAnalyzeStore.algorithmAnalyzerStore; + const { t } = useTranslation(); + + const formWidth = calcAlgorithmFormWidth( + graphManagementStore.isExpanded, + 340, + 400 + ); - const isValidExec = !Object.values( - algorithmAnalyzerStore.shortestPathAllParams - ).some((value) => value === ''); + const isValidExec = + Object.values( + algorithmAnalyzerStore.validateShortestPathAllParamsErrorMessage + ).every((value) => value === '') && + algorithmAnalyzerStore.shortestPathAllParams.source !== '' && + algorithmAnalyzerStore.shortestPathAllParams.target !== '' && + algorithmAnalyzerStore.shortestPathAllParams.max_depth !== ''; return (
@@ -31,7 +45,7 @@ const ShortestPathAll = observer(() => {
{ 'data-analyze.algorithm-forms.shortest-path-all.placeholder.no-edge-types' )} disabled={dataAnalyzeStore.requestStatus.fetchGraphs === 'pending'} - width={400} + width={formWidth} onChange={(value: string) => { algorithmAnalyzerStore.mutateShortestPathAllParams( 'label', @@ -104,11 +118,11 @@ const ShortestPathAll = observer(() => {
{ 'data-analyze.algorithm-forms.shortest-path-all.options.max_degree' )} +
{
{ />
{ />
{ + const graphManagementStore = useContext(GraphManagementStoreContext); + const dataAnalyzeStore = useContext(DataAnalyzeStore); + const algorithmAnalyzerStore = dataAnalyzeStore.algorithmAnalyzerStore; + const { t } = useTranslation(); + + const formWidth = calcAlgorithmFormWidth( + graphManagementStore.isExpanded, + 340, + 380 + ); + + const isValidExec = + Object.values( + algorithmAnalyzerStore.validateSingleSourceWeightedShortestPathParamsErrorMessage + ).every((value) => value === '') && + algorithmAnalyzerStore.singleSourceWeightedShortestPathParams.source !== ''; + + return ( +
+
+
+
+ * + + {t( + 'data-analyze.algorithm-forms.single-source-weighted-shortest-path.options.source' + )} + +
+ { + algorithmAnalyzerStore.mutateSingleSourceWeightedShortestPathParams( + 'source', + e.value as string + ); + + algorithmAnalyzerStore.validateSingleSourceWeightedShortestPathParams( + 'source' + ); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateSingleSourceWeightedShortestPathParams( + 'source' + ); + } + }} + /> +
+
+
+ + {t( + 'data-analyze.algorithm-forms.single-source-weighted-shortest-path.options.label' + )} + +
+ +
+
+
+
+
+ * + + {t( + 'data-analyze.algorithm-forms.single-source-weighted-shortest-path.options.direction' + )} + +
+ ) => { + algorithmAnalyzerStore.mutateSingleSourceWeightedShortestPathParams( + 'direction', + e.target.value + ); + }} + > + both + out + in + +
+
+
+ + {t( + 'data-analyze.algorithm-forms.single-source-weighted-shortest-path.options.max_degree' + )} + + +
+ { + algorithmAnalyzerStore.mutateSingleSourceWeightedShortestPathParams( + 'max_degree', + e.value as string + ); + + algorithmAnalyzerStore.validateSingleSourceWeightedShortestPathParams( + 'max_degree' + ); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateSingleSourceWeightedShortestPathParams( + 'max_degree' + ); + } + }} + /> +
+
+
+
+
+ + {t( + 'data-analyze.algorithm-forms.single-source-weighted-shortest-path.options.weight' + )} + +
+ +
+
+
+ + {t( + 'data-analyze.algorithm-forms.single-source-weighted-shortest-path.options.skip_degree' + )} + + +
+ { + algorithmAnalyzerStore.mutateSingleSourceWeightedShortestPathParams( + 'skip_degree', + e.value as string + ); + + algorithmAnalyzerStore.validateSingleSourceWeightedShortestPathParams( + 'skip_degree' + ); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateSingleSourceWeightedShortestPathParams( + 'skip_degree' + ); + } + }} + /> +
+
+
+
+
+ + {t( + 'data-analyze.algorithm-forms.single-source-weighted-shortest-path.options.with_vertex' + )} + +
+ { + algorithmAnalyzerStore.mutateSingleSourceWeightedShortestPathParams( + 'with_vertex', + checked + ); + }} + /> +
+
+
+ + {t( + 'data-analyze.algorithm-forms.single-source-weighted-shortest-path.options.capacity' + )} + +
+ { + algorithmAnalyzerStore.mutateSingleSourceWeightedShortestPathParams( + 'capacity', + e.value as string + ); + + algorithmAnalyzerStore.validateSingleSourceWeightedShortestPathParams( + 'capacity' + ); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateSingleSourceWeightedShortestPathParams( + 'capacity' + ); + } + }} + /> +
+
+
+
+
+
+ + {t( + 'data-analyze.algorithm-forms.single-source-weighted-shortest-path.options.limit' + )} + +
+ { + algorithmAnalyzerStore.mutateSingleSourceWeightedShortestPathParams( + 'limit', + e.value as string + ); + + algorithmAnalyzerStore.validateSingleSourceWeightedShortestPathParams( + 'limit' + ); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateSingleSourceWeightedShortestPathParams( + 'limit' + ); + } + }} + /> +
+
+
+ + +
+
+ ); +}); + +export default SingleSourceWeightedShortestPath; diff --git a/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/WeightedShortestPath.tsx b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/WeightedShortestPath.tsx new file mode 100644 index 000000000..07fcdc2af --- /dev/null +++ b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/algorithm/WeightedShortestPath.tsx @@ -0,0 +1,483 @@ +import React, { useContext } from 'react'; +import { observer } from 'mobx-react'; +import { Button, Radio, Input, Select, Switch } from 'hubble-ui'; +import { useTranslation } from 'react-i18next'; +import { styles } from '../QueryAndAlgorithmLibrary'; +import { Tooltip as CustomTooltip } from '../../../common'; + +import { GraphManagementStoreContext } from '../../../../stores'; +import DataAnalyzeStore from '../../../../stores/GraphManagementStore/dataAnalyzeStore/dataAnalyzeStore'; +import { Algorithm } from '../../../../stores/factory/dataAnalyzeStore/algorithmStore'; +import { isDataTypeNumeric, calcAlgorithmFormWidth } from '../../../../utils'; + +import QuestionMarkIcon from '../../../../assets/imgs/ic_question_mark.svg'; + +const WeightedShortestPath = observer(() => { + const graphManagementStore = useContext(GraphManagementStoreContext); + const dataAnalyzeStore = useContext(DataAnalyzeStore); + const algorithmAnalyzerStore = dataAnalyzeStore.algorithmAnalyzerStore; + const { t } = useTranslation(); + + const formWidth = calcAlgorithmFormWidth( + graphManagementStore.isExpanded, + 320, + 390 + ); + + const isValidExec = + Object.values( + algorithmAnalyzerStore.validateWeightedShortestPathParamsErrorMessage + ).every((value) => value === '') && + algorithmAnalyzerStore.weightedShortestPathParams.source !== '' && + algorithmAnalyzerStore.weightedShortestPathParams.target !== '' && + algorithmAnalyzerStore.weightedShortestPathParams.weight !== ''; + + return ( +
+
+
+
+ * + + {t( + 'data-analyze.algorithm-forms.weighted-shortest-path.options.source' + )} + +
+ { + algorithmAnalyzerStore.mutateWeightedShortestPathParams( + 'source', + e.value as string + ); + + algorithmAnalyzerStore.validateWeightedShortestPathParams( + 'source' + ); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateWeightedShortestPathParams( + 'source' + ); + } + }} + /> +
+
+
+ + {t( + 'data-analyze.algorithm-forms.weighted-shortest-path.options.label' + )} + +
+ +
+
+
+
+
+ * + + {t( + 'data-analyze.algorithm-forms.weighted-shortest-path.options.target' + )} + +
+ { + algorithmAnalyzerStore.mutateWeightedShortestPathParams( + 'target', + e.value as string + ); + + algorithmAnalyzerStore.validateWeightedShortestPathParams( + 'target' + ); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateWeightedShortestPathParams( + 'target' + ); + } + }} + /> +
+
+
+ + {t( + 'data-analyze.algorithm-forms.weighted-shortest-path.options.max_degree' + )} + + +
+ { + algorithmAnalyzerStore.mutateWeightedShortestPathParams( + 'max_degree', + e.value as string + ); + + algorithmAnalyzerStore.validateWeightedShortestPathParams( + 'max_degree' + ); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateWeightedShortestPathParams( + 'max_degree' + ); + } + }} + /> +
+
+
+
+
+ * + + {t( + 'data-analyze.algorithm-forms.weighted-shortest-path.options.direction' + )} + +
+ ) => { + algorithmAnalyzerStore.mutateWeightedShortestPathParams( + 'direction', + e.target.value + ); + }} + > + both + out + in + +
+
+
+ + {t( + 'data-analyze.algorithm-forms.weighted-shortest-path.options.skip_degree' + )} + + +
+ { + algorithmAnalyzerStore.mutateWeightedShortestPathParams( + 'skip_degree', + e.value as string + ); + + algorithmAnalyzerStore.validateWeightedShortestPathParams( + 'skip_degree' + ); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateWeightedShortestPathParams( + 'skip_degree' + ); + } + }} + /> +
+
+
+
+
+ * + + {t( + 'data-analyze.algorithm-forms.weighted-shortest-path.options.weight' + )} + +
+ +
+
+
+ + {t( + 'data-analyze.algorithm-forms.weighted-shortest-path.options.capacity' + )} + +
+ { + algorithmAnalyzerStore.mutateWeightedShortestPathParams( + 'capacity', + e.value as string + ); + + algorithmAnalyzerStore.validateWeightedShortestPathParams( + 'capacity' + ); + }} + originInputProps={{ + onBlur() { + algorithmAnalyzerStore.validateWeightedShortestPathParams( + 'capacity' + ); + } + }} + /> +
+
+
+
+
+ + {t( + 'data-analyze.algorithm-forms.weighted-shortest-path.options.with_vertex' + )} + +
+ { + algorithmAnalyzerStore.mutateWeightedShortestPathParams( + 'with_vertex', + checked + ); + }} + /> +
+
+
+ + +
+
+ ); +}); + +export default WeightedShortestPath; diff --git a/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/query-result/GraphPopOver.tsx b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/query-result/GraphPopOver.tsx index bb0763eb2..92fc33fc6 100644 --- a/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/query-result/GraphPopOver.tsx +++ b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/query-result/GraphPopOver.tsx @@ -1,7 +1,7 @@ import React, { useContext, useEffect, useRef, useCallback } from 'react'; import { observer } from 'mobx-react'; import { Message } from 'hubble-ui'; -import { isUndefined } from 'lodash-es'; +import { isUndefined, size, isEmpty } from 'lodash-es'; import { DataAnalyzeStoreContext } from '../../../../stores'; import { addGraphNodes, addGraphEdges } from '../../../../stores/utils'; @@ -68,11 +68,27 @@ const GraphPopOver: React.FC = observer(
{ + const node = dataAnalyzeStore.graphData.data.graph_view.vertices.find( + ({ id }) => id === dataAnalyzeStore.rightClickedGraphData.id + ); + + if (isUndefined(node)) { + return; + } + + if (node.label === '~undefined') { + Message.info({ + content: t('addition.message.illegal-vertex'), + size: 'medium', + showCloseIcon: false, + duration: 1 + }); + } + if ( isUndefined( dataAnalyzeStore.vertexTypes.find( - ({ name }) => - name === dataAnalyzeStore.rightClickedGraphData.label + ({ name }) => name === node.label ) ) ) { @@ -84,6 +100,36 @@ const GraphPopOver: React.FC = observer( if ( dataAnalyzeStore.requestStatus.expandGraphNode === 'success' ) { + // prompt if there's no extra node + if ( + size( + dataAnalyzeStore.expandedGraphData.data.graph_view + .vertices + ) === 0 + ) { + if ( + isEmpty( + dataAnalyzeStore.visNetwork?.getConnectedNodes(node.id) + ) + ) { + Message.info({ + content: t('addition.message.no-adjacency-points'), + size: 'medium', + showCloseIcon: false, + duration: 1 + }); + } else { + Message.info({ + content: t('addition.message.no-more-points'), + size: 'medium', + showCloseIcon: false, + duration: 1 + }); + } + + return; + } + addGraphNodes( dataAnalyzeStore.expandedGraphData.data.graph_view.vertices, dataAnalyzeStore.visDataSet?.nodes, diff --git a/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/query-result/QueryFilterOptions.tsx b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/query-result/QueryFilterOptions.tsx index 1d78c6d74..7379304fd 100644 --- a/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/query-result/QueryFilterOptions.tsx +++ b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/query-result/QueryFilterOptions.tsx @@ -96,7 +96,7 @@ const QueryFilterOptions: React.FC = observer(() => { placeholder={t('addition.message.please-enter-number')} value={value} onChange={(e: any) => { - handlePropertyChange('value', e.value, index); + handlePropertyChange('value', Number(e.value), index); }} disabled={shouldDisabled} /> diff --git a/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/query-result/QueryResult.tsx b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/query-result/QueryResult.tsx index b87f5d8b6..e275c6e77 100644 --- a/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/query-result/QueryResult.tsx +++ b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/query-result/QueryResult.tsx @@ -62,10 +62,6 @@ const QueryResult: React.FC = observer( if (algorithmAnalyzerStore.currentAlgorithm === '') { dynHeightStyle.height = 'calc(100vh - 441px)'; } - - if (algorithmAnalyzerStore.currentAlgorithm === Algorithm.shortestPath) { - dynHeightStyle.height = 'calc(100vh - 555px)'; - } } return ( diff --git a/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/query-result/TableQueryResult.tsx b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/query-result/TableQueryResult.tsx index 7d89daf32..56425d96d 100644 --- a/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/query-result/TableQueryResult.tsx +++ b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-analyze/query-result/TableQueryResult.tsx @@ -1,6 +1,7 @@ import React, { useContext, useCallback } from 'react'; import { observer } from 'mobx-react'; import { Table } from 'hubble-ui'; +import { size } from 'lodash-es'; import { DataAnalyzeStoreContext } from '../../../../stores'; @@ -11,6 +12,9 @@ const TableQueryResult: React.FC = observer(() => { (title) => ({ title, dataIndex: title, + width: + 100 / size(dataAnalyzeStore.originalGraphData.data.table_view.header) + + '%', render(text: any) { if (title === 'path') { return ; diff --git a/hugegraph-hubble/hubble-fe/src/components/graph-management/data-import/import-tasks/ImportTasks.less b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-import/import-tasks/ImportTasks.less index e8a3f988c..9a50eb026 100644 --- a/hugegraph-hubble/hubble-fe/src/components/graph-management/data-import/import-tasks/ImportTasks.less +++ b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-import/import-tasks/ImportTasks.less @@ -1,9 +1,4 @@ .import-tasks { - &-with-expand-sidebar { - width: calc(100% - 200px); - left: 200px; - } - &-breadcrumb-wrapper { margin: 16px 0; } diff --git a/hugegraph-hubble/hubble-fe/src/components/graph-management/data-import/import-tasks/datamap-configs/EdgeMap.tsx b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-import/import-tasks/datamap-configs/EdgeMap.tsx index acc02ae3d..197341b35 100644 --- a/hugegraph-hubble/hubble-fe/src/components/graph-management/data-import/import-tasks/datamap-configs/EdgeMap.tsx +++ b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-import/import-tasks/datamap-configs/EdgeMap.tsx @@ -101,8 +101,9 @@ const EdgeMap: React.FC = observer( const addNullValueClassName = classnames({ 'import-tasks-manipulation': true, - 'import-tasks-manipulation-disabled': - edgeMap.null_values.customized.includes('') + 'import-tasks-manipulation-disabled': edgeMap.null_values.customized.includes( + '' + ) }); const addPropertyMapClassName = classnames({ @@ -149,6 +150,9 @@ const EdgeMap: React.FC = observer( size="medium" getPopupContainer={(e: any) => e} selectorName={t('data-configs.type.placeholder.select-edge-type')} + notFoundContent={t( + 'data-configs.type.placeholder.select-vertex-type' + )} value={ isEdit ? dataMapStore.editedEdgeMap!.label @@ -1302,11 +1306,10 @@ const EdgeMap: React.FC = observer(
= observer(
{ { size="medium" width={122} countMode="en" + disabled={dataMapStore.readOnly || dataMapStore.lock} placeholder={t( 'data-configs.file.placeholder.input-charset' )} @@ -253,6 +255,7 @@ const FileConfigs: React.FC = observer(() => { size="medium" width={122} countMode="en" + disabled={dataMapStore.readOnly || dataMapStore.lock} placeholder={t( 'data-configs.file.placeholder.input-date-format' )} diff --git a/hugegraph-hubble/hubble-fe/src/components/graph-management/data-import/import-tasks/datamap-configs/VertexMap.tsx b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-import/import-tasks/datamap-configs/VertexMap.tsx index cecc1800d..94617e677 100644 --- a/hugegraph-hubble/hubble-fe/src/components/graph-management/data-import/import-tasks/datamap-configs/VertexMap.tsx +++ b/hugegraph-hubble/hubble-fe/src/components/graph-management/data-import/import-tasks/datamap-configs/VertexMap.tsx @@ -159,6 +159,9 @@ const VertexMap: React.FC = observer( selectorName={t( 'data-configs.type.placeholder.select-vertex-type' )} + notFoundContent={t( + 'data-configs.type.placeholder.select-vertex-type' + )} value={ isEdit ? dataMapStore.editedVertexMap!.label @@ -442,11 +445,20 @@ const VertexMap: React.FC = observer( ); }} > - {selectedVertex?.properties.map(({ name }) => ( - - {name} - - ))} + {selectedVertex?.properties + .filter( + ({ name }) => + !( + selectedVertex.id_strategy === + 'PRIMARY_KEY' && + selectedVertex.primary_keys.includes(name) + ) + ) + .map(({ name }) => ( + + {name} + + ))} )}
@@ -1199,11 +1211,10 @@ const VertexMap: React.FC = observer(
= observer(
{ const handleMenuItemChange = ({ key }: { key: string }) => { // reset store current tab status to default switch (selectedMenuItem) { + case 'property': + metadataConfigRootStore.metadataPropertyStore.changeCurrentTabStatus( + 'list' + ); case 'vertex-type': metadataConfigRootStore.vertexTypeStore.changeCurrentTabStatus('list'); case 'edge-type': @@ -79,7 +83,7 @@ const MetadataConfig: React.FC = observer(() => { dataAnalyzeStore.fetchAllNodeStyle(); dataAnalyzeStore.fetchAllEdgeStyle(); metadataConfigRootStore.setCurrentId(Number(params.id)); - metadataConfigRootStore.fetchIdList(); + // metadataConfigRootStore.fetchIdList(); } return () => { @@ -151,20 +155,17 @@ const MetadataConfig: React.FC = observer(() => { type="primary" style={{ width: 88 }} onClick={() => { + metadataConfigRootStore.setCurrentId(null); setLocation('/'); }} > {t('addition.dataAnalyze.return-home')} ]} - visible={ - !metadataConfigRootStore.metadataPropertyStore - .validateLicenseOrMemories || - !metadataConfigRootStore.vertexTypeStore.validateLicenseOrMemories || - !metadataConfigRootStore.edgeTypeStore.validateLicenseOrMemories || - !metadataConfigRootStore.metadataPropertyIndexStore - .validateLicenseOrMemories - } + visible={graphManagementStore.graphData.some( + ({ id, enabled }) => + metadataConfigRootStore.currentId === id && !enabled + )} destroyOnClose needCloseIcon={false} > diff --git a/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/edge-type/EdgeTypeList.tsx b/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/edge-type/EdgeTypeList.tsx index 86289872b..16739616c 100644 --- a/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/edge-type/EdgeTypeList.tsx +++ b/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/edge-type/EdgeTypeList.tsx @@ -113,10 +113,14 @@ const EdgeTypeList: React.FC = observer(() => { const [isShowModal, switchShowModal] = useState(false); const [isAddProperty, switchIsAddProperty] = useState(false); const [isEditEdge, switchIsEditEdge] = useState(false); - const [deleteExistPopIndexInDrawer, setDeleteExistPopIndexInDrawer] = - useState(null); - const [deleteAddedPopIndexInDrawer, setDeleteAddedPopIndexInDrawer] = - useState(null); + const [ + deleteExistPopIndexInDrawer, + setDeleteExistPopIndexInDrawer + ] = useState(null); + const [ + deleteAddedPopIndexInDrawer, + setDeleteAddedPopIndexInDrawer + ] = useState(null); const [, setLocation] = useLocation(); const dropdownWrapperRef = useRef(null); @@ -130,6 +134,28 @@ const EdgeTypeList: React.FC = observer(() => { edgeTypeStore.edgeTypes.map(({ name }) => name) ); + const handleOutSideClick = useCallback( + (e: MouseEvent) => { + if ( + isAddProperty && + dropdownWrapperRef.current && + !dropdownWrapperRef.current.contains(e.target as Element) + ) { + switchIsAddProperty(false); + } + + if ( + (deleteExistPopIndexInDrawer || deleteAddedPopIndexInDrawer) && + deleteWrapperInDrawerRef.current && + !deleteWrapperInDrawerRef.current.contains(e.target as Element) + ) { + setDeleteExistPopIndexInDrawer(null); + setDeleteAddedPopIndexInDrawer(null); + } + }, + [deleteExistPopIndexInDrawer, deleteWrapperInDrawerRef, isAddProperty] + ); + const handleSelectedTableRow = (newSelectedRowKeys: string[]) => { mutateSelectedRowKeys(newSelectedRowKeys); }; @@ -224,8 +250,8 @@ const EdgeTypeList: React.FC = observer(() => { icon: null, with_arrow: edgeTypeStore.selectedEdgeType!.style.with_arrow, thickness: edgeTypeStore.selectedEdgeType!.style.thickness, - display_fields: - edgeTypeStore.selectedEdgeType!.style.display_fields + display_fields: edgeTypeStore.selectedEdgeType!.style + .display_fields } }); }} @@ -377,6 +403,14 @@ const EdgeTypeList: React.FC = observer(() => { edgeTypeStore ]); + useEffect(() => { + document.addEventListener('click', handleOutSideClick, false); + + return () => { + document.removeEventListener('click', handleOutSideClick, false); + }; + }, [handleOutSideClick]); + if (edgeTypeStore.currentTabStatus === 'new') { return ; } @@ -557,12 +591,12 @@ const EdgeTypeList: React.FC = observer(() => { style: { color: edgeTypeStore.selectedEdgeType!.style.color, icon: null, - with_arrow: - edgeTypeStore.selectedEdgeType!.style.with_arrow, - thickness: - edgeTypeStore.selectedEdgeType!.style.thickness, - display_fields: - edgeTypeStore.selectedEdgeType!.style.display_fields + with_arrow: edgeTypeStore.selectedEdgeType!.style + .with_arrow, + thickness: edgeTypeStore.selectedEdgeType!.style + .thickness, + display_fields: edgeTypeStore.selectedEdgeType!.style + .display_fields } }); } else { @@ -659,7 +693,9 @@ const EdgeTypeList: React.FC = observer(() => {
{t('addition.common.edge-type-name')}:
- {edgeTypeStore.selectedEdgeType!.name} +
+ {edgeTypeStore.selectedEdgeType!.name} +
@@ -876,13 +912,17 @@ const EdgeTypeList: React.FC = observer(() => {
{t('addition.common.source-type')}:
- {edgeTypeStore.selectedEdgeType!.source_label} +
+ {edgeTypeStore.selectedEdgeType!.source_label} +
{t('addition.common.target-type')}:
- {edgeTypeStore.selectedEdgeType!.target_label} +
+ {edgeTypeStore.selectedEdgeType!.target_label} +
@@ -913,7 +953,7 @@ const EdgeTypeList: React.FC = observer(() => { className="metadata-drawer-options-list-row" key={name} > -
{name}
+
{name}
{ className="metadata-drawer-options-list-row" key={name} > -
{name}
+
{name}
{ append_properties: [ ...addedPropertiesInSelectedVertextType ].map((propertyName) => { - const currentProperty = - edgeTypeStore.newEdgeType.properties.find( - ({ name }) => name === propertyName - ); + const currentProperty = edgeTypeStore.newEdgeType.properties.find( + ({ name }) => name === propertyName + ); return { name: propertyName, @@ -1040,7 +1079,9 @@ const EdgeTypeList: React.FC = observer(() => { {t('addition.common.distinguishing-key-property')}:
- {edgeTypeStore.selectedEdgeType!.sort_keys.join(';')} +
+ {edgeTypeStore.selectedEdgeType!.sort_keys.join(';')} +
@@ -1091,10 +1132,9 @@ const EdgeTypeList: React.FC = observer(() => { ) .filter(({ nullable }) => !nullable) .map((item) => { - const order = - edgeTypeStore.editedSelectedEdgeType.style.display_fields.findIndex( - (name) => name === item.name - ); + const order = edgeTypeStore.editedSelectedEdgeType.style.display_fields.findIndex( + (name) => name === item.name + ); const multiSelectOptionClassName = classnames({ 'metadata-configs-sorted-multiSelect-option': true, @@ -1113,18 +1153,16 @@ const EdgeTypeList: React.FC = observer(() => {
{order !== -1 ? order + 1 : ''} @@ -1141,7 +1179,7 @@ const EdgeTypeList: React.FC = observer(() => { })} ) : ( -
+
{edgeTypeStore.selectedEdgeType?.style.display_fields .map((field) => formatVertexIdText( @@ -1281,12 +1319,11 @@ const EdgeTypeList: React.FC = observer(() => { cursor: 'pointer' }} onClick={() => { - const removedPropertyIndex = - cloneDeep( - edgeTypeStore - .editedSelectedEdgeType - .remove_property_indexes - ); + const removedPropertyIndex = cloneDeep( + edgeTypeStore + .editedSelectedEdgeType + .remove_property_indexes + ); removedPropertyIndex.push( edgeTypeStore.selectedEdgeType! @@ -1296,8 +1333,7 @@ const EdgeTypeList: React.FC = observer(() => { edgeTypeStore.mutateEditedSelectedEdgeType( { ...edgeTypeStore.editedSelectedEdgeType, - remove_property_indexes: - removedPropertyIndex + remove_property_indexes: removedPropertyIndex } ); @@ -1359,13 +1395,11 @@ const EdgeTypeList: React.FC = observer(() => { errorMessage={ edgeTypeStore.validateEditEdgeTypeErrorMessage .propertyIndexes.length !== 0 - ? ( - edgeTypeStore - .validateEditEdgeTypeErrorMessage - .propertyIndexes[ - index - ] as EdgeTypeValidatePropertyIndexes - ).name + ? (edgeTypeStore + .validateEditEdgeTypeErrorMessage + .propertyIndexes[ + index + ] as EdgeTypeValidatePropertyIndexes).name : '' } value={name} @@ -1379,8 +1413,7 @@ const EdgeTypeList: React.FC = observer(() => { edgeTypeStore.mutateEditedSelectedEdgeType({ ...edgeTypeStore.editedSelectedEdgeType, - append_property_indexes: - propertyIndexEntities + append_property_indexes: propertyIndexEntities }); }} originInputProps={{ @@ -1411,8 +1444,7 @@ const EdgeTypeList: React.FC = observer(() => { edgeTypeStore.mutateEditedSelectedEdgeType({ ...edgeTypeStore.editedSelectedEdgeType, - append_property_indexes: - propertyIndexEntities + append_property_indexes: propertyIndexEntities }); edgeTypeStore.validateEditEdgeType(); @@ -1460,8 +1492,7 @@ const EdgeTypeList: React.FC = observer(() => { edgeTypeStore.mutateEditedSelectedEdgeType({ ...edgeTypeStore.editedSelectedEdgeType, - append_property_indexes: - propertyIndexEntities + append_property_indexes: propertyIndexEntities }); edgeTypeStore.validateEditEdgeType(); @@ -1474,20 +1505,19 @@ const EdgeTypeList: React.FC = observer(() => { .append_properties ) .map((property) => { - const order = - edgeTypeStore.editedSelectedEdgeType.append_property_indexes[ - index - ].fields.findIndex( - (name) => name === property.name - ); + const order = edgeTypeStore.editedSelectedEdgeType.append_property_indexes[ + index + ].fields.findIndex( + (name) => name === property.name + ); - const multiSelectOptionClassName = - classnames({ - 'metadata-configs-sorted-multiSelect-option': - true, + const multiSelectOptionClassName = classnames( + { + 'metadata-configs-sorted-multiSelect-option': true, 'metadata-configs-sorted-multiSelect-option-selected': order !== -1 - }); + } + ); return ( { .append_properties ) .filter((property) => { - const matchedProperty = - metadataPropertyStore.metadataProperties.find( - ({ name }) => name === property.name - ); + const matchedProperty = metadataPropertyStore.metadataProperties.find( + ({ name }) => name === property.name + ); if (!isUndefined(matchedProperty)) { const { data_type } = matchedProperty; @@ -1544,10 +1573,9 @@ const EdgeTypeList: React.FC = observer(() => { .append_properties ) .filter((property) => { - const matchedProperty = - metadataPropertyStore.metadataProperties.find( - ({ name }) => name === property.name - ); + const matchedProperty = metadataPropertyStore.metadataProperties.find( + ({ name }) => name === property.name + ); if (!isUndefined(matchedProperty)) { const { data_type } = matchedProperty; @@ -1610,11 +1638,10 @@ const EdgeTypeList: React.FC = observer(() => { cursor: 'pointer' }} onClick={() => { - const appendPropertyIndexes = - cloneDeep( - edgeTypeStore.editedSelectedEdgeType! - .append_property_indexes - ); + const appendPropertyIndexes = cloneDeep( + edgeTypeStore.editedSelectedEdgeType! + .append_property_indexes + ); appendPropertyIndexes.splice( index, @@ -1624,8 +1651,7 @@ const EdgeTypeList: React.FC = observer(() => { edgeTypeStore.mutateEditedSelectedEdgeType( { ...edgeTypeStore.editedSelectedEdgeType, - append_property_indexes: - appendPropertyIndexes + append_property_indexes: appendPropertyIndexes } ); @@ -1763,8 +1789,8 @@ const EdgeTypeListManipulation: React.FC = observer( icon: null, with_arrow: edgeTypeStore.selectedEdgeType!.style.with_arrow, thickness: edgeTypeStore.selectedEdgeType!.style.thickness, - display_fields: - edgeTypeStore.selectedEdgeType!.style.display_fields + display_fields: edgeTypeStore.selectedEdgeType!.style + .display_fields } }); }} diff --git a/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/edge-type/ReuseEdgeTypes.tsx b/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/edge-type/ReuseEdgeTypes.tsx index b9d330e1d..3e388c5b6 100644 --- a/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/edge-type/ReuseEdgeTypes.tsx +++ b/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/edge-type/ReuseEdgeTypes.tsx @@ -10,13 +10,17 @@ import { Message } from 'hubble-ui'; +import { GraphManagementStoreContext } from '../../../../stores'; import MetadataConfigsRootStore from '../../../../stores/GraphManagementStore/metadataConfigsStore/metadataConfigsStore'; + import PassIcon from '../../../../assets/imgs/ic_pass.svg'; + import './ReuseEdgeTypes.less'; import { cloneDeep } from 'lodash-es'; import { useTranslation } from 'react-i18next'; const ReuseEdgeTypes: React.FC = observer(() => { + const graphManagementStore = useContext(GraphManagementStoreContext); const metadataConfigsRootStore = useContext(MetadataConfigsRootStore); const { t } = useTranslation(); const { edgeTypeStore } = metadataConfigsRootStore; @@ -153,12 +157,10 @@ const ReuseEdgeTypes: React.FC = observer(() => { dataIndex: 'manipulation', width: '20%', render(_: never, records: any, index: number) { - const originalName = - edgeTypeStore.checkedReusableData!.edgelabel_conflicts[index].entity - .name; - const changedName = - edgeTypeStore.editedCheckedReusableData!.edgelabel_conflicts[index] - .entity.name; + const originalName = edgeTypeStore.checkedReusableData! + .edgelabel_conflicts[index].entity.name; + const changedName = edgeTypeStore.editedCheckedReusableData! + .edgelabel_conflicts[index].entity.name; const isChanged = changedName !== originalName; if (edgeTypeEditIndex === index) { @@ -370,12 +372,10 @@ const ReuseEdgeTypes: React.FC = observer(() => { dataIndex: 'manipulation', width: '20%', render(_: never, records: any, index: number) { - const originalName = - edgeTypeStore.checkedReusableData!.vertexlabel_conflicts[index].entity - .name; - const changedName = - edgeTypeStore.editedCheckedReusableData!.vertexlabel_conflicts[index] - .entity.name; + const originalName = edgeTypeStore.checkedReusableData! + .vertexlabel_conflicts[index].entity.name; + const changedName = edgeTypeStore.editedCheckedReusableData! + .vertexlabel_conflicts[index].entity.name; const isChanged = changedName !== originalName; if (index === vertexTypeEditIndex) { @@ -559,13 +559,10 @@ const ReuseEdgeTypes: React.FC = observer(() => { width: '20%', render(_: never, records: any, index: number) { if (index === propertyEditIndex) { - const originalName = - edgeTypeStore.checkedReusableData!.propertykey_conflicts[index] - .entity.name; - const changedName = - edgeTypeStore.editedCheckedReusableData!.propertykey_conflicts[ - index - ].entity.name; + const originalName = edgeTypeStore.checkedReusableData! + .propertykey_conflicts[index].entity.name; + const changedName = edgeTypeStore.editedCheckedReusableData! + .propertykey_conflicts[index].entity.name; const isChanged = changedName !== originalName; return ( @@ -760,13 +757,10 @@ const ReuseEdgeTypes: React.FC = observer(() => { width: '20%', render(_: never, records: any, index: number) { if (index === propertyIndexEditIndex) { - const originalName = - edgeTypeStore.checkedReusableData!.propertyindex_conflicts[index] - .entity.name; - const changedName = - edgeTypeStore.editedCheckedReusableData!.propertyindex_conflicts[ - index - ].entity.name; + const originalName = edgeTypeStore.checkedReusableData! + .propertyindex_conflicts[index].entity.name; + const changedName = edgeTypeStore.editedCheckedReusableData! + .propertyindex_conflicts[index].entity.name; const isChanged = changedName !== originalName; return ( @@ -915,7 +909,7 @@ const ReuseEdgeTypes: React.FC = observer(() => { onChange={(selectedName: string) => { mutateSelectedId(selectedName); - const id = metadataConfigsRootStore.idList.find( + const id = graphManagementStore.idList.find( ({ name }) => name === selectedName )!.id; @@ -924,10 +918,22 @@ const ReuseEdgeTypes: React.FC = observer(() => { edgeTypeStore.fetchEdgeTypeList({ reuseId: Number(id) }); + + const enable = graphManagementStore.graphData.find( + ({ name }) => name === selectedName + )?.enabled; + + if (!enable) { + Message.error({ + content: t('data-analyze.hint.graph-disabled'), + size: 'medium', + showCloseIcon: false + }); + } }} value={selectedId} > - {metadataConfigsRootStore.idList + {graphManagementStore.idList .filter( ({ id }) => Number(id) !== metadataConfigsRootStore.currentId @@ -989,6 +995,14 @@ const ReuseEdgeTypes: React.FC = observer(() => { selectedId as string, selectedList ); + + if (edgeTypeStore.requestStatus.checkConflict === 'failed') { + Message.error({ + content: edgeTypeStore.errorMessage, + size: 'medium', + showCloseIcon: false + }); + } }} > {t('addition.operate.next-step')} diff --git a/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/graph-view/CheckAndEditEdge.tsx b/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/graph-view/CheckAndEditEdge.tsx index e19339e78..9df7ddb6e 100644 --- a/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/graph-view/CheckAndEditEdge.tsx +++ b/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/graph-view/CheckAndEditEdge.tsx @@ -51,10 +51,14 @@ const CheckAndEditEdge: React.FC = observer(() => { const { t } = useTranslation(); const [isAddProperty, switchIsAddProperty] = useState(false); const [isDeletePop, switchDeletePop] = useState(false); - const [deleteExistPopIndexInDrawer, setDeleteExistPopIndexInDrawer] = - useState(null); - const [deleteAddedPopIndexInDrawer, setDeleteAddedPopIndexInDrawer] = - useState(null); + const [ + deleteExistPopIndexInDrawer, + setDeleteExistPopIndexInDrawer + ] = useState(null); + const [ + deleteAddedPopIndexInDrawer, + setDeleteAddedPopIndexInDrawer + ] = useState(null); const deleteWrapperRef = useRef(null); const dropdownWrapperRef = useRef(null); @@ -322,7 +326,7 @@ const CheckAndEditEdge: React.FC = observer(() => { } }} tooltipWrapperProps={{ - className: 'metadata-properties-tooltips', + className: 'metadata-graph-tooltips', style: { zIndex: 1042 } @@ -390,7 +394,9 @@ const CheckAndEditEdge: React.FC = observer(() => {
{t('addition.common.edge-type-name')}:
- {edgeTypeStore.selectedEdgeType!.name} +
+ {edgeTypeStore.selectedEdgeType!.name} +
@@ -626,13 +632,17 @@ const CheckAndEditEdge: React.FC = observer(() => {
{t('addition.common.source-type')}:
- {edgeTypeStore.selectedEdgeType!.source_label} +
+ {edgeTypeStore.selectedEdgeType!.source_label} +
{t('addition.common.target-type')}:
- {edgeTypeStore.selectedEdgeType!.target_label} +
+ {edgeTypeStore.selectedEdgeType!.target_label} +
@@ -658,7 +668,7 @@ const CheckAndEditEdge: React.FC = observer(() => { {edgeTypeStore.selectedEdgeType!.properties.map( ({ name, nullable }) => (
-
{name}
+
{name}
{ append_properties: [ ...addedPropertiesInSelectedVertextType ].map((propertyName) => { - const currentProperty = - edgeTypeStore.newEdgeType.properties.find( - ({ name }) => name === propertyName - ); + const currentProperty = edgeTypeStore.newEdgeType.properties.find( + ({ name }) => name === propertyName + ); return { name: propertyName, @@ -782,7 +791,9 @@ const CheckAndEditEdge: React.FC = observer(() => {
{t('addition.common.distinguishing-key-property')}:
- {edgeTypeStore.selectedEdgeType!.sort_keys.join(';')} +
+ {edgeTypeStore.selectedEdgeType!.sort_keys.join(';')} +
@@ -833,10 +844,9 @@ const CheckAndEditEdge: React.FC = observer(() => { ) .filter(({ nullable }) => !nullable) .map((item) => { - const order = - edgeTypeStore.editedSelectedEdgeType.style.display_fields.findIndex( - (name) => name === item.name - ); + const order = edgeTypeStore.editedSelectedEdgeType.style.display_fields.findIndex( + (name) => name === item.name + ); const multiSelectOptionClassName = classnames({ 'metadata-configs-sorted-multiSelect-option': true, @@ -855,18 +865,16 @@ const CheckAndEditEdge: React.FC = observer(() => {
{order !== -1 ? order + 1 : ''} @@ -883,7 +891,7 @@ const CheckAndEditEdge: React.FC = observer(() => { })} ) : ( -
+
{edgeTypeStore.selectedEdgeType?.style.display_fields .map((field) => formatVertexIdText( @@ -1027,8 +1035,7 @@ const CheckAndEditEdge: React.FC = observer(() => { edgeTypeStore.mutateEditedSelectedEdgeType( { ...edgeTypeStore.editedSelectedEdgeType, - remove_property_indexes: - removedPropertyIndex + remove_property_indexes: removedPropertyIndex } ); @@ -1084,12 +1091,10 @@ const CheckAndEditEdge: React.FC = observer(() => { errorMessage={ edgeTypeStore.validateEditEdgeTypeErrorMessage .propertyIndexes.length !== 0 - ? ( - edgeTypeStore.validateEditEdgeTypeErrorMessage - .propertyIndexes[ - index - ] as EdgeTypeValidatePropertyIndexes - ).name + ? (edgeTypeStore.validateEditEdgeTypeErrorMessage + .propertyIndexes[ + index + ] as EdgeTypeValidatePropertyIndexes).name : '' } value={name} @@ -1185,16 +1190,14 @@ const CheckAndEditEdge: React.FC = observer(() => { .append_properties ) .map((property) => { - const order = - edgeTypeStore.editedSelectedEdgeType.append_property_indexes[ - index - ].fields.findIndex( - (name) => name === property.name - ); + const order = edgeTypeStore.editedSelectedEdgeType.append_property_indexes[ + index + ].fields.findIndex( + (name) => name === property.name + ); const multiSelectOptionClassName = classnames({ - 'metadata-configs-sorted-multiSelect-option': - true, + 'metadata-configs-sorted-multiSelect-option': true, 'metadata-configs-sorted-multiSelect-option-selected': order !== -1 }); @@ -1219,10 +1222,9 @@ const CheckAndEditEdge: React.FC = observer(() => { .append_properties ) .filter((property) => { - const matchedProperty = - metadataPropertyStore.metadataProperties.find( - ({ name }) => name === property.name - ); + const matchedProperty = metadataPropertyStore.metadataProperties.find( + ({ name }) => name === property.name + ); if (!isUndefined(matchedProperty)) { const { data_type } = matchedProperty; @@ -1248,10 +1250,9 @@ const CheckAndEditEdge: React.FC = observer(() => { .append_properties ) .filter((property) => { - const matchedProperty = - metadataPropertyStore.metadataProperties.find( - ({ name }) => name === property.name - ); + const matchedProperty = metadataPropertyStore.metadataProperties.find( + ({ name }) => name === property.name + ); if (!isUndefined(matchedProperty)) { const { data_type } = matchedProperty; @@ -1319,8 +1320,7 @@ const CheckAndEditEdge: React.FC = observer(() => { edgeTypeStore.mutateEditedSelectedEdgeType({ ...edgeTypeStore.editedSelectedEdgeType, - append_property_indexes: - appendPropertyIndexes + append_property_indexes: appendPropertyIndexes }); setDeleteAddedPopIndexInDrawer(null); diff --git a/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/graph-view/CheckAndEditVertex.tsx b/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/graph-view/CheckAndEditVertex.tsx index 3bb8e4c42..6277339ec 100644 --- a/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/graph-view/CheckAndEditVertex.tsx +++ b/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/graph-view/CheckAndEditVertex.tsx @@ -57,10 +57,14 @@ const CheckAndEditVertex: React.FC = observer(() => { const { t } = useTranslation(); const [isAddProperty, switchIsAddProperty] = useState(false); const [isDeletePop, switchDeletePop] = useState(false); - const [deleteExistPopIndexInDrawer, setDeleteExistPopIndexInDrawer] = - useState(null); - const [deleteAddedPopIndexInDrawer, setDeleteAddedPopIndexInDrawer] = - useState(null); + const [ + deleteExistPopIndexInDrawer, + setDeleteExistPopIndexInDrawer + ] = useState(null); + const [ + deleteAddedPopIndexInDrawer, + setDeleteAddedPopIndexInDrawer + ] = useState(null); const deleteWrapperRef = useRef(null); const dropdownWrapperRef = useRef(null); @@ -330,7 +334,7 @@ const CheckAndEditVertex: React.FC = observer(() => { } }} tooltipWrapperProps={{ - className: 'metadata-properties-tooltips', + className: 'metadata-graph-tooltips', style: { zIndex: 1042 } @@ -410,7 +414,9 @@ const CheckAndEditVertex: React.FC = observer(() => {
{t('addition.vertex.vertex-type-name')}:
- {vertexTypeStore.selectedVertexType!.name} +
+ {vertexTypeStore.selectedVertexType!.name} +
@@ -559,7 +565,7 @@ const CheckAndEditVertex: React.FC = observer(() => { {vertexTypeStore.selectedVertexType!.properties.map( ({ name, nullable }) => (
-
{name}
+
{name}
{ append_properties: [ ...addedPropertiesInSelectedVertextType ].map((propertyName) => { - const currentProperty = - vertexTypeStore.newVertexType.properties.find( - ({ name }) => name === propertyName - ); + const currentProperty = vertexTypeStore.newVertexType.properties.find( + ({ name }) => name === propertyName + ); return { name: propertyName, @@ -678,7 +683,9 @@ const CheckAndEditVertex: React.FC = observer(() => {
{t('addition.common.primary-key-property')}:
- {vertexTypeStore.selectedVertexType!.primary_keys.join(';')} +
+ {vertexTypeStore.selectedVertexType!.primary_keys.join(';')} +
@@ -732,10 +739,9 @@ const CheckAndEditVertex: React.FC = observer(() => { ) .filter(({ nullable }) => !nullable) .map((item) => { - const order = - vertexTypeStore.editedSelectedVertexType.style.display_fields.findIndex( - (name) => name === item.name - ); + const order = vertexTypeStore.editedSelectedVertexType.style.display_fields.findIndex( + (name) => name === item.name + ); const multiSelectOptionClassName = classnames({ 'metadata-configs-sorted-multiSelect-option': true, @@ -754,18 +760,16 @@ const CheckAndEditVertex: React.FC = observer(() => {
{order !== -1 ? order + 1 : ''} @@ -782,7 +786,7 @@ const CheckAndEditVertex: React.FC = observer(() => { })} ) : ( -
+
{vertexTypeStore.selectedVertexType?.style.display_fields .map((field) => formatVertexIdText( @@ -920,8 +924,7 @@ const CheckAndEditVertex: React.FC = observer(() => { vertexTypeStore.mutateEditedSelectedVertexType( { ...vertexTypeStore.editedSelectedVertexType, - remove_property_indexes: - removedPropertyIndexes + remove_property_indexes: removedPropertyIndexes } ); @@ -977,13 +980,11 @@ const CheckAndEditVertex: React.FC = observer(() => { errorMessage={ vertexTypeStore.validateEditVertexTypeErrorMessage .propertyIndexes.length !== 0 - ? ( - vertexTypeStore - .validateEditVertexTypeErrorMessage - .propertyIndexes[ - index - ] as VertexTypeValidatePropertyIndexes - ).name + ? (vertexTypeStore + .validateEditVertexTypeErrorMessage + .propertyIndexes[ + index + ] as VertexTypeValidatePropertyIndexes).name : '' } value={name} @@ -1083,16 +1084,14 @@ const CheckAndEditVertex: React.FC = observer(() => { ) ) .map((property) => { - const order = - vertexTypeStore.editedSelectedVertexType.append_property_indexes[ - index - ].fields.findIndex( - (name) => name === property.name - ); + const order = vertexTypeStore.editedSelectedVertexType.append_property_indexes[ + index + ].fields.findIndex( + (name) => name === property.name + ); const multiSelectOptionClassName = classnames({ - 'metadata-configs-sorted-multiSelect-option': - true, + 'metadata-configs-sorted-multiSelect-option': true, 'metadata-configs-sorted-multiSelect-option-selected': order !== -1 }); @@ -1117,10 +1116,9 @@ const CheckAndEditVertex: React.FC = observer(() => { .append_properties ) .filter((property) => { - const matchedProperty = - metadataPropertyStore.metadataProperties.find( - ({ name }) => name === property.name - ); + const matchedProperty = metadataPropertyStore.metadataProperties.find( + ({ name }) => name === property.name + ); if (!isUndefined(matchedProperty)) { const { data_type } = matchedProperty; @@ -1146,10 +1144,9 @@ const CheckAndEditVertex: React.FC = observer(() => { .append_properties ) .filter((property) => { - const matchedProperty = - metadataPropertyStore.metadataProperties.find( - ({ name }) => name === property.name - ); + const matchedProperty = metadataPropertyStore.metadataProperties.find( + ({ name }) => name === property.name + ); if (!isUndefined(matchedProperty)) { const { data_type } = matchedProperty; @@ -1218,8 +1215,7 @@ const CheckAndEditVertex: React.FC = observer(() => { vertexTypeStore.mutateEditedSelectedVertexType( { ...vertexTypeStore.editedSelectedVertexType, - append_property_indexes: - appendPropertyIndexes + append_property_indexes: appendPropertyIndexes } ); diff --git a/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/graph-view/CheckProperty.tsx b/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/graph-view/CheckProperty.tsx index acd2a8d71..ff8ef1b58 100644 --- a/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/graph-view/CheckProperty.tsx +++ b/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/graph-view/CheckProperty.tsx @@ -13,6 +13,7 @@ import { Tooltip } from '../../../common'; import MetadataConfigsRootStore from '../../../../stores/GraphManagementStore/metadataConfigsStore/metadataConfigsStore'; import type { MetadataProperty } from '../../../../stores/types/GraphManagementStore/metadataConfigsStore'; +import { signal } from 'codemirror'; const CheckProperty: React.FC = observer(() => { const { metadataPropertyStore, graphViewStore } = useContext( diff --git a/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/graph-view/CreateVertex.tsx b/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/graph-view/CreateVertex.tsx index e2d5dcd56..06518c016 100644 --- a/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/graph-view/CreateVertex.tsx +++ b/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/graph-view/CreateVertex.tsx @@ -106,8 +106,13 @@ const CreateVertex: React.FC = observer(() => { return; } - const { name, properties, primary_keys, style, ...rest } = - vertexTypeStore.newVertexType; + const { + name, + properties, + primary_keys, + style, + ...rest + } = vertexTypeStore.newVertexType; const mappedProperties = mapMetadataProperties( properties, @@ -166,6 +171,13 @@ const CreateVertex: React.FC = observer(() => { }, hover: { background: '#ec3112', border: '#ec3112' } }, + // reveal label when zoom to max + scaling: { + label: { + max: Infinity, + maxVisible: Infinity + } + }, chosen: { node( values: any, @@ -454,8 +466,10 @@ const CreateVertex: React.FC = observer(() => { { - currentProperties[index].nullable = - !currentProperties[index].nullable; + currentProperties[ + index + ].nullable = !currentProperties[index] + .nullable; vertexTypeStore.mutateNewProperty({ ...vertexTypeStore.newVertexType, @@ -539,10 +553,9 @@ const CreateVertex: React.FC = observer(() => { properties: [ ...addedPropertiesInSelectedVertextType ].map((propertyName) => { - const currentProperty = - vertexTypeStore.newVertexType.properties.find( - ({ name }) => name === propertyName - ); + const currentProperty = vertexTypeStore.newVertexType.properties.find( + ({ name }) => name === propertyName + ); return { name: propertyName, @@ -600,10 +613,9 @@ const CreateVertex: React.FC = observer(() => { {vertexTypeStore.newVertexType.properties .filter(({ nullable }) => !nullable) .map((item) => { - const order = - vertexTypeStore.newVertexType.primary_keys.findIndex( - (name) => name === item.name - ); + const order = vertexTypeStore.newVertexType.primary_keys.findIndex( + (name) => name === item.name + ); const multiSelectOptionClassName = classnames({ 'metadata-configs-sorted-multiSelect-option': true, @@ -670,10 +682,9 @@ const CreateVertex: React.FC = observer(() => { .concat({ name: '~id', nullable: false }) .filter(({ nullable }) => !nullable) .map((item) => { - const order = - vertexTypeStore.newVertexType.style.display_fields.findIndex( - (name) => name === item.name - ); + const order = vertexTypeStore.newVertexType.style.display_fields.findIndex( + (name) => name === item.name + ); const multiSelectOptionClassName = classnames({ 'metadata-configs-sorted-multiSelect-option': true, @@ -692,18 +703,16 @@ const CreateVertex: React.FC = observer(() => {
{order !== -1 ? order + 1 : ''} @@ -756,8 +765,8 @@ const CreateVertex: React.FC = observer(() => { onChange={() => { vertexTypeStore.mutateNewProperty({ ...vertexTypeStore.newVertexType, - open_label_index: - !vertexTypeStore.newVertexType.open_label_index + open_label_index: !vertexTypeStore.newVertexType + .open_label_index }); }} size="large" @@ -811,13 +820,11 @@ const CreateVertex: React.FC = observer(() => { errorMessage={ vertexTypeStore.validateNewVertexTypeErrorMessage .propertyIndexes.length !== 0 - ? ( - vertexTypeStore - .validateNewVertexTypeErrorMessage - .propertyIndexes[ - index - ] as VertexTypeValidatePropertyIndexes - ).name + ? (vertexTypeStore + .validateNewVertexTypeErrorMessage + .propertyIndexes[ + index + ] as VertexTypeValidatePropertyIndexes).name : '' } value={name} @@ -921,16 +928,14 @@ const CreateVertex: React.FC = observer(() => { ) ) .map((property) => { - const order = - vertexTypeStore.newVertexType.property_indexes[ - index - ].fields.findIndex( - (name) => name === property.name - ); + const order = vertexTypeStore.newVertexType.property_indexes[ + index + ].fields.findIndex( + (name) => name === property.name + ); const multiSelectOptionClassName = classnames({ - 'metadata-configs-sorted-multiSelect-option': - true, + 'metadata-configs-sorted-multiSelect-option': true, 'metadata-configs-sorted-multiSelect-option-selected': order !== -1 }); @@ -951,10 +956,9 @@ const CreateVertex: React.FC = observer(() => { {type === 'RANGE' && vertexTypeStore.newVertexType.properties .filter((property) => { - const matchedProperty = - metadataPropertyStore.metadataProperties.find( - ({ name }) => name === property.name - ); + const matchedProperty = metadataPropertyStore.metadataProperties.find( + ({ name }) => name === property.name + ); if (!isUndefined(matchedProperty)) { const { data_type } = matchedProperty; @@ -976,10 +980,9 @@ const CreateVertex: React.FC = observer(() => { {type === 'SEARCH' && vertexTypeStore.newVertexType.properties .filter((property) => { - const matchedProperty = - metadataPropertyStore.metadataProperties.find( - ({ name }) => name === property.name - ); + const matchedProperty = metadataPropertyStore.metadataProperties.find( + ({ name }) => name === property.name + ); if (!isUndefined(matchedProperty)) { const { data_type } = matchedProperty; diff --git a/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/graph-view/GraphView.less b/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/graph-view/GraphView.less index 0505d50a3..41938fae9 100644 --- a/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/graph-view/GraphView.less +++ b/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/graph-view/GraphView.less @@ -123,9 +123,10 @@ font-size: 14px; color: #333; - & p { - margin: 0; - line-height: 28px; + &-title { + font-size: 16px; + font-weight: 900; + margin-bottom: 16px; } } } diff --git a/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/graph-view/GraphView.tsx b/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/graph-view/GraphView.tsx index 85f47de6b..37a1c5d53 100644 --- a/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/graph-view/GraphView.tsx +++ b/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/graph-view/GraphView.tsx @@ -240,6 +240,9 @@ const GraphDataView: React.FC = observer(() => { physics: { maxVelocity: 50, solver: 'forceAtlas2Based', + forceAtlas2Based: { + avoidOverlap: 0 + }, timestep: 0.3, stabilization: { iterations: 150 } } @@ -278,8 +281,8 @@ const GraphDataView: React.FC = observer(() => { color: vertexTypeStore.selectedVertexType!.style.color, icon: null, size: vertexTypeStore.selectedVertexType!.style.size, - display_fields: - vertexTypeStore.selectedVertexType!.style.display_fields + display_fields: vertexTypeStore.selectedVertexType!.style + .display_fields } }); @@ -321,8 +324,8 @@ const GraphDataView: React.FC = observer(() => { icon: null, with_arrow: edgeTypeStore.selectedEdgeType!.style.with_arrow, thickness: edgeTypeStore.selectedEdgeType!.style.thickness, - display_fields: - edgeTypeStore.selectedEdgeType!.style.display_fields + display_fields: edgeTypeStore.selectedEdgeType!.style + .display_fields } }); diff --git a/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/property/ReuseProperties.tsx b/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/property/ReuseProperties.tsx index 63f11fcc4..324cb3b3c 100644 --- a/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/property/ReuseProperties.tsx +++ b/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/property/ReuseProperties.tsx @@ -1,14 +1,26 @@ import React, { useContext, useState, useEffect } from 'react'; import { observer } from 'mobx-react'; -import { Select, Steps, Transfer, Button, Table, Input } from 'hubble-ui'; +import { + Select, + Steps, + Transfer, + Button, + Table, + Input, + Message +} from 'hubble-ui'; +import { cloneDeep } from 'lodash-es'; import { useTranslation } from 'react-i18next'; +import { GraphManagementStoreContext } from '../../../../stores'; import MetadataConfigsRootStore from '../../../../stores/GraphManagementStore/metadataConfigsStore/metadataConfigsStore'; + import PassIcon from '../../../../assets/imgs/ic_pass.svg'; + import './ReuseProperties.less'; -import { cloneDeep } from 'lodash-es'; const ReuseProperties: React.FC = observer(() => { + const graphManagementStore = useContext(GraphManagementStoreContext); const metadataConfigsRootStore = useContext(MetadataConfigsRootStore); const { metadataPropertyStore } = metadataConfigsRootStore; const { t } = useTranslation(); @@ -144,8 +156,9 @@ const ReuseProperties: React.FC = observer(() => { className="metadata-properties-manipulation" style={{ marginRight: 16 }} onClick={() => { - const isReady = - metadataPropertyStore.validateRenameReuseProperty(index); + const isReady = metadataPropertyStore.validateRenameReuseProperty( + index + ); if (isReady) { setEditIndex(null); @@ -285,7 +298,7 @@ const ReuseProperties: React.FC = observer(() => { onChange={(selectedName: string) => { mutateSelectedId(selectedName); - const id = metadataConfigsRootStore.idList.find( + const id = graphManagementStore.idList.find( ({ name }) => name === selectedName )!.id; @@ -294,10 +307,22 @@ const ReuseProperties: React.FC = observer(() => { metadataPropertyStore.fetchMetadataPropertyList({ reuseId: Number(id) }); + + const enable = graphManagementStore.graphData.find( + ({ name }) => name === selectedName + )?.enabled; + + if (!enable) { + Message.error({ + content: t('data-analyze.hint.graph-disabled'), + size: 'medium', + showCloseIcon: false + }); + } }} value={selectedId} > - {metadataConfigsRootStore.idList + {graphManagementStore.idList .filter( ({ id }) => Number(id) !== metadataConfigsRootStore.currentId @@ -356,6 +381,17 @@ const ReuseProperties: React.FC = observer(() => { onClick={() => { setCurrentStatus(2); metadataPropertyStore.checkConflict(selectedList); + + if ( + metadataPropertyStore.requestStatus.checkConflict === + 'failed' + ) { + Message.error({ + content: metadataPropertyStore.errorMessage, + size: 'medium', + showCloseIcon: false + }); + } }} > {t('addition.operate.next-step')} diff --git a/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/vertex-type/ReuseVertexTypes.tsx b/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/vertex-type/ReuseVertexTypes.tsx index b2d8d2d48..98b75042a 100644 --- a/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/vertex-type/ReuseVertexTypes.tsx +++ b/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/vertex-type/ReuseVertexTypes.tsx @@ -10,13 +10,17 @@ import { Message } from 'hubble-ui'; +import { GraphManagementStoreContext } from '../../../../stores'; import MetadataConfigsRootStore from '../../../../stores/GraphManagementStore/metadataConfigsStore/metadataConfigsStore'; + import PassIcon from '../../../../assets/imgs/ic_pass.svg'; + import './ReuseVertexTypes.less'; import { cloneDeep } from 'lodash-es'; import { useTranslation } from 'react-i18next'; const ReuseVertexTypes: React.FC = observer(() => { + const graphManagementStore = useContext(GraphManagementStoreContext); const metadataConfigsRootStore = useContext(MetadataConfigsRootStore); const { vertexTypeStore } = metadataConfigsRootStore; const { t } = useTranslation(); @@ -154,13 +158,10 @@ const ReuseVertexTypes: React.FC = observer(() => { width: '20%', render(_: never, records: any, index: number) { if (index === vertexTypeEditIndex) { - const originalName = - vertexTypeStore.checkedReusableData!.vertexlabel_conflicts[index] - .entity.name; - const changedName = - vertexTypeStore.editedCheckedReusableData!.vertexlabel_conflicts[ - index - ].entity.name; + const originalName = vertexTypeStore.checkedReusableData! + .vertexlabel_conflicts[index].entity.name; + const changedName = vertexTypeStore.editedCheckedReusableData! + .vertexlabel_conflicts[index].entity.name; const isChanged = changedName !== originalName; return ( @@ -381,13 +382,10 @@ const ReuseVertexTypes: React.FC = observer(() => { width: '20%', render(_: never, records: any, index: number) { if (index === propertyEditIndex) { - const originalName = - vertexTypeStore.checkedReusableData!.propertykey_conflicts[index] - .entity.name; - const changedName = - vertexTypeStore.editedCheckedReusableData!.propertykey_conflicts[ - index - ].entity.name; + const originalName = vertexTypeStore.checkedReusableData! + .propertykey_conflicts[index].entity.name; + const changedName = vertexTypeStore.editedCheckedReusableData! + .propertykey_conflicts[index].entity.name; const isChanged = changedName !== originalName; return ( @@ -586,13 +584,10 @@ const ReuseVertexTypes: React.FC = observer(() => { width: '20%', render(_: never, records: any, index: number) { if (index === propertyIndexEditIndex) { - const originalName = - vertexTypeStore.checkedReusableData!.propertyindex_conflicts[index] - .entity.name; - const changedName = - vertexTypeStore.editedCheckedReusableData!.propertyindex_conflicts[ - index - ].entity.name; + const originalName = vertexTypeStore.checkedReusableData! + .propertyindex_conflicts[index].entity.name; + const changedName = vertexTypeStore.editedCheckedReusableData! + .propertyindex_conflicts[index].entity.name; const isChanged = changedName !== originalName; return ( @@ -747,7 +742,7 @@ const ReuseVertexTypes: React.FC = observer(() => { onChange={(selectedName: string) => { mutateSelectedId(selectedName); - const id = metadataConfigsRootStore.idList.find( + const id = graphManagementStore.idList.find( ({ name }) => name === selectedName )!.id; @@ -756,10 +751,22 @@ const ReuseVertexTypes: React.FC = observer(() => { vertexTypeStore.fetchVertexTypeList({ reuseId: Number(id) }); + + const enable = graphManagementStore.graphData.find( + ({ name }) => name === selectedName + )?.enabled; + + if (!enable) { + Message.error({ + content: t('data-analyze.hint.graph-disabled'), + size: 'medium', + showCloseIcon: false + }); + } }} value={selectedId} > - {metadataConfigsRootStore.idList + {graphManagementStore.idList .filter( ({ id }) => Number(id) !== metadataConfigsRootStore.currentId @@ -821,6 +828,16 @@ const ReuseVertexTypes: React.FC = observer(() => { selectedId as string, selectedList ); + + if ( + vertexTypeStore.requestStatus.checkConflict === 'failed' + ) { + Message.error({ + content: vertexTypeStore.errorMessage, + size: 'medium', + showCloseIcon: false + }); + } }} > {t('addition.operate.next-step')} diff --git a/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/vertex-type/VertexTypeList.tsx b/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/vertex-type/VertexTypeList.tsx index b25bb16fc..5a99c3ca2 100644 --- a/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/vertex-type/VertexTypeList.tsx +++ b/hugegraph-hubble/hubble-fe/src/components/graph-management/metadata-configs/vertex-type/VertexTypeList.tsx @@ -115,10 +115,14 @@ const VertexTypeList: React.FC = observer(() => { const [isShowModal, switchShowModal] = useState(false); const [isAddProperty, switchIsAddProperty] = useState(false); const [isEditVertex, switchIsEditVertex] = useState(false); - const [deleteExistPopIndexInDrawer, setDeleteExistPopIndexInDrawer] = - useState(null); - const [deleteAddedPopIndexInDrawer, setDeleteAddedPopIndexInDrawer] = - useState(null); + const [ + deleteExistPopIndexInDrawer, + setDeleteExistPopIndexInDrawer + ] = useState(null); + const [ + deleteAddedPopIndexInDrawer, + setDeleteAddedPopIndexInDrawer + ] = useState(null); const [, setLocation] = useLocation(); const dropdownWrapperRef = useRef(null); @@ -133,6 +137,31 @@ const VertexTypeList: React.FC = observer(() => { vertexTypeStore.vertexTypes.map(({ name }) => name) ); + // need useCallback to stop infinite callings of useEffect + const handleOutSideClick = useCallback( + (e: MouseEvent) => { + // if clicked element is not on dropdown, collpase it + if ( + isEditVertex && + isAddProperty && + dropdownWrapperRef.current && + !dropdownWrapperRef.current.contains(e.target as Element) + ) { + switchIsAddProperty(false); + } + + if ( + (deleteExistPopIndexInDrawer || deleteAddedPopIndexInDrawer) && + deleteWrapperInDrawerRef.current && + !deleteWrapperInDrawerRef.current.contains(e.target as Element) + ) { + setDeleteExistPopIndexInDrawer(null); + setDeleteAddedPopIndexInDrawer(null); + } + }, + [deleteExistPopIndexInDrawer, deleteAddedPopIndexInDrawer, isAddProperty] + ); + const handleSelectedTableRow = (newSelectedRowKeys: string[]) => { mutateSelectedRowKeys(newSelectedRowKeys); }; @@ -240,8 +269,8 @@ const VertexTypeList: React.FC = observer(() => { color: vertexTypeStore.selectedVertexType!.style.color, icon: null, size: vertexTypeStore.selectedVertexType!.style.size, - display_fields: - vertexTypeStore.selectedVertexType!.style.display_fields + display_fields: vertexTypeStore.selectedVertexType!.style + .display_fields } }); }} @@ -382,6 +411,14 @@ const VertexTypeList: React.FC = observer(() => { vertexTypeStore ]); + useEffect(() => { + document.addEventListener('click', handleOutSideClick, false); + + return () => { + document.removeEventListener('click', handleOutSideClick, false); + }; + }, [handleOutSideClick]); + if (vertexTypeStore.currentTabStatus === 'new') { return ; } @@ -591,8 +628,8 @@ const VertexTypeList: React.FC = observer(() => { color: vertexTypeStore.selectedVertexType!.style.color, icon: null, size: vertexTypeStore.selectedVertexType!.style.size, - display_fields: - vertexTypeStore.selectedVertexType!.style.display_fields + display_fields: vertexTypeStore.selectedVertexType!.style + .display_fields } }); } else { @@ -692,7 +729,9 @@ const VertexTypeList: React.FC = observer(() => {
{t('addition.vertex.vertex-type-name')}:
- {vertexTypeStore.selectedVertexType!.name} +
+ {vertexTypeStore.selectedVertexType!.name} +
@@ -845,7 +884,7 @@ const VertexTypeList: React.FC = observer(() => { className="metadata-drawer-options-list-row" key={name} > -
{name}
+
{name}
{ className="metadata-drawer-options-list-row" key={name} > -
{name}
+
{name}
{ append_properties: [ ...addedPropertiesInSelectedVertextType ].map((propertyName) => { - const currentProperty = - vertexTypeStore.newVertexType.properties.find( - ({ name }) => name === propertyName - ); + const currentProperty = vertexTypeStore.newVertexType.properties.find( + ({ name }) => name === propertyName + ); return { name: propertyName, @@ -1026,10 +1064,9 @@ const VertexTypeList: React.FC = observer(() => { ) .filter(({ nullable }) => !nullable) .map((item) => { - const order = - vertexTypeStore.editedSelectedVertexType.style.display_fields.findIndex( - (name) => name === item.name - ); + const order = vertexTypeStore.editedSelectedVertexType.style.display_fields.findIndex( + (name) => name === item.name + ); const multiSelectOptionClassName = classnames({ 'metadata-configs-sorted-multiSelect-option': true, @@ -1048,18 +1085,16 @@ const VertexTypeList: React.FC = observer(() => {
{order !== -1 ? order + 1 : ''} @@ -1076,7 +1111,7 @@ const VertexTypeList: React.FC = observer(() => { })} ) : ( -
+
{vertexTypeStore.selectedVertexType?.style.display_fields .map((field) => formatVertexIdText( @@ -1210,12 +1245,11 @@ const VertexTypeList: React.FC = observer(() => { cursor: 'pointer' }} onClick={() => { - const removedPropertyIndexes = - cloneDeep( - vertexTypeStore - .editedSelectedVertexType - .remove_property_indexes - ); + const removedPropertyIndexes = cloneDeep( + vertexTypeStore + .editedSelectedVertexType + .remove_property_indexes + ); removedPropertyIndexes.push( vertexTypeStore.selectedVertexType! @@ -1225,8 +1259,7 @@ const VertexTypeList: React.FC = observer(() => { vertexTypeStore.mutateEditedSelectedVertexType( { ...vertexTypeStore.editedSelectedVertexType, - remove_property_indexes: - removedPropertyIndexes + remove_property_indexes: removedPropertyIndexes } ); @@ -1284,13 +1317,11 @@ const VertexTypeList: React.FC = observer(() => { vertexTypeStore .validateEditVertexTypeErrorMessage .propertyIndexes.length !== 0 - ? ( - vertexTypeStore - .validateEditVertexTypeErrorMessage - .propertyIndexes[ - index - ] as VertexTypeValidatePropertyIndexes - ).name + ? (vertexTypeStore + .validateEditVertexTypeErrorMessage + .propertyIndexes[ + index + ] as VertexTypeValidatePropertyIndexes).name : '' } value={name} @@ -1398,20 +1429,19 @@ const VertexTypeList: React.FC = observer(() => { ) ) .map((property) => { - const order = - vertexTypeStore.editedSelectedVertexType.append_property_indexes[ - index - ].fields.findIndex( - (name) => name === property.name - ); + const order = vertexTypeStore.editedSelectedVertexType.append_property_indexes[ + index + ].fields.findIndex( + (name) => name === property.name + ); - const multiSelectOptionClassName = - classnames({ - 'metadata-configs-sorted-multiSelect-option': - true, + const multiSelectOptionClassName = classnames( + { + 'metadata-configs-sorted-multiSelect-option': true, 'metadata-configs-sorted-multiSelect-option-selected': order !== -1 - }); + } + ); return ( { .append_properties ) .filter((property) => { - const matchedProperty = - metadataPropertyStore.metadataProperties.find( - ({ name }) => name === property.name - ); + const matchedProperty = metadataPropertyStore.metadataProperties.find( + ({ name }) => name === property.name + ); if (!isUndefined(matchedProperty)) { const { data_type } = matchedProperty; @@ -1466,10 +1495,9 @@ const VertexTypeList: React.FC = observer(() => { .append_properties ) .filter((property) => { - const matchedProperty = - metadataPropertyStore.metadataProperties.find( - ({ name }) => name === property.name - ); + const matchedProperty = metadataPropertyStore.metadataProperties.find( + ({ name }) => name === property.name + ); if (!isUndefined(matchedProperty)) { const { data_type } = matchedProperty; @@ -1540,8 +1568,7 @@ const VertexTypeList: React.FC = observer(() => { vertexTypeStore.mutateEditedSelectedVertexType( { ...vertexTypeStore.editedSelectedVertexType, - append_property_indexes: - appendPropertyIndexes + append_property_indexes: appendPropertyIndexes } ); @@ -1627,8 +1654,8 @@ export interface VertexTypeListManipulation { switchIsEditVertex: (flag: boolean) => void; } -const VertexTypeListManipulation: React.FC = - observer(({ vertexName, vertexIndex, switchIsEditVertex }) => { +const VertexTypeListManipulation: React.FC = observer( + ({ vertexName, vertexIndex, switchIsEditVertex }) => { const { vertexTypeStore } = useContext(MetadataConfigsRootStore); const [isPopDeleteModal, switchPopDeleteModal] = useState(false); const [isDeleting, switchDeleting] = useState(false); @@ -1677,8 +1704,8 @@ const VertexTypeListManipulation: React.FC = color: vertexTypeStore.selectedVertexType!.style.color, icon: null, size: vertexTypeStore.selectedVertexType!.style.size, - display_fields: - vertexTypeStore.selectedVertexType!.style.display_fields + display_fields: vertexTypeStore.selectedVertexType!.style + .display_fields } }); }} @@ -1791,7 +1818,8 @@ const VertexTypeListManipulation: React.FC =
); - }); + } +); const EmptyVertxTypeHints: React.FC = observer(() => { const { vertexTypeStore } = useContext(MetadataConfigsRootStore); diff --git a/hugegraph-hubble/hubble-fe/src/i18n/resources/en-US/graph-managment/addition.json b/hugegraph-hubble/hubble-fe/src/i18n/resources/en-US/graph-managment/addition.json index 9b3a35b0f..d137ee317 100644 --- a/hugegraph-hubble/hubble-fe/src/i18n/resources/en-US/graph-managment/addition.json +++ b/hugegraph-hubble/hubble-fe/src/i18n/resources/en-US/graph-managment/addition.json @@ -132,7 +132,8 @@ "vertex-type-reuse-success": "已成功复用顶点类型", "property-using-cannot-delete": "当前属性数据正在使用中,不可删除。", "reuse-property-success": "已成功复用属性", - "reuse-vertex-type-notice": "顶点类型关联的属性和属性索引将一同复用" + "reuse-vertex-type-notice": "顶点类型关联的属性和属性索引将一同复用", + "illegal-vertex": "该顶点是非法顶点,可能是由悬空边导致" }, "operate": { "reuse-vertex-type": "复用顶点类型", diff --git a/hugegraph-hubble/hubble-fe/src/i18n/resources/zh-CN/graph-managment/addition.json b/hugegraph-hubble/hubble-fe/src/i18n/resources/zh-CN/graph-managment/addition.json index 9b3a35b0f..d137ee317 100644 --- a/hugegraph-hubble/hubble-fe/src/i18n/resources/zh-CN/graph-managment/addition.json +++ b/hugegraph-hubble/hubble-fe/src/i18n/resources/zh-CN/graph-managment/addition.json @@ -132,7 +132,8 @@ "vertex-type-reuse-success": "已成功复用顶点类型", "property-using-cannot-delete": "当前属性数据正在使用中,不可删除。", "reuse-property-success": "已成功复用属性", - "reuse-vertex-type-notice": "顶点类型关联的属性和属性索引将一同复用" + "reuse-vertex-type-notice": "顶点类型关联的属性和属性索引将一同复用", + "illegal-vertex": "该顶点是非法顶点,可能是由悬空边导致" }, "operate": { "reuse-vertex-type": "复用顶点类型", diff --git a/hugegraph-hubble/hubble-fe/src/i18n/resources/zh-CN/graph-managment/data-import/import-tasks/ImportTasks.json b/hugegraph-hubble/hubble-fe/src/i18n/resources/zh-CN/graph-managment/data-import/import-tasks/ImportTasks.json index da8577c09..2edf4240c 100644 --- a/hugegraph-hubble/hubble-fe/src/i18n/resources/zh-CN/graph-managment/data-import/import-tasks/ImportTasks.json +++ b/hugegraph-hubble/hubble-fe/src/i18n/resources/zh-CN/graph-managment/data-import/import-tasks/ImportTasks.json @@ -256,7 +256,8 @@ "placeholder": { "select-vertex-type": "请选择顶点类型", "select-edge-type": "请选择边类型", - "select-id-column": "请选择 ID 列" + "select-id-column": "请选择 ID 列", + "empty-value": "空" } }, "manipulations": { diff --git a/hugegraph-hubble/hubble-fe/src/i18n/resources/zh-CN/graph-managment/dataAnalyze.json b/hugegraph-hubble/hubble-fe/src/i18n/resources/zh-CN/graph-managment/dataAnalyze.json index 87afb8a40..8f01b76e3 100644 --- a/hugegraph-hubble/hubble-fe/src/i18n/resources/zh-CN/graph-managment/dataAnalyze.json +++ b/hugegraph-hubble/hubble-fe/src/i18n/resources/zh-CN/graph-managment/dataAnalyze.json @@ -9,26 +9,27 @@ "favorite": "收藏", "reset": "重置" }, + "hint": { + "graph-disabled": "该图不可用" + }, "algorithm-list": { "title": "算法目录", "loop-detection": "环路检测", "focus-detection": "交点检测", "shortest-path": "最短路径", "shortest-path-all": "全最短路径", - "all-path": "全部路径", - "neighbor-rank-recommendation": "Neighbor Rank推荐算法", + "all-path": "所有路径", "model-similarity": "模型相似度算法", - "real-time-recommendation": "实时推荐", + "neighbor-rank": "Neighbor Rank推荐算法", "k-step-neighbor": "k步邻居", "k-hop": "k跳算法", "custom-path": "自定义路径", - "custom-intersection-detection": "自定义交点检测", "radiographic-inspection": "射线检测", - "common-neighbor": "共同邻居", + "same-neighbor": "共同邻居", "weighted-shortest-path": "带权最短路径", - "single-source-weighted-path": "单源带权路径", - "jaccard-similarity": "Jaccard相似度", - "personal-rank-recommendation": "Personal Rank推荐算法" + "single-source-weighted-shortest-path": "单源带权最短路径", + "jaccard": "Jaccard相似度", + "personal-rank": "Personal Rank推荐算法" }, "algorithm-forms": { "loop-detection": { @@ -45,18 +46,20 @@ "pre-value": "全部", "placeholder": { "input-source-id": "请输入起点ID", - "input-integer": "请填写大于等于0的整数", + "input-positive-integer-or-negative-one-max-degree": "请填写-1或大于0的整数,默认为10000", + "input-positive-integer-or-negative-one-capacity": "请填写-1或大于0的整数,默认为10000000", + "input-positive-integer-or-negative-one-limit": "请填写-1或大于0的整数,默认为10", "input-positive-integer": "请填写大于0的整数", "no-edge-types": "无边类型" }, "hint": { "max-depth": "为保证性能,建议不超过10步,推荐5步", - "skip-degree": "填写查询过程中需要跳过的顶点的最小的边数目,即当顶点的边数目大于超级顶点度数时,跳过该顶点,可用于规避超级点" + "max-degree": "查询过程中,单个顶点的最大边数目" }, "validations": { "no-empty": "该项不能为空", - "integer-only": "请填写大于等于0的整数", - "postive-integer-only": "请填写大于0的整数" + "postive-integer-only": "请填写大于0的整数", + "positive-integer-or-negative-one-only": "请填写-1或大于0的整数" } }, "focus-detection": { @@ -67,25 +70,27 @@ "max_depth": "最大步数:", "label": "边类型:", "max_degree": "最大度数:", - "limit": "访问顶点最大值:", - "capacity": "返回交点最大值:" + "capacity": "访问顶点最大值:", + "limit": "返回交点最大值:" }, "pre-value": "全部", "placeholder": { "input-source-id": "请输入起点ID", "input-target-id": "请输入终点ID", - "input-integer": "请填写大于等于0的整数", + "input-positive-integer-or-negative-one-max-degree": "请填写-1或大于0的整数,默认为10000", + "input-positive-integer-or-negative-one-capacity": "请填写-1或大于0的整数,默认为10000000", + "input-positive-integer-or-negative-one-limit": "请填写-1或大于0的整数,默认为10", "input-positive-integer": "请填写大于0的整数", "no-edge-types": "无边类型" }, "hint": { "max-depth": "为保证性能,建议不超过10步,推荐5步", - "skip-degree": "填写查询过程中需要跳过的顶点的最小的边数目,即当顶点的边数目大于超级顶点度数时,跳过该顶点,可用于规避超级点" + "max-degree": "查询过程中,单个顶点的最大边数目" }, "validations": { "no-empty": "该项不能为空", - "integer-only": "请填写大于等于0的整数", - "postive-integer-only": "请填写大于0的整数" + "postive-integer-only": "请填写大于0的整数", + "positive-integer-or-negative-one-only": "请填写-1或大于0的整数" } }, "shortest-path": { @@ -94,28 +99,29 @@ "target": "终点ID:", "direction": "方向:", "max_depth": "最大步数:", - "maxDepth": "最大步数:", "label": "边类型:", "max_degree": "最大度数:", - "maxDegree": "最大度数:", "skip_degree": "超级顶点度数:", - "skipDegree": "超级顶点度数:", "capacity": "访问顶点最大值:" }, "pre-value": "全部", "placeholder": { "input-source-id": "请输入起点ID", "input-target-id": "请输入终点ID", - "input-integer": "请填写大于等于0的整数", + "input-positive-integer-or-negative-one-max-degree": "请填写-1或大于0的整数,默认为10000", + "input-positive-integer-or-negative-one-capacity": "请填写-1或大于0的整数,默认为10000000", + "input-integer": "请填写大于等于0的整数,默认为0", "input-positive-integer": "请填写大于0的整数", "no-edge-types": "无边类型" }, "hint": { "max-depth": "为保证性能,建议不超过10步,推荐5步", + "max-degree": "查询过程中,单个顶点的最大边数目", "skip-degree": "填写查询过程中需要跳过的顶点的最小的边数目,即当顶点的边数目大于超级顶点度数时,跳过该顶点,可用于规避超级点" }, "validations": { "no-empty": "该项不能为空", + "positive-integer-or-negative-one-only": "请填写-1或大于0的整数", "integer-only": "请填写大于等于0的整数", "postive-integer-only": "请填写大于0的整数" } @@ -135,18 +141,22 @@ "placeholder": { "input-source-id": "请输入起点ID", "input-target-id": "请输入终点ID", - "input-integer": "请填写大于等于0的整数", + "input-positive-integer-or-negative-one-max-degree": "请填写-1或大于0的整数,默认为10000", + "input-positive-integer-or-negative-one-capacity": "请填写-1或大于0的整数,默认为10000000", "input-positive-integer": "请填写大于0的整数", + "input-integer": "请填写大于等于0的整数,默认为0", "no-edge-types": "无边类型" }, "hint": { "max-depth": "为保证性能,建议不超过10步,推荐5步", + "max-degree": "查询过程中,单个顶点的最大边数目", "skip-degree": "填写查询过程中需要跳过的顶点的最小的边数目,即当顶点的边数目大于超级顶点度数时,跳过该顶点,可用于规避超级点" }, "validations": { "no-empty": "该项不能为空", "integer-only": "请填写大于等于0的整数", - "postive-integer-only": "请填写大于0的整数" + "postive-integer-only": "请填写大于0的整数", + "positive-integer-or-negative-one-only": "请填写-1或大于0的整数" } }, "all-path": { @@ -164,32 +174,34 @@ "placeholder": { "input-source-id": "请输入起点ID", "input-target-id": "请输入终点ID", - "input-integer": "请填写大于等于0的整数", + "input-positive-integer-or-negative-one-max-degree": "请填写-1或大于0的整数,默认为10000", + "input-positive-integer-or-negative-one-capacity": "请填写-1或大于0的整数,默认为10000000", + "input-positive-integer-or-negative-one-limit": "请填写-1或大于0的整数,默认为10", "input-positive-integer": "请填写大于0的整数", "no-edge-types": "无边类型" }, "hint": { "max-depth": "为保证性能,建议不超过10步,推荐5步", - "skip-degree": "填写查询过程中需要跳过的顶点的最小的边数目,即当顶点的边数目大于超级顶点度数时,跳过该顶点,可用于规避超级点" + "max-degree": "查询过程中,单个顶点的最大边数目" }, "validations": { "no-empty": "该项不能为空", - "integer-only": "请填写大于等于0的整数", - "postive-integer-only": "请填写大于0的整数" + "postive-integer-only": "请填写大于0的整数", + "positive-integer-or-negative-one-only": "请填写-1或大于0的整数" } }, "model-similarity": { "options": { "method": "起点选择方式:", "source": "起点ID:", - "vertex-type": "顶点类型", - "vertex-property": "顶点属性", + "vertex-type": "顶点类型:", + "vertex-property": "顶点属性及值:", "direction": "方向:", "least_neighbor": "最少邻居数:", "similarity": "相似度:", "label": "边类型:", "max_similar": "相似度最高个数:", - "least_similar": "梭形相似点最小个数:", + "least_similar": "模形相似点最小个数:", "property_filter": "属性过滤:", "least_property_number": "最小属性值个数:", "max_degree": "最大度数:", @@ -204,18 +216,26 @@ "filtered-type-property": "筛选类型属性" }, "placeholder": { - "input-source-id": "请输入起点ID", + "input-source-id": "请输入起点ID,多个ID用逗号分隔", "input-vertex-type": "请选择顶点类型", - "input-vertex-property": "请选择顶点属性", + "select-vertex-property": "请选择顶点属性", + "input-vertex-property": "多属性值以逗号分隔", + "input-positive-integer-or-negative-one-max-degree": "请填写-1或大于0的整数,默认为10000", + "input-positive-integer-or-negative-one-capacity": "请填写-1或大于0的整数,默认为10000000", + "input-positive-integer-or-negative-one-limit": "请填写-1或大于0的整数,默认为10", "input-integer": "请填写大于等于0的整数", "input-positive-integer": "请填写大于0的整数", "input-integer-gt-1": "请填写大于1的整数", + "positive-integer-or-negative-one-only": "请填写-1或大于0的整数", "input-filtered-property": "请选择需要过滤的属性", "no-properties": "无属性", - "no-vertex-type": "无顶点类型" + "no-vertex-type": "无顶点类型", + "similarity": "请输入(0-1]的数字" }, "hint": { + "vertex_type_or_property": "顶点类型/顶点属性至少填写一项", "least_property_number": "属性过滤和最小属性值个数需一起使用;设置后效果为:当起点跟其所有的梭形相似点某个属性的值大于等于最小属性值个数时,才会返回该起点及其梭形相似点", + "max-degree": "查询过程中,单个顶点的最大边数目", "least_neighbor": "邻居数少于当前设定值,则认为起点没有梭形相似点", "similarity": "起点与\"梭形相似点\"的共同邻居数目占起点的全部邻居数目的比例", "max_similar": "返回起点的梭形相似点中相似度最高的top个数,0表示全部", @@ -225,40 +245,387 @@ "no-empty": "该项不能为空", "no-edge-typs": "无边类型", "integer-only": "请填写大于等于0的整数", - "positive-integer-only": "请填写大于0的整数", - "integer-gt-1": "请填写大于1的整数" + "positive-integer-or-negative-one-only": "请填写-1或大于0的整数", + "postive-integer-only": "请填写大于0的整数", + "integer-gt-1": "请填写大于1的整数", + "similarity": "请输入(0-1]的数字", + "no-gt-1000": "该值不能大于等于1000" }, + "add": "添加", + "delete": "删除", "pre-value": "全部" }, "neighbor-rank": { "options": { "source": "起点ID:", - "alpha": "Alpha", + "alpha": "Alpha:", "direction": "方向:", "capacity": "访问顶点最大值:", "label": "边类型:", "degree": "最大度数:", - "top": "每层保留权重Top N:" + "top": "每层保留权重Top N:", + "steps": "steps:" }, "placeholder": { "input-source-id": "请输入起点ID", - "input-integer": "请填写大于等于0的整数", + "input-integer-lt-1000": "请填写大于等于0小于1000的整数, 默认为100", + "input-positive-integer-or-negative-one-max-degree": "请填写-1或大于0的整数,默认为10000", + "input-positive-integer-or-negative-one-capacity": "请填写-1或大于0的整数,默认为10000000", "input-positive-integer": "请填写大于0的整数", "range": "范围(0-1]" }, "hint": { - "top": "在结果中每一层只保留权重最高的N个结果" + "top": "在结果中每一层只保留权重最高的N个结果", + "max-degree": "查询过程中,单个顶点的最大边数目" }, "validations": { "no-empty": "该项不能为空", "no-edge-typs": "无边类型", "range": "请填写大于0且小于等于1的数值", + "integer-only-lt-1000": "请填写大于等于0的整数小于1000的整数", + "postive-integer-only": "请填写大于0的整数", + "positive-integer-or-negative-one-only": "请填写-1或大于0的整数" + }, + "pre-value": "全部", + "add-new-rule": "添加规则" + }, + "k-step-neighbor": { + "options": { + "source": "起点ID:", + "direction": "方向:", + "max_depth": "最大步数:", + "label": "边类型:", + "max_degree": "最大度数:", + "limit": "返回顶点最大值:" + }, + "pre-value": "全部", + "placeholder": { + "input-source-id": "请输入起点ID", + "input-positive-integer-or-negative-one-max-degree": "请填写-1或大于0的整数,默认为10000", + "input-positive-integer-or-negative-one-limit": "请填写-1或大于0的整数,默认为10", + "input-positive-integer": "请填写大于0的整数", + "no-edge-types": "无边类型" + }, + "hint": { + "max-depth": "为保证性能,建议不超过10步,推荐5步", + "max-degree": "查询过程中,单个顶点的最大边数目" + }, + "validations": { + "no-empty": "该项不能为空", + "postive-integer-only": "请填写大于0的整数", + "positive-integer-or-negative-one-only": "请填写-1或大于0的整数" + } + }, + "k-hop": { + "options": { + "source": "起点ID:", + "direction": "方向:", + "max_depth": "最大步数:", + "nearest": "最短路径:", + "label": "边类型:", + "max_degree": "最大度数:", + "capacity": "遍历中访问顶点最大值:", + "limit": "返回顶点最大值:" + }, + "pre-value": "全部", + "placeholder": { + "input-source-id": "请输入起点ID", + "input-positive-integer-or-negative-one-max-degree": "请填写-1或大于0的整数,默认为10000", + "input-positive-integer-or-negative-one-capacity": "请填写-1或大于0的整数,默认为10000000", + "input-positive-integer-or-negative-one-limit": "请填写-1或大于0的整数,默认为10", + "input-positive-integer": "请填写大于0的整数", + "no-edge-types": "无边类型" + }, + "hint": { + "max-depth": "为保证性能,建议不超过10步,推荐5步", + "max-degree": "查询过程中,单个顶点的最大边数目", + "shortest-path": "开启后,则查询出起始顶点最短路径为depth步的顶点,关闭后,则查询出起始顶点路径为depth步的顶点,可能有环,且不一定是最短路径" + }, + "validations": { + "no-empty": "该项不能为空", + "postive-integer-only": "请填写大于0的整数", + "positive-integer-or-negative-one-only": "请填写-1或大于0的整数" + } + }, + "custom-path": { + "options": { + "method": "起点选择方式:", + "source": "起点ID:", + "vertex-type": "顶点类型:", + "vertex-property": "顶点属性:", + "sort_by": "路径权重排序:", + "capacity": "遍历中访问顶点最大值:", + "limit": "返回顶点最大值:", + "direction": "方向:", + "labels": "边类型:", + "properties": "边属性:", + "weight_by": "根据属性计算边权重:", + "degree": "最大度数:", + "sample": "采样值:", + "steps": "steps:" + }, + "placeholder": { + "input-source-id": "请输入起点ID,多个ID用逗号分隔", + "select-vertex-type": "请选择顶点类型", + "select-vertex-property": "请选择顶点属性", + "input-multiple-properties": "多属性值以逗号分隔", + "input-integer": "请填写大于等于0的整数", + "input-positive-integer": "请填写大于0的整数", + "input-positive-integer-or-negative-one-degree": "请填写-1或大于0的整数,默认为10000", + "input-positive-integer-or-negative-one-capacity": "请填写-1或大于0的整数,默认为10000000", + "input-positive-integer-or-negative-one-limit": "请填写-1或大于0的整数,默认为10", + "input-property": "请输入边属性", + "input-number": "请输入浮点数字", + "select-edge-type": "请选择边类型", + "select-edge-property": "请选择边属性", + "select-property": "请选择属性", + "no-vertex-type": "无顶点类型", + "no-vertex-property": "无顶点属性", + "no-edge-type": "无边类型", + "no-edge-property": "无边属性", + "no-properties": "无属性" + }, + "hint": { + "top": "在结果中每一层只保留权重最高的N个结果", + "vertex_type_or_property": "顶点类型/顶点属性至少填写一项" + }, + "radio-value": { + "specific-id": "指定ID", + "filtered-type-property": "筛选类型属性", + "none": "不排序", + "ascend": "升序", + "descend": "降序" + }, + "validations": { + "no-empty": "该项不能为空", + "no-edge-typs": "无边类型", + "range": "请填写大于0且小于等于1的数值", + "integer-only": "请填写大于等于0的整数", + "postive-integer-only": "请填写大于0的整数", + "positive-integer-or-negative-one-only": "请填写-1或大于0的整数", + "input-number": "请输入浮点数字" + }, + "custom-weight": "自定义权重值", + "add": "添加", + "delete": "删除", + "add-new-rule": "添加规则" + }, + "radiographic-inspection": { + "options": { + "source": "起点ID:", + "direction": "方向:", + "max_depth": "最大步数:", + "label": "边类型:", + "max_degree": "最大度数:", + "capacity": "遍历中访问顶点最大值:", + "limit": "返回非环路路径最大值:" + }, + "pre-value": "全部", + "placeholder": { + "input-source-id": "请输入起点ID", + "input-positive-integer": "请填写大于0的整数", + "input-positive-integer-or-negative-one-max-degree": "请填写-1或大于0的整数,默认为10000", + "input-positive-integer-or-negative-one-capacity": "请填写-1或大于0的整数,默认为10000000", + "input-positive-integer-or-negative-one-limit": "请填写-1或大于0的整数,默认为10000000", + "no-edge-types": "无边类型" + }, + "hint": { + "max-depth": "为保证性能,建议不超过10步,推荐5步", + "max-degree": "查询过程中,单个顶点的最大边数目" + }, + "validations": { + "no-empty": "该项不能为空", + "postive-integer-only": "请填写大于0的整数", + "positive-integer-or-negative-one-only": "请填写-1或大于0的整数" + } + }, + "same-neighbor": { + "options": { + "vertex": "顶点1:", + "other": "顶点2:", + "direction": "方向:", + "label": "边类型:", + "max_degree": "最大度数:", + "limit": "返回共同邻居最大值:" + }, + "pre-value": "全部", + "placeholder": { + "input-source-id": "请输入顶点ID", + "input-other-id": "请输入不同于顶点1的ID", + "input-positive-integer-or-negative-one-max-degree": "请填写-1或大于0的整数,默认为10000", + "input-positive-integer-or-negative-one-limit": "请填写-1或大于0的整数,默认为10000000", + "input-positive-integer": "请填写大于0的整数", + "no-edge-types": "无边类型" + }, + "hint": { + "max-depth": "为保证性能,建议不超过10步,推荐5步", + "max-degree": "查询过程中,单个顶点的最大边数目" + }, + "validations": { + "no-empty": "该项不能为空", + "postive-integer-only": "请填写大于0的整数", + "positive-integer-or-negative-one-only": "请填写-1或大于0的整数", + "no-same-value-with-other": "不能与顶点2相同", + "no-same-value-with-vertex": "不能与顶点1相同" + } + }, + "weighted-shortest-path": { + "options": { + "source": "起点ID:", + "target": "终点ID:", + "direction": "方向:", + "weight": "权重属性:", + "with_vertex": "返回顶点完整信息:", + "label": "边类型:", + "max_degree": "最大度数:", + "skip_degree": "跳过点的度数:", + "capacity": "遍历中访问顶点最大值:" + }, + "pre-value": "全部", + "placeholder": { + "input-source-id": "请输入起点ID", + "input-target-id": "请输入终点ID", + "input-integer": "请填写大于等于0的整数", + "input-positive-integer-or-negative-one-max-degree": "请填写-1或大于0的整数,默认为10000", + "input-positive-integer-or-negative-one-capacity": "请填写-1或大于0的整数,默认为10000000", + "select-property": "请选择属性", + "input-positive-integer": "请填写大于0的整数,默认为0", + "no-property": "无属性值为数字类型的属性", + "no-edge-types": "无边类型" + }, + "hint": { + "max-depth": "为保证性能,建议不超过10步,推荐5步", + "skip-degree": "当顶点的边数目大于填写值,则跳过当前顶点,用于规避超级点", + "max-degree": "查询过程中,单个顶点的最大边数目" + }, + "validations": { + "no-empty": "该项不能为空", "integer-only": "请填写大于等于0的整数", + "positive-integer-or-negative-one-only": "请填写-1或大于0的整数", + "postive-integer-only": "请填写大于0的整数" + } + }, + "single-source-weighted-shortest-path": { + "options": { + "source": "起点ID:", + "direction": "方向:", + "weight": "权重属性:", + "with_vertex": "返回顶点完整信息:", + "label": "边类型:", + "max_degree": "最大度数:", + "skip_degree": "跳过点的度数:", + "capacity": "遍历中访问顶点最大值:", + "limit": "返回顶点/最短路径最大值:" + }, + "pre-value": "全部", + "placeholder": { + "input-source-id": "请输入起点ID", + "input-integer": "请填写大于等于0的整数,默认为0", + "input-positive-integer": "请填写大于0的整数", + "input-positive-integer-or-negative-one-max-degree": "请填写-1或大于0的整数,默认为10000", + "input-positive-integer-or-negative-one-capacity": "请填写-1或大于0的整数,默认为10000000", + "input-positive-integer-or-negative-one-limit": "请填写-1或大于0的整数,默认为10", + "no-property": "若不填写权重为1.0", + "no-edge-types": "无边类型" + }, + "hint": { + "max-depth": "为保证性能,建议不超过10步,推荐5步", + "skip-degree": "当顶点的边数目大于填写值,则跳过当前顶点,用于规避超级点", + "max-degree": "查询过程中,单个顶点的最大边数目" + }, + "validations": { + "no-empty": "该项不能为空", + "integer-only": "请填写大于等于0的整数", + "postive-integer-only": "请填写大于0的整数", + "positive-integer-or-negative-one-only": "请填写-1或大于0的整数" + } + }, + "jaccard": { + "options": { + "vertex": "顶点1:", + "other": "顶点2:", + "direction": "方向:", + "label": "边类型:", + "max_degree": "最大度数:" + }, + "pre-value": "全部", + "placeholder": { + "input-source-id": "请输入顶点ID", + "input-other-id": "请输入不同于顶点1的ID", + "input-positive-integer": "请填写大于0的整数", + "input-positive-integer-or-negative-one-max-degree": "请填写-1或大于0的整数,默认为10000", + "no-edge-types": "无边类型" + }, + "hint": { + "max-depth": "为保证性能,建议不超过10步,推荐5步", + "max-degree": "查询过程中,单个顶点的最大边数目" + }, + "validations": { + "no-empty": "该项不能为空", + "postive-integer-only": "请填写大于0的整数", + "positive-integer-or-negative-one-only": "请填写-1或大于0的整数", + "no-same-value-with-other": "不能与顶点2相同", + "no-same-value-with-vertex": "不能与顶点1相同" + } + }, + "personal-rank": { + "options": { + "source": "起点ID:", + "alpha": "Alpha:", + "max_depth": "迭代次数:", + "with_label": "返回结果筛选:", + "label": "边类型:", + "degree": "最大度数:", + "limit": "返回顶点最大值:", + "sorted": "返回结果排序:" + }, + "with-label-radio-value": { + "same_label": "相同类型顶点", + "other_label": "不同类型顶点", + "both_label": "全部类型顶点" + }, + "placeholder": { + "input-source-id": "请输入起点ID", + "input-positive-integer-or-negative-one-degree": "请填写-1或大于0的整数,默认为10000", + "input-positive-integer-or-negative-one-limit": "请填写-1或大于0的整数,默认为10000000", + "select-edge": "请选择边类型", + "input-positive-integer": "请填写大于0的整数", + "alpha": "请输入(0-1]的数字", + "max_depth": "请输入(0-50]的数字" + }, + "hint": { + "degree": "查询过程中,单个顶点的最大边数目", + "with-label": "根据是否与起点类型相同,筛选返回结果", + "sorted": "选择开,则降序排列,选择关,则不排序" + }, + "validations": { + "no-empty": "该项不能为空", + "no-edge-typs": "无边类型", + "alpha-range": "请填写大于0且小于等于1的数值", + "depth-range": "请填写大于0且小于等于50的数值", "postive-integer-only": "请填写大于0的整数", - "input-chars": "规则不能其它重复" + "positive-integer-or-negative-one-only": "请填写-1或大于0的整数" }, "pre-value": "全部", "add-new-rule": "添加规则" + }, + "api-name-mapping": { + "rings": "环路检测", + "crosspoints": "交点检测", + "shortpath": "最短路径", + "allshortpath": "全最短路径", + "paths": "所有路径", + "fsimilarity": "模型相似度算法", + "neighborrank": "Neighbor Rank推荐算法", + "kneighbor": "k步邻居", + "kout": "k跳算法", + "customizedpaths": "自定义路径", + "rays": "射线检测", + "sameneighbors": "共同邻居", + "weightedshortpath": "带权最短路径", + "singleshortpath": "单源带权最短路径", + "jaccardsimilarity": "Jaccard相似度", + "personalrank": "Personal Rank推荐算法" } }, "exec-logs": { diff --git a/hugegraph-hubble/hubble-fe/src/index.less b/hugegraph-hubble/hubble-fe/src/index.less index 4b8f8c932..d7aaf82a3 100644 --- a/hugegraph-hubble/hubble-fe/src/index.less +++ b/hugegraph-hubble/hubble-fe/src/index.less @@ -70,9 +70,10 @@ body { url(./assets/imgs/ic_biaoge_hover.svg) url(./assets/imgs/ic_biaoge_pressed.svg) url(./assets/imgs/ic_json_normal.svg) url(./assets/imgs/ic_json_hover.svg) - url(./assets/imgs/ic_json_pressed.svg) - url(./assets/imgs/ic_close_16.svg) - url(./assets/imgs/ic_refresh.svg); + url(./assets/imgs/ic_json_pressed.svg) url(./assets/imgs/ic_close_16.svg) + url(./assets/imgs/ic_refresh.svg) url(./assets/imgs/ic_loading_back.svg) + url(./assets/imgs/ic_loading_front.svg) + url(./assets/imgs/ic_question_mark.svg); } } @@ -298,5 +299,5 @@ code { // menu center .data-analyze-sidebar .ant-menu.ant-menu-inline-collapsed > .ant-menu-item { - padding: 0 12px ; + padding: 0 12px; } diff --git a/hugegraph-hubble/hubble-fe/src/stores/GraphManagementStore/dataAnalyzeStore/algorithmAnalyzerStore.ts b/hugegraph-hubble/hubble-fe/src/stores/GraphManagementStore/dataAnalyzeStore/algorithmAnalyzerStore.ts index 18373d163..672e4aaa6 100644 --- a/hugegraph-hubble/hubble-fe/src/stores/GraphManagementStore/dataAnalyzeStore/algorithmAnalyzerStore.ts +++ b/hugegraph-hubble/hubble-fe/src/stores/GraphManagementStore/dataAnalyzeStore/algorithmAnalyzerStore.ts @@ -1,6 +1,6 @@ import { DataAnalyzeStore } from './dataAnalyzeStore'; -import { observable, action, toJS } from 'mobx'; -import { isEmpty, remove, isEqual } from 'lodash-es'; +import { observable, action, computed } from 'mobx'; +import { isEmpty, remove, isEqual, isUndefined, flatten } from 'lodash-es'; import { v4 } from 'uuid'; import isInt from 'validator/lib/isInt'; @@ -20,9 +20,28 @@ import { createModelSimilarityDefaultParams, createValidateModelSimilarParamsErrorMessage, createNeighborRankDefaultParams, - createValidateNeighborRankErrorMessage + createValidateNeighborRankErrorMessage, + createKStepNeighborDefaultParams, + createValidateKStepNeighborParamsErrorMessage, + createKHopDefaultParams, + createValidateKHopParamsErrorMessage, + createRadiographicInspectionDefaultParams, + createValidateRadiographicInspectionParamsErrorMessage, + createSameNeighborDefaultParams, + createValidateSameNeighborParamsErrorMessage, + createWeightedShortestPathDefaultParams, + createValidateWeightedShortestPathParamsErrorMessage, + createSingleSourceWeightedShortestPathDefaultParams, + createValidateSingleSourceWeightedShortestPathParamsErrorMessage, + createJaccardDefaultParams, + createValidateJaccardParamsErrorMessage, + createPersonalRankDefaultParams, + createValidatePersonalRankParamsErrorMessage, + createCustomPathDefaultParams, + createValidateCustomPathParamsErrorMessage } from '../../factory/dataAnalyzeStore/algorithmStore'; import i18next from '../../../i18n'; +import { isGtNegativeOneButZero } from '../../utils'; import type { dict } from '../../types/common'; import type { @@ -33,9 +52,19 @@ import type { AllPathAlgorithmParams, ModelSimilarityParams, NeighborRankParams, - NeighborRankRule + NeighborRankRule, + KStepNeighbor, + KHop, + RadiographicInspection, + SameNeighbor, + WeightedShortestPath, + SingleSourceWeightedShortestPath, + Jaccard, + PersonalRank, + CustomPathParams, + CustomPathRule } from '../../types/GraphManagementStore/dataAnalyzeStore'; -import { isUndefined } from 'util'; +import isFloat from 'validator/lib/isFloat'; export class AlgorithmAnalyzerStore { dataAnalyzeStore: DataAnalyzeStore; @@ -94,8 +123,102 @@ export class AlgorithmAnalyzerStore { @observable validateNeighborRankParamsParamsErrorMessage = createValidateNeighborRankErrorMessage(); - @observable isDuplicateNeighborRankRule = false; - duplicateNeighborRankRuleSet = new Set(); + @observable + kStepNeighborParams: KStepNeighbor = createKStepNeighborDefaultParams(); + + @observable + validateKStepNeighborParamsErrorMessage = createValidateKStepNeighborParamsErrorMessage(); + + @observable + kHopParams: KHop = createKHopDefaultParams(); + + @observable + validateKHopParamsErrorMessage = createValidateKHopParamsErrorMessage(); + + @observable + customPathParams: CustomPathParams = createCustomPathDefaultParams(); + + @observable + validateCustomPathParmasErrorMessage = createValidateCustomPathParamsErrorMessage(); + + @observable + radiographicInspectionParams: RadiographicInspection = createRadiographicInspectionDefaultParams(); + + @observable + validateRadiographicInspectionParamsErrorMessage = createValidateRadiographicInspectionParamsErrorMessage(); + + @observable + sameNeighborParams: SameNeighbor = createSameNeighborDefaultParams(); + + @observable + validateSameNeighborParamsErrorMessage: SameNeighbor = createValidateSameNeighborParamsErrorMessage(); + + @observable + weightedShortestPathParams: WeightedShortestPath = createWeightedShortestPathDefaultParams(); + + @observable + validateWeightedShortestPathParamsErrorMessage = createValidateWeightedShortestPathParamsErrorMessage(); + + @observable + singleSourceWeightedShortestPathParams: SingleSourceWeightedShortestPath = createSingleSourceWeightedShortestPathDefaultParams(); + + @observable + validateSingleSourceWeightedShortestPathParamsErrorMessage = createValidateSingleSourceWeightedShortestPathParamsErrorMessage(); + + @observable + jaccardParams: Jaccard = createJaccardDefaultParams(); + + @observable + validateJaccardParamsErrorMessage = createValidateJaccardParamsErrorMessage(); + + @observable + personalRankParams: PersonalRank = createPersonalRankDefaultParams(); + + @observable + validatePersonalRankErrorMessage = createValidatePersonalRankParamsErrorMessage(); + + @computed get allPropertyIndexProperty() { + return flatten( + this.dataAnalyzeStore.propertyIndexes.map(({ fields }) => fields) + ); + } + + @computed get currentAlgorithmParams() { + switch (this.currentAlgorithm) { + case 'loop-detection': + return this.loopDetectionParams; + case 'focus-detection': + return this.focusDetectionParams; + case 'shortest-path': + return this.shortestPathAlgorithmParams; + case 'shortest-path-all': + return this.shortestPathAllParams; + case 'all-path': + return this.allPathParams; + case 'model-similarity': + return this.modelSimilarityParams; + case 'neighbor-rank': + return this.neighborRankParams; + case 'k-step-neighbor': + return this.kStepNeighborParams; + case 'k-hop': + return this.kHopParams; + case 'custom-path': + return this.customPathParams; + case 'radiographic-inspection': + return this.loopDetectionParams; + case 'same-neighbor': + return this.sameNeighborParams; + case 'weighted-shortest-path': + return this.weightedShortestPathParams; + case 'single-source-weighted-shortest-path': + return this.singleSourceWeightedShortestPathParams; + case 'jaccard': + return this.jaccardParams; + case 'personal-rank': + return this.personalRankParams; + } + } @action switchCollapse(flag: boolean) { @@ -108,25 +231,22 @@ export class AlgorithmAnalyzerStore { } @action - mutateShortestPathParams( + mutateLoopDetectionParams( key: T, - value: ShortestPathAlgorithmParams[T] + value: LoopDetectionParams[T] ) { - this.shortestPathAlgorithmParams[key] = value; + this.loopDetectionParams[key] = value; } @action - validateShortestPathParams( - key: T - ) { - const value = this.shortestPathAlgorithmParams[key]; + validateLoopDetectionParams(key: T) { + const value = this.loopDetectionParams[key]; switch (key) { case 'source': - case 'target': if (isEmpty(value)) { - this.validateShortestPathParamsErrorMessage[key] = i18next.t( - 'data-analyze.algorithm-forms.shortest-path.validations.no-empty' + this.validateLoopDetectionParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.loop-detection.validations.no-empty' ); return; @@ -134,16 +254,16 @@ export class AlgorithmAnalyzerStore { break; case 'max_depth': if (isEmpty(value)) { - this.validateShortestPathParamsErrorMessage[key] = i18next.t( - 'data-analyze.algorithm-forms.shortest-path.validations.no-empty' + this.validateLoopDetectionParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.loop-detection.validations.no-empty' ); return; } - if (!isInt(value, { min: 1 })) { - this.validateShortestPathParamsErrorMessage[key] = i18next.t( - 'data-analyze.algorithm-forms.shortest-path.validations.postive-integer-only' + if (!isInt(value as string, { min: 1 })) { + this.validateLoopDetectionParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.loop-detection.validations.postive-integer-only' ); return; @@ -151,19 +271,19 @@ export class AlgorithmAnalyzerStore { break; case 'max_degree': - if (!isInt(value, { min: 1 })) { - this.validateShortestPathParamsErrorMessage[key] = i18next.t( - 'data-analyze.algorithm-forms.shortest-path.validations.postive-integer-only' + if (!isGtNegativeOneButZero(value as string)) { + this.validateLoopDetectionParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.loop-detection.validations.positive-integer-or-negative-one-only' ); return; } break; - case 'skip_degree': - if (!isInt(value, { min: 0 })) { - this.validateShortestPathParamsErrorMessage[key] = i18next.t( - 'data-analyze.algorithm-forms.shortest-path.validations.integer-only' + case 'limit': + if (!isGtNegativeOneButZero(value as string)) { + this.validateLoopDetectionParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.loop-detection.validations.positive-integer-or-negative-one-only' ); return; @@ -171,9 +291,9 @@ export class AlgorithmAnalyzerStore { break; case 'capacity': - if (!isInt(value, { min: 1 })) { - this.validateShortestPathParamsErrorMessage[key] = i18next.t( - 'data-analyze.algorithm-forms.shortest-path.validations.postive-integer-only' + if (!isGtNegativeOneButZero(value as string)) { + this.validateLoopDetectionParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.loop-detection.validations.positive-integer-or-negative-one-only' ); return; @@ -182,32 +302,33 @@ export class AlgorithmAnalyzerStore { break; } - this.validateShortestPathParamsErrorMessage[key] = ''; + this.validateLoopDetectionParamsErrorMessage[key] = ''; } @action - resetShortestPathParams() { - this.shortestPathAlgorithmParams = createShortestPathDefaultParams(); - this.validateShortestPathParamsErrorMessage = createValidateShortestPathParamsErrorMessage(); + resetLoopDetectionParams() { + this.loopDetectionParams = createLoopDetectionDefaultParams(); + this.validateLoopDetectionParamsErrorMessage = createValidateLoopDetectionParamsErrorMessage(); } @action - mutateLoopDetectionParams( + mutateFocusDetectionParams( key: T, - value: LoopDetectionParams[T] + value: FocusDetectionParams[T] ) { - this.loopDetectionParams[key] = value; + this.focusDetectionParams[key] = value; } @action - validateLoopDetectionParams(key: T) { - const value = this.loopDetectionParams[key]; + validateFocusDetectionParams(key: T) { + const value = this.focusDetectionParams[key]; switch (key) { case 'source': + case 'target': if (isEmpty(value)) { - this.validateLoopDetectionParamsErrorMessage[key] = i18next.t( - 'data-analyze.algorithm-forms.shortest-path.validations.no-empty' + this.validateFocusDetectionParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.focus-detection.validations.no-empty' ); return; @@ -215,16 +336,16 @@ export class AlgorithmAnalyzerStore { break; case 'max_depth': if (isEmpty(value)) { - this.validateLoopDetectionParamsErrorMessage[key] = i18next.t( - 'data-analyze.algorithm-forms.shortest-path.validations.no-empty' + this.validateFocusDetectionParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.focus-detection.validations.no-empty' ); return; } - if (!isInt(value as string, { min: 1 })) { - this.validateLoopDetectionParamsErrorMessage[key] = i18next.t( - 'data-analyze.algorithm-forms.shortest-path.validations.postive-integer-only' + if (!isInt(value, { min: 1 })) { + this.validateFocusDetectionParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.focus-detection.validations.postive-integer-only' ); return; @@ -232,9 +353,9 @@ export class AlgorithmAnalyzerStore { break; case 'max_degree': - if (!isInt(value as string, { min: 1 })) { - this.validateLoopDetectionParamsErrorMessage[key] = i18next.t( - 'data-analyze.algorithm-forms.shortest-path.validations.postive-integer-only' + if (!isGtNegativeOneButZero(value as string)) { + this.validateFocusDetectionParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.focus-detection.validations.positive-integer-or-negative-one-only' ); return; @@ -242,9 +363,9 @@ export class AlgorithmAnalyzerStore { break; case 'limit': - if (!isInt(value as string, { min: 0 })) { - this.validateLoopDetectionParamsErrorMessage[key] = i18next.t( - 'data-analyze.algorithm-forms.shortest-path.validations.integer-only' + if (!isGtNegativeOneButZero(value as string)) { + this.validateFocusDetectionParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.focus-detection.validations.positive-integer-or-negative-one-only' ); return; @@ -252,9 +373,9 @@ export class AlgorithmAnalyzerStore { break; case 'capacity': - if (!isInt(value as string, { min: 1 })) { - this.validateLoopDetectionParamsErrorMessage[key] = i18next.t( - 'data-analyze.algorithm-forms.shortest-path.validations.postive-integer-only' + if (!isGtNegativeOneButZero(value as string)) { + this.validateFocusDetectionParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.focus-detection.validations.positive-integer-or-negative-one-only' ); return; @@ -263,32 +384,34 @@ export class AlgorithmAnalyzerStore { break; } - this.validateLoopDetectionParamsErrorMessage[key] = ''; + this.validateFocusDetectionParamsErrorMessage[key] = ''; } @action - resetLoopDetectionParams() { - this.loopDetectionParams = createLoopDetectionDefaultParams(); - this.validateLoopDetectionParamsErrorMessage = createValidateLoopDetectionParamsErrorMessage(); + resetFocusDetectionParams() { + this.focusDetectionParams = createFocusDetectionDefaultParams(); + this.validateFocusDetectionParamsErrorMessage = createValidateFocusDetectionParamsErrorMessage(); } @action - mutateFocusDetectionParams( + mutateShortestPathParams( key: T, - value: FocusDetectionParams[T] + value: ShortestPathAlgorithmParams[T] ) { - this.focusDetectionParams[key] = value; + this.shortestPathAlgorithmParams[key] = value; } @action - validateFocusDetectionParams(key: T) { - const value = this.focusDetectionParams[key]; + validateShortestPathParams( + key: T + ) { + const value = this.shortestPathAlgorithmParams[key]; switch (key) { case 'source': case 'target': if (isEmpty(value)) { - this.validateFocusDetectionParamsErrorMessage[key] = i18next.t( + this.validateShortestPathParamsErrorMessage[key] = i18next.t( 'data-analyze.algorithm-forms.shortest-path.validations.no-empty' ); @@ -297,7 +420,7 @@ export class AlgorithmAnalyzerStore { break; case 'max_depth': if (isEmpty(value)) { - this.validateFocusDetectionParamsErrorMessage[key] = i18next.t( + this.validateShortestPathParamsErrorMessage[key] = i18next.t( 'data-analyze.algorithm-forms.shortest-path.validations.no-empty' ); @@ -305,7 +428,7 @@ export class AlgorithmAnalyzerStore { } if (!isInt(value, { min: 1 })) { - this.validateFocusDetectionParamsErrorMessage[key] = i18next.t( + this.validateShortestPathParamsErrorMessage[key] = i18next.t( 'data-analyze.algorithm-forms.shortest-path.validations.postive-integer-only' ); @@ -314,18 +437,18 @@ export class AlgorithmAnalyzerStore { break; case 'max_degree': - if (!isInt(value, { min: 1 })) { - this.validateFocusDetectionParamsErrorMessage[key] = i18next.t( - 'data-analyze.algorithm-forms.shortest-path.validations.postive-integer-only' + if (!isGtNegativeOneButZero(value as string)) { + this.validateShortestPathParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.shortest-path.validations.positive-integer-or-negative-one-only' ); return; } break; - case 'limit': - if (!isInt(value, { min: 0 })) { - this.validateFocusDetectionParamsErrorMessage[key] = i18next.t( + case 'skip_degree': + if (value !== '' && !isInt(value, { min: 0 })) { + this.validateShortestPathParamsErrorMessage[key] = i18next.t( 'data-analyze.algorithm-forms.shortest-path.validations.integer-only' ); @@ -334,9 +457,9 @@ export class AlgorithmAnalyzerStore { break; case 'capacity': - if (!isInt(value, { min: 1 })) { - this.validateFocusDetectionParamsErrorMessage[key] = i18next.t( - 'data-analyze.algorithm-forms.shortest-path.validations.postive-integer-only' + if (!isGtNegativeOneButZero(value as string)) { + this.validateShortestPathParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.shortest-path.validations.positive-integer-or-negative-one-only' ); return; @@ -345,13 +468,13 @@ export class AlgorithmAnalyzerStore { break; } - this.validateFocusDetectionParamsErrorMessage[key] = ''; + this.validateShortestPathParamsErrorMessage[key] = ''; } @action - resetFocusDetectionParams() { - this.focusDetectionParams = createFocusDetectionDefaultParams(); - this.validateFocusDetectionParamsErrorMessage = createValidateFocusDetectionParamsErrorMessage(); + resetShortestPathParams() { + this.shortestPathAlgorithmParams = createShortestPathDefaultParams(); + this.validateShortestPathParamsErrorMessage = createValidateShortestPathParamsErrorMessage(); } @action @@ -372,7 +495,16 @@ export class AlgorithmAnalyzerStore { case 'source': if (isEmpty(value)) { this.validateShortestPathAllParamsErrorMessage[key] = i18next.t( - 'data-analyze.algorithm-forms.shortest-path.validations.no-empty' + 'data-analyze.algorithm-forms.shortest-path-all.validations.no-empty' + ); + + return; + } + break; + case 'target': + if (isEmpty(value)) { + this.validateShortestPathAllParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.shortest-path-all.validations.no-empty' ); return; @@ -381,7 +513,7 @@ export class AlgorithmAnalyzerStore { case 'max_depth': if (isEmpty(value)) { this.validateShortestPathAllParamsErrorMessage[key] = i18next.t( - 'data-analyze.algorithm-forms.shortest-path.validations.no-empty' + 'data-analyze.algorithm-forms.shortest-path-all.validations.no-empty' ); return; @@ -389,7 +521,7 @@ export class AlgorithmAnalyzerStore { if (!isInt(value, { min: 1 })) { this.validateShortestPathAllParamsErrorMessage[key] = i18next.t( - 'data-analyze.algorithm-forms.shortest-path.validations.postive-integer-only' + 'data-analyze.algorithm-forms.shortest-path-all.validations.postive-integer-only' ); return; @@ -397,35 +529,33 @@ export class AlgorithmAnalyzerStore { break; case 'max_degree': - if (!isInt(value, { min: 1 })) { + if (!isGtNegativeOneButZero(value)) { this.validateShortestPathAllParamsErrorMessage[key] = i18next.t( - 'data-analyze.algorithm-forms.shortest-path.validations.postive-integer-only' + 'data-analyze.algorithm-forms.shortest-path-all.validations.positive-integer-or-negative-one-only' ); return; } break; - case 'max_capacity': - if (!isInt(value, { min: 0 })) { + case 'capacity': + if (!isGtNegativeOneButZero(value)) { this.validateShortestPathAllParamsErrorMessage[key] = i18next.t( - 'data-analyze.algorithm-forms.shortest-path.validations.integer-only' + 'data-analyze.algorithm-forms.shortest-path-all.validations.positive-integer-or-negative-one-only' ); return; } break; - case 'capacity': - if (!isInt(value, { min: 1 })) { + case 'skip_degree': + if (value !== '' && !isInt(value, { min: 0 })) { this.validateShortestPathAllParamsErrorMessage[key] = i18next.t( - 'data-analyze.algorithm-forms.shortest-path.validations.postive-integer-only' + 'data-analyze.algorithm-forms.shortest-path-all.validations.integer-only' ); return; } - - break; } this.validateShortestPathAllParamsErrorMessage[key] = ''; @@ -453,7 +583,16 @@ export class AlgorithmAnalyzerStore { case 'source': if (isEmpty(value)) { this.validateAllPathParamsErrorMessage[key] = i18next.t( - 'data-analyze.algorithm-forms.shortest-path.validations.no-empty' + 'data-analyze.algorithm-forms.all-path.validations.no-empty' + ); + + return; + } + break; + case 'target': + if (isEmpty(value)) { + this.validateAllPathParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.all-path.validations.no-empty' ); return; @@ -462,7 +601,7 @@ export class AlgorithmAnalyzerStore { case 'max_depth': if (isEmpty(value)) { this.validateAllPathParamsErrorMessage[key] = i18next.t( - 'data-analyze.algorithm-forms.shortest-path.validations.no-empty' + 'data-analyze.algorithm-forms.all-path.validations.no-empty' ); return; @@ -470,7 +609,7 @@ export class AlgorithmAnalyzerStore { if (!isInt(value, { min: 1 })) { this.validateAllPathParamsErrorMessage[key] = i18next.t( - 'data-analyze.algorithm-forms.shortest-path.validations.postive-integer-only' + 'data-analyze.algorithm-forms.all-path.validations.postive-integer-only' ); return; @@ -478,29 +617,29 @@ export class AlgorithmAnalyzerStore { break; case 'max_degree': - if (!isInt(value, { min: 1 })) { + if (!isGtNegativeOneButZero(value)) { this.validateAllPathParamsErrorMessage[key] = i18next.t( - 'data-analyze.algorithm-forms.shortest-path.validations.postive-integer-only' + 'data-analyze.algorithm-forms.all-path.validations.positive-integer-or-negative-one-only' ); return; } break; - case 'max_capacity': - if (!isInt(value, { min: 0 })) { + case 'capacity': + if (!isGtNegativeOneButZero(value)) { this.validateAllPathParamsErrorMessage[key] = i18next.t( - 'data-analyze.algorithm-forms.shortest-path.validations.integer-only' + 'data-analyze.algorithm-forms.shortest-path.validations.positive-integer-or-negative-one-only' ); return; } break; - case 'capacity': - if (!isInt(value, { min: 1 })) { + case 'limit': + if (!isGtNegativeOneButZero(value)) { this.validateAllPathParamsErrorMessage[key] = i18next.t( - 'data-analyze.algorithm-forms.shortest-path.validations.postive-integer-only' + 'data-analyze.algorithm-forms.shortest-path.validations.positive-integer-or-negative-one-only' ); return; @@ -514,10 +653,36 @@ export class AlgorithmAnalyzerStore { @action resetAllPathParams() { - this.allPathParams = createValidateAllPathParamsErrorMessage(); + this.allPathParams = createAllPathDefaultParams(); this.validateAllPathParamsErrorMessage = createValidateAllPathParamsErrorMessage(); } + @action + addModelSimilarityVertexProperty() { + this.modelSimilarityParams.vertexProperty.push(['', '']); + } + + @action + editModelSimilarityVertexProperty( + index: number, + type: 'key' | 'value', + value: string + ) { + if (type === 'key') { + this.modelSimilarityParams.vertexProperty[index][0] = value; + } else { + this.modelSimilarityParams.vertexProperty[index][1] = value; + } + } + + @action + removeModelSimilarityVertexProperty(propertyIndex: number) { + remove( + this.modelSimilarityParams.vertexProperty, + (_, index) => index === propertyIndex + ); + } + @action mutateModelSimilarityParams( key: T, @@ -532,7 +697,33 @@ export class AlgorithmAnalyzerStore { switch (key) { case 'source': + if (isEmpty(value)) { + this.validateModelSimilartiyParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.model-similarity.validations.no-empty' + ); + + return; + } + + break; case 'least_neighbor': + if (isEmpty(value)) { + this.validateModelSimilartiyParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.model-similarity.validations.no-empty' + ); + + return; + } + + if (!isInt(value as string, { min: 1 })) { + this.validateModelSimilartiyParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.model-similarity.validations.postive-integer-only' + ); + + return; + } + + break; case 'similarity': if (isEmpty(value)) { this.validateModelSimilartiyParamsErrorMessage[key] = i18next.t( @@ -542,22 +733,21 @@ export class AlgorithmAnalyzerStore { return; } - // if (!isInt(value, { min: 1 })) { - // this.validateModelSimilartiyParamsErrorMessage[key] = i18next.t( - // 'data-analyze.algorithm-forms.shortest-path.validations.postive-integer-only' - // ); + if ( + Object.is(Number(value), NaN) || + Number(value) > 1 || + Number(value) <= 0 + ) { + this.validateModelSimilartiyParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.model-similarity.validations.similarity' + ); - // return; - // } + return; + } break; case 'max_similar': - // case 'least_similar': - // case 'max_degree': - // case 'skip_degree': - // case 'capacity': - // case 'limit': - if (!isInt(value as string, { min: 0 })) { + if (value !== '' && !isInt(value as string, { min: 0 })) { this.validateModelSimilartiyParamsErrorMessage[key] = i18next.t( 'data-analyze.algorithm-forms.model-similarity.validations.integer-only' ); @@ -566,20 +756,71 @@ export class AlgorithmAnalyzerStore { } break; - case 'least_property_number': - if (value !== '' && !isInt(value as string, { min: 2 })) { + case 'least_similar': + if (value !== '' && !isInt(value as string, { min: 1 })) { this.validateModelSimilartiyParamsErrorMessage[key] = i18next.t( - 'data-analyze.algorithm-forms.model-similarity.validations.integer-gt-1' + 'data-analyze.algorithm-forms.model-similarity.validations.postive-integer-only' ); return; } break; - } - - this.validateModelSimilartiyParamsErrorMessage[key] = ''; - } + case 'least_property_number': + if ( + !isEmpty(this.modelSimilarityParams.property_filter) && + isEmpty(value) + ) { + this.validateModelSimilartiyParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.model-similarity.validations.no-empty' + ); + + return; + } + + if (value !== '' && !isInt(value as string, { min: 2 })) { + this.validateModelSimilartiyParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.model-similarity.validations.integer-gt-1' + ); + + return; + } + + break; + case 'max_degree': + if (!isGtNegativeOneButZero(value as string)) { + this.validateModelSimilartiyParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.model-similarity.validations.positive-integer-or-negative-one-only' + ); + + return; + } + + break; + case 'capacity': + if (!isGtNegativeOneButZero(value as string)) { + this.validateModelSimilartiyParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.model-similarity.validations.positive-integer-or-negative-one-only' + ); + + return; + } + + break; + case 'limit': + if (!isGtNegativeOneButZero(value as string)) { + this.validateModelSimilartiyParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.model-similarity.validations.positive-integer-or-negative-one-only' + ); + + return; + } + + break; + } + + this.validateModelSimilartiyParamsErrorMessage[key] = ''; + } @action resetModelSimilarityParams() { @@ -593,7 +834,7 @@ export class AlgorithmAnalyzerStore { if (method === 'id') { this.modelSimilarityParams.vertexType = ''; - this.modelSimilarityParams.vertexProperty = []; + this.modelSimilarityParams.vertexProperty = [['', '']]; this.validateModelSimilartiyParamsErrorMessage.vertexType = ''; this.validateModelSimilartiyParamsErrorMessage.vertexProperty = ''; } else { @@ -607,16 +848,21 @@ export class AlgorithmAnalyzerStore { this.neighborRankParams.steps.push({ uuid: v4(), direction: 'BOTH', - label: '__all__', + labels: ['__all__'], degree: '10000', top: '100' }); // add error message together + this.addValidateNeighborRankRule(); + } + + @action + addValidateNeighborRankRule() { this.validateNeighborRankParamsParamsErrorMessage.steps.push({ uuid: '', direction: '', - label: '', + labels: '', degree: '', top: '' }); @@ -626,6 +872,11 @@ export class AlgorithmAnalyzerStore { removeNeighborRankRule(ruleIndex: number) { remove(this.neighborRankParams.steps, (_, index) => index === ruleIndex); // remove error message together + this.removeValidateNeighborRankRule(ruleIndex); + } + + @action + removeValidateNeighborRankRule(ruleIndex: number) { remove( this.validateNeighborRankParamsParamsErrorMessage.steps, (_, index) => index === ruleIndex @@ -666,7 +917,7 @@ export class AlgorithmAnalyzerStore { this.validateNeighborRankParamsParamsErrorMessage.source = ''; break; case 'alpha': - if (Number(value) > 1 && Number(value) <= 0) { + if (isEmpty(value)) { this.validateNeighborRankParamsParamsErrorMessage[key] = i18next.t( 'data-analyze.algorithm-forms.neighbor-rank.validations.no-empty' ); @@ -674,12 +925,24 @@ export class AlgorithmAnalyzerStore { return; } + if ( + Object.is(Number(value), NaN) || + Number(value) > 1 || + Number(value) <= 0 + ) { + this.validateNeighborRankParamsParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.neighbor-rank.validations.range' + ); + + return; + } + this.validateNeighborRankParamsParamsErrorMessage.alpha = ''; break; case 'capacity': - if (!isEmpty(value) && !isInt(value as string, { min: 0 })) { + if (!isGtNegativeOneButZero(value as string)) { this.validateNeighborRankParamsParamsErrorMessage[key] = i18next.t( - 'data-analyze.algorithm-forms.neighbor-rank.validations.integer-only' + 'data-analyze.algorithm-forms.neighbor-rank.validations.positive-integer-or-negative-one-only' ); return; @@ -699,12 +962,22 @@ export class AlgorithmAnalyzerStore { switch (key) { case 'degree': + if (!isGtNegativeOneButZero(value as string)) { + this.validateNeighborRankParamsParamsErrorMessage.steps[ruleIndex][ + key + ] = i18next.t( + 'data-analyze.algorithm-forms.neighbor-rank.validations.positive-integer-or-negative-one-only' + ); + + return; + } + break; case 'top': - if (!isEmpty(value) && !isInt(value as string, { min: 0 })) { + if (!isEmpty(value) && !isInt(value as string, { min: 0, max: 999 })) { this.validateNeighborRankParamsParamsErrorMessage.steps[ruleIndex][ key ] = i18next.t( - 'data-analyze.algorithm-forms.neighbor-rank.validations.integer-only' + 'data-analyze.algorithm-forms.neighbor-rank.validations.integer-only-lt-1000' ); return; @@ -719,102 +992,893 @@ export class AlgorithmAnalyzerStore { } @action - validateDuplicateNeighborRankRules(uuid: string) { - // for (let index = 0; index < this.neighborRankParams.steps.length; index++) { - // if (index !== ruleIndex) { - // if ( - // isEqual( - // this.neighborRankParams.steps[index], - // this.neighborRankParams.steps[ruleIndex] - // ) - // ) { - // this.duplicateNeighborRankRuleSet.add(ruleIndex); - // return; - // } - // } - // } - const currentStep = this.neighborRankParams.steps.find( - ({ uuid: currentUUID }) => currentUUID === uuid - ); + resetNeighborRankParams() { + this.neighborRankParams = createNeighborRankDefaultParams(); + this.validateNeighborRankParamsParamsErrorMessage = createValidateNeighborRankErrorMessage(); + } + + @action + mutateKStepNeighborParams( + key: T, + value: KStepNeighbor[T] + ) { + this.kStepNeighborParams[key] = value; + } + + @action + validateKStepNeighborParams(key: T) { + const value = this.kStepNeighborParams[key]; + + switch (key) { + case 'source': + if (isEmpty(value)) { + this.validateKStepNeighborParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.k-step-neighbor.validations.no-empty' + ); + + return; + } + + break; + case 'max_depth': + if (isEmpty(value)) { + this.validateKStepNeighborParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.k-step-neighbor.validations.no-empty' + ); + + return; + } + + if (!isInt(value, { min: 1 })) { + this.validateKStepNeighborParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.k-step-neighbor.validations.postive-integer-only' + ); + + return; + } + + break; + case 'max_degree': + if (!isGtNegativeOneButZero(value)) { + this.validateKStepNeighborParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.k-step-neighbor.validations.positive-integer-or-negative-one-only' + ); - for (const step of this.neighborRankParams.steps) { - if (step.uuid !== uuid && !isUndefined(currentStep)) { - // need toJS util here since there will not be converted to object - // console.log('wtf is currentstep: ', { ...currentStep, id: '' }); - // console.log('wtf is step: ', { ...step, id: '' }); - // if (isEqual(toJS(currentStep), toJS(step))) { - if (isEqual({ ...currentStep, uuid: '' }, { ...step, uuid: '' })) { - this.duplicateNeighborRankRuleSet.add(uuid); return; } - } + + break; + case 'limit': + if (!isGtNegativeOneButZero(value)) { + this.validateKStepNeighborParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.k-step-neighbor.validations.positive-integer-or-negative-one-only' + ); + + return; + } + + break; } - this.duplicateNeighborRankRuleSet.delete(uuid); + this.validateKStepNeighborParamsErrorMessage[key] = ''; + } + + @action + resetKStepNeighborParams() { + this.kStepNeighborParams = createKStepNeighborDefaultParams(); + this.validateKStepNeighborParamsErrorMessage = createValidateKStepNeighborParamsErrorMessage(); + } + + @action + mutateKHopParams(key: T, value: KHop[T]) { + this.kHopParams[key] = value; + } + + @action + validateKHopParams(key: T) { + const value = this.kHopParams[key]; + + switch (key) { + case 'source': + if (isEmpty(value)) { + this.validateKHopParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.k-hop.validations.no-empty' + ); + + return; + } + + break; + case 'max_depth': + if (isEmpty(value)) { + this.validateKHopParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.k-hop.validations.no-empty' + ); + + return; + } + + if (!isInt(value as string, { min: 1 })) { + this.validateKHopParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.k-hop.validations.postive-integer-only' + ); + + return; + } + + break; + case 'max_degree': + if (!isGtNegativeOneButZero(value as string)) { + this.validateKHopParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.k-hop.validations.positive-integer-or-negative-one-only' + ); + + return; + } + + break; + case 'limit': + if (!isGtNegativeOneButZero(value as string)) { + this.validateKHopParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.k-hop.validations.positive-integer-or-negative-one-only' + ); + + return; + } + + break; + case 'capacity': + if (!isGtNegativeOneButZero(value as string)) { + this.validateKHopParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.k-hop.validations.positive-integer-or-negative-one-only' + ); + + return; + } - if (this.duplicateNeighborRankRuleSet.size !== 0) { - this.duplicateNeighborRankRuleSet.forEach((uuid) => { - this.validateDuplicateNeighborRankRules(uuid); - }); + break; } - // const arr: number[][] = []; + this.validateKHopParamsErrorMessage[key] = ''; + } - // const keys: (keyof NeighborRankRule)[] = [ - // 'direction', - // 'label', - // 'degree', - // 'top' - // ]; + @action + resetKHopParams() { + this.kHopParams = createKHopDefaultParams(); + this.validateKHopParamsErrorMessage = createValidateKHopParamsErrorMessage(); + } - // keys.forEach((key) => { - // this.neighborRankParams.steps.forEach((step, stepIndex) => { - // const ruleValue = step[key]; - // const index = arr.findIndex( - // (value) => - // value.length !== 0 && - // this.neighborRankParams.steps[value[0]][key] === ruleValue - // ); + @action + addCustomPathRule() { + this.customPathParams.steps.push({ + uuid: v4(), + direction: 'BOTH', + labels: this.dataAnalyzeStore.edgeTypes.map(({ name }) => name), + weight_by: '', + default_weight: '', + properties: [['', '']], + degree: '10000', + sample: '100' + }); - // if (index !== -1) { - // arr.push([stepIndex]); - // } else { - // arr[index].push(stepIndex); - // } - // }); - // }); + // add error message together + this.validateCustomPathParmasErrorMessage.steps.push({ + uuid: '', + direction: '', + labels: '', + weight_by: '', + default_weight: '', + properties: '', + degree: '', + sample: '' + }); } @action - resetNeighborRankParams() { - this.neighborRankParams = createNeighborRankDefaultParams(); - this.validateNeighborRankParamsParamsErrorMessage = createValidateNeighborRankErrorMessage(); + removeCustomPathRule(ruleIndex: number) { + remove(this.customPathParams.steps, (_, index) => index === ruleIndex); + // remove error message together + remove( + this.validateCustomPathParmasErrorMessage.steps, + (_, index) => index === ruleIndex + ); } @action - dispose() { - this.requestStatus = initializeRequestStatus(); - this.errorInfo = initializeErrorInfo(); - this.currentAlgorithm = ''; - this.shortestPathAlgorithmParams = createShortestPathDefaultParams(); - this.validateShortestPathParamsErrorMessage = createValidateShortestPathParamsErrorMessage(); + addCustomPathVertexProperty() { + this.customPathParams.vertexProperty.push(['', '']); + } - this.loopDetectionParams = createLoopDetectionDefaultParams(); - this.validateLoopDetectionParamsErrorMessage = createValidateLoopDetectionParamsErrorMessage(); + @action + removeCustomPathVertexProperty(propertyIndex: number) { + remove( + this.customPathParams.vertexProperty, + (_, index) => index === propertyIndex + ); + } - this.focusDetectionParams = createFocusDetectionDefaultParams(); - this.validateFocusDetectionParamsErrorMessage = createValidateFocusDetectionParamsErrorMessage(); + @action + addCustomPathRuleProperty(ruleIndex: number) { + this.customPathParams.steps[ruleIndex].properties.push(['', '']); + } - this.shortestPathAllParams = createShortestPathAllDefaultParams(); - this.validateShortestPathAllParamsErrorMessage = createValidateShortestPathAllParamsErrorMessage(); + @action + removeCustomPathRuleProperty(ruleIndex: number, propertyIndex: number) { + remove( + this.customPathParams.steps[ruleIndex].properties, + (_, index) => index === propertyIndex + ); + } - this.allPathParams = createAllPathDefaultParams(); - this.validateAllPathParamsErrorMessage = createValidateAllPathParamsErrorMessage(); + @action + mutateCustomPathParams( + key: T, + value: CustomPathParams[T] + ) { + this.customPathParams[key] = value; + } - this.modelSimilarityParams = createModelSimilarityDefaultParams(); - this.validateModelSimilartiyParamsErrorMessage = createValidateModelSimilarParamsErrorMessage(); + @action + mutateCustomPathRuleParams( + key: T, + value: CustomPathRule[T], + index: number + ) { + this.customPathParams.steps[index][key] = value; + } - this.neighborRankParams = createNeighborRankDefaultParams(); - this.validateNeighborRankParamsParamsErrorMessage = createValidateNeighborRankErrorMessage(); + @action + validateCustomPathParams(key: T) { + const value = this.customPathParams[key]; + + switch (key) { + case 'source': + if (isEmpty(value)) { + this.validateCustomPathParmasErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.custom-path.validations.no-empty' + ); + + return; + } + + this.validateCustomPathParmasErrorMessage.source = ''; + break; + case 'capacity': + if (!isGtNegativeOneButZero(value as string)) { + this.validateCustomPathParmasErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.custom-path.validations.positive-integer-or-negative-one-only' + ); + + return; + } + + this.validateCustomPathParmasErrorMessage.capacity = ''; + break; + case 'limit': + if (!isGtNegativeOneButZero(value as string)) { + this.validateCustomPathParmasErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.custom-path.validations.positive-integer-or-negative-one-only' + ); + + return; + } + + this.validateCustomPathParmasErrorMessage.capacity = ''; + break; + } + } + + @action + validateCustomPathRules( + key: T, + ruleIndex: number + ) { + const value = this.customPathParams.steps[ruleIndex][key]; + + switch (key) { + case 'properties': + if (isEmpty(value)) { + this.validateCustomPathParmasErrorMessage.steps[ruleIndex][ + key + ] = i18next.t( + 'data-analyze.algorithm-forms.custom-path.validations.no-empty' + ); + + return; + } + + break; + case 'degree': + if (!isGtNegativeOneButZero(value as string)) { + this.validateCustomPathParmasErrorMessage.steps[ruleIndex][ + key + ] = i18next.t( + 'data-analyze.algorithm-forms.neighbor-rank.validations.positive-integer-or-negative-one-only' + ); + + return; + } + break; + case 'sample': + if (!isGtNegativeOneButZero(value as string)) { + this.validateCustomPathParmasErrorMessage.steps[ruleIndex][ + key + ] = i18next.t( + 'data-analyze.algorithm-forms.neighbor-rank.validations.positive-integer-or-negative-one-only' + ); + + return; + } + break; + case 'default_weight': + if (isEmpty(value)) { + this.validateCustomPathParmasErrorMessage.steps[ruleIndex][ + key + ] = i18next.t( + 'data-analyze.algorithm-forms.custom-path.validations.no-empty' + ); + + return; + } + + if (!isFloat(value as string)) { + this.validateCustomPathParmasErrorMessage.steps[ruleIndex][ + key + ] = i18next.t( + 'data-analyze.algorithm-forms.custom-path.validations.input-number' + ); + + return; + } + + break; + default: + return; + } + + this.validateCustomPathParmasErrorMessage.steps[ruleIndex][key] = ''; + } + + @action + switchCustomPathMethod(method: string) { + this.customPathParams.method = method; + + if (method === 'id') { + this.customPathParams.vertexType = ''; + this.customPathParams.vertexProperty = [['', '']]; + this.validateCustomPathParmasErrorMessage.vertexType = ''; + this.validateCustomPathParmasErrorMessage.vertexProperty = ''; + } else { + this.customPathParams.source = ''; + this.validateCustomPathParmasErrorMessage.source = ''; + } + } + + @action + resetCustomPathParams() { + this.customPathParams = createCustomPathDefaultParams(); + + // manually assign step edge values + this.customPathParams.steps[0].labels = this.dataAnalyzeStore.edgeTypes.map( + ({ name }) => name + ); + this.validateCustomPathParmasErrorMessage = createValidateCustomPathParamsErrorMessage(); + } + + @action + mutateRadiographicInspectionParams( + key: T, + value: RadiographicInspection[T] + ) { + this.radiographicInspectionParams[key] = value; + } + + @action + validateRadiographicInspectionParams( + key: T + ) { + const value = this.radiographicInspectionParams[key]; + + switch (key) { + case 'source': + if (isEmpty(value)) { + this.validateRadiographicInspectionParamsErrorMessage[ + key + ] = i18next.t( + 'data-analyze.algorithm-forms.radiographic-inspection.validations.no-empty' + ); + + return; + } + + break; + case 'max_depth': + if (isEmpty(value)) { + this.validateRadiographicInspectionParamsErrorMessage[ + key + ] = i18next.t( + 'data-analyze.algorithm-forms.radiographic-inspection.validations.no-empty' + ); + + return; + } + + if (!isInt(value, { min: 1 })) { + this.validateRadiographicInspectionParamsErrorMessage[ + key + ] = i18next.t( + 'data-analyze.algorithm-forms.radiographic-inspection.validations.postive-integer-only' + ); + + return; + } + + break; + case 'max_degree': + if (!isGtNegativeOneButZero(value)) { + this.validateRadiographicInspectionParamsErrorMessage[ + key + ] = i18next.t( + 'data-analyze.algorithm-forms.radiographic-inspection.validations.positive-integer-or-negative-one-only' + ); + + return; + } + + break; + case 'capacity': + if (!isGtNegativeOneButZero(value)) { + this.validateRadiographicInspectionParamsErrorMessage[ + key + ] = i18next.t( + 'data-analyze.algorithm-forms.radiographic-inspection.validations.positive-integer-or-negative-one-only' + ); + + return; + } + + break; + case 'limit': + if (!isGtNegativeOneButZero(value)) { + this.validateRadiographicInspectionParamsErrorMessage[ + key + ] = i18next.t( + 'data-analyze.algorithm-forms.radiographic-inspection.validations.positive-integer-or-negative-one-only' + ); + + return; + } + + break; + } + + this.validateRadiographicInspectionParamsErrorMessage[key] = ''; + } + + @action + resetRadiographicInspectionParams() { + this.radiographicInspectionParams = createRadiographicInspectionDefaultParams(); + this.validateRadiographicInspectionParamsErrorMessage = createValidateRadiographicInspectionParamsErrorMessage(); + } + + @action + mutateSameNeighborParams( + key: T, + value: SameNeighbor[T] + ) { + this.sameNeighborParams[key] = value; + } + + @action + validateSameNeighborParams(key: T) { + const value = this.sameNeighborParams[key]; + + switch (key) { + case 'vertex': + if (isEmpty(value)) { + this.validateSameNeighborParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.same-neighbor.validations.no-empty' + ); + + return; + } + + if (value === this.sameNeighborParams.other) { + this.validateSameNeighborParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.same-neighbor.validations.no-same-value-with-other' + ); + + return; + } + + break; + case 'other': + if (isEmpty(value)) { + this.validateSameNeighborParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.same-neighbor.validations.no-empty' + ); + + return; + } + + if (value === this.sameNeighborParams.vertex) { + this.validateSameNeighborParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.same-neighbor.validations.no-same-value-with-vertex' + ); + + return; + } + + break; + case 'max_degree': + if (!isGtNegativeOneButZero(value)) { + this.validateSameNeighborParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.same-neighbor.validations.positive-integer-or-negative-one-only' + ); + + return; + } + + break; + case 'limit': + if (!isGtNegativeOneButZero(value)) { + this.validateSameNeighborParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.same-neighbor.validations.positive-integer-or-negative-one-only' + ); + + return; + } + + break; + } + + this.validateSameNeighborParamsErrorMessage[key] = ''; + } + + @action + resetSameNeighborParams() { + this.sameNeighborParams = createSameNeighborDefaultParams(); + this.validateSameNeighborParamsErrorMessage = createValidateSameNeighborParamsErrorMessage(); + } + + @action + mutateWeightedShortestPathParams( + key: T, + value: WeightedShortestPath[T] + ) { + this.weightedShortestPathParams[key] = value; + } + + @action + validateWeightedShortestPathParams( + key: T + ) { + const value = this.weightedShortestPathParams[key]; + + switch (key) { + case 'source': + case 'target': + if (isEmpty(value)) { + this.validateWeightedShortestPathParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.weighted-shortest-path.validations.no-empty' + ); + + return; + } + + break; + case 'max_degree': + if (!isGtNegativeOneButZero(value as string)) { + this.validateWeightedShortestPathParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.weighted-shortest-path.validations.positive-integer-or-negative-one-only' + ); + + return; + } + + break; + case 'skip_degree': + if (value !== '' && !isInt(value as string, { min: 0 })) { + this.validateWeightedShortestPathParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.weighted-shortest-path.validations.integer-only' + ); + + return; + } + + break; + case 'capacity': + if (!isGtNegativeOneButZero(value as string)) { + this.validateWeightedShortestPathParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.weighted-shortest-path.validations.positive-integer-or-negative-one-only' + ); + + return; + } + + break; + } + + this.validateWeightedShortestPathParamsErrorMessage[key] = ''; + } + + @action + resetWeightedShortestPathParams() { + this.weightedShortestPathParams = createWeightedShortestPathDefaultParams(); + this.validateWeightedShortestPathParamsErrorMessage = createValidateWeightedShortestPathParamsErrorMessage(); + } + + @action + mutateSingleSourceWeightedShortestPathParams< + T extends keyof SingleSourceWeightedShortestPath + >(key: T, value: SingleSourceWeightedShortestPath[T]) { + this.singleSourceWeightedShortestPathParams[key] = value; + } + + @action + validateSingleSourceWeightedShortestPathParams< + T extends keyof SingleSourceWeightedShortestPath + >(key: T) { + const value = this.singleSourceWeightedShortestPathParams[key]; + + switch (key) { + case 'source': + if (isEmpty(value)) { + this.validateSingleSourceWeightedShortestPathParamsErrorMessage[ + key + ] = i18next.t( + 'data-analyze.algorithm-forms.single-source-weighted-shortest-path.validations.no-empty' + ); + + return; + } + + break; + case 'max_degree': + if (!isGtNegativeOneButZero(value as string)) { + this.validateSingleSourceWeightedShortestPathParamsErrorMessage[ + key + ] = i18next.t( + 'data-analyze.algorithm-forms.single-source-weighted-shortest-path.validations.positive-integer-or-negative-one-only' + ); + + return; + } + + break; + case 'skip_degree': + if (value !== '' && !isInt(value as string, { min: 0 })) { + this.validateSingleSourceWeightedShortestPathParamsErrorMessage[ + key + ] = i18next.t( + 'data-analyze.algorithm-forms.single-source-weighted-shortest-path.validations.integer-only' + ); + + return; + } + + break; + case 'capacity': + if (!isGtNegativeOneButZero(value as string)) { + this.validateSingleSourceWeightedShortestPathParamsErrorMessage[ + key + ] = i18next.t( + 'data-analyze.algorithm-forms.single-source-weighted-shortest-path.validations.positive-integer-or-negative-one-only' + ); + + return; + } + + break; + case 'limit': + if (!isGtNegativeOneButZero(value as string)) { + this.validateSingleSourceWeightedShortestPathParamsErrorMessage[ + key + ] = i18next.t( + 'data-analyze.algorithm-forms.single-source-weighted-shortest-path.validations.positive-integer-or-negative-one-only' + ); + + return; + } + + break; + } + + this.validateSingleSourceWeightedShortestPathParamsErrorMessage[key] = ''; + } + + @action + resetSingleSourceWeightedShortestPathParams() { + this.singleSourceWeightedShortestPathParams = createSingleSourceWeightedShortestPathDefaultParams(); + this.validateSingleSourceWeightedShortestPathParamsErrorMessage = createValidateSingleSourceWeightedShortestPathParamsErrorMessage(); + } + + @action + mutateJaccardParams(key: T, value: Jaccard[T]) { + this.jaccardParams[key] = value; + } + + @action + validateJaccardParams(key: T) { + const value = this.jaccardParams[key]; + + switch (key) { + case 'vertex': + if (isEmpty(value)) { + this.validateJaccardParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.jaccard.validations.no-empty' + ); + + return; + } + + if (value === this.jaccardParams.other) { + this.validateJaccardParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.jaccard.validations.no-same-value-with-other' + ); + + return; + } + + break; + case 'other': + if (isEmpty(value)) { + this.validateJaccardParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.jaccard.validations.no-empty' + ); + + return; + } + + if (value === this.jaccardParams.vertex) { + this.validateJaccardParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.jaccard.validations.no-same-value-with-vertex' + ); + + return; + } + + break; + case 'max_degree': + if (!isGtNegativeOneButZero(value)) { + this.validateJaccardParamsErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.jaccard.validations.positive-integer-or-negative-one-only' + ); + + return; + } + + break; + } + + this.validateJaccardParamsErrorMessage[key] = ''; + } + + @action + resetJaccardParams() { + this.jaccardParams = createJaccardDefaultParams(); + this.validateJaccardParamsErrorMessage = createValidateJaccardParamsErrorMessage(); + } + + @action + mutatePersonalRankParams( + key: T, + value: PersonalRank[T] + ) { + this.personalRankParams[key] = value; + } + + @action + validatePersonalRankParams(key: T) { + const value = this.personalRankParams[key]; + + switch (key) { + case 'source': + if (isEmpty(value)) { + this.validatePersonalRankErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.personal-rank.validations.no-empty' + ); + + return; + } + + break; + case 'alpha': + if (isEmpty(value)) { + this.validatePersonalRankErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.personal-rank.validations.no-empty' + ); + + return; + } + + if ( + Object.is(Number(value), NaN) || + Number(value) > 1 || + Number(value) <= 0 + ) { + this.validatePersonalRankErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.personal-rank.validations.alpha-range' + ); + + return; + } + + break; + case 'max_depth': + if (isEmpty(value)) { + this.validatePersonalRankErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.personal-rank.validations.no-empty' + ); + + return; + } + + if ( + Object.is(Number(value), NaN) || + Number(value) > 50 || + Number(value) <= 0 + ) { + this.validatePersonalRankErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.personal-rank.validations.depth-range' + ); + + return; + } + + break; + case 'degree': + if (!isGtNegativeOneButZero(value as string)) { + this.validatePersonalRankErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.personal-rank.validations.positive-integer-or-negative-one-only' + ); + + return; + } + + break; + case 'limit': + if (!isGtNegativeOneButZero(value as string)) { + this.validatePersonalRankErrorMessage[key] = i18next.t( + 'data-analyze.algorithm-forms.personal-rank.validations.positive-integer-or-negative-one-only' + ); + + return; + } + + break; + } + + this.validatePersonalRankErrorMessage[key] = ''; + } + + @action + resetPersonalRankParams() { + this.personalRankParams = createPersonalRankDefaultParams(); + this.validatePersonalRankErrorMessage = createValidatePersonalRankParamsErrorMessage(); + } + + @action + dispose() { + this.requestStatus = initializeRequestStatus(); + this.errorInfo = initializeErrorInfo(); + this.currentAlgorithm = ''; + + this.resetLoopDetectionParams(); + this.resetFocusDetectionParams(); + this.resetShortestPathParams(); + this.resetShortestPathAllParams(); + this.resetAllPathParams(); + this.resetModelSimilarityParams(); + this.resetNeighborRankParams(); + this.resetKStepNeighborParams(); + this.resetKHopParams(); + this.resetCustomPathParams(); + this.resetRadiographicInspectionParams(); + this.resetSameNeighborParams(); + this.resetWeightedShortestPathParams(); + this.resetSingleSourceWeightedShortestPathParams(); + this.resetJaccardParams(); + this.resetPersonalRankParams(); } } diff --git a/hugegraph-hubble/hubble-fe/src/stores/GraphManagementStore/dataAnalyzeStore/dataAnalyzeStore.ts b/hugegraph-hubble/hubble-fe/src/stores/GraphManagementStore/dataAnalyzeStore/dataAnalyzeStore.ts index fa57fbdcf..3ec9345ad 100644 --- a/hugegraph-hubble/hubble-fe/src/stores/GraphManagementStore/dataAnalyzeStore/dataAnalyzeStore.ts +++ b/hugegraph-hubble/hubble-fe/src/stores/GraphManagementStore/dataAnalyzeStore/dataAnalyzeStore.ts @@ -1,7 +1,17 @@ import { createContext } from 'react'; import { observable, action, flow, computed, runInAction } from 'mobx'; import axios, { AxiosResponse } from 'axios'; -import { isUndefined, cloneDeep, isEmpty, remove, size } from 'lodash-es'; +import { + isUndefined, + cloneDeep, + isEmpty, + remove, + size, + fromPairs, + invert, + flatten, + uniq +} from 'lodash-es'; import vis from 'vis-network'; import isInt from 'validator/lib/isInt'; import isUUID from 'validator/lib/isUUID'; @@ -54,14 +64,29 @@ import type { AllPathAlgorithmParams, ModelSimilarityParams, NeighborRankParams, - NeighborRankRule + NeighborRankRule, + KHop, + RadiographicInspection, + SameNeighbor, + WeightedShortestPath, + SingleSourceWeightedShortestPath, + Jaccard, + PersonalRank, + CustomPathRule, + KStepNeighbor } from '../../types/GraphManagementStore/dataAnalyzeStore'; import type { VertexTypeListResponse, VertexType, - EdgeType + EdgeType, + MetadataPropertyIndex } from '../../types/GraphManagementStore/metadataConfigsStore'; import type { EdgeTypeListResponse } from '../../types/GraphManagementStore/metadataConfigsStore'; +import { + AlgorithmInternalNameMapping, + removeLabelKey, + filterEmptyAlgorightmParams +} from '../../../utils'; const ruleMap: RuleMap = { 大于: 'gt', @@ -112,6 +137,7 @@ export class DataAnalyzeStore { @observable favoritePopUp = ''; // whether user selects vertex or edge @observable graphInfoDataSet = ''; + @observable codeEditorInstance: CodeMirror.Editor | null = null; @observable codeEditorText = ''; @observable dynamicAddGraphDataStatus = ''; @observable favoriteQueriesSortOrder: Record< @@ -141,6 +167,7 @@ export class DataAnalyzeStore { @observable.ref valueTypes: Record = {}; @observable.ref vertexTypes: VertexType[] = []; @observable.ref edgeTypes: EdgeType[] = []; + @observable.ref propertyIndexes: MetadataPropertyIndex[] = []; @observable.ref colorSchemas: ColorSchemas = {}; @observable.ref colorList: string[] = []; @observable.ref colorMappings: Record = {}; @@ -217,6 +244,16 @@ export class DataAnalyzeStore { @observable.shallow requestStatus = initalizeRequestStatus(); @observable errorInfo = initalizeErrorInfo(); + @computed get allPropertiesFromEdge() { + return uniq( + flatten( + this.edgeTypes.map(({ properties }) => + properties.map(({ name }) => name) + ) + ) + ); + } + @computed get graphNodes(): GraphNode[] { return this.originalGraphData.data.graph_view.vertices.map( ({ id, label, properties }) => { @@ -263,6 +300,13 @@ export class DataAnalyzeStore { highlight: { background: '#fb6a02', border: '#fb6a02' }, hover: { background: '#ec3112', border: '#ec3112' } }, + // reveal label when zoom to max + scaling: { + label: { + max: Infinity, + maxVisible: Infinity + } + }, chosen: { node( values: any, @@ -578,6 +622,11 @@ export class DataAnalyzeStore { this.pulse = !this.pulse; } + @action + assignCodeEditorInstance(instance: CodeMirror.Editor) { + this.codeEditorInstance = instance; + } + @action mutateCodeEditorText(text: string) { this.codeEditorText = text; @@ -647,8 +696,23 @@ export class DataAnalyzeStore { const tempData: ExecutionLogs = { id: NaN, async_id: NaN, - type: 'GREMLIN', - content: this.codeEditorText, + algorithm_name: + this.currentTab === 'algorithm-analyze' + ? invert(AlgorithmInternalNameMapping)[ + this.algorithmAnalyzerStore.currentAlgorithm + ] + : '', + async_status: 'UNKNOWN', + type: + this.currentTab === 'algorithm-analyze' + ? 'ALGORITHM' + : this.queryMode === 'query' + ? 'GREMLIN' + : 'GREMLIN_ASYNC', + content: + this.currentTab === 'algorithm-analyze' + ? JSON.stringify(this.algorithmAnalyzerStore.currentAlgorithmParams) + : this.codeEditorText, status: 'RUNNING', duration: '0ms', create_time: timeString @@ -1047,6 +1111,7 @@ export class DataAnalyzeStore { this.isFullScreenReuslt = false; this.isClickOnNodeOrEdge = false; this.queryMode = 'query'; + this.codeEditorInstance = null; this.codeEditorText = ''; this.dynamicAddGraphDataStatus = ''; this.graphData = {} as FetchGraphResponse; @@ -1104,12 +1169,13 @@ export class DataAnalyzeStore { this.requestStatus.fetchIdList = 'pending'; try { - const result: AxiosResponse = - yield axios.get(baseUrl, { - params: { - page_size: -1 - } - }); + const result: AxiosResponse = yield axios.get< + GraphData + >(baseUrl, { + params: { + page_size: -1 + } + }); if (result.data.status !== 200) { this.errorInfo.fetchIdList.code = result.data.status; @@ -1169,17 +1235,18 @@ export class DataAnalyzeStore { this.requestStatus.fetchVertexTypeList = 'pending'; try { - const result: AxiosResponse> = - yield axios - .get>( - `${baseUrl}/${this.currentId}/schema/vertexlabels`, - { - params: { - page_size: -1 - } + const result: AxiosResponse> = yield axios + .get>( + `${baseUrl}/${this.currentId}/schema/vertexlabels`, + { + params: { + page_size: -1 } - ) - .catch(checkIfLocalNetworkOffline); + } + ) + .catch(checkIfLocalNetworkOffline); if (result.data.status !== 200) { this.errorInfo.fetchVertexTypeList.code = result.data.status; @@ -1198,10 +1265,9 @@ export class DataAnalyzeStore { this.requestStatus.fetchColorSchemas = 'pending'; try { - const result: AxiosResponse = - yield axios.get( - `${baseUrl}/${this.currentId}/schema/vertexlabels/style` - ); + const result: AxiosResponse = yield axios.get< + FetchGraphResponse + >(`${baseUrl}/${this.currentId}/schema/vertexlabels/style`); if (result.data.status !== 200) { this.errorInfo.fetchColorSchemas.code = result.data.status; @@ -1241,13 +1307,14 @@ export class DataAnalyzeStore { fetchAllNodeStyle = flow(function* fetchAllNodeStyle(this: DataAnalyzeStore) { this.requestStatus.fetchAllNodeStyle = 'pending'; try { - const result: AxiosResponse> = - yield axios.get(`${baseUrl}/${this.currentId}/schema/vertexlabels`, { - params: { - page_no: 1, - page_size: -1 - } - }); + const result: AxiosResponse> = yield axios.get(`${baseUrl}/${this.currentId}/schema/vertexlabels`, { + params: { + page_no: 1, + page_size: -1 + } + }); if (result.data.status !== 200) { throw new Error(result.data.message); @@ -1277,13 +1344,14 @@ export class DataAnalyzeStore { this.requestStatus.fetchEdgeTypes = 'pending'; try { - const result: AxiosResponse> = - yield axios.get(`${baseUrl}/${this.currentId}/schema/edgelabels`, { - params: { - page_no: 1, - page_size: -1 - } - }); + const result: AxiosResponse> = yield axios.get(`${baseUrl}/${this.currentId}/schema/edgelabels`, { + params: { + page_no: 1, + page_size: -1 + } + }); if (result.data.status !== 200) { throw new Error(result.data.message); @@ -1302,13 +1370,14 @@ export class DataAnalyzeStore { this.requestStatus.fetchAllEdgeStyle = 'pending'; try { - const result: AxiosResponse> = - yield axios.get(`${baseUrl}/${this.currentId}/schema/edgelabels`, { - params: { - page_no: 1, - page_size: -1 - } - }); + const result: AxiosResponse> = yield axios.get(`${baseUrl}/${this.currentId}/schema/edgelabels`, { + params: { + page_no: 1, + page_size: -1 + } + }); if (result.data.status !== 200) { throw new Error(result.data.message); @@ -1337,6 +1406,34 @@ export class DataAnalyzeStore { } }); + fetchAllPropertyIndexes = flow(function* fetchAllPropertyIndexes( + this: DataAnalyzeStore, + indexType: 'vertex' | 'edge' + ) { + this.requestStatus.fetchAllPropertyIndexes = 'pending'; + + try { + const result = yield axios + .get(`${baseUrl}/${this.currentId}/schema/propertyindexes`, { + params: { + page_size: -1, + is_vertex_label: indexType === 'vertex' + } + }) + .catch(checkIfLocalNetworkOffline); + + if (result.data.status !== 200) { + throw new Error(result.data.message); + } + + this.propertyIndexes = result.data.data.records; + this.requestStatus.fetchAllPropertyIndexes = 'success'; + } catch (error) { + this.requestStatus.fetchAllPropertyIndexes = 'failed'; + this.errorMessage = error.message; + } + }); + fetchGraphs = flow(function* fetchGraphs( this: DataAnalyzeStore, algorithmConfigs?: { url: string; type: string } @@ -1346,98 +1443,55 @@ export class DataAnalyzeStore { this.requestStatus.fetchGraphs = 'pending'; this.isLoadingGraph = true; - let params: - | LoopDetectionParams - | FocusDetectionParams - | ShortestPathAlgorithmParams - | ShortestPathAllAlgorithmParams - | AllPathAlgorithmParams - | ModelSimilarityParams - | NeighborRankParams - | null = null; + let params: object | null = null; if (!isUndefined(algorithmConfigs)) { switch (algorithmConfigs.type) { case Algorithm.loopDetection: { - if ( - this.algorithmAnalyzerStore.loopDetectionParams.label === '__all__' - ) { - const clonedParams: LoopDetectionParams = cloneDeep( - this.algorithmAnalyzerStore.loopDetectionParams - ); - - delete clonedParams.label; - params = clonedParams; - break; - } - - params = this.algorithmAnalyzerStore.loopDetectionParams; + const filterdObject = filterEmptyAlgorightmParams( + this.algorithmAnalyzerStore.loopDetectionParams, + ['max_degree', 'capacity', 'limit'] + ); + removeLabelKey(filterdObject); + params = filterdObject; break; } case Algorithm.focusDetection: { - if ( - this.algorithmAnalyzerStore.loopDetectionParams.label === '__all__' - ) { - const clonedParams: FocusDetectionParams = cloneDeep( - this.algorithmAnalyzerStore.focusDetectionParams - ); - - delete clonedParams.label; - params = clonedParams; - break; - } - - params = this.algorithmAnalyzerStore.focusDetectionParams; + const filterdObject = filterEmptyAlgorightmParams( + this.algorithmAnalyzerStore.focusDetectionParams, + ['max_degree', 'capacity', 'limit'] + ); + removeLabelKey(filterdObject); + params = filterdObject; break; } case Algorithm.shortestPath: { - if ( - this.algorithmAnalyzerStore.shortestPathAlgorithmParams.label === - '__all__' - ) { - const clonedParams: ShortestPathAlgorithmParams = cloneDeep( - this.algorithmAnalyzerStore.shortestPathAlgorithmParams - ); - - delete clonedParams.label; - params = clonedParams; - break; - } - - params = this.algorithmAnalyzerStore.shortestPathAlgorithmParams; + const filterdObject = filterEmptyAlgorightmParams( + this.algorithmAnalyzerStore.shortestPathAlgorithmParams, + ['max_degree', 'capacity', 'skip_degree'] + ); + removeLabelKey(filterdObject); + params = filterdObject; break; } case Algorithm.shortestPathAll: { - if ( - this.algorithmAnalyzerStore.shortestPathAllParams.label === - '__all__' - ) { - const clonedParams: ShortestPathAllAlgorithmParams = cloneDeep( - this.algorithmAnalyzerStore.shortestPathAllParams - ); - - delete clonedParams.label; - params = clonedParams; - break; - } - - params = this.algorithmAnalyzerStore.shortestPathAllParams; + const filterdObject = filterEmptyAlgorightmParams( + this.algorithmAnalyzerStore.shortestPathAllParams, + ['max_degree', 'capacity', 'skip_degree'] + ); + removeLabelKey(filterdObject); + params = filterdObject; break; } case Algorithm.allPath: { - if (this.algorithmAnalyzerStore.allPathParams.label === '__all__') { - const clonedParams: AllPathAlgorithmParams = cloneDeep( - this.algorithmAnalyzerStore.allPathParams - ); - - delete clonedParams.label; - params = clonedParams; - break; - } - - params = this.algorithmAnalyzerStore.allPathParams; + const filterdObject = filterEmptyAlgorightmParams( + this.algorithmAnalyzerStore.allPathParams, + ['max_degree', 'capacity', 'limit'] + ); + removeLabelKey(filterdObject); + params = filterdObject; break; } @@ -1455,19 +1509,32 @@ export class DataAnalyzeStore { property_filter, least_property_number, max_degree, - skip_degree, capacity, limit, return_common_connection, return_complete_info } = this.algorithmAnalyzerStore.modelSimilarityParams; + const sources: Record = {}; + + if (source !== '') { + sources.ids = source.split(','); + } else { + if (vertexType !== '') { + sources.label = vertexType; + } + + if (vertexProperty[0][0] !== '') { + const convertedVertexProperty = vertexProperty.map( + ([key, value]) => [key, value.split(',')] + ); + + sources.properties = fromPairs(convertedVertexProperty); + } + } + const convertedParams = { - sources: { - ids: [source], - label: vertexType, - properties: vertexProperty - }, + sources, label, direction, min_neighbors: least_neighbor, @@ -1487,22 +1554,61 @@ export class DataAnalyzeStore { delete convertedParams.label; } + if (max_degree === '') { + delete convertedParams.max_degree; + } + + if (capacity === '') { + delete convertedParams.capacity; + } + + if (limit === '') { + delete convertedParams.limit; + } + + if (max_similar === '') { + delete convertedParams.top; + } + + if (least_similar === '') { + delete convertedParams.min_similars; + } + + if (convertedParams.group_property === '') { + delete convertedParams.group_property; + delete convertedParams.min_groups; + } + // @ts-ignore params = convertedParams; break; } - case Algorithm.neighborRankRecommendation: { + case Algorithm.neighborRank: { const clonedNeighborRankParams = cloneDeep( this.algorithmAnalyzerStore.neighborRankParams ); + if (clonedNeighborRankParams.capacity === '') { + clonedNeighborRankParams.capacity = '10000000'; + } + clonedNeighborRankParams.steps.forEach((step, index) => { delete step.uuid; + const clonedStep = cloneDeep(step); - if (step.label === '__all__') { - const clonedStep: NeighborRankRule = cloneDeep(step); - delete clonedStep.label; + if (step.labels[0] === '__all__') { + delete clonedStep.labels; + clonedNeighborRankParams.steps[index] = clonedStep; + } + + if (step.degree === '') { + clonedStep.degree = '10000'; + clonedNeighborRankParams.steps[index] = clonedStep; + } + + if (step.top === '') { + clonedStep.top = '100'; clonedNeighborRankParams.steps[index] = clonedStep; } }); @@ -1511,8 +1617,171 @@ export class DataAnalyzeStore { break; } - // default: - // params = this.algorithmAnalyzerStore.shortestPathAlgorithmParams; + case Algorithm.kStepNeighbor: { + const filterdObject = filterEmptyAlgorightmParams( + this.algorithmAnalyzerStore.kStepNeighborParams, + ['max_degree', 'limit'] + ); + removeLabelKey(filterdObject); + params = filterdObject; + break; + } + + case Algorithm.kHop: { + const filterdObject = filterEmptyAlgorightmParams( + this.algorithmAnalyzerStore.kHopParams, + ['max_degree', 'capacity', 'limit'] + ); + removeLabelKey(filterdObject); + params = filterdObject; + break; + } + + case Algorithm.customPath: { + const { + source, + vertexType, + vertexProperty, + sort_by, + capacity, + limit, + steps + } = this.algorithmAnalyzerStore.customPathParams; + + const sources: Record = {}; + + if (source !== '') { + sources.ids = source.split(','); + } else { + if (vertexType !== '') { + sources.label = vertexType; + } + + if (vertexProperty[0][0] !== '') { + const convertedVertexProperty = vertexProperty.map( + ([key, value]) => [key, value.split(',')] + ); + + sources.properties = fromPairs(convertedVertexProperty); + } + } + + const clonedCustomPathRules = cloneDeep(steps); + + clonedCustomPathRules.forEach((step, index) => { + delete step.uuid; + + if (isEmpty(step.labels)) { + delete step.labels; + } + + if (step.properties[0][0] !== '') { + // omit property types here + // @ts-ignore + step.properties = fromPairs( + step.properties.map(([key, value]) => [key, value.split(',')]) + ); + } else { + delete step.properties; + } + + if (isEmpty(step.degree)) { + delete step.degree; + } + + if (isEmpty(step.sample)) { + delete step.sample; + } + + if (step.weight_by === '__CUSTOM_WEIGHT__') { + delete step.weight_by; + } else { + delete step.default_weight; + } + + if (step.weight_by === '') { + delete step.weight_by; + } + }); + + const convertedParams: Record = { + sources, + sort_by, + steps: clonedCustomPathRules + }; + + if (!isEmpty(capacity)) { + convertedParams.capactiy = capacity; + } + + if (!isEmpty(limit)) { + convertedParams.limit = limit; + } + + // @ts-ignore + params = convertedParams; + break; + } + + case Algorithm.radiographicInspection: { + const filterdObject = filterEmptyAlgorightmParams( + this.algorithmAnalyzerStore.radiographicInspectionParams, + ['max_degree', 'capacity', 'limit'] + ); + removeLabelKey(filterdObject); + params = filterdObject; + break; + } + + case Algorithm.sameNeighbor: { + const filterdObject = filterEmptyAlgorightmParams( + this.algorithmAnalyzerStore.sameNeighborParams, + ['max_degree', 'limit'] + ); + removeLabelKey(filterdObject); + params = filterdObject; + break; + } + + case Algorithm.weightedShortestPath: { + const filterdObject = filterEmptyAlgorightmParams( + this.algorithmAnalyzerStore.weightedShortestPathParams, + ['max_degree', 'capacity', 'skip_degree'] + ); + removeLabelKey(filterdObject); + params = filterdObject; + break; + } + + case Algorithm.singleSourceWeightedShortestPath: { + const filterdObject = filterEmptyAlgorightmParams( + this.algorithmAnalyzerStore.singleSourceWeightedShortestPathParams, + ['max_degree', 'capacity', 'skip_degree', 'limit'] + ); + removeLabelKey(filterdObject); + params = filterdObject; + break; + } + + case Algorithm.jaccard: { + const filterdObject = filterEmptyAlgorightmParams( + this.algorithmAnalyzerStore.jaccardParams, + ['max_degree'] + ); + removeLabelKey(filterdObject); + params = filterdObject; + break; + } + + case Algorithm.personalRankRecommendation: { + const filterdObject = filterEmptyAlgorightmParams( + this.algorithmAnalyzerStore.personalRankParams, + ['degree', 'limit'] + ); + removeLabelKey(filterdObject); + params = filterdObject; + break; + } } } @@ -1568,8 +1837,7 @@ export class DataAnalyzeStore { } this.graphData = result.data; - this.pageConfigs.tableResult.pageTotal = - this.originalGraphData.data.table_view.rows.length; + this.pageConfigs.tableResult.pageTotal = this.originalGraphData.data.table_view.rows.length; this.requestStatus.fetchGraphs = 'success'; this.isLoadingGraph = false; } catch (error) { @@ -1830,10 +2098,9 @@ export class DataAnalyzeStore { @action hideGraphNode(nodeId: any) { - this.graphData.data.graph_view.vertices = - this.graphData.data.graph_view.vertices.filter( - (data) => data.id !== this.rightClickedGraphData.id - ); + this.graphData.data.graph_view.vertices = this.graphData.data.graph_view.vertices.filter( + (data) => data.id !== this.rightClickedGraphData.id + ); // only delete node in vertexCollection, not edges in EdgeCollection this.vertexCollection.delete(nodeId); @@ -1878,25 +2145,26 @@ export class DataAnalyzeStore { }); try { - const result: AxiosResponse> = - yield axios.put>( - `${baseUrl}/${this.currentId}/graph/${ - this.graphInfoDataSet === 'node' ? 'vertex' : 'edge' - }/${encodeURIComponent(id)}`, - this.graphInfoDataSet === 'node' - ? { - id, - label, - properties: editedProperties - } - : { - id, - label, - properties: editedProperties, - source: this.selectedGraphLinkData.source, - target: this.selectedGraphLinkData.target - } - ); + const result: AxiosResponse> = yield axios.put>( + `${baseUrl}/${this.currentId}/graph/${ + this.graphInfoDataSet === 'node' ? 'vertex' : 'edge' + }/${encodeURIComponent(id)}`, + this.graphInfoDataSet === 'node' + ? { + id, + label, + properties: editedProperties + } + : { + id, + label, + properties: editedProperties, + source: this.selectedGraphLinkData.source, + target: this.selectedGraphLinkData.target + } + ); if (result.data.status !== 200) { this.errorInfo.updateGraphProperties.code = result.data.status; @@ -1973,10 +2241,9 @@ export class DataAnalyzeStore { this.requestStatus.fetchFilteredPropertyOptions = 'pending'; try { - const result: AxiosResponse = - yield axios.get( - `${baseUrl}/${this.currentId}/schema/edgelabels/${edgeName}` - ); + const result: AxiosResponse = yield axios.get( + `${baseUrl}/${this.currentId}/schema/edgelabels/${edgeName}` + ); if (result.data.status !== 200) { this.errorInfo.filteredPropertyOptions.code = result.data.status; @@ -2187,16 +2454,14 @@ export class DataAnalyzeStore { this.requestStatus.fetchExecutionLogs = 'pending'; try { - const result: AxiosResponse = - yield axios.get( - `${baseUrl}/${this.currentId}/execute-histories`, - { - params: { - page_size: this.pageConfigs.executionLog.pageSize, - page_no: this.pageConfigs.executionLog.pageNumber - } - } - ); + const result: AxiosResponse = yield axios.get< + ExecutionLogsResponse + >(`${baseUrl}/${this.currentId}/execute-histories`, { + params: { + page_size: this.pageConfigs.executionLog.pageSize, + page_no: this.pageConfigs.executionLog.pageNumber + } + }); if (result.data.status !== 200) { this.errorInfo.fetchExecutionLogs.code = result.data.status; @@ -2233,8 +2498,9 @@ export class DataAnalyzeStore { this.requestStatus.fetchFavoriteQueries = 'pending'; try { - const result: AxiosResponse = - yield axios.get(url); + const result: AxiosResponse = yield axios.get< + FavoriteQueryResponse + >(url); if (result.data.status !== 200) { this.errorInfo.fetchFavoriteQueries.code = result.data.status; diff --git a/hugegraph-hubble/hubble-fe/src/stores/GraphManagementStore/dataImportStore/dataMapStore.ts b/hugegraph-hubble/hubble-fe/src/stores/GraphManagementStore/dataImportStore/dataMapStore.ts index 3e4cac7fe..7f0292182 100644 --- a/hugegraph-hubble/hubble-fe/src/stores/GraphManagementStore/dataImportStore/dataMapStore.ts +++ b/hugegraph-hubble/hubble-fe/src/stores/GraphManagementStore/dataImportStore/dataMapStore.ts @@ -1,4 +1,4 @@ -import { observable, action, flow, computed, toJS } from 'mobx'; +import { observable, action, flow, computed } from 'mobx'; import axios, { AxiosResponse } from 'axios'; import { isUndefined, isEmpty, cloneDeep, remove, flatMap } from 'lodash-es'; @@ -61,11 +61,9 @@ export class DataMapStore { // validators @observable - validateFileInfoErrorMessage: FileValidator = - createValidateFileInfoErrorMessage(); + validateFileInfoErrorMessage: FileValidator = createValidateFileInfoErrorMessage(); @observable - validateAdvanceConfigErrorMessage: ValueMapValidator = - createValidateAdvanceConfigErrorMessage(); + validateAdvanceConfigErrorMessage: ValueMapValidator = createValidateAdvanceConfigErrorMessage(); @observable requestStatus = initRequestStatus(); @observable errorInfo = initErrorInfo(); @@ -274,8 +272,9 @@ export class DataMapStore { if (type === 'new') { this.newVertexType.field_mapping[vertexMapFieldIndex].mapped_name = value; } else { - this.editedVertexMap!.field_mapping[vertexMapFieldIndex].mapped_name = - value; + this.editedVertexMap!.field_mapping[ + vertexMapFieldIndex + ].mapped_name = value; } } @@ -639,8 +638,9 @@ export class DataMapStore { valueIndex: number ) { if (type === 'new') { - this.newEdgeType.value_mapping[valueMapIndex].values[valueIndex][field] = - value; + this.newEdgeType.value_mapping[valueMapIndex].values[valueIndex][ + field + ] = value; } else { this.editedEdgeMap!.value_mapping[valueMapIndex].values[valueIndex][ field @@ -797,8 +797,9 @@ export class DataMapStore { const value = mapping!.null_values.customized[optionIndex]; if (isEmpty(value)) { - this.validateAdvanceConfigErrorMessage.null_values[optionIndex] = - i18next.t('data-configs.validator.no-empty'); + this.validateAdvanceConfigErrorMessage.null_values[ + optionIndex + ] = i18next.t('data-configs.validator.no-empty'); } else { this.validateAdvanceConfigErrorMessage.null_values[optionIndex] = ''; } @@ -814,8 +815,9 @@ export class DataMapStore { ? i18next.t('data-configs.validator.no-empty') : ''; } else { - const { column_value, mapped_value } = - values[valueMapOptions?.valueIndex!]; + const { column_value, mapped_value } = values[ + valueMapOptions?.valueIndex! + ]; if (valueMapOptions?.field === 'column_value') { this.validateAdvanceConfigErrorMessage.value_mapping[ @@ -1000,8 +1002,7 @@ export class DataMapStore { this.validateAdvanceConfigErrorMessage[category] = []; return; case 'all': - this.validateAdvanceConfigErrorMessage = - createValidateAdvanceConfigErrorMessage(); + this.validateAdvanceConfigErrorMessage = createValidateAdvanceConfigErrorMessage(); return; } } @@ -1018,8 +1019,7 @@ export class DataMapStore { this.editedEdgeMap = null; this.validateFileInfoErrorMessage = createValidateFileInfoErrorMessage(); - this.validateAdvanceConfigErrorMessage = - createValidateAdvanceConfigErrorMessage(); + this.validateAdvanceConfigErrorMessage = createValidateAdvanceConfigErrorMessage(); this.requestStatus = initRequestStatus(); this.errorInfo = initErrorInfo(); @@ -1243,7 +1243,9 @@ export class DataMapStore { this.requestStatus.deleteVertexMap = 'pending'; try { - const result: AxiosResponse> = yield axios + const result: AxiosResponse> = yield axios .delete>( `${baseUrl}/${this.dataImportRootStore.currentId}/job-manager/${ this.dataImportRootStore.currentJobId @@ -1274,7 +1276,9 @@ export class DataMapStore { this.requestStatus.deleteEdgeMap = 'pending'; try { - const result: AxiosResponse> = yield axios + const result: AxiosResponse> = yield axios .delete>( `${baseUrl}/${this.dataImportRootStore.currentId}/job-manager/${ this.dataImportRootStore.currentJobId diff --git a/hugegraph-hubble/hubble-fe/src/stores/GraphManagementStore/graphManagementStore.ts b/hugegraph-hubble/hubble-fe/src/stores/GraphManagementStore/graphManagementStore.ts index 015d255db..b14c9d5c2 100644 --- a/hugegraph-hubble/hubble-fe/src/stores/GraphManagementStore/graphManagementStore.ts +++ b/hugegraph-hubble/hubble-fe/src/stores/GraphManagementStore/graphManagementStore.ts @@ -227,8 +227,7 @@ export class GraphManagementStore { validate(type: 'new' | 'edit') { const nameReg = /^[A-Za-z]\w{0,47}$/; const hostReg = /((\d{1,3}\.){3}\d{1,3}|([\w!~*'()-]+\.)*[\w!~*'()-]+)$/; - const portReg = - /^([1-9]|[1-9]\d{1}|[1-9]\d{2}|[1-9]\d{3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$/; + const portReg = /^([1-9]|[1-9]\d{1}|[1-9]\d{2}|[1-9]\d{3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$/; const dataName = type + 'GraphData'; let readyToSubmit = true; @@ -361,12 +360,13 @@ export class GraphManagementStore { this.requestStatus.fetchIdList = 'pending'; try { - const result: AxiosResponse = - yield axios.get(baseUrl, { - params: { - page_size: -1 - } - }); + const result: AxiosResponse = yield axios.get< + GraphData + >(baseUrl, { + params: { + page_size: -1 + } + }); if (result.data.status === 200 || result.data.status === 401) { if (result.data.status === 200) { @@ -377,6 +377,9 @@ export class GraphManagementStore { id, name })); + + this.graphData = result.data.data.records; + this.graphDataPageConfig.pageTotal = result.data.data.total; } if (result.data.status !== 200) { @@ -403,8 +406,9 @@ export class GraphManagementStore { this.requestStatus.fetchGraphData = 'pending'; try { - const result: AxiosResponse = - yield axios.get(url); + const result: AxiosResponse = yield axios.get< + GraphData + >(url); if (result.data.status === 200 || result.data.status === 401) { if (result.data.status === 200) { @@ -432,8 +436,9 @@ export class GraphManagementStore { const filteredParams = filterParams(this.newGraphData); try { - const result: AxiosResponse = - yield axios.post(baseUrl, filteredParams); + const result: AxiosResponse = yield axios.post< + GraphDataResponse + >(baseUrl, filteredParams); if (result.data.status === 200 || result.data.status === 401) { if (result.data.status === 200) { @@ -461,8 +466,9 @@ export class GraphManagementStore { const filteredParams = filterParams(this.editGraphData); try { - const result: AxiosResponse = - yield axios.put(`${baseUrl}/${id}`, filteredParams); + const result: AxiosResponse = yield axios.put< + GraphDataResponse + >(`${baseUrl}/${id}`, filteredParams); if (result.data.status === 200 || result.data.status === 401) { if (result.data.status === 200) { @@ -489,8 +495,9 @@ export class GraphManagementStore { this.requestStatus.deleteGraphData = 'pending'; try { - const result: AxiosResponse = - yield axios.delete(`${baseUrl}/${id}`); + const result: AxiosResponse = yield axios.delete< + GraphDataResponse + >(`${baseUrl}/${id}`); if (result.data.status === 200 || result.data.status === 401) { if (result.data.status === 200) { @@ -533,4 +540,7 @@ function filterParams(originParams: GraphDataConfig): GraphDataConfig { return newParams; } -export default createContext(new GraphManagementStore()); +// For DI in subclass +export const GraphManagementStoreInstance = new GraphManagementStore(); + +export default createContext(GraphManagementStoreInstance); diff --git a/hugegraph-hubble/hubble-fe/src/stores/GraphManagementStore/metadataConfigsStore/edgeTypeStore.ts b/hugegraph-hubble/hubble-fe/src/stores/GraphManagementStore/metadataConfigsStore/edgeTypeStore.ts index 00981554c..b9a90c25e 100644 --- a/hugegraph-hubble/hubble-fe/src/stores/GraphManagementStore/metadataConfigsStore/edgeTypeStore.ts +++ b/hugegraph-hubble/hubble-fe/src/stores/GraphManagementStore/metadataConfigsStore/edgeTypeStore.ts @@ -145,14 +145,18 @@ export class EdgeTypeStore { @observable editedCheckedReusableData: CheckedReusableData | null = null; - @observable reusableEdgeTypeNameChangeIndexes: Set = - new Set(); - @observable reusableVertexTypeNameChangeIndexes: Set = - new Set(); - @observable reusablePropertyNameChangeIndexes: Set = - new Set(); - @observable reusablePropertyIndexNameChangeIndexes: Set = - new Set(); + @observable reusableEdgeTypeNameChangeIndexes: Set = new Set< + number + >(); + @observable reusableVertexTypeNameChangeIndexes: Set = new Set< + number + >(); + @observable reusablePropertyNameChangeIndexes: Set = new Set< + number + >(); + @observable reusablePropertyIndexNameChangeIndexes: Set = new Set< + number + >(); @observable validateNewEdgeTypeErrorMessage: Record< EdgeTypeValidateFields, @@ -341,29 +345,41 @@ export class EdgeTypeStore { // if cancel clicked, reset to the original name @action resetEditedReusableEdgeTypeName(index: number) { - this.editedCheckedReusableData!.edgelabel_conflicts[index].entity.name = - this.checkedReusableData!.edgelabel_conflicts[index].entity.name; + this.editedCheckedReusableData!.edgelabel_conflicts[ + index + ].entity.name = this.checkedReusableData!.edgelabel_conflicts[ + index + ].entity.name; } // if cancel clicked, reset to the original name @action resetEditedReusableVertexTypeName(index: number) { - this.editedCheckedReusableData!.vertexlabel_conflicts[index].entity.name = - this.checkedReusableData!.vertexlabel_conflicts[index].entity.name; + this.editedCheckedReusableData!.vertexlabel_conflicts[ + index + ].entity.name = this.checkedReusableData!.vertexlabel_conflicts[ + index + ].entity.name; } // if cancel clicked, reset to the original name @action resetEditedReusablePropertyName(index: number) { - this.editedCheckedReusableData!.propertykey_conflicts[index].entity.name = - this.checkedReusableData!.propertykey_conflicts[index].entity.name; + this.editedCheckedReusableData!.propertykey_conflicts[ + index + ].entity.name = this.checkedReusableData!.propertykey_conflicts[ + index + ].entity.name; } // if cancel clicked, reset to the original name @action resetEditedReusablePropertyIndexName(index: number) { - this.editedCheckedReusableData!.propertyindex_conflicts[index].entity.name = - this.checkedReusableData!.propertyindex_conflicts[index].entity.name; + this.editedCheckedReusableData!.propertyindex_conflicts[ + index + ].entity.name = this.checkedReusableData!.propertyindex_conflicts[ + index + ].entity.name; } @action @@ -461,8 +477,8 @@ export class EdgeTypeStore { if (category === 'propertyIndexes') { this.isAddNewPropertyIndexReady = true; - this.validateNewEdgeTypeErrorMessage.propertyIndexes = - this.newEdgeType.property_indexes.map(({ name, type, fields }) => { + this.validateNewEdgeTypeErrorMessage.propertyIndexes = this.newEdgeType.property_indexes.map( + ({ name, type, fields }) => { const validatedPropertyIndex = { name: '', type: '', @@ -511,7 +527,8 @@ export class EdgeTypeStore { } return validatedPropertyIndex; - }); + } + ); } return isReady; @@ -533,56 +550,55 @@ export class EdgeTypeStore { validateEditEdgeType(initial = false) { this.isEditReady = true; - this.validateEditEdgeTypeErrorMessage.propertyIndexes = - this.editedSelectedEdgeType.append_property_indexes.map( - ({ name, type, fields }) => { - const validatedPropertyIndex = { - name: '', - type: '', - properties: '' - }; - - if (!/^[\w\u4e00-\u9fa5]{1,128}$/.test(name)) { - if (!initial) { - if (name.length !== 0) { - validatedPropertyIndex.name = i18next.t('addition.store.rule4'); - } else { - validatedPropertyIndex.name = i18next.t( - 'addition.store.item-is-required' - ); - } + this.validateEditEdgeTypeErrorMessage.propertyIndexes = this.editedSelectedEdgeType.append_property_indexes.map( + ({ name, type, fields }) => { + const validatedPropertyIndex = { + name: '', + type: '', + properties: '' + }; + + if (!/^[\w\u4e00-\u9fa5]{1,128}$/.test(name)) { + if (!initial) { + if (name.length !== 0) { + validatedPropertyIndex.name = i18next.t('addition.store.rule4'); + } else { + validatedPropertyIndex.name = i18next.t( + 'addition.store.item-is-required' + ); } - - this.isEditReady = false; - } else { - validatedPropertyIndex.name = ''; } - if (type.length === 0) { + this.isEditReady = false; + } else { + validatedPropertyIndex.name = ''; + } + + if (type.length === 0) { + !initial && + (validatedPropertyIndex.type = i18next.t( + 'addition.store.item-is-required' + )); + this.isEditReady = false; + } else { + validatedPropertyIndex.type = ''; + } + + if (Array.isArray(fields)) { + if (fields.length === 0) { !initial && - (validatedPropertyIndex.type = i18next.t( + (validatedPropertyIndex.properties = i18next.t( 'addition.store.item-is-required' )); this.isEditReady = false; - } else { - validatedPropertyIndex.type = ''; } - - if (Array.isArray(fields)) { - if (fields.length === 0) { - !initial && - (validatedPropertyIndex.properties = i18next.t( - 'addition.store.item-is-required' - )); - this.isEditReady = false; - } - } else { - validatedPropertyIndex.properties = ''; - } - - return validatedPropertyIndex; + } else { + validatedPropertyIndex.properties = ''; } - ); + + return validatedPropertyIndex; + } + ); } @action @@ -819,12 +835,11 @@ export class EdgeTypeStore { this.reusableVertexTypeNameChangeIndexes.add(index); // current vertex belongs to which edge - const mutateEdgeIndex = - editedCheckedReusableData!.edgelabel_conflicts.findIndex( - (edge) => - edge.entity.source_label === entity.name || - edge.entity.target_label === entity.name - ); + const mutateEdgeIndex = editedCheckedReusableData!.edgelabel_conflicts.findIndex( + (edge) => + edge.entity.source_label === entity.name || + edge.entity.target_label === entity.name + ); // property name in source_label or target_label has been edited this.reusableEdgeTypeNameChangeIndexes.add(mutateEdgeIndex); @@ -1091,8 +1106,9 @@ export class EdgeTypeStore { } if (category === 'propertyindex_conflicts') { - const { name: deletedPropertyIndexName } = - editedCheckedReusableData.propertyindex_conflicts[index].entity; + const { + name: deletedPropertyIndexName + } = editedCheckedReusableData.propertyindex_conflicts[index].entity; editedCheckedReusableData.propertyindex_conflicts.splice(index, 1); @@ -1147,12 +1163,10 @@ export class EdgeTypeStore { } if (category === 'propertyIndexes') { - ( - this.validateNewEdgeTypeErrorMessage - .propertyIndexes as EdgeTypeValidatePropertyIndexes[] - )[propertIndexIndex as number][ - propertIndexProperty as keyof EdgeTypeValidatePropertyIndexes - ] = ''; + (this.validateNewEdgeTypeErrorMessage + .propertyIndexes as EdgeTypeValidatePropertyIndexes[])[ + propertIndexIndex as number + ][propertIndexProperty as keyof EdgeTypeValidatePropertyIndexes] = ''; return; } @@ -1239,19 +1253,20 @@ export class EdgeTypeStore { : this.metadataConfigsRootStore.currentId; try { - const result: AxiosResponse> = - yield axios - .get(`${baseUrl}/${conn_id}/schema/edgelabels`, { - params: { - page_no: this.edgeTypeListPageConfig.pageNumber, - page_size: !options ? 10 : -1, - name_order: - this.edgeTypeListPageConfig.sort !== '' - ? this.edgeTypeListPageConfig.sort - : null - } - }) - .catch(checkIfLocalNetworkOffline); + const result: AxiosResponse> = yield axios + .get(`${baseUrl}/${conn_id}/schema/edgelabels`, { + params: { + page_no: this.edgeTypeListPageConfig.pageNumber, + page_size: !options ? 10 : -1, + name_order: + this.edgeTypeListPageConfig.sort !== '' + ? this.edgeTypeListPageConfig.sort + : null + } + }) + .catch(checkIfLocalNetworkOffline); if (result.data.status !== 200) { if (result.data.status === 401) { @@ -1324,10 +1339,10 @@ export class EdgeTypeStore { }/schema/edgelabels/${this.selectedEdgeType!.name}`, { append_properties: this.editedSelectedEdgeType.append_properties, - append_property_indexes: - this.editedSelectedEdgeType.append_property_indexes, - remove_property_indexes: - this.editedSelectedEdgeType.remove_property_indexes, + append_property_indexes: this.editedSelectedEdgeType + .append_property_indexes, + remove_property_indexes: this.editedSelectedEdgeType + .remove_property_indexes, style: this.editedSelectedEdgeType.style } ) @@ -1395,26 +1410,27 @@ export class EdgeTypeStore { this.requestStatus.checkConflict = 'pending'; try { - const result: AxiosResponse> = - yield axios - .post( - `${baseUrl}/${this.metadataConfigsRootStore.currentId}/schema/edgelabels/check_conflict`, - { - edgelabels: selectedEdgeTypes.map((selectedEdgeType) => - this.reusableEdgeTypes.find( - ({ name }) => name === selectedEdgeType - ) + const result: AxiosResponse> = yield axios + .post( + `${baseUrl}/${this.metadataConfigsRootStore.currentId}/schema/edgelabels/check_conflict`, + { + edgelabels: selectedEdgeTypes.map((selectedEdgeType) => + this.reusableEdgeTypes.find( + ({ name }) => name === selectedEdgeType ) - }, - { - params: { - reused_conn_id: this.metadataConfigsRootStore.idList.find( - ({ name }) => name === reuseId - )!.id - } + ) + }, + { + params: { + reused_conn_id: this.metadataConfigsRootStore.graphManagementStore.idList.find( + ({ name }) => name === reuseId + )!.id } - ) - .catch(checkIfLocalNetworkOffline); + } + ) + .catch(checkIfLocalNetworkOffline); if (result.data.status !== 200) { throw new Error(result.data.message); @@ -1433,38 +1449,35 @@ export class EdgeTypeStore { this.requestStatus.recheckConflict = 'pending'; try { - const result: AxiosResponse> = - yield axios - .post( - `${baseUrl}/${this.metadataConfigsRootStore.currentId}/schema/edgelabels/recheck_conflict`, - { - propertykeys: - this.editedCheckedReusableData!.propertykey_conflicts.map( - ({ entity }) => ({ - ...entity - }) - ), - propertyindexes: - this.editedCheckedReusableData!.propertyindex_conflicts.map( - ({ entity }) => ({ - ...entity - }) - ), - vertexlabels: - this.editedCheckedReusableData!.vertexlabel_conflicts.map( - ({ entity }) => ({ - ...entity - }) - ), - edgelabels: - this.editedCheckedReusableData!.edgelabel_conflicts.map( - ({ entity }) => ({ - ...entity - }) - ) - } - ) - .catch(checkIfLocalNetworkOffline); + const result: AxiosResponse> = yield axios + .post( + `${baseUrl}/${this.metadataConfigsRootStore.currentId}/schema/edgelabels/recheck_conflict`, + { + propertykeys: this.editedCheckedReusableData!.propertykey_conflicts.map( + ({ entity }) => ({ + ...entity + }) + ), + propertyindexes: this.editedCheckedReusableData!.propertyindex_conflicts.map( + ({ entity }) => ({ + ...entity + }) + ), + vertexlabels: this.editedCheckedReusableData!.vertexlabel_conflicts.map( + ({ entity }) => ({ + ...entity + }) + ), + edgelabels: this.editedCheckedReusableData!.edgelabel_conflicts.map( + ({ entity }) => ({ + ...entity + }) + ) + } + ) + .catch(checkIfLocalNetworkOffline); if (result.data.status !== 200) { throw new Error(result.data.message); diff --git a/hugegraph-hubble/hubble-fe/src/stores/GraphManagementStore/metadataConfigsStore/graphViewStore.ts b/hugegraph-hubble/hubble-fe/src/stores/GraphManagementStore/metadataConfigsStore/graphViewStore.ts index bbf6c5ef2..6e2731f38 100644 --- a/hugegraph-hubble/hubble-fe/src/stores/GraphManagementStore/metadataConfigsStore/graphViewStore.ts +++ b/hugegraph-hubble/hubble-fe/src/stores/GraphManagementStore/metadataConfigsStore/graphViewStore.ts @@ -111,6 +111,13 @@ export class GraphViewStore { highlight: { background: '#fb6a02', border: '#fb6a02' }, hover: { background: '#ec3112', border: '#ec3112' } }, + // reveal label when zoom to max + scaling: { + label: { + max: Infinity, + maxVisible: Infinity + } + }, chosen: { node( values: any, @@ -145,7 +152,7 @@ export class GraphViewStore { ({ id, label, source, target, properties, sort_keys }) => { return { id, - label, + label: label.length < 15 ? label : label.slice(0, 15) + '...', properties, source, target, diff --git a/hugegraph-hubble/hubble-fe/src/stores/GraphManagementStore/metadataConfigsStore/metadataConfigsStore.ts b/hugegraph-hubble/hubble-fe/src/stores/GraphManagementStore/metadataConfigsStore/metadataConfigsStore.ts index 8e3582957..e0adb479a 100644 --- a/hugegraph-hubble/hubble-fe/src/stores/GraphManagementStore/metadataConfigsStore/metadataConfigsStore.ts +++ b/hugegraph-hubble/hubble-fe/src/stores/GraphManagementStore/metadataConfigsStore/metadataConfigsStore.ts @@ -1,15 +1,19 @@ import { createContext } from 'react'; -import { observable, action, flow } from 'mobx'; -import axios from 'axios'; +import { observable, action } from 'mobx'; import { MetadataPropertyStore } from './metadataPropertyStore'; import { VertexTypeStore } from './vertexTypeStore'; import { EdgeTypeStore } from './edgeTypeStore'; import { MetadataPropertyIndexStore } from './metadataPropertyIndexStore'; import { GraphViewStore } from './graphViewStore'; -import { baseUrl } from '../../types/common'; + +import { + GraphManagementStore, + GraphManagementStoreInstance +} from '../graphManagementStore'; export class MetadataConfigsRootStore { + graphManagementStore: GraphManagementStore; metadataPropertyStore: MetadataPropertyStore; vertexTypeStore: VertexTypeStore; edgeTypeStore: EdgeTypeStore; @@ -18,20 +22,9 @@ export class MetadataConfigsRootStore { @observable currentId: number | null = null; - @observable requestStatus = { - fetchIdList: 'pending' - }; - - @observable errorInfo = { - fetchIdList: { - code: NaN, - message: '' - } - }; + constructor(GraphManagementStore: GraphManagementStore) { + this.graphManagementStore = GraphManagementStore; - @observable idList: { id: string; name: string }[] = []; - - constructor() { this.metadataPropertyStore = new MetadataPropertyStore(this); this.vertexTypeStore = new VertexTypeStore(this); this.edgeTypeStore = new EdgeTypeStore(this); @@ -40,53 +33,16 @@ export class MetadataConfigsRootStore { } @action - setCurrentId(id: number) { + setCurrentId(id: number | null) { this.currentId = id; } @action dispose() { this.currentId = null; - this.requestStatus = { - fetchIdList: 'pending' - }; - this.errorInfo = { - fetchIdList: { - code: NaN, - message: '' - } - }; - this.idList = []; } - - fetchIdList = flow(function* fetchIdList(this: MetadataConfigsRootStore) { - this.requestStatus.fetchIdList = 'pending'; - - try { - const result = yield axios.get(`${baseUrl}`, { - params: { - page_size: -1 - } - }); - - if (result.data.status !== 200) { - this.errorInfo.fetchIdList.code = result.data.status; - throw new Error(result.data.message); - } - - this.idList = result.data.data.records.map( - ({ id, name }: { id: string; name: string }) => ({ - id, - name - }) - ); - this.requestStatus.fetchIdList = 'success'; - } catch (error) { - this.requestStatus.fetchIdList = 'failed'; - this.errorInfo.fetchIdList.message = error.message; - console.error(error.message); - } - }); } -export default createContext(new MetadataConfigsRootStore()); +export default createContext( + new MetadataConfigsRootStore(GraphManagementStoreInstance) +); diff --git a/hugegraph-hubble/hubble-fe/src/stores/GraphManagementStore/metadataConfigsStore/vertexTypeStore.ts b/hugegraph-hubble/hubble-fe/src/stores/GraphManagementStore/metadataConfigsStore/vertexTypeStore.ts index 9a42a36a7..47e7d7dbd 100644 --- a/hugegraph-hubble/hubble-fe/src/stores/GraphManagementStore/metadataConfigsStore/vertexTypeStore.ts +++ b/hugegraph-hubble/hubble-fe/src/stores/GraphManagementStore/metadataConfigsStore/vertexTypeStore.ts @@ -126,12 +126,15 @@ export class VertexTypeStore { @observable editedCheckedReusableData: CheckedReusableData | null = null; - @observable reusableVertexTypeNameChangeIndexes: Set = - new Set(); - @observable reusablePropertyNameChangeIndexes: Set = - new Set(); - @observable reusablePropertyIndexNameChangeIndexes: Set = - new Set(); + @observable reusableVertexTypeNameChangeIndexes: Set = new Set< + number + >(); + @observable reusablePropertyNameChangeIndexes: Set = new Set< + number + >(); + @observable reusablePropertyIndexNameChangeIndexes: Set = new Set< + number + >(); @observable validateNewVertexTypeErrorMessage: Record< VertexTypeValidateFields, @@ -322,22 +325,31 @@ export class VertexTypeStore { // if cancel clicked, reset to the original name @action resetEditedReusableVertexTypeName(index: number) { - this.editedCheckedReusableData!.vertexlabel_conflicts[index].entity.name = - this.checkedReusableData!.vertexlabel_conflicts[index].entity.name; + this.editedCheckedReusableData!.vertexlabel_conflicts[ + index + ].entity.name = this.checkedReusableData!.vertexlabel_conflicts[ + index + ].entity.name; } // if cancel clicked, reset to the original name @action resetEditedReusablePropertyName(index: number) { - this.editedCheckedReusableData!.propertykey_conflicts[index].entity.name = - this.checkedReusableData!.propertykey_conflicts[index].entity.name; + this.editedCheckedReusableData!.propertykey_conflicts[ + index + ].entity.name = this.checkedReusableData!.propertykey_conflicts[ + index + ].entity.name; } // if cancel clicked, reset to the original name @action resetEditedReusablePropertyIndexName(index: number) { - this.editedCheckedReusableData!.propertyindex_conflicts[index].entity.name = - this.checkedReusableData!.propertyindex_conflicts[index].entity.name; + this.editedCheckedReusableData!.propertyindex_conflicts[ + index + ].entity.name = this.checkedReusableData!.propertyindex_conflicts[ + index + ].entity.name; } @action @@ -417,8 +429,8 @@ export class VertexTypeStore { if (category === 'propertyIndexes') { this.isAddNewPropertyIndexReady = true; - this.validateNewVertexTypeErrorMessage.propertyIndexes = - this.newVertexType.property_indexes.map(({ name, type, fields }) => { + this.validateNewVertexTypeErrorMessage.propertyIndexes = this.newVertexType.property_indexes.map( + ({ name, type, fields }) => { const validatedPropertyIndex = { name: '', type: '', @@ -467,7 +479,8 @@ export class VertexTypeStore { } return validatedPropertyIndex; - }); + } + ); } return isReady; @@ -487,55 +500,54 @@ export class VertexTypeStore { validateEditVertexType(initial = false) { this.isEditReady = true; - this.validateEditVertexTypeErrorMessage.propertyIndexes = - this.editedSelectedVertexType.append_property_indexes.map( - ({ name, type, fields }) => { - const validatedPropertyIndex = { - name: '', - type: '', - properties: '' - }; - if (!/^[\w\u4e00-\u9fa5]{1,128}$/.test(name)) { - if (!initial) { - if (name.length !== 0) { - validatedPropertyIndex.name = i18next.t('addition.store.rule4'); - } else { - validatedPropertyIndex.name = i18next.t( - 'addition.store.item-is-required' - ); - } + this.validateEditVertexTypeErrorMessage.propertyIndexes = this.editedSelectedVertexType.append_property_indexes.map( + ({ name, type, fields }) => { + const validatedPropertyIndex = { + name: '', + type: '', + properties: '' + }; + if (!/^[\w\u4e00-\u9fa5]{1,128}$/.test(name)) { + if (!initial) { + if (name.length !== 0) { + validatedPropertyIndex.name = i18next.t('addition.store.rule4'); + } else { + validatedPropertyIndex.name = i18next.t( + 'addition.store.item-is-required' + ); } - - this.isEditReady = false; - } else { - validatedPropertyIndex.name = ''; } - if (type.length === 0) { + this.isEditReady = false; + } else { + validatedPropertyIndex.name = ''; + } + + if (type.length === 0) { + !initial && + (validatedPropertyIndex.type = i18next.t( + 'addition.store.item-is-required' + )); + this.isEditReady = false; + } else { + validatedPropertyIndex.type = ''; + } + + if (Array.isArray(fields)) { + if (fields.length === 0) { !initial && - (validatedPropertyIndex.type = i18next.t( + (validatedPropertyIndex.properties = i18next.t( 'addition.store.item-is-required' )); this.isEditReady = false; - } else { - validatedPropertyIndex.type = ''; } - - if (Array.isArray(fields)) { - if (fields.length === 0) { - !initial && - (validatedPropertyIndex.properties = i18next.t( - 'addition.store.item-is-required' - )); - this.isEditReady = false; - } - } else { - validatedPropertyIndex.properties = ''; - } - - return validatedPropertyIndex; + } else { + validatedPropertyIndex.properties = ''; } - ); + + return validatedPropertyIndex; + } + ); } @action @@ -833,8 +845,10 @@ export class VertexTypeStore { } if (category === 'propertyindex_conflicts') { - const { name: deletedPropertyIndexName, fields } = - editedCheckedReusableData.propertyindex_conflicts[index].entity; + const { + name: deletedPropertyIndexName, + fields + } = editedCheckedReusableData.propertyindex_conflicts[index].entity; editedCheckedReusableData.propertyindex_conflicts.splice(index, 1); @@ -876,12 +890,10 @@ export class VertexTypeStore { } if (category === 'propertyIndexes') { - ( - this.validateNewVertexTypeErrorMessage - .propertyIndexes as VertexTypeValidatePropertyIndexes[] - )[propertIndexIndex as number][ - propertIndexProperty as keyof VertexTypeValidatePropertyIndexes - ] = ''; + (this.validateNewVertexTypeErrorMessage + .propertyIndexes as VertexTypeValidatePropertyIndexes[])[ + propertIndexIndex as number + ][propertIndexProperty as keyof VertexTypeValidatePropertyIndexes] = ''; return; } @@ -956,19 +968,20 @@ export class VertexTypeStore { : this.metadataConfigsRootStore.currentId; try { - const result: AxiosResponse> = - yield axios - .get(`${baseUrl}/${conn_id}/schema/vertexlabels`, { - params: { - page_no: this.vertexListPageConfig.pageNumber, - page_size: !options ? 10 : -1, - name_order: - this.vertexListPageConfig.sort !== '' - ? this.vertexListPageConfig.sort - : null - } - }) - .catch(checkIfLocalNetworkOffline); + const result: AxiosResponse> = yield axios + .get(`${baseUrl}/${conn_id}/schema/vertexlabels`, { + params: { + page_no: this.vertexListPageConfig.pageNumber, + page_size: !options ? 10 : -1, + name_order: + this.vertexListPageConfig.sort !== '' + ? this.vertexListPageConfig.sort + : null + } + }) + .catch(checkIfLocalNetworkOffline); if (result.data.status !== 200) { if (result.data.status === 401) { @@ -1005,15 +1018,16 @@ export class VertexTypeStore { this.requestStatus.checkIfUsing = 'pending'; try { - const result: AxiosResponse>> = - yield axios - .post( - `${baseUrl}/${this.metadataConfigsRootStore.currentId}/schema/vertexlabels/check_using`, - { - names: selectedPropertyNames - } - ) - .catch(checkIfLocalNetworkOffline); + const result: AxiosResponse + >> = yield axios + .post( + `${baseUrl}/${this.metadataConfigsRootStore.currentId}/schema/vertexlabels/check_using`, + { + names: selectedPropertyNames + } + ) + .catch(checkIfLocalNetworkOffline); if (result.data.status !== 200) { throw new Error(result.data.message); @@ -1068,10 +1082,10 @@ export class VertexTypeStore { }/schema/vertexlabels/${this.selectedVertexType!.name}`, { append_properties: this.editedSelectedVertexType.append_properties, - append_property_indexes: - this.editedSelectedVertexType.append_property_indexes, - remove_property_indexes: - this.editedSelectedVertexType.remove_property_indexes, + append_property_indexes: this.editedSelectedVertexType + .append_property_indexes, + remove_property_indexes: this.editedSelectedVertexType + .remove_property_indexes, style: this.editedSelectedVertexType.style } ) @@ -1148,7 +1162,7 @@ export class VertexTypeStore { }, { params: { - reused_conn_id: this.metadataConfigsRootStore.idList.find( + reused_conn_id: this.metadataConfigsRootStore.graphManagementStore.idList.find( ({ name }) => name === reuseId )!.id } @@ -1173,32 +1187,30 @@ export class VertexTypeStore { this.requestStatus.recheckConflict = 'pending'; try { - const result: AxiosResponse> = - yield axios - .post( - `${baseUrl}/${this.metadataConfigsRootStore.currentId}/schema/vertexlabels/recheck_conflict`, - { - propertykeys: - this.editedCheckedReusableData!.propertykey_conflicts.map( - ({ entity }) => ({ - ...entity - }) - ), - propertyindexes: - this.editedCheckedReusableData!.propertyindex_conflicts.map( - ({ entity }) => ({ - ...entity - }) - ), - vertexlabels: - this.editedCheckedReusableData!.vertexlabel_conflicts.map( - ({ entity }) => ({ - ...entity - }) - ) - } - ) - .catch(checkIfLocalNetworkOffline); + const result: AxiosResponse> = yield axios + .post( + `${baseUrl}/${this.metadataConfigsRootStore.currentId}/schema/vertexlabels/recheck_conflict`, + { + propertykeys: this.editedCheckedReusableData!.propertykey_conflicts.map( + ({ entity }) => ({ + ...entity + }) + ), + propertyindexes: this.editedCheckedReusableData!.propertyindex_conflicts.map( + ({ entity }) => ({ + ...entity + }) + ), + vertexlabels: this.editedCheckedReusableData!.vertexlabel_conflicts.map( + ({ entity }) => ({ + ...entity + }) + ) + } + ) + .catch(checkIfLocalNetworkOffline); if (result.data.status !== 200) { throw new Error(result.data.message); diff --git a/hugegraph-hubble/hubble-fe/src/stores/factory/dataAnalyzeStore/algorithmStore.ts b/hugegraph-hubble/hubble-fe/src/stores/factory/dataAnalyzeStore/algorithmStore.ts index ad96b6dd3..9115f0922 100644 --- a/hugegraph-hubble/hubble-fe/src/stores/factory/dataAnalyzeStore/algorithmStore.ts +++ b/hugegraph-hubble/hubble-fe/src/stores/factory/dataAnalyzeStore/algorithmStore.ts @@ -7,18 +7,16 @@ export enum Algorithm { shortestPathAll = 'shortest-path-all', allPath = 'all-path', modelSimilarity = 'model-similarity', - neighborRankRecommendation = 'neighbor-rank-recommendation', - realTimeRecommendation = 'real-time-recommendation', + neighborRank = 'neighbor-rank', kStepNeighbor = 'k-step-neighbor', kHop = 'k-hop', customPath = 'custom-path', - customIntersectionDetection = 'custom-intersection-detection', radiographicInspection = 'radiographic-inspection', - commonNeighbor = 'common-neighbor', + sameNeighbor = 'same-neighbor', weightedShortestPath = 'weighted-shortest-path', - singleSourceWeightedPath = 'single-source-weighted-path', - jaccardSimilarity = 'jaccard-similarity', - personalRankRecommendation = 'personal-rank-recommendation' + singleSourceWeightedShortestPath = 'single-source-weighted-shortest-path', + jaccard = 'jaccard', + personalRankRecommendation = 'personal-rank' } export function initializeRequestStatus() { @@ -70,8 +68,8 @@ export function createFocusDetectionDefaultParams() { max_depth: '', label: '__all__', max_degree: '10000', - limit: '10', - capacity: '10000000' + capacity: '10000000', + limit: '10' }; } @@ -98,8 +96,7 @@ export function createShortestPathDefaultParams() { label: '__all__', max_degree: '10000', skip_degree: '0', - capacity: '10000000', - limit: '1000000' + capacity: '10000000' }; } @@ -112,8 +109,7 @@ export function createValidateShortestPathParamsErrorMessage() { label: '', max_degree: '', skip_degree: '', - capacity: '', - limit: '' + capacity: '' }; } @@ -153,7 +149,7 @@ export function createAllPathDefaultParams() { max_degree: '10000', skip_degree: '0', capacity: '10000000', - limit: '1000000' + limit: '10' }; } @@ -176,7 +172,7 @@ export function createModelSimilarityDefaultParams() { method: 'id', source: '', vertexType: '', - vertexProperty: [], + vertexProperty: [['', '']], direction: 'BOTH', least_neighbor: '', similarity: '', @@ -186,7 +182,6 @@ export function createModelSimilarityDefaultParams() { property_filter: '', least_property_number: '', max_degree: '10000', - skip_degree: '10000000', capacity: '10000000', limit: '10', return_common_connection: false, @@ -209,7 +204,6 @@ export function createValidateModelSimilarParamsErrorMessage() { property_filter: '', least_property_number: '', max_degree: '', - skip_degree: '', capacity: '', limit: '', return_common_connection: '', @@ -220,12 +214,11 @@ export function createValidateModelSimilarParamsErrorMessage() { export function createNeighborRankDefaultParams(): { source: string; alpha: string; - direction: string; capacity: string; steps: { uuid: string; direction: string; - label: string; + labels: string[]; degree: string; top: string; }[]; @@ -233,13 +226,12 @@ export function createNeighborRankDefaultParams(): { return { source: '', alpha: '', - direction: 'BOTH', capacity: '10000000', steps: [ { uuid: v4(), direction: 'BOTH', - label: '__all__', + labels: ['__all__'], degree: '10000', top: '100' } @@ -250,12 +242,11 @@ export function createNeighborRankDefaultParams(): { export function createValidateNeighborRankErrorMessage(): { source: string; alpha: string; - direction: string; capacity: string; steps: { uuid: string; direction: string; - label: string; + labels: string; degree: string; top: string; }[]; @@ -263,16 +254,259 @@ export function createValidateNeighborRankErrorMessage(): { return { source: '', alpha: '', - direction: '', capacity: '', steps: [ { uuid: '', direction: '', - label: '', + labels: '', degree: '', top: '' } ] }; } + +export function createKStepNeighborDefaultParams() { + return { + source: '', + direction: 'BOTH', + max_depth: '', + label: '__all__', + max_degree: '10000', + limit: '10000000' + }; +} + +export function createValidateKStepNeighborParamsErrorMessage() { + return { + source: '', + direction: '', + max_depth: '', + label: '', + max_degree: '', + limit: '' + }; +} + +export function createKHopDefaultParams() { + return { + source: '', + direction: 'BOTH', + max_depth: '', + nearest: true, + label: '__all__', + max_degree: '10000', + limit: '10000000', + capacity: '10000000' + }; +} + +export function createValidateKHopParamsErrorMessage() { + return { + source: '', + direction: '', + max_depth: '', + nearest: '', + label: '', + max_degree: '', + limit: '', + capacity: '' + }; +} + +export function createCustomPathDefaultParams() { + return { + method: 'id', + source: '', + vertexType: '', + vertexProperty: [['', '']], + sort_by: 'NONE', + capacity: '10000000', + limit: '10', + steps: [ + { + uuid: v4(), + direction: 'BOTH', + labels: [], + properties: [['', '']], + weight_by: '', + default_weight: '', + degree: '10000', + sample: '100' + } + ] + }; +} + +export function createValidateCustomPathParamsErrorMessage() { + return { + method: '', + source: '', + vertexType: '', + vertexProperty: '', + sort_by: '', + capacity: '', + limit: '', + steps: [ + { + uuid: '', + direction: '', + labels: '', + properties: '', + weight_by: '', + default_weight: '', + degree: '', + sample: '' + } + ] + }; +} + +export function createRadiographicInspectionDefaultParams() { + return { + source: '', + direction: 'BOTH', + max_depth: '', + label: '__all__', + max_degree: '10000', + capacity: '1000000', + limit: '10' + }; +} + +export function createValidateRadiographicInspectionParamsErrorMessage() { + return { + source: '', + direction: '', + max_depth: '', + label: '', + max_degree: '', + capacity: '', + limit: '' + }; +} + +export function createSameNeighborDefaultParams() { + return { + vertex: '', + other: '', + direction: 'BOTH', + label: '__all__', + max_degree: '10000', + limit: '10000000' + }; +} + +export function createValidateSameNeighborParamsErrorMessage() { + return { + vertex: '', + other: '', + direction: '', + label: '', + max_degree: '', + limit: '' + }; +} + +export function createWeightedShortestPathDefaultParams() { + return { + source: '', + target: '', + direction: 'BOTH', + weight: '', + with_vertex: true, + label: '__all__', + max_degree: '10000', + skip_degree: '0', + capacity: '10000000' + }; +} + +export function createValidateWeightedShortestPathParamsErrorMessage() { + return { + source: '', + target: '', + direction: '', + weight: '', + with_vertex: '', + label: '', + max_degree: '', + skip_degree: '', + capacity: '' + }; +} + +export function createSingleSourceWeightedShortestPathDefaultParams() { + return { + source: '', + direction: 'BOTH', + weight: '', + with_vertex: true, + label: '__all__', + max_degree: '10000', + skip_degree: '0', + capacity: '10000000', + limit: '10' + }; +} + +export function createValidateSingleSourceWeightedShortestPathParamsErrorMessage() { + return { + source: '', + direction: '', + weight: '', + with_vertex: '', + label: '', + max_degree: '', + skip_degree: '', + capacity: '', + limit: '' + }; +} + +export function createJaccardDefaultParams() { + return { + vertex: '', + other: '', + direction: 'BOTH', + label: '__all__', + max_degree: '10000' + }; +} + +export function createValidateJaccardParamsErrorMessage() { + return { + vertex: '', + other: '', + direction: '', + label: '', + max_degree: '' + }; +} + +export function createPersonalRankDefaultParams() { + return { + source: '', + alpha: '', + max_depth: '', + with_label: 'SAME_LABEL', + label: '', + degree: '10000', + limit: '10000000', + sorted: true + }; +} + +export function createValidatePersonalRankParamsErrorMessage() { + return { + source: '', + alpha: '', + max_depth: '', + with_label: '', + label: '', + degree: '', + limit: '', + sorted: '' + }; +} diff --git a/hugegraph-hubble/hubble-fe/src/stores/factory/dataAnalyzeStore/dataAnalyzeStore.ts b/hugegraph-hubble/hubble-fe/src/stores/factory/dataAnalyzeStore/dataAnalyzeStore.ts index 3f6acf710..62f5dbeac 100644 --- a/hugegraph-hubble/hubble-fe/src/stores/factory/dataAnalyzeStore/dataAnalyzeStore.ts +++ b/hugegraph-hubble/hubble-fe/src/stores/factory/dataAnalyzeStore/dataAnalyzeStore.ts @@ -8,6 +8,7 @@ export function initalizeRequestStatus() { fetchEdgeTypes: 'standby', fetchAllNodeStyle: 'standby', fetchAllEdgeStyle: 'standby', + fetchAllPropertyIndexes: 'standby', fetchGraphs: 'standby', createAsyncTask: 'standby', addGraphNode: 'standby', @@ -60,6 +61,10 @@ export function initalizeErrorInfo() { code: NaN, message: '' }, + fetchAllPropertyIndexes: { + code: NaN, + message: '' + }, fetchGraphs: { code: NaN, message: '' diff --git a/hugegraph-hubble/hubble-fe/src/stores/types/GraphManagementStore/dataAnalyzeStore.ts b/hugegraph-hubble/hubble-fe/src/stores/types/GraphManagementStore/dataAnalyzeStore.ts index f1e6efdd6..94db4133c 100644 --- a/hugegraph-hubble/hubble-fe/src/stores/types/GraphManagementStore/dataAnalyzeStore.ts +++ b/hugegraph-hubble/hubble-fe/src/stores/types/GraphManagementStore/dataAnalyzeStore.ts @@ -78,7 +78,19 @@ export interface AddQueryCollectionParams { export interface ExecutionLogs { id: number; async_id: number; + async_status: + | 'UNKNOWN' + | 'SCHEDULING' + | 'SCHEDULED' + | 'QUEUED' + | 'RESTORING' + | 'RUNNING' + | 'SUCCESS' + | 'CANCELLING' + | 'CANCELLED' + | 'FAILED'; type: string; + algorithm_name: string; content: string; status: 'SUCCESS' | 'RUNNING' | 'FAILED'; duration: string; @@ -134,7 +146,6 @@ export interface ShortestPathAlgorithmParams { max_degree: string; skip_degree: string; capacity: string; - limit: string; } // export type ShortestPathAllAlgorithmParams = ShortestPathAlgorithmParams; @@ -149,13 +160,22 @@ export interface ShortestPathAllAlgorithmParams { capacity: string; } -export type AllPathAlgorithmParams = ShortestPathAlgorithmParams; +export type AllPathAlgorithmParams = { + source: string; + target: string; + direction: string; + max_depth: string; + label: string; + max_degree: string; + capacity: string; + limit: string; +}; export interface ModelSimilarityParams { method: string; source: string; vertexType: string; - vertexProperty: string[]; + vertexProperty: string[][]; direction: string; least_neighbor: string; similarity: string; @@ -165,7 +185,6 @@ export interface ModelSimilarityParams { property_filter: string; least_property_number: string; max_degree: string; - skip_degree: string; capacity: string; limit: string; return_common_connection: boolean; @@ -175,7 +194,7 @@ export interface ModelSimilarityParams { export interface NeighborRankRule { uuid: string; direction: string; - label: string; + labels: string[]; degree: string; top: string; } @@ -183,7 +202,110 @@ export interface NeighborRankRule { export interface NeighborRankParams { source: string; alpha: string; - direction: string; capacity: string; steps: NeighborRankRule[]; } + +export interface KStepNeighbor { + source: string; + direction: string; + max_depth: string; + label: string; + max_degree: string; + limit: string; +} + +export interface KHop { + source: string; + direction: string; + max_depth: string; + nearest: boolean; + label: string; + max_degree: string; + limit: string; + capacity: string; +} + +export interface CustomPathParams { + method: string; + source: string; + vertexType: string; + vertexProperty: string[][]; + sort_by: string; + capacity: string; + limit: string; + steps: CustomPathRule[]; +} + +export interface CustomPathRule { + uuid: string; + direction: string; + labels: string[]; + properties: string[][]; + weight_by: string; + default_weight: string; + degree: string; + sample: string; +} + +export interface RadiographicInspection { + source: string; + direction: string; + max_depth: string; + label: string; + max_degree: string; + capacity: string; + limit: string; +} + +export interface SameNeighbor { + vertex: string; + other: string; + direction: string; + label: string; + max_degree: string; + limit: string; +} + +export interface WeightedShortestPath { + source: string; + target: string; + direction: string; + weight: string; + with_vertex: boolean; + label: string; + max_degree: string; + skip_degree: string; + capacity: string; +} + +export interface SingleSourceWeightedShortestPath { + source: string; + direction: string; + weight: string; + with_vertex: boolean; + label: string; + max_degree: string; + skip_degree: string; + capacity: string; + limit: string; +} + +export interface Jaccard { + vertex: string; + other: string; + direction: string; + label: string; + max_degree: string; +} + +export interface PersonalRank { + source: string; + alpha: string; + max_depth: string; + with_label: string; + label: string; + degree: string; + limit: string; + sorted: boolean; +} diff --git a/hugegraph-hubble/hubble-fe/src/stores/utils/index.ts b/hugegraph-hubble/hubble-fe/src/stores/utils/index.ts index 3599d466d..2dc39bfdd 100644 --- a/hugegraph-hubble/hubble-fe/src/stores/utils/index.ts +++ b/hugegraph-hubble/hubble-fe/src/stores/utils/index.ts @@ -156,6 +156,13 @@ export function addGraphNodes( highlight: { background: '#fb6a02', border: '#fb6a02' }, hover: { background: '#ec3112', border: '#ec3112' } }, + // reveal label when zoom to max + scaling: { + label: { + max: Infinity, + maxVisible: Infinity + } + }, chosen: { node(values: any, id: string, selected: boolean, hovering: boolean) { if (hovering || selected) { @@ -236,3 +243,14 @@ export function formatVertexIdText( return text === replacedText ? '~id' : text; } } + +export function isGtNegativeOneButZero(value: string | number) { + if (typeof value === 'number') { + value = String(value); + } + + return !( + !isEmpty(value) && + (!isInt(value as string, { min: -1 }) || String(Number(value)) === '0') + ); +} diff --git a/hugegraph-hubble/hubble-fe/src/utils/calcAlgorithmFormWidth.ts b/hugegraph-hubble/hubble-fe/src/utils/calcAlgorithmFormWidth.ts new file mode 100644 index 000000000..f60ca8c28 --- /dev/null +++ b/hugegraph-hubble/hubble-fe/src/utils/calcAlgorithmFormWidth.ts @@ -0,0 +1,7 @@ +export function calcAlgorithmFormWidth( + expand: boolean, + min: number, + max: number +) { + return expand ? min : max; +} diff --git a/hugegraph-hubble/hubble-fe/src/utils/filterEmptyAlgorightmParams.ts b/hugegraph-hubble/hubble-fe/src/utils/filterEmptyAlgorightmParams.ts new file mode 100644 index 000000000..2097ce3f0 --- /dev/null +++ b/hugegraph-hubble/hubble-fe/src/utils/filterEmptyAlgorightmParams.ts @@ -0,0 +1,19 @@ +import { omitBy, cloneDeep, isEmpty } from 'lodash-es'; + +export function removeLabelKey>( + params: T, + isCloned = true, + key = 'label' +) { + const paramObject = isCloned ? params : cloneDeep(params); + + if (paramObject[key] === '__all__') { + delete paramObject[key]; + } + + return paramObject; +} + +export function filterEmptyAlgorightmParams(params: object, keys: string[]) { + return omitBy(params, (value, key) => keys.includes(key) && isEmpty(value)); +} diff --git a/hugegraph-hubble/hubble-fe/src/utils/formatAlgorithmStatement.ts b/hugegraph-hubble/hubble-fe/src/utils/formatAlgorithmStatement.ts new file mode 100644 index 000000000..96c4d4bfc --- /dev/null +++ b/hugegraph-hubble/hubble-fe/src/utils/formatAlgorithmStatement.ts @@ -0,0 +1,254 @@ +import { + isUndefined, + cloneDeep, + trimEnd, + isEmpty, + isObject, + fromPairs +} from 'lodash-es'; + +import type { TFunction, i18n } from 'i18next'; +import type { + NeighborRankParams, + CustomPathParams +} from '../stores/types/GraphManagementStore/dataAnalyzeStore'; + +export const AlgorithmInternalNameMapping: Record = { + rings: 'loop-detection', + crosspoints: 'focus-detection', + shortpath: 'shortest-path', + allshortpath: 'shortest-path-all', + paths: 'all-path', + fsimilarity: 'model-similarity', + neighborrank: 'neighbor-rank', + kneighbor: 'k-step-neighbor', + kout: 'k-hop', + customizedpaths: 'custom-path', + rays: 'radiographic-inspection', + sameneighbors: 'same-neighbor', + weightedshortpath: 'weighted-shortest-path', + singleshortpath: 'single-source-weighted-shortest-path', + jaccardsimilarity: 'jaccard', + personalrank: 'personal-rank' +}; + +export function formatAlgorithmStatement( + content: string, + algorithmType: string | undefined, + translator: TFunction, + i18n: i18n +) { + if (isUndefined(algorithmType)) { + return ['']; + } + + const algorithmName = translator( + `data-analyze.algorithm-forms.api-name-mapping.${algorithmType}` + ); + let algorithmParams = JSON.parse(content); + const statements: string[] = [algorithmName]; + + if (algorithmType === 'fsimilarity') { + let convertedParams: Record; + + if (!isUndefined(algorithmParams.sources)) { + convertedParams = { + source: algorithmParams.sources.ids ?? [], + 'vertex-type': algorithmParams.sources.label ?? '', + 'vertex-property': algorithmParams.sources.properties ?? [], + direction: algorithmParams.direction, + least_neighbor: algorithmParams.min_neighbors, + similarity: algorithmParams.alpha, + label: + algorithmParams.label === null || algorithmParams.label === '__all__' + ? translator( + `data-analyze.algorithm-forms.model-similarity.pre-value` + ) + : algorithmParams.label, + max_similar: algorithmParams.top, + least_similar: algorithmParams.min_similars, + property_filter: algorithmParams.group_property, + least_property_number: algorithmParams.min_groups, + max_degree: algorithmParams.max_degree, + capacity: algorithmParams.capacity, + limit: algorithmParams.limit, + return_common_connection: algorithmParams.with_intermediary, + return_complete_info: algorithmParams.with_vertex + }; + } else { + // no need to convert relative fields in temp log (which sources field is undefined) + convertedParams = { ...algorithmParams }; + convertedParams['vertex-type'] = convertedParams.vertexType; + convertedParams['vertex-property'] = convertedParams.vertexProperty; + convertedParams.label = + algorithmParams.label === null || algorithmParams.label === '__all__' + ? translator( + `data-analyze.algorithm-forms.model-similarity.pre-value` + ) + : algorithmParams.label; + + delete convertedParams.vertexType; + delete convertedParams.vertexProperty; + } + + algorithmParams = convertedParams; + } + + if (algorithmType === 'neighborrank') { + const convertedParams = cloneDeep(algorithmParams); + + convertedParams.steps = []; + + (algorithmParams as NeighborRankParams).steps.forEach( + ({ degree, direction, labels, top }, stepIndex) => { + const step: Record = {}; + + step[ + trimEnd( + translator( + 'data-analyze.algorithm-forms.neighbor-rank.options.degree' + ), + ':' + ) + ] = degree; + step[ + trimEnd( + translator( + 'data-analyze.algorithm-forms.neighbor-rank.options.direction' + ), + ':' + ) + ] = direction; + step[ + trimEnd( + translator( + 'data-analyze.algorithm-forms.neighbor-rank.options.label' + ), + ':' + ) + ] = isEmpty(labels) + ? translator('data-analyze.algorithm-forms.neighbor-rank.pre-value') + : labels.map((label) => + // value may be "__all__" from temp log (local) + label === '__all__' + ? translator( + 'data-analyze.algorithm-forms.neighbor-rank.pre-value' + ) + : label + ); + step[ + trimEnd( + translator( + 'data-analyze.algorithm-forms.neighbor-rank.options.top' + ), + ':' + ) + ] = top; + + convertedParams.steps[stepIndex] = step; + } + ); + + algorithmParams = convertedParams; + } + + // params struct fetched from server is not as consistent as front end + if (algorithmType === 'kout' && algorithmParams.hasOwnProperty('step')) { + algorithmParams.direction = algorithmParams.step.direction; + algorithmParams.max_degree = algorithmParams.step.degree; + algorithmParams.skip_degree = algorithmParams.step.skip_degree; + algorithmParams.label = algorithmParams.step.labels[0] ?? null; + + delete algorithmParams.step; + } + + if (algorithmType === 'customizedpaths') { + let convertedParams: Record; + + if (!isUndefined(algorithmParams.sources)) { + convertedParams = { + source: algorithmParams.sources.ids ?? [], + 'vertex-type': algorithmParams.sources.label ?? '', + 'vertex-property': algorithmParams.sources.properties ?? [], + capacity: algorithmParams.capacity, + limit: algorithmParams.limit, + sort_by: algorithmParams.sort_by, + steps: [] + }; + } else { + convertedParams = { + ...algorithmParams, + source: algorithmParams.source.split(','), + 'vertex-type': algorithmParams.vertexType, + 'vertex-property': fromPairs( + algorithmParams.vertexProperty.map( + ([key, value]: [string, string]) => [key, value.split(',')] + ) + ) + }; + + delete convertedParams.vertexType; + delete convertedParams.vertexProperty; + } + + (algorithmParams as CustomPathParams).steps.forEach((step, stepIndex) => { + const convertedStep: Record = {}; + + delete step.uuid; + + if (step.default_weight !== '') { + step.weight_by = step.default_weight; + } + + delete step.default_weight; + + Object.entries(step).forEach(([key, value]) => { + convertedStep[ + trimEnd( + translator( + `data-analyze.algorithm-forms.custom-path.options.${key}` + ), + ':' + ) + ] = value; + }); + + convertedParams.steps[stepIndex] = convertedStep; + }); + + algorithmParams = convertedParams; + } + + Object.entries(algorithmParams).forEach(([key, value]) => { + value = isObject(value) ? JSON.stringify(value, null, 4) : value; + + // label value could be null when it means all + if (key === 'label' && (value === null || value === '__all__')) { + value = translator( + `data-analyze.algorithm-forms.${AlgorithmInternalNameMapping[algorithmType]}.pre-value` + ); + } + + // personal rank customized + if (algorithmType === 'personalrank' && key === 'with_label') { + value = translator( + `data-analyze.algorithm-forms.personal-rank.with-label-radio-value.${(value as string).toLowerCase()}` + ); + } + + const fullPathKey = `data-analyze.algorithm-forms.${AlgorithmInternalNameMapping[algorithmType]}.options.${key}`; + + // remove redundant key from server + if (!i18n.exists(fullPathKey)) { + return; + } + + key = translator( + `data-analyze.algorithm-forms.${AlgorithmInternalNameMapping[algorithmType]}.options.${key}` + ); + + statements.push(`${key} ${value}`); + }); + + return statements; +} diff --git a/hugegraph-hubble/hubble-fe/src/utils/index.ts b/hugegraph-hubble/hubble-fe/src/utils/index.ts index ebf26b01f..1eb305dac 100644 --- a/hugegraph-hubble/hubble-fe/src/utils/index.ts +++ b/hugegraph-hubble/hubble-fe/src/utils/index.ts @@ -1,4 +1,23 @@ import convertStringToJSON from './convertStringToJSON'; import getUnicodeLength from './getUnicodeLength'; +import { + AlgorithmInternalNameMapping, + formatAlgorithmStatement +} from './formatAlgorithmStatement'; +import isDataTypeNumeric from './isDataTypeNumeric'; +import { calcAlgorithmFormWidth } from './calcAlgorithmFormWidth'; +import { + removeLabelKey, + filterEmptyAlgorightmParams +} from './filterEmptyAlgorightmParams'; -export { convertStringToJSON, getUnicodeLength }; +export { + convertStringToJSON, + getUnicodeLength, + AlgorithmInternalNameMapping, + formatAlgorithmStatement, + isDataTypeNumeric, + calcAlgorithmFormWidth, + removeLabelKey, + filterEmptyAlgorightmParams +}; diff --git a/hugegraph-hubble/hubble-fe/src/utils/isDataTypeNumeric.ts b/hugegraph-hubble/hubble-fe/src/utils/isDataTypeNumeric.ts new file mode 100644 index 000000000..f7c632b5f --- /dev/null +++ b/hugegraph-hubble/hubble-fe/src/utils/isDataTypeNumeric.ts @@ -0,0 +1,10 @@ +export default function isDataTypeNumeric(type: string | undefined) { + return ( + type === 'BYTE' || + type === 'INT' || + type === 'LONG' || + type === 'FLOAT' || + type === 'DOUBLE' || + type === 'DATE' + ); +}