From a3a2bf2e3a937e110d080fcdfd3ddda79bb854de Mon Sep 17 00:00:00 2001 From: Nishtha Ahuja <45535788+nishthaAhujaa@users.noreply.github.com> Date: Wed, 15 Oct 2025 18:12:27 +0530 Subject: [PATCH] reset states fixes (#2207) * reset states fixes * fixed sdk response --------- Co-authored-by: nishthaAhujaa --- .../Tabs/QueryTab/IndexAdvisorUtils.tsx | 127 +++++++++--------- .../Tabs/QueryTab/QueryResultSection.tsx | 13 +- .../Tabs/QueryTab/QueryTabComponent.tsx | 10 +- src/Explorer/Tabs/QueryTab/ResultsView.tsx | 68 +++++++--- 4 files changed, 136 insertions(+), 82 deletions(-) diff --git a/src/Explorer/Tabs/QueryTab/IndexAdvisorUtils.tsx b/src/Explorer/Tabs/QueryTab/IndexAdvisorUtils.tsx index 168aa7e43..488ece9eb 100644 --- a/src/Explorer/Tabs/QueryTab/IndexAdvisorUtils.tsx +++ b/src/Explorer/Tabs/QueryTab/IndexAdvisorUtils.tsx @@ -13,80 +13,85 @@ interface IndexObject { path?: string; } -export interface IndexMetricsJson { - included?: IIndexMetric[]; - notIncluded?: IIndexMetric[]; +// SDK response format +export interface IndexMetricsResponse { + UtilizedIndexes?: { + SingleIndexes?: Array<{ IndexSpec: string; IndexImpactScore?: string }>; + CompositeIndexes?: Array<{ IndexSpecs: string[]; IndexImpactScore?: string }>; + }; + PotentialIndexes?: { + SingleIndexes?: Array<{ IndexSpec: string; IndexImpactScore?: string }>; + CompositeIndexes?: Array<{ IndexSpecs: string[]; IndexImpactScore?: string }>; + }; } -export function parseIndexMetrics(indexMetrics: string | IndexMetricsJson): { + +export function parseIndexMetrics(indexMetrics: IndexMetricsResponse): { included: IIndexMetric[]; notIncluded: IIndexMetric[]; } { - // If already JSON, just extract arrays - if (typeof indexMetrics === "object" && indexMetrics !== null) { - return { - included: Array.isArray(indexMetrics.included) ? indexMetrics.included : [], - notIncluded: Array.isArray(indexMetrics.notIncluded) ? indexMetrics.notIncluded : [], - }; - } - - // Otherwise, parse as string (current SDK) const included: IIndexMetric[] = []; const notIncluded: IIndexMetric[] = []; - const lines = (indexMetrics as string) - .split("\n") - .map((line) => line.trim()) - .filter(Boolean); - let currentSection = ""; - for (let i = 0; i < lines.length; i++) { - const line = lines[i]; - if (line.startsWith("Utilized Single Indexes") || line.startsWith("Utilized Composite Indexes")) { - currentSection = "included"; - } else if (line.startsWith("Potential Single Indexes") || line.startsWith("Potential Composite Indexes")) { - currentSection = "notIncluded"; - } else if (line.startsWith("Index Spec:")) { - const index = line.replace("Index Spec:", "").trim(); - const impactLine = lines[i + 1]; - const impact = impactLine?.includes("Index Impact Score:") ? impactLine.split(":")[1].trim() : "Unknown"; - const isComposite = index.includes(","); + // Process UtilizedIndexes (Included) + if (indexMetrics.UtilizedIndexes) { + // Single indexes + indexMetrics.UtilizedIndexes.SingleIndexes?.forEach((index) => { + included.push({ + index: index.IndexSpec, + impact: index.IndexImpactScore || "Utilized", + section: "Included", + path: index.IndexSpec, + }); + }); - const sectionMap: Record = { - included: "Included", - notIncluded: "Not Included", - }; - - const indexObj: IndexObject = { index, impact, section: sectionMap[currentSection] ?? "Header" }; - if (isComposite) { - indexObj.composite = index.split(",").map((part: string) => { - const [path, order] = part.trim().split(/\s+/); + // Composite indexes + indexMetrics.UtilizedIndexes.CompositeIndexes?.forEach((index) => { + const compositeSpec = index.IndexSpecs.join(", "); + included.push({ + index: compositeSpec, + impact: index.IndexImpactScore || "Utilized", + section: "Included", + composite: index.IndexSpecs.map((spec) => { + const [path, order] = spec.trim().split(/\s+/); return { path: path.trim(), order: order?.toLowerCase() === "desc" ? "descending" : "ascending", }; - }); - } else { - let path = "/unknown/*"; - const pathRegex = /\/[^/\s*?]+(?:\/[^/\s*?]+)*(\/\*|\?)/; - const match = index.match(pathRegex); - if (match) { - path = match[0]; - } else { - const simplePathRegex = /\/[^/\s]+/; - const simpleMatch = index.match(simplePathRegex); - if (simpleMatch) { - path = simpleMatch[0] + "/*"; - } - } - indexObj.path = path; - } - - if (currentSection === "included") { - included.push(indexObj); - } else if (currentSection === "notIncluded") { - notIncluded.push(indexObj); - } - } + }), + }); + }); } + + // Process PotentialIndexes (Not Included) + if (indexMetrics.PotentialIndexes) { + // Single indexes + indexMetrics.PotentialIndexes.SingleIndexes?.forEach((index) => { + notIncluded.push({ + index: index.IndexSpec, + impact: index.IndexImpactScore || "Unknown", + section: "Not Included", + path: index.IndexSpec, + }); + }); + + // Composite indexes + indexMetrics.PotentialIndexes.CompositeIndexes?.forEach((index) => { + const compositeSpec = index.IndexSpecs.join(", "); + notIncluded.push({ + index: compositeSpec, + impact: index.IndexImpactScore || "Unknown", + section: "Not Included", + composite: index.IndexSpecs.map((spec) => { + const [path, order] = spec.trim().split(/\s+/); + return { + path: path.trim(), + order: order?.toLowerCase() === "desc" ? "descending" : "ascending", + }; + }), + }); + }); + } + return { included, notIncluded }; } diff --git a/src/Explorer/Tabs/QueryTab/QueryResultSection.tsx b/src/Explorer/Tabs/QueryTab/QueryResultSection.tsx index 90a8a5672..e343cdb2c 100644 --- a/src/Explorer/Tabs/QueryTab/QueryResultSection.tsx +++ b/src/Explorer/Tabs/QueryTab/QueryResultSection.tsx @@ -3,18 +3,21 @@ import QueryError from "Common/QueryError"; import { IndeterminateProgressBar } from "Explorer/Controls/IndeterminateProgressBar"; import { MessageBanner } from "Explorer/Controls/MessageBanner"; import { useQueryTabStyles } from "Explorer/Tabs/QueryTab/Styles"; +import useZoomLevel from "hooks/useZoomLevel"; import React from "react"; +import { conditionalClass } from "Utils/StyleUtils"; import RunQuery from "../../../../images/RunQuery.png"; import { QueryResults } from "../../../Contracts/ViewModels"; import { ErrorList } from "./ErrorList"; import { ResultsView } from "./ResultsView"; -import useZoomLevel from "hooks/useZoomLevel"; -import { conditionalClass } from "Utils/StyleUtils"; export interface ResultsViewProps { isMongoDB: boolean; queryResults: QueryResults; executeQueryDocumentsPage: (firstItemIndex: number) => Promise; + queryText?: string; + databaseId?: string; + containerId?: string; } interface QueryResultProps extends ResultsViewProps { @@ -49,6 +52,9 @@ export const QueryResultSection: React.FC = ({ queryResults, executeQueryDocumentsPage, isExecuting, + queryText, + databaseId, + containerId, }: QueryResultProps): JSX.Element => { const styles = useQueryTabStyles(); const maybeSubQuery = queryEditorContent && /.*\(.*SELECT.*\)/i.test(queryEditorContent); @@ -91,6 +97,9 @@ export const QueryResultSection: React.FC = ({ queryResults={queryResults} executeQueryDocumentsPage={executeQueryDocumentsPage} isMongoDB={isMongoDB} + queryText={queryText || queryEditorContent} + databaseId={databaseId} + containerId={containerId} /> ) : ( diff --git a/src/Explorer/Tabs/QueryTab/QueryTabComponent.tsx b/src/Explorer/Tabs/QueryTab/QueryTabComponent.tsx index e9b37dc0c..67eca1b01 100644 --- a/src/Explorer/Tabs/QueryTab/QueryTabComponent.tsx +++ b/src/Explorer/Tabs/QueryTab/QueryTabComponent.tsx @@ -375,9 +375,9 @@ class QueryTabComponentImpl extends React.Component ) : ( this._executeQueryDocumentsPage(firstItemIndex) } + databaseId={this.props.collection.databaseId} + containerId={this.props.collection.id()} /> )} diff --git a/src/Explorer/Tabs/QueryTab/ResultsView.tsx b/src/Explorer/Tabs/QueryTab/ResultsView.tsx index ec51f989e..59cdcdc95 100644 --- a/src/Explorer/Tabs/QueryTab/ResultsView.tsx +++ b/src/Explorer/Tabs/QueryTab/ResultsView.tsx @@ -28,8 +28,7 @@ import { HttpHeaders } from "Common/Constants"; import MongoUtility from "Common/MongoUtility"; import { QueryMetrics } from "Contracts/DataModels"; import { EditorReact } from "Explorer/Controls/Editor/EditorReact"; -import { parseIndexMetrics, renderImpactDots } from "Explorer/Tabs/QueryTab/IndexAdvisorUtils"; -import { IDocument, useQueryMetadataStore } from "Explorer/Tabs/QueryTab/QueryTabComponent"; +import { IDocument } from "Explorer/Tabs/QueryTab/QueryTabComponent"; import { useQueryTabStyles } from "Explorer/Tabs/QueryTab/Styles"; import React, { useCallback, useEffect, useState } from "react"; import { userContext } from "UserContext"; @@ -37,6 +36,7 @@ import { logConsoleProgress } from "Utils/NotificationConsoleUtils"; import create from "zustand"; import { client } from "../../../Common/CosmosClient"; import { handleError } from "../../../Common/ErrorHandlingUtils"; +import { parseIndexMetrics, renderImpactDots, type IndexMetricsResponse } from "./IndexAdvisorUtils"; import { ResultsViewProps } from "./QueryResultSection"; import { useIndexAdvisorStyles } from "./StylesAdvisor"; enum ResultsTabs { @@ -394,9 +394,8 @@ const QueryStatsTab: React.FC> = ({ query }, { metric: "User defined function execution time", - value: `${ - aggregatedQueryMetrics.runtimeExecutionTimes?.userDefinedFunctionExecutionTime?.toString() || 0 - } ms`, + value: `${aggregatedQueryMetrics.runtimeExecutionTimes?.userDefinedFunctionExecutionTime?.toString() || 0 + } ms`, toolTip: "Total time spent executing user-defined functions", }, { @@ -544,11 +543,14 @@ export interface IIndexMetric { path?: string; composite?: { path: string; order: string }[]; } -export const IndexAdvisorTab: React.FC = () => { +export const IndexAdvisorTab: React.FC<{ + queryText?: string; + databaseId?: string; + containerId?: string; +}> = ({ queryText, databaseId, containerId }) => { const style = useIndexAdvisorStyles(); - const { userQuery, databaseId, containerId } = useQueryMetadataStore(); const [loading, setLoading] = useState(true); - const [indexMetrics, setIndexMetrics] = useState(null); + const [indexMetrics, setIndexMetrics] = useState(null); const [showIncluded, setShowIncluded] = useState(true); const [showNotIncluded, setShowNotIncluded] = useState(true); const [selectedIndexes, setSelectedIndexes] = useState([]); @@ -562,10 +564,26 @@ export const IndexAdvisorTab: React.FC = () => { useEffect(() => { const fetchIndexMetrics = async () => { + // Reset all states when query parameters change + setLoading(true); + setIndexMetrics(null); + setIncludedIndexes([]); + setNotIncludedIndexes([]); + setSelectedIndexes([]); + setSelectAll(false); + setUpdateMessageShown(false); + setIsUpdating(false); + setJustUpdatedPolicy(false); + + if (!queryText || !databaseId || !containerId) { + setLoading(false); + return; + } + const clearMessage = logConsoleProgress(`Querying items with IndexMetrics in container ${containerId}`); try { const querySpec = { - query: userQuery, + query: queryText, }; const sdkResponse = await client() .database(databaseId) @@ -574,7 +592,12 @@ export const IndexAdvisorTab: React.FC = () => { populateIndexMetrics: true, }) .fetchAll(); - setIndexMetrics(sdkResponse.indexMetrics); + + const parsedIndexMetrics = typeof sdkResponse.indexMetrics === 'string' + ? JSON.parse(sdkResponse.indexMetrics) + : sdkResponse.indexMetrics; + + setIndexMetrics(parsedIndexMetrics); } catch (error) { handleError(error, "queryItemsWithIndexMetrics", `Error querying items from ${containerId}`); } finally { @@ -582,10 +605,9 @@ export const IndexAdvisorTab: React.FC = () => { setLoading(false); } }; - if (userQuery && databaseId && containerId) { - fetchIndexMetrics(); - } - }, [userQuery, databaseId, containerId]); + + fetchIndexMetrics(); + }, [queryText, databaseId, containerId]); useEffect(() => { if (!indexMetrics) { @@ -828,13 +850,21 @@ export const IndexAdvisorTab: React.FC = () => { ); }; -export const ResultsView: React.FC = ({ isMongoDB, queryResults, executeQueryDocumentsPage }) => { +export const ResultsView: React.FC = ({ + isMongoDB, + queryResults, + executeQueryDocumentsPage, + queryText, + databaseId, + containerId +}) => { const styles = useQueryTabStyles(); const [activeTab, setActiveTab] = useState(ResultsTabs.Results); const onTabSelect = useCallback((event: SelectTabEvent, data: SelectTabData) => { setActiveTab(data.value as ResultsTabs); }, []); + return (
@@ -869,7 +899,13 @@ export const ResultsView: React.FC = ({ isMongoDB, queryResult /> )} {activeTab === ResultsTabs.QueryStats && } - {activeTab === ResultsTabs.IndexAdvisor && } + {activeTab === ResultsTabs.IndexAdvisor && ( + + )}
);