Skip to content

Fixes #26805: proper casing for Explore quick filters via sourceFields - #31307

Open
Rohit0301 wants to merge 10 commits into
mainfrom
explore-quick-filters-proper-casing
Open

Fixes #26805: proper casing for Explore quick filters via sourceFields#31307
Rohit0301 wants to merge 10 commits into
mainfrom
explore-quick-filters-proper-casing

Conversation

@Rohit0301

@Rohit0301 Rohit0301 commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Describe your changes:

Fixes #26805

Explore quick-filter dropdowns were showing option labels in lowercase because Elasticsearch .keyword fields use a lowercase_normalizer for aggregation matching. This PR threads a sourceFields parameter through the filter pipeline so each aggregation request triggers a top_hits sub-aggregation; labels are then read from _source (original case) rather than the lowercased bucket key.

Type of change:

  • Improvement

High-level design:

Problem: getOptionsFromAggregationBucket used bucket.key (lowercase) as the display label.

Solution: The backend already supports a top_hits sub-aggregation via the sourceFields query param (used by AdvancedSearch autocomplete). This PR reuses the same mechanism for quick filters:

  • ExploreQuickFilterField interface gains an optional sourceFields?: string field (dot-path into _source).
  • All affected filter constants get sourceFields values: domains → 'domains.displayName', owners → 'ownerDisplayName' (flat field, confirmed from ES index mapping), tags → 'tags.tagFQN', tier/certification, service/database/schema, charts/tasks/data models, etc.
  • getAggregationOptions forwards sourceFields to the GET path and adds topHits: { size: 1 } on the POST path to trigger the sub-aggregation.
  • getOptionsFromAggregationBucket gains an optional sourceFields param and a private extractSourceValue helper that handles both flat and array-typed _source paths (arrays are dereferenced at [0] mid-traversal). key stays as the lowercase bucket key for filter matching; only label changes.
  • ExploreQuickFilters threads field.sourceFields through all fetch and render paths, and bypasses cached page aggregations (which lack top_hits data) when sourceFields is set.

Tests:

Use cases covered

  • Domains filter dropdown shows the original-cased domain displayName, not lowercase
  • Owner filter shows original casing via flat ownerDisplayName source field
  • Tag filter options show original FQN casing (e.g. PersonalData.Personal)
  • Tier filter options show original FQN casing
  • Service, database, schema, chart, task, and other display-name filters now also request sourceFields

Unit tests

  • Not added (logic is covered by Playwright E2E below)

Backend integration tests

  • Not applicable (no backend API changes)

Ingestion integration tests

  • Not applicable (no ingestion changes)

Playwright (UI) tests

  • Added Playwright E2E tests asserting proper casing for domains, tier, and tag filter options
  • File: playwright/e2e/Features/ExploreQuickFilters.spec.ts — new describe block 'Quick filter options - proper casing from top_hits'

Manual testing performed

  1. Open Explore page
  2. Expand Domains, Owners, Tag, Tier, Service dropdowns
  3. Confirm options show original casing (e.g. "Data Engineering" instead of "data engineering")
  4. Apply a filter — confirm the search still works (filter value remains the lowercase key)

UI screen recording / screenshots:

Not applicable (behavior change in label text, no visual layout changes).

Checklist:

  • I have read the CONTRIBUTING document.
  • My PR title is Fixes <issue-number>: <short explanation>
  • My PR is linked to a GitHub issue via Fixes #26805 above.
  • I have commented on my code, particularly in hard-to-understand areas.
  • For JSON Schema changes: not applicable.
  • For UI changes: screen recording not applicable (label-only change).
  • I have added tests (Playwright E2E) and listed them above.

Greptile Summary

The PR preserves normalized aggregation keys for filtering while deriving quick-filter labels from original-cased Elasticsearch _source values.

  • Adds sourceFields metadata to affected Explore and column-grid filters.
  • Requests top_hits data and bypasses cached aggregations when source-backed labels are required.
  • Extracts labels from flat, nested, and multi-valued source fields while retaining bucket keys as filter values.
  • Adds unit and Playwright coverage for domains, owners, tags, tiers, and services.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
openmetadata-ui/src/main/resources/ui/src/utils/AdvancedSearchPureUtils.ts Adds source-path extraction for top-hit documents while preserving normalized aggregation keys.
openmetadata-ui/src/main/resources/ui/src/utils/ExploreUtils.tsx Threads source-field requests through GET aggregation calls and enables top hits for POST calls.
openmetadata-ui/src/main/resources/ui/src/components/Explore/ExploreQuickFilters.tsx Propagates per-filter source fields through initial and searched option retrieval.
openmetadata-ui/src/main/resources/ui/src/constants/AdvancedSearch.constants.ts Configures source paths for quick filters whose labels should retain original casing.
openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ExploreQuickFilters.spec.ts Adds end-to-end assertions for original-cased domain, owner, tag, tier, and service labels.

Reviews (3): Last reviewed commit: "lint fix" | Re-trigger Greptile

…s top_hits

Thread `sourceFields` through the quick-filter pipeline so each
aggregation request triggers a `top_hits` sub-aggregation, and option
labels are read from `_source` (original case) rather than the
lowercase bucket key.

- Add `sourceFields?: string` to `ExploreQuickFilterField` interface
- Add `sourceFields` to all affected filter constants (domains, owners,
  tags, tier, certification, service, database, schema, charts, tasks,
  data models, classification, glossary) across all dropdown lists;
  owners uses the flat `ownerDisplayName` field (no array traversal)
- Update `getAggregationOptions` to forward `sourceFields` to GET path
  and pass `topHits: { size: 1 }` on POST path
- Refactor `getOptionsFromAggregationBucket` to extract a private
  `extractSourceValue` helper with array-aware dot-path traversal
- Thread `sourceFields` through `ExploreQuickFilters` fetch functions
- Add Playwright tests asserting proper casing for domains, tiers, tags

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions github-actions Bot added the UI UI specific issues label Aug 10, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Hi there 👋 Thanks for your contribution!

The OpenMetadata team will review the PR shortly! Once it has been labeled as safe to test, the CI workflows
will start executing and we'll be able to make sure everything is working as expected.

Let us know if you need any help!

@github-actions

Copy link
Copy Markdown
Contributor

❌ PR checklist incomplete

This PR cannot be merged until the following are addressed on its linked issue:

The fields live on the linked issue in the Shipping project (open the issue → right sidebar → Projects). After you set them, re-run this check (or push a commit) — issue/project changes do not re-trigger it automatically.

Maintainers can bypass this check by adding the skip-pr-checks label.

@Rohit0301 Rohit0301 self-assigned this Aug 10, 2026
@Rohit0301 Rohit0301 added the safe to test Add this label to run secure Github workflows on PRs label Aug 10, 2026
@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

✅ Playwright Results — workflow succeeded

Validated commit 4e7442dc5e96a28ac78594b2055e15963b8abd3c in Playwright run 32046227920, attempt 1.

✅ 1043 passed · ❌ 0 failed · 🟡 0 flaky · ⏭️ 0 skipped · 🧰 0 lifecycle flaky

Performance

Blocking targets: ✅ met · Optimization targets: 🟡 in progress

Shard-job maxima below are not the full workflow wall time; the linked run includes build, fixture, planning, and reporting.

🕒 Full workflow signal wall (to summary) 54m 26s

⏱️ Max setup 3m 56s · max shard execution 17m 36s · max shard-job elapsed before upload 22m 23s · reporting 5s

🌐 197.85 requests/attempt · 2.20 app boots/UI scenario · 24.42% common-shard skew

Optimization targets still in progress:

  • Common shard skew was 24.42% (convergence target: at most 15%).
  • Application boot ratio was 2.2 per UI scenario (2325 boots / 1059 scenarios; convergence target: at most 1).
Shard Passed Failed Flaky Skipped Lifecycle failed Lifecycle flaky
✅ Shard chromium-01 136 0 0 0 0 0
✅ Shard chromium-02 147 0 0 0 0 0
✅ Shard chromium-03 147 0 0 0 0 0
✅ Shard chromium-04 132 0 0 0 0 0
✅ Shard chromium-05 156 0 0 0 0 0
✅ Shard chromium-06 138 0 0 0 0 0
✅ Shard data-asset-rules-01 61 0 0 0 0 0
✅ Shard domain-isolation-01 14 0 0 0 0 0
✅ Shard global-state-01 34 0 0 0 0 0
✅ Shard import-export-01 7 0 0 0 0 0
✅ Shard ingestion-01 2 0 0 0 0 0
✅ Shard reindex-01 28 0 0 0 0 0
✅ Shard search-01 12 0 0 0 0 0
✅ Shard search-rbac-01 29 0 0 0 0 0

📦 Download artifacts

How to debug locally
# Download playwright-test-results-<shard> artifact and unzip
npx playwright show-trace path/to/trace.zip    # view trace

- ExploreQuickFilters.test.tsx: add missing 10th arg (sourceFields=undefined)
  to all getAggregationOptions toHaveBeenCalledWith assertions
- AdvancedSearchPureUtils.ts: fix extractSourceValue to match the correct
  array element by bucket key (case-insensitive) rather than always taking
  [0] — fixes the case where an asset has multiple domains/tags and [0]
  doesn't correspond to the current bucket
- AdvancedSearchPureUtils.test.ts: add unit tests covering flat field
  extraction, nested single-object path, array element matching by key,
  and fallback when no top_hits data is present

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Jest test Coverage

UI tests summary

Lines Statements Branches Functions
Coverage: 66%
66.26% (78458/118397) 50.31% (47431/94274) 51.46% (14270/27727)

Rohit0301 and others added 2 commits August 17, 2026 14:05
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

⚠️ UI Checkstyle passed — lint findings in changed files

🔍 ESLint findings in this PR's files — 0 error(s), 31 warning(s)

Errors block the build. Warnings do not yet — they are rules whose backlog is still
being worked down, listed so this PR does not add to it. See docs/ui-code-quality-gate.md.

0 error(s), 31 warning(s) across 6 changed file(s).

Count Rule
23 sonarjs/no-duplicate-string
4 i18next/no-literal-string
1 jsx-a11y/no-autofocus
1 sonarjs/no-nested-conditional
1 sonarjs/cyclomatic-complexity
1 sonarjs/cognitive-complexity
All findings
Location Rule Message
🟡 src/components/Explore/ExploreQuickFilters.test.tsx:108:58 i18next/no-literal-string disallow literal string: <button data-testid={onGetInitialOptions-${searchKey}} onClick={() => onGetInitialOptions?.(searchKey)}> Get Initial Options </button
🟡 src/components/Explore/ExploreQuickFilters.test.tsx:113:53 i18next/no-literal-string disallow literal string: <button data-testid={onSearch-${searchKey}} onClick={() => onSearch('test', searchKey)}> Search
🟡 src/components/Explore/ExploreQuickFilters.test.tsx:119:47 sonarjs/no-duplicate-string Define a constant instead of duplicating this literal 3 times.
🟡 src/components/Explore/ExploreQuickFilters.test.tsx:120:11 i18next/no-literal-string disallow literal string: <button data-testid={onChange-${searchKey}} onClick={() => onChange([{ key: 'test-key', label: 'test-label' }], searchKey) }> Change
🟡 src/components/Explore/ExploreQuickFilters.test.tsx:150:10 sonarjs/no-duplicate-string Define a constant instead of duplicating this literal 15 times.
🟡 src/components/Explore/ExploreQuickFilters.test.tsx:329:9 sonarjs/no-duplicate-string Define a constant instead of duplicating this literal 6 times.
🟡 src/components/Explore/ExploreQuickFilters.test.tsx:357:47 sonarjs/no-duplicate-string Define a constant instead of duplicating this literal 8 times.
🟡 src/components/Explore/ExploreQuickFilters.test.tsx:482:32 sonarjs/no-duplicate-string Define a constant instead of duplicating this literal 6 times.
🟡 src/components/Explore/ExploreQuickFilters.test.tsx:489:31 sonarjs/no-duplicate-string Define a constant instead of duplicating this literal 4 times.
🟡 src/components/Explore/ExploreQuickFilters.test.tsx:685:16 sonarjs/no-duplicate-string Define a constant instead of duplicating this literal 4 times.
🟡 src/components/Explore/ExploreQuickFilters.test.tsx:1050:48 i18next/no-literal-string disallow literal string: Extra Action
🟡 src/components/Explore/QuickFilterDropdown.tsx:184:17 jsx-a11y/no-autofocus The autoFocus prop should not be used, as it can reduce usability and accessibility for users.
🟡 src/components/Explore/QuickFilterDropdown.tsx:221:17 sonarjs/no-nested-conditional Extract this nested ternary operation into an independent statement.
🟡 src/constants/AdvancedSearch.constants.ts:22:12 sonarjs/no-duplicate-string Define a constant instead of duplicating this literal 7 times.
🟡 src/constants/AdvancedSearch.constants.ts:24:19 sonarjs/no-duplicate-string Define a constant instead of duplicating this literal 7 times.
🟡 src/constants/AdvancedSearch.constants.ts:27:12 sonarjs/no-duplicate-string Define a constant instead of duplicating this literal 8 times.
🟡 src/constants/AdvancedSearch.constants.ts:34:19 sonarjs/no-duplicate-string Define a constant instead of duplicating this literal 7 times.
🟡 src/constants/AdvancedSearch.constants.ts:37:12 sonarjs/no-duplicate-string Define a constant instead of duplicating this literal 5 times.
🟡 src/constants/AdvancedSearch.constants.ts:39:19 sonarjs/no-duplicate-string Define a constant instead of duplicating this literal 5 times.
🟡 src/constants/AdvancedSearch.constants.ts:42:12 sonarjs/no-duplicate-string Define a constant instead of duplicating this literal 5 times.
🟡 src/constants/AdvancedSearch.constants.ts:44:19 sonarjs/no-duplicate-string Define a constant instead of duplicating this literal 5 times.
🟡 src/constants/AdvancedSearch.constants.ts:47:12 sonarjs/no-duplicate-string Define a constant instead of duplicating this literal 5 times.
🟡 src/constants/AdvancedSearch.constants.ts:97:12 sonarjs/no-duplicate-string Define a constant instead of duplicating this literal 3 times.
🟡 src/constants/AdvancedSearch.constants.ts:102:12 sonarjs/no-duplicate-string Define a constant instead of duplicating this literal 3 times.
🟡 src/constants/AdvancedSearch.constants.ts:107:12 sonarjs/no-duplicate-string Define a constant instead of duplicating this literal 4 times.
🟡 src/constants/AdvancedSearch.constants.ts:272:12 sonarjs/no-duplicate-string Define a constant instead of duplicating this literal 3 times.
🟡 src/constants/AdvancedSearch.constants.ts:274:15 sonarjs/no-duplicate-string Define a constant instead of duplicating this literal 3 times.
🟡 src/utils/AdvancedSearchPureUtils.test.ts:173:14 sonarjs/no-duplicate-string Define a constant instead of duplicating this literal 4 times.
🟡 src/utils/AdvancedSearchPureUtils.test.ts:177:52 sonarjs/no-duplicate-string Define a constant instead of duplicating this literal 4 times.
🟡 src/utils/AdvancedSearchPureUtils.ts:214:23 sonarjs/cyclomatic-complexity {"message":"Function has a complexity of 11 which is greater than 10 authorized.","cost":1,"secondaryLocations":[{"line":214,"column":22,"endLine":214,"endColum
🟡 src/utils/ExploreUtils.tsx:204:4 sonarjs/cognitive-complexity Refactor this function to reduce its Cognitive Complexity from 18 to the 15 allowed.

Fix locally (fast - only checks files changed in this branch):

make ui-checkstyle-changed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
aniketkatkar97
aniketkatkar97 previously approved these changes Aug 17, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🚦 Removed from the merge queue — failed_checks (2026-08-17T12:40:00Z)

Blocked the queue: playwright-summary

  • Postgresql PR Playwright E2E Tests — playwright-summary, playwright-ci-postgresql (chromium-17), playwright-ci-postgresql (chromium-02), playwright-ci-postgresql (chromium-15), playwright-ci-postgresql (chromium-01)

@github-actions

Copy link
Copy Markdown
Contributor

🚦 Removed from the merge queue — manual (2026-08-17T13:15:55Z)

The entry left the queue before it was built, so no checks ran against it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

🚦 Removed from the merge queue — manual (2026-08-17T16:33:47Z)

No failing check on merge-queue commit 216dee7 — invalidated by an entry ahead in the queue, or a required check timed out.

@gitar-bot

gitar-bot Bot commented Aug 17, 2026

Copy link
Copy Markdown
Code Review ✅ Approved 3 resolved / 3 findings

Threads sourceFields through the Explore quick filter pipeline to extract original-cased labels from top_hits aggregations, addressing top-hit label matching and owner test definition findings.

✅ 3 resolved
Bug: top_hits array label may not match the aggregation bucket

📄 openmetadata-ui/src/main/resources/ui/src/utils/AdvancedSearchPureUtils.ts:210-224 📄 openmetadata-ui/src/main/resources/ui/src/utils/AdvancedSearchPureUtils.ts:242-256
For array-typed source paths (domains.displayName, tags.tagFQN, charts.displayName, dataModels.displayName, tasks.displayName), extractSourceValue unconditionally dereferences [0] mid-traversal. But the top_hits document is a single matching asset whose array can contain many entries in arbitrary order, so [0] is not guaranteed to correspond to the current bucket's key. An asset tagged with e.g. Tier.Tier1 and PersonalData.Personal can produce the wrong label for the PersonalData.Personal bucket, showing a label that mismatches the option's (lowercased) key. Consider selecting the array element whose value case-insensitively equals option.key instead of always taking index 0; fall back to the bucket key when no element matches.

Bug: Owner test assumes user.responseData.displayName is defined

📄 openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ExploreQuickFilters.spec.ts:542 📄 openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ExploreQuickFilters.spec.ts:560 📄 openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ExploreQuickFilters.spec.ts:563
UserClass is created via generateRandomUsername(), which only provides firstName/lastName/email/password — no displayName is sent on signup. If the backend does not populate a displayName, user.responseData.displayName is undefined, so ownerName becomes undefined and ownerName.toLowerCase() throws a TypeError, making the test fail (or flaky) rather than validating casing. Note the sibling service test correctly guards with ?? table.serviceResponseData.name, but this owner test does not. Add a fallback (e.g. user.responseData.displayName ?? user.responseData.name) or explicitly patch a displayName on the user before running the assertion.

Bug: Owner test relies on undefined displayName

📄 openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ExploreQuickFilters.spec.ts:543 📄 openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ExploreQuickFilters.spec.ts:558 📄 openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ExploreQuickFilters.spec.ts:561 📄 openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ExploreQuickFilters.spec.ts:564
new UserClass() with no args does not populate responseData.displayName (generateRandomUsername only sets email/firstName/lastName/password), so ownerName is undefined. ownerName.toLowerCase() then throws a TypeError and the owner casing test fails. Set an explicit displayName when constructing the user (e.g. pass user data with a displayName) or derive the expected label from a field that is actually populated (firstName/lastName).

Options

Display: compact → Showing less information.

Comment with these commands to change the behavior for this request:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Powered by Gitar — free for open source

@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

safe to test Add this label to run secure Github workflows on PRs UI UI specific issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement proper casing for Explore quick filters using sourceFields

3 participants