Skip to content

Improve query insights static analysis and performance rating#615

Merged
tnaum-ms merged 30 commits into
nextfrom
dev/tnaum/query-insights-performance-rating
Apr 27, 2026
Merged

Improve query insights static analysis and performance rating#615
tnaum-ms merged 30 commits into
nextfrom
dev/tnaum/query-insights-performance-rating

Conversation

@tnaum-ms
Copy link
Copy Markdown
Collaborator

Summary

Improves the Query Insights static query performance evaluation based on user feedback gathered during testing. Also includes infrastructure fixes for filter/project/sort object handling and a workaround for Azure DocumentDB explain results.

Changes

Infrastructure Fixes

  • Filter/project/sort object conversion: Updated QueryInsightsAIService and collectionViewRouter to use the new QueryObject conversion, passing the user's actual parsed filter to analyzeExecutionStats() instead of extracting from the unreliable command.filter in the explain result (DocumentDB returns command as a string, not a document).
  • Azure DocumentDB explain fixup: Added fixupDocumentDbExplain() as a temporary workaround for Azure DocumentDB reporting totalKeysExamined values in explain results when no keys were actually examined. Includes test coverage.

Static Query Performance Evaluation Improvements

New features:

  • Added diagnosticId to all PerformanceDiagnostic objects for stable filtering/matching
  • Added index strategy advisories: coverage badges, low-cardinality index detection, multikey expansion warnings
  • Replaced executionStrategy / examinedReturnedRatio cells with Selectivity (% of collection returned) and Fetch Overhead (state-based label: Direct fetch, Covered query, Collection scan, etc.)
  • Added dynamic tooltips to all 4 efficiency analysis cells and info icons to metric labels
  • Three-color badge system: positive (green), neutral (blue), negative (orange)
  • Badge filtering (only 3 high-signal positives shown) and ordering (positive → neutral → negative)

Edge case fixes based on user feedback:

  • Zero results: show "No matching documents" (neutral) instead of misleading "Very low efficiency ratio"
  • Empty query detection: use parsed filter from session instead of heuristic/explain result
  • Compound index cardinality: skip per-key cardinality check for compound indexes and gate on efficiency >= 90% to avoid false positives on well-performing compound indexes
  • Collection scan messages: distinguish "no filter" (neutral, expected) from "has filter but no index" (negative, actionable)

Styling:

  • Shared SCSS tooltip classes across MetricBase and CellBase
  • Info icon on labels with tooltips (consistent across metrics and cells)
  • Removed cursor:help, added focus-visible outlines for keyboard accessibility

Documentation

  • Added query-insights-static-analysis.md: comprehensive reference covering all 4 indicators, all badges, the score decision tree, and 5 documented design decisions with reasoning

Testing

  • All existing tests pass
  • New test coverage for fixupDocumentDbExplain
  • Build, lint, l10n, and prettier all pass

tnaum-ms and others added 19 commits April 26, 2026 15:38
Co-authored-by: Copilot <copilot@github.com>
…eshold constants

Task 1: Added 'diagnosticId' field to PerformanceDiagnostic interface in both
ExplainPlanAnalyzer.ts and types/queryInsights.ts. All existing diagnostic
pushes now include a stable snake_case diagnosticId for filtering/matching.

Task 2: Added index strategy advisory threshold constants at the top of
ExplainPlanAnalyzer.ts (COVERAGE_LOW_SELECTIVITY, COVERAGE_HIGH_RETURN,
CARDINALITY_PER_KEY_RATIO, MULTIKEY_WARN_THRESHOLD, MULTIKEY_SEVERE_THRESHOLD).
… addIndexStrategyAdvisories

Task 3: Added findStageInPlan() - generic recursive stage search in plan trees.
Task 4: Added detectLowCardinalityIndex() - detects low-cardinality indexes via
  three signals: isBitmap flag, boolean filter literals, and estimatedEntryCount.
Task 5: Added addIndexStrategyAdvisories() - appends neutral/negative diagnostics
  for coverage, cardinality, and multikey expansion. Severe multikey (≥20×)
  demotes score by one level.
Task 6: In collectionViewRouter.ts, fetch estimateDocumentCount() after
  execution stats, call addIndexStrategyAdvisories() before transform,
  and pass totalCollectionDocs to transformStage2Response().

Task 7: Updated transformStage2Response signature to accept totalCollectionDocs.
  Replaced efficiencyAnalysis.executionStrategy with selectivity (percentage
  of collection returned) and efficiencyAnalysis.examinedReturnedRatio with
  fetchOverhead (state-based label). Added computeSelectivity() and
  computeFetchOverhead() helpers. Updated createFailedQueryResponse() for
  the new efficiencyAnalysis shape. Removed unused formatRatioForDisplay().
Task 8: Updated QueryInsightsTab 2×2 grid:
  - Replaced 'Execution Strategy' cell with 'Selectivity' (percentage of
    collection returned, or '—' if unavailable)
  - Replaced 'Examined-to-Returned Ratio' cell with 'Fetch Overhead'
    (state-based label: Direct fetch, Covered query, Collection scan, etc.)
  - Changed 'Index Used' null placeholder from 'None' to 'None (collection scan)'

Task 9: Updated PerformanceRatingCell badge rendering:
  - Filter: Only show 3 positive badges (high_efficiency_ratio, fast_execution,
    index_used). All neutral/negative badges always shown.
  - Sort: Badges ordered positive → neutral → negative.
  - Color: positive=success (green), neutral=informative (blue/gray),
    negative=warning (orange/yellow). Previously negative used informative.
Task 10: Updated localization bundle with new strings (Selectivity,
Fetch Overhead, Direct fetch, Covered query, Collection scan,
Multikey expansion, No matches, None (collection scan), Query failed).
Applied prettier formatting.
Added PERFORMANCE-RATING.md next to ExplainPlanAnalyzer.ts with:
- End-user-friendly explanation of how the rating is computed
- ASCII diagrams showing the data flow
- Threshold tables for all scoring dimensions
- Four worked examples (well-optimized, missing index, low cardinality,
  multikey expansion)
- Glossary of terms

Added implementation-notes.md documenting deviations from the approved
implementation plan and all user-facing message changes.
…s scenario

Co-authored-by: Copilot <copilot@github.com>
… edge case fixes

- Added tooltipExplanation support to CellBase/GenericCell (wraps entire cell)
- All 4 efficiency analysis cells have dynamic tooltips based on current value
- Fixed empty-query detection: pass user's actual parsed filter from session
  instead of unreliable command.filter extraction from explain result
- Fixed zero-results edge case: show 'No matching documents' (neutral) instead
  of misleading 'Very low efficiency ratio'
- Removed em dashes from user-facing strings
- Formatted low-cardinality reasons with bullet points
- l10n bundle updated
- Added InfoRegular icon to MetricBase labels when tooltipExplanation is
  present, matching the pattern used on efficiency analysis cells
- Removed cursor:help from cellWithTooltip CSS class
- Added inline-flex + align-items to dataHeader for icon alignment
…yling for metrics display

Co-authored-by: Copilot <copilot@github.com>
…metrics and summary cells

Co-authored-by: Copilot <copilot@github.com>
… indexes and improving efficiency checks

Co-authored-by: Copilot <copilot@github.com>
…is.md

Comprehensive reference covering:
- Data flow from router to webview
- All 4 summary indicators with possible values and tooltip logic
- All diagnostic badges with IDs, conditions, types, and messages
- Performance score decision tree
- Index strategy advisories (coverage, cardinality, multikey)
- Badge visibility filtering, ordering, and coloring
- 5 documented design decisions with problem/fix reasoning:
  1. Zero-results edge case
  2. Empty query detection (DocumentDB command string)
  3. Compound index cardinality false positives
  4. Badge message formatting
  5. Collection scan message accuracy
Copilot AI review requested due to automatic review settings April 27, 2026 10:25
@tnaum-ms tnaum-ms requested a review from a team as a code owner April 27, 2026 10:25
@tnaum-ms tnaum-ms added this to the 0.8.0 milestone Apr 27, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refines the Query Insights Stage 2 “static” performance evaluation and its UI presentation, adds index-strategy advisories, and introduces a connector-side workaround for incorrect Azure DocumentDB explain metrics.

Changes:

  • Reworked Stage 2 “Query Efficiency Analysis” cells to use Selectivity and Fetch Overhead, plus richer tooltips and badge filtering/sorting via new diagnosticId.
  • Added index strategy advisories (coverage / low-cardinality / multikey expansion) to Stage 2 diagnostics and plumbed estimateDocumentCount() for selectivity + advisory gating.
  • Added Azure DocumentDB explain fixup to correct totalKeysExamined/keysExamined for COLLSCAN and covered it with unit tests.

Reviewed changes

Copilot reviewed 21 out of 21 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/webviews/documentdb/collectionView/types/queryInsights.ts Updates Stage 2 response shape (selectivity, fetchOverhead) and adds diagnosticId to diagnostics.
src/webviews/documentdb/collectionView/components/queryInsightsTab/queryInsights.scss Adds shared tooltip/info-icon styling used by metrics and summary cells.
src/webviews/documentdb/collectionView/components/queryInsightsTab/components/summaryCard/custom/PerformanceRatingCell.tsx Filters/sorts diagnostics using diagnosticId and adds negative badge coloring.
src/webviews/documentdb/collectionView/components/queryInsightsTab/components/summaryCard/SummaryCard.scss Adds focus-visible styling for tooltip-wrapped cells.
src/webviews/documentdb/collectionView/components/queryInsightsTab/components/summaryCard/GenericCell.tsx Plumbs optional tooltipExplanation down to CellBase.
src/webviews/documentdb/collectionView/components/queryInsightsTab/components/summaryCard/CellBase.tsx Adds label tooltips + inline info icon for summary cells.
src/webviews/documentdb/collectionView/components/queryInsightsTab/components/metricsRow/MetricsRow.scss Removes old metric tooltip styles (moved to shared tooltip classes).
src/webviews/documentdb/collectionView/components/queryInsightsTab/components/metricsRow/MetricBase.tsx Uses shared tooltip classes and adds info icon next to labels with tooltips.
src/webviews/documentdb/collectionView/components/queryInsightsTab/QueryInsightsTab.tsx Replaces Stage 2 cells, adds dynamic tooltips, and wires new response fields.
src/webviews/documentdb/collectionView/collectionViewRouter.ts Passes parsed filter to analysis, fetches collection count, and builds QueryObject for AI stage.
src/services/ai/QueryInsightsAIService.ts Switches Stage 3 input from FindQueryParams to QueryObject and removes old conversion code.
src/documentdb/utils/fixupDocumentDbExplain.ts Implements Azure DocumentDB explain workaround (zero out keys examined on COLLSCAN).
src/documentdb/utils/fixupDocumentDbExplain.test.ts Adds test coverage for explain fixup across COLLSCAN, IXSCAN, sharded, and edge cases.
src/documentdb/queryInsights/transformations.ts Computes selectivity and fetchOverhead and updates Stage 2 transformation accordingly.
src/documentdb/queryInsights/query-insights-static-analysis.md Adds a comprehensive static-analysis reference doc for indicators, badges, and scoring.
src/documentdb/queryInsights/ExplainPlanAnalyzer.ts Adds diagnosticId, index-strategy advisory logic, and updated diagnostic wording.
src/documentdb/ClustersClient.ts Applies explain fixup to LLM enhanced explain APIs (find/aggregate/count).
src/documentdb/ClusterSession.ts Applies explain fixup to Query Insights explain paths and updates Azure DocumentDB wording.
l10n/bundle.l10n.json Updates localization bundle entries for new labels/strings (but see review comments re key mismatches).
docs/ai-and-plans/query-insights-implementation-plan.md Adds an implementation plan document for the performance rating work.
docs/ai-and-plans/query-insights-implementation-notes.md Adds implementation notes documenting deviations and final message set.
Comments suppressed due to low confidence (1)

src/webviews/documentdb/collectionView/components/queryInsightsTab/components/summaryCard/custom/PerformanceRatingCell.tsx:160

  • key={index} on diagnostic badges is unstable when the list is filtered/sorted, which can cause React to reuse the wrong tooltip/badge when diagnostics change. Use a stable key such as diagnostic.diagnosticId (or a composite including it) instead of the array index.
                            {visibleDiagnostics.map((diagnostic, index) => (
                                <Tooltip
                                    key={index}
                                    content={{
                                        children: (

Comment thread src/documentdb/queryInsights/ExplainPlanAnalyzer.ts Outdated
Comment thread src/webviews/documentdb/collectionView/types/queryInsights.ts
The low-cardinality advisory was reading queryFilter from
explainResult.command.filter, which DocumentDB can return as a string.
Now accepts an optional queryFilter parameter and passes the user's
actual filter from the router, with a fallback to command.filter.
The boolean-filter signal in detectLowCardinalityIndex only checked
top-level values, missing operator forms like { $eq: true },
logical operators ($and/$or/$nor), and nested document shapes.
Added filterContainsBoolean() that recursively walks the filter tree
to detect boolean primitives at any depth.
The fetch overhead tooltip was branching on localized display text
(e.g., checking for 'Covered', 'Collection scan' substrings), which
breaks in non-English locales. Added a FetchOverheadKind type and
return it alongside the localized label from computeFetchOverhead().
The UI now branches on the stable kind instead of the localized string.
The remarks block still referenced the removed examinedReturnedRatio
display field and rating-specific concerns array. Updated to describe
the current selectivity, fetchOverhead/fetchOverheadKind, and
performanceRating.diagnostics fields.
When estimateDocumentCount() returns a stale count lower than
nReturned, coverage/selectivity could exceed 100%. Now clamped
to 1.0 (100%) in both addIndexStrategyAdvisories and
computeSelectivity to prevent misleading display values.
Updated JSDoc to document that the input is mutated in place and the
return value is the same reference. Removed the unnecessary
'| undefined' from the type assertions in ClustersClient since
fixupDocumentDbExplain only returns undefined when its input is
undefined (which cannot happen in these call sites).
Wrapped all user-facing diagnostic messages, details, and reason
strings in ExplainPlanAnalyzer with l10n.t() calls. This includes
performance rating diagnostics, execution error messages,
low-cardinality detection reasons, and index strategy advisory
messages. Regenerated the l10n bundle to fix the CI gate failure.
@tnaum-ms
Copy link
Copy Markdown
Collaborator Author

Review fixes applied

Addressed review findings from docs/analysis/pr-615-review.md:

Issue Severity Summary Commit
R-001 High Pass queryFilter to addIndexStrategyAdvisories instead of reading from explainResult.command.filter (unreliable on DocumentDB) a405a9fb
R-002 High Wrap all analyzer diagnostic strings in l10n.t() and regenerate the l10n bundle to fix CI 4babfc4e
R-003 Medium Add FetchOverheadKind type so UI branches on a stable key instead of localized display text 7ba0df02
R-006 Low Update Stage 2 response JSDoc to match current fields (selectivity, fetchOverhead, fetchOverheadKind) 4207076d
R-007 Medium Recursive boolean detection in filters — filterContainsBoolean() walks $and/$or/$nor, operator objects, and nested docs ccab3f8e
R-008 Low Clamp coverage and selectivity to 100% max (handles stale estimateDocumentCount values) c61d2afc
R-009 Low Tighten fixupDocumentDbExplain JSDoc to document in-place mutation; remove dead ?? result fallback cast 06f13107

Deferred per operator instruction:

  • R-004 (sharded plan traversal) — left as-is, no user reports
  • R-005 (targeted tests) — left as-is, preview feature with changing behavior

Added 'npx jest --no-coverage' as step 4 in the PR Completion
Checklist to ensure agents verify all tests pass before finishing.
Three build errors existed because lint/tests use different tsconfig:
1. Sharded path used old computeFetchOverhead return type (string vs object)
2. Failed-query response missing fetchOverheadKind property
3. queryParams referenced outside its block scope — hoisted filter

Also added 'npm run build' as step 5 in the PR Completion Checklist.
@github-actions
Copy link
Copy Markdown
Contributor

✅ Code Quality Checks

Check Status How to fix
Localization (l10n) ✅ Passed
ESLint ✅ Passed
Prettier formatting ✅ Passed

This comment is updated automatically on each push.

@github-actions
Copy link
Copy Markdown
Contributor

📦 Build Size Report

Metric Base (next) PR Delta
VSIX (vscode-documentdb-0.8.0-beta.vsix) 6.76 MB 6.76 MB ⬆️ +3 KB (+0.0%)
Webview bundle (views.js) 5.78 MB 5.79 MB ⬆️ +5 KB (+0.1%)

Download artifact · updated automatically on each push.

@tnaum-ms tnaum-ms merged commit 30aed11 into next Apr 27, 2026
8 checks passed
@tnaum-ms tnaum-ms deleted the dev/tnaum/query-insights-performance-rating branch April 27, 2026 13:51
@tnaum-ms tnaum-ms mentioned this pull request May 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Development

Successfully merging this pull request may close these issues.

3 participants