diff --git a/src/webviews/QueryEditor/ResultPanel/IndexMetricsView.tsx b/src/webviews/QueryEditor/ResultPanel/IndexMetricsView.tsx new file mode 100644 index 000000000..e2fcdb3d4 --- /dev/null +++ b/src/webviews/QueryEditor/ResultPanel/IndexMetricsView.tsx @@ -0,0 +1,163 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { + Label, + Link, + Table, + TableBody, + TableCell, + TableHeader, + TableHeaderCell, + TableRow, +} from '@fluentui/react-components'; +import type React from 'react'; + +interface IndexMetricsSection { + title: string; + indexes: { [key: string]: string }[]; +} + +interface IndexMetrics { + title: string; + sections: IndexMetricsSection[]; +} + +const INDEX_METRICS_DOC_URL = 'https://learn.microsoft.com/azure/cosmos-db/nosql/index-metrics'; + +export const IndexMetricsView: React.FC<{ indexMetricsStr: string; topLabelStyle?: string }> = ({ + indexMetricsStr, + topLabelStyle, +}) => { + // TODO Uncomment this example for testing + //indexMetricsStr = "Index Utilization Information\n Utilized Single Indexes\n Index Spec: /name/?\n Index Impact Score: High\n ---\n Index Spec: /age/?\n Index Impact Score: High\n ---\n Index Spec: /town/?\n Index Impact Score: High\n ---\n Index Spec: /timestamp/?\n Index Impact Score: High\n ---\n Potential Single Indexes\n Utilized Composite Indexes\n Potential Composite Indexes\n Index Spec: /name ASC, /town ASC, /age ASC\n Index Impact Score: High\n ---\n Index Spec: /name ASC, /town ASC, /timestamp ASC\n Index Impact Score: High\n ---" + const parsed = parseIndexMetrics(indexMetricsStr); + const columns = ['Index Spec', 'Index Impact Score']; + + return ( + <> +
+ (Learn More…) +
+ + + + + Metric + +
+ + + {columns.map((column) => ( + {column} + ))} + + +
+ + + + + {parsed.sections.map((section) => ( + + {section.title} + + {section.indexes && section.indexes.length > 0 ? ( + + + {section.indexes.map((cosmosdbIndex, index) => ( + + {columns.map((column) => ( + {cosmosdbIndex[column]} + ))} + + ))} + +
+ ) : ( + '-' + )} +
+
+ ))} +
+ + + ); +}; + +/** +Parse the indexMetricsStr and display the following information in a table format: +Index Utilization Information + Utilized Single Indexes + Index Spec: /type/? + Index Impact Score: High + --- + Potential Single Indexes + Utilized Composite Indexes + Potential Composite Indexes +*/ + +const oneSpace = ' '; +const twoSpaces = oneSpace.repeat(2); +const fourSpaces = oneSpace.repeat(4); + +const parseIndexMetrics = (indexMetricsStr: string): IndexMetrics => { + const lines = indexMetricsStr.split('\n'); + let title: string = ''; + const sections: IndexMetricsSection[] = []; + + let currentSection: IndexMetricsSection | undefined = undefined; + let currentIndex: { [key: string]: string } | undefined = undefined; + + for (const line of lines) { + if (line.length === 0) { + continue; + } + if (line[0] !== oneSpace) { + title = line; + continue; + } + + if (line.startsWith(twoSpaces) && line[twoSpaces.length] !== oneSpace) { + currentSection = { + title: line.trim(), + indexes: [], + }; + currentIndex = undefined; + + sections.push(currentSection); + continue; + } + + if (line.startsWith(fourSpaces) && line[fourSpaces.length] !== oneSpace && currentSection) { + if (line === `${fourSpaces}---`) { + currentIndex = undefined; + continue; + } + + if (!currentIndex) { + currentIndex = {}; + currentSection.indexes.push(currentIndex); + } + + const [key, value] = line.split(':'); + if (value !== undefined) { + currentIndex[key.trim()] = value.trim(); + } + continue; + } + } + + return { + title, + sections, + }; +}; diff --git a/src/webviews/QueryEditor/ResultPanel/StatsTab.tsx b/src/webviews/QueryEditor/ResultPanel/StatsTab.tsx index 825b4b43f..fa9d12107 100644 --- a/src/webviews/QueryEditor/ResultPanel/StatsTab.tsx +++ b/src/webviews/QueryEditor/ResultPanel/StatsTab.tsx @@ -5,6 +5,7 @@ import { Label, + Link, makeStyles, Table, TableBody, @@ -17,6 +18,7 @@ import { } from '@fluentui/react-components'; import { queryMetricsToTable } from '../../utils'; import { useQueryEditorState } from '../state/QueryEditorContext'; +import { IndexMetricsView } from './IndexMetricsView'; const useStyles = makeStyles({ topLabel: { @@ -39,12 +41,12 @@ const useStyles = makeStyles({ panel1: { flexGrow: '1', flexShrink: '1', - flexBasis: '70%', + flexBasis: '49%', }, panel2: { flexGrow: '1', flexShrink: '1', - flexBasis: '28%', + flexBasis: '49%', }, }); @@ -53,13 +55,15 @@ export const StatsTab = () => { const state = useQueryEditorState(); const items = queryMetricsToTable(state.currentQueryResult); const indexMetrics = state.currentQueryResult?.indexMetrics?.trim(); + const QUERY_METRICS_DOC_URL = 'https://learn.microsoft.com/azure/cosmos-db/nosql/query-metrics'; return ( <>
- + ( + Learn More…)
@@ -89,14 +93,11 @@ export const StatsTab = () => {
-
-
- + {indexMetrics && ( +
+
-
-
{indexMetrics}
-
-
+ )}
);