Skip to content

Commit

Permalink
Improved Index Metrics (#2534)
Browse files Browse the repository at this point in the history
* Improve metrics stats
* Fix alignment and borders in Index Metrics
* Add Learn More link to Query Metrics
* Address code review comments

---------

Co-authored-by: Laurent Nguyen <[email protected]>
  • Loading branch information
sevoku and languy authored Jan 16, 2025
1 parent f5139e0 commit fc0468c
Show file tree
Hide file tree
Showing 2 changed files with 174 additions and 10 deletions.
163 changes: 163 additions & 0 deletions src/webviews/QueryEditor/ResultPanel/IndexMetricsView.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<>
<div className={topLabelStyle}>
<Label size={'large'}>{parsed.title}</Label> (<Link href={INDEX_METRICS_DOC_URL}>Learn More…</Link>)
</div>

<Table arial-label="Index metrics table" style={{ minWidth: '510px' }}>
<TableHeader>
<TableRow>
<TableHeaderCell>Metric</TableHeaderCell>
<TableHeaderCell>
<Table>
<TableBody>
<TableRow style={{ borderBottom: '0px' }}>
{columns.map((column) => (
<TableCell key={column}>{column}</TableCell>
))}
</TableRow>
</TableBody>
</Table>
</TableHeaderCell>
</TableRow>
</TableHeader>
<TableBody>
{parsed.sections.map((section) => (
<TableRow key={section.title}>
<TableCell>{section.title}</TableCell>
<TableCell>
{section.indexes && section.indexes.length > 0 ? (
<Table>
<TableBody>
{section.indexes.map((cosmosdbIndex, index) => (
<TableRow
key={index}
{...(index === section.indexes.length - 1
? { style: { borderBottom: '0px' } }
: {})}
>
{columns.map((column) => (
<TableCell>{cosmosdbIndex[column]}</TableCell>
))}
</TableRow>
))}
</TableBody>
</Table>
) : (
'-'
)}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</>
);
};

/**
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,
};
};
21 changes: 11 additions & 10 deletions src/webviews/QueryEditor/ResultPanel/StatsTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import {
Label,
Link,
makeStyles,
Table,
TableBody,
Expand All @@ -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: {
Expand All @@ -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%',
},
});

Expand All @@ -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 (
<>
<div className={styles.container}>
<div className={styles.panel1}>
<div className={styles.topLabel}>
<Label size={'large'}>Query metrics</Label>
<Label size={'large'}>Query metrics</Label> (
<Link href={QUERY_METRICS_DOC_URL}>Learn More…</Link>)
</div>
<Table arial-label="Stats table" style={{ minWidth: '510px' }}>
<TableHeader>
Expand Down Expand Up @@ -89,14 +93,11 @@ export const StatsTab = () => {
</TableBody>
</Table>
</div>
<div className={styles.panel2}>
<div className={styles.bottomLabel}>
<Label size={'large'}>Index metrics</Label>
{indexMetrics && (
<div className={styles.panel2}>
<IndexMetricsView indexMetricsStr={indexMetrics} topLabelStyle={styles.topLabel} />
</div>
<div>
<pre className={styles.pre}>{indexMetrics}</pre>
</div>
</div>
)}
</div>
</>
);
Expand Down

0 comments on commit fc0468c

Please sign in to comment.