Skip to content

fix(table): server-side column tag filter across all pages (#25063)#29324

Merged
sonika-shah merged 12 commits into
mainfrom
fix/25063-column-tag-filter-server-side
Jul 1, 2026
Merged

fix(table): server-side column tag filter across all pages (#25063)#29324
sonika-shah merged 12 commits into
mainfrom
fix/25063-column-tag-filter-server-side

Conversation

@sonika-shah

@sonika-shah sonika-shah commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator

Fixes #25063

Screen.Recording.2026-06-29.at.1.05.17.PM.mov

Problem

  • On the table Schema view, columns paginate server-side but tag filtering ran client-side over the loaded page only — filtering on page 1 hid matching columns on later pages.

What changed

Backend

  • Added tags + glossaryTerms query params (comma-separated FQNs) to the two /columns/search endpoints (by-id and by-fqn). All other column endpoints are untouched.
  • Filtering now runs server-side in the same layer as pagination, so it spans the whole table, not just the loaded page.
  • Results keep the column tree: a matching nested column is returned at its real depth under its ancestors (non-matching siblings/children pruned) — no more flattening matches into top-level rows.
  • total now counts top-level columns that contain a match — the correct unit once rows are nested.
  • Filter semantics: values within a group are OR-ed; the tags and glossaryTerms groups are AND-ed (matches the table's multi-column filter behavior).
  • Glossary-derived classification tags are matched as well.
  • The search path works on a copy of the column tree, so filtering never mutates the loaded entity.

Frontend (Schema table)

  • Tag / Glossary column filters now call the server search and reset to page 1 on change.
  • Filter dropdown options come from the full table (all pages) and immediately reflect a tag just added to a column on the current page.
  • Ancestors of matches auto-expand so deep nested matches are visible.

Tests

  • Integration tests: cross-page tag filter, glossary + derived-tag filter, AND across the two groups, and nested STRUCT pruning (both by-fqn and by-id).
  • UI unit test: filter dropdown options are sourced from the full table, not the loaded page.

Greptile Summary

This PR fixes server-side column tag filtering in the table Schema view. Previously, tag filters applied client-side over only the currently loaded page, so columns with matching tags on other pages were invisible. Now both /columns/search endpoints accept tags and glossaryTerms query params, the backend prunes the full column tree server-side (keeping matches at their real nested depth), and pagination counts top-level root columns rather than flattened matches.

  • Backend: Adds ColumnTagFilter record, deep-copies the column tree before pruning, resolves tags directly via the raw DAO (avoiding the certification-classification strip in the service layer), and wires parseColumnTagFilters into both by-id and by-fqn search endpoints.
  • Frontend: Removes useTreeTagFilter client-side hook, stores active filter state in activeTagFilter, delegates filtering to searchTableColumns on change (resetting to page 1), and sources tag filter dropdown options from the full table.columns entity rather than only the loaded page — with ancestors auto-expanded to reveal deep matches.

Confidence Score: 5/5

Safe to merge. The server-side filter runs on a deep copy of the column tree, pagination and field hydration apply to the correct page slice, and the frontend correctly resets pagination and switches between fetch paths on filter change.

The core tree-pruning logic correctly deep-copies before mutation, deduplicates tag filter options by FQN, and wires PIIMasker against the page slice whose objects are shared with the returned roots. Integration tests cover cross-page, derived-tag, AND-between-groups, and nested-STRUCT pruning scenarios. No functional regressions were found in the changed code paths.

No files require special attention. The one inline comment is a documentation nit around the PIIMasker call in TableRepository.

Important Files Changed

Filename Overview
openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TableRepository.java Core filtering logic: adds ColumnTagFilter record, deep-copy guard, pruneColumnsToMatches tree traversal, and resolveColumnTagsForFilter using raw DAO. PIIMasker is called on paginatedColumns which shares object references with paginatedRoots, so in-place mutations propagate correctly. Logic is sound.
openmetadata-service/src/main/java/org/openmetadata/service/resources/databases/TableResource.java Adds tags and glossaryTerms query params to both /columns/search endpoints and wires parseColumnTagFilters into searchTableColumnsById/ByFQN. parseFqnCsv correctly handles null/blank input by returning Set.of(), which isEmpty() treats correctly.
openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaTable/SchemaTable.component.tsx Removes useTreeTagFilter, adds activeTagFilter state, handleColumnFilterChange resets to page 1 and calls the server search path. tagFilter useMemo combines table.columns (full entity) and tableColumns (current page) — getAllTags deduplicates by FQN, so no duplicate filter options.
openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaTable/SchemaTable.test.tsx New test verifies that tag filter options are sourced from full table columns. The assertion passes because the initial render fires getAllTags(mockColumns) when tableColumns is still []. Correct and works as intended.
openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TableResourceIT.java Adds four integration tests covering: cross-page tag filter, glossary-term and derived-tag filter, AND semantics across tag/glossary groups, and nested STRUCT pruning with root-count verification. Tests are well-structured.
openmetadata-ui/src/main/resources/ui/src/rest/tableAPI.ts Adds tags and glossaryTerms to SearchTableColumnsParams interface. Minimal, correct change.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant UI as SchemaTable (Frontend)
    participant API as TableResource (Backend)
    participant Repo as TableRepository
    participant DAO as TagUsageDAO

    UI->>API: "GET /columns/search?tags=PII.Sensitive&glossaryTerms=..."
    API->>API: parseColumnTagFilters(tags, glossaryTerms)
    API->>Repo: searchTableColumnsByFQN(..., columnTagFilter)
    Repo->>Repo: deepCopyList(table.columns)
    Repo->>DAO: getTagsByPrefix(tableFQN, ".%")
    DAO-->>Repo: directTagsByHash (raw, includes certification tags)
    Repo->>Repo: batchFetchDerivedTags(allDirectTags)
    Repo->>Repo: addDerivedTagsWithPreFetched → effectiveTagsByHash
    Repo->>Repo: pruneColumnsToMatches(allColumns, searchTerm, filter, tagsByHash)
    Note over Repo: Tree pruned: matched nodes kept at depth,<br/>non-matching siblings/children removed
    Repo->>Repo: sort root-level columns, paginate
    Repo->>Repo: populateEntityFieldTags(paginatedColumns)
    Repo-->>API: "ResultList(paginatedRoots, total=rootsWithMatch)"
    API-->>UI: "TableColumnList {data, paging}"
    UI->>UI: setTableColumns(pruneEmptyChildren(data))
    UI->>UI: "auto-expand ancestors of matches (depth=MAX_SAFE_INTEGER)"
    UI->>UI: tagFilter options from getAllTags([table.columns, tableColumns])
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant UI as SchemaTable (Frontend)
    participant API as TableResource (Backend)
    participant Repo as TableRepository
    participant DAO as TagUsageDAO

    UI->>API: "GET /columns/search?tags=PII.Sensitive&glossaryTerms=..."
    API->>API: parseColumnTagFilters(tags, glossaryTerms)
    API->>Repo: searchTableColumnsByFQN(..., columnTagFilter)
    Repo->>Repo: deepCopyList(table.columns)
    Repo->>DAO: getTagsByPrefix(tableFQN, ".%")
    DAO-->>Repo: directTagsByHash (raw, includes certification tags)
    Repo->>Repo: batchFetchDerivedTags(allDirectTags)
    Repo->>Repo: addDerivedTagsWithPreFetched → effectiveTagsByHash
    Repo->>Repo: pruneColumnsToMatches(allColumns, searchTerm, filter, tagsByHash)
    Note over Repo: Tree pruned: matched nodes kept at depth,<br/>non-matching siblings/children removed
    Repo->>Repo: sort root-level columns, paginate
    Repo->>Repo: populateEntityFieldTags(paginatedColumns)
    Repo-->>API: "ResultList(paginatedRoots, total=rootsWithMatch)"
    API-->>UI: "TableColumnList {data, paging}"
    UI->>UI: setTableColumns(pruneEmptyChildren(data))
    UI->>UI: "auto-expand ancestors of matches (depth=MAX_SAFE_INTEGER)"
    UI->>UI: tagFilter options from getAllTags([table.columns, tableColumns])
Loading

Comments Outside Diff (1)

  1. openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TableRepository.java, line 3403-3409 (link)

    P2 Profile field hydration operates on all matching columns, not just the page

    When fields includes "profile", setColumnProfile and populateEntityFieldTags are called on matchingColumns (the full tag-filtered, sorted set) rather than on paginatedResults. For a table with many columns and an active tag filter that still returns dozens of matches, this fetches profile data for every matching column even though only a page of them is returned to the caller. This is pre-existing behaviour in the non-tag path, but the tag-filter path now makes it more reachable with large result sets.

    Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Reviews (13): Last reviewed commit: "Merge branch 'main' into fix/25063-colum..." | Re-trigger Greptile

Column tag filtering ran client-side on the loaded page only, so columns
matching a tag on a later page stayed hidden once columns paginate
server-side. Push the filter down to the same layer as pagination.

Backend: add tags/glossaryTerms query params to the existing
/columns/search endpoints (matching the /columns/grid vocabulary) and
filter the full matching column set before sort/pagination in
searchTableColumnsInternal. Matching uses a single tag_usage prefix
lookup (getTagsByPrefix) instead of hydrating tags onto every column.

Frontend: route the SchemaTable tag/glossary dropdown through the search
call, reset to page 1 on change, drop the client-side onFilter, and
source the dropdown options from the full table columns so every tag is
selectable regardless of page.
@sonika-shah
sonika-shah requested a review from a team as a code owner June 22, 2026 20:12
Copilot AI review requested due to automatic review settings June 22, 2026 20:12
@github-actions github-actions Bot added backend safe to test Add this label to run secure Github workflows on PRs labels Jun 22, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 fixes table schema column tag/glossary filtering so it works across the full column set with server-side pagination (instead of only filtering the currently loaded page on the client).

Changes:

  • Backend: adds tags and glossaryTerms query params to /columns/search endpoints and applies tag-based filtering before sort/pagination in TableRepository.
  • Frontend: routes tag/glossary filters through the /columns/search call, removes client-side filtering, and sources filter options from the full table.columns.
  • Tests: adds an integration test to validate tag filtering across pages and updates SchemaTable unit tests.

Reviewed changes

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

Show a summary per file
File Description
openmetadata-ui/src/main/resources/ui/src/rest/tableAPI.ts Extends search params to include tags/glossaryTerms for the table columns search API.
openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaTable/SchemaTable.component.tsx Converts tag/glossary filtering to server-side by driving it through search + controlled filter state.
openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaTable/SchemaTable.test.tsx Adds coverage ensuring tag filter options come from full table columns, not just the page slice.
openmetadata-service/src/main/java/org/openmetadata/service/resources/databases/TableResource.java Adds tags/glossaryTerms query params and parses them into a filter set for repository search.
openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TableRepository.java Applies tag/glossary filtering to matched columns before sort/pagination using a prefix tag-usage lookup.
openmetadata-integration-tests/src/test/java/org/openmetadata/it/tests/TableResourceIT.java Adds an IT validating tag filtering returns matches across pages (including when the match is beyond page 1).

…filter

The shorter searchTableColumns* overloads still called the new arity
without the filterTagFQNs argument (openmetadata-service compile failure),
and the IT used the wrong TagSource package. Pass null through the
delegators and import the nested TagLabel.TagSource.
@github-actions

github-actions Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Jest test Coverage

UI tests summary

Lines Statements Branches Functions
Coverage: 62%
63.05% (70473/111766) 45.87% (40637/88574) 47.32% (12573/26566)

@github-actions

github-actions Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

🟡 Playwright Results — all passed (38 flaky)

✅ 4448 passed · ❌ 0 failed · 🟡 38 flaky · ⏭️ 37 skipped

Shard Passed Failed Flaky Skipped
🟡 Shard 1 382 0 5 11
🟡 Shard 2 808 0 9 8
🟡 Shard 3 817 0 4 7
🟡 Shard 4 808 0 4 10
🟡 Shard 5 859 0 6 0
🟡 Shard 6 774 0 10 1
🟡 38 flaky test(s) (passed on retry)
  • Features/DataAssetRulesEnabled.spec.ts › Verify the Topic Entity Action items after rules is Enabled (shard 1, 2 retries)
  • Features/CustomizeDetailPage.spec.ts › Domain - customization should work (shard 1, 1 retry)
  • Features/Glossary/GlossaryPagination.spec.ts › should check for nested glossary term search (shard 1, 1 retry)
  • Features/TagsSuggestion.spec.ts › should add and accept requested tags for a topic schema field (shard 1, 1 retry)
  • Flow/SearchRBAC.spec.ts › a dashboard-scoped user sees dashboards but never tables (shard 1, 2 retries)
  • Features/BulkEditEntity.spec.ts › Glossary (shard 2, 1 retry)
  • Features/BulkImport.spec.ts › Database service (shard 2, 1 retry)
  • Features/BulkImport.spec.ts › Database (shard 2, 2 retries)
  • Features/BulkImport.spec.ts › Database Schema (shard 2, 1 retry)
  • Features/Container.spec.ts › Copy column link should have valid URL format (shard 2, 1 retry)
  • Features/ContextCenterPermission.spec.ts › user with view-only permission cannot see restore or delete actions on an archived document (shard 2, 2 retries)
  • Features/ContextCenterPermission.spec.ts › user with editAll permission can see restore action but not delete action on an archived document, and can restore it (shard 2, 2 retries)
  • Features/DataQuality/TestCaseImportExportE2eFlow.spec.ts › Admin: Complete export-import-validate flow (shard 2, 2 retries)
  • Features/ExploreQuickFilters.spec.ts › explore tree sidebar selection is not cleared when a top dropdown filter is applied (shard 2, 1 retry)
  • Features/Glossary/LargeGlossaryPerformance.spec.ts › should handle drag and drop for term reordering (shard 3, 1 retry)
  • Features/KnowledgeCenterTextEditor.spec.ts › Rich Text Editor - Text Formatting (shard 3, 1 retry)
  • Features/RestoreEntityInheritedFields.spec.ts › Validate restore with Inherited domain and data products assigned (shard 3, 1 retry)
  • Features/Table.spec.ts › Table pagination with sorting should works (shard 3, 1 retry)
  • Flow/IngestionBot.spec.ts › Ingestion bot should be able to access domain specific domain (shard 4, 1 retry)
  • Flow/NestedChildrenUpdates.spec.ts › should update nested column displayName immediately without refresh (shard 4, 1 retry)
  • Pages/CustomProperties.spec.ts › Email (shard 4, 1 retry)
  • Pages/DataContracts.spec.ts › Create Data Contract and validate for ApiEndpoint (shard 4, 1 retry)
  • Pages/Entity.spec.ts › User as Owner Add, Update and Remove (shard 5, 1 retry)
  • Pages/Entity.spec.ts › User as Owner Add, Update and Remove (shard 5, 1 retry)
  • Pages/Entity.spec.ts › User should be denied access to edit description when deny policy rule is applied on an entity (shard 5, 1 retry)
  • Pages/EntityDataConsumer.spec.ts › Follow & Un-follow entity (shard 5, 1 retry)
  • Pages/EntityDataSteward.spec.ts › Follow & Un-follow entity (shard 5, 1 retry)
  • Pages/ExploreBrowse.spec.ts › service type drill-down disables unrelated roots and query-panel Clear resets it (shard 5, 2 retries)
  • Features/AutoPilot.spec.ts › Create Service and check the AutoPilot status (shard 6, 1 retry)
  • Pages/Lineage/DataAssetLineage.spec.ts › Column lineage for container -> container (shard 6, 1 retry)
  • ... and 8 more

📦 Download artifacts

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

…ation tags)

filterColumnsByTags resolved column tags via getTagsByPrefix, which returns
only direct tag_usage rows and strips certification-classification tags. The
column tag filter dropdown is built from column.tags (direct + glossary-derived
tags), so selecting a derived or certification tag returned zero matches.

Resolve column tags the same way the column list responses do: raw tag_usage
prefix query (no certification stripping) enriched with glossary-derived tags
via batchFetchDerivedTags. Derived-tag fetch failures degrade to direct tags.

Add an IT covering the glossaryTerms param, the derived classification tag, and
the by-id /columns/search endpoint.
Copilot AI review requested due to automatic review settings June 24, 2026 11:03

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

…filter-server-side

# Conflicts:
#	openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaTable/SchemaTable.component.tsx
@sonika-shah
sonika-shah enabled auto-merge (squash) June 29, 2026 08:54
Copilot AI review requested due to automatic review settings June 29, 2026 08:57

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@sonika-shah

Copy link
Copy Markdown
Collaborator Author

@gitar-bot re: the ⚠️ "Table column search docs may orphan on ancestor cascade delete" finding (TableRepository.java:184-188) — this is not part of this PR's changes. It's a diff-attribution artifact from merging main into the branch.

The flagged line belongs to a different PR:
descendantsCoveredByAncestorCascade = true was introduced by bc6087907c#29322 "perf(delete): bound memory + skip ancestor-covered work in bulk recursive hard delete", which is already on main. It entered this branch only via f0cc662fb4 Merge branch 'main'. This PR's changes are confined to searchTableColumnsInternal (server-side column tag filtering) and the SchemaTable UI — nothing here touches the delete cascade or the TABLE_COLUMN index.

The orphan concern is also already handled on main:
#29549 ("fix(search): clear orphaned column docs on recursive service hard delete"), also merged into this branch, added exactly the per-entity cleanup the finding asks for. In SearchRepository.deleteEntityIndex:

deleteOrUpdateChildren(entity, indexMapping);
if (Entity.TABLE.equals(entityType)) {
  deleteTableColumns((Table) entity);
} else {
  deleteDescendantColumns(entity, entityType); // #29549: clears TABLE_COLUMN docs for db/schema/service
}

So a recursive hard-delete of a database/schema/service still clears descendant column docs via deleteDescendantColumns — the per-table skip introduced by #29322 does not orphan them.

Not actioning this in this PR (out of scope — belongs to #29322/#29549). If there's a residual gap, it should be raised against those PRs, not this one.

@gitar-bot

gitar-bot Bot commented Jul 1, 2026

Copy link
Copy Markdown

@gitar-bot re: the ⚠️ "Table column search docs may orphan on ancestor cascade delete" finding (TableRepository.java:184-188) — this is not part of this PR's changes. It's a diff-attribution artifact from merging main into the branch.

The flagged line belongs to a different PR:
descendantsCoveredByAncestorCascade = true was introduced by bc6087907c#29322 "perf(delete): bound memory + skip ancestor-covered work in bulk recursive hard delete", which is already on main. It entered this branch only via f0cc662fb4 Merge branch 'main'. This PR's changes are confined to searchTableColumnsInternal (server-side column tag filtering) and the SchemaTable UI — nothing here touches the delete cascade or the TABLE_COLUMN index.

The orphan concern is also already handled on main:
#29549 ("fix(search): clear orphaned column docs on recursive service hard delete"), also merged into this branch, added exactly the per-entity cleanup the finding asks for. In SearchRepository.deleteEntityIndex:

deleteOrUpdateChildren(entity, indexMapping);
if (Entity.TABLE.equals(entityType)) {
  deleteTableColumns((Table) entity);
} else {
  deleteDescendantColumns(entity, entityType); // #29549: clears TABLE_COLUMN docs for db/schema/service
}

So a recursive hard-delete of a database/schema/service still clears descendant column docs via deleteDescendantColumns — the per-table skip introduced by #29322 does not orphan them.

Not actioning this in this PR (out of scope — belongs to #29322/#29549). If there's a residual gap, it should be raised against those PRs, not this one.

Acknowledged — the feedback is well-reasoned and the attribution is correct.

The flagged line (descendantsCoveredByAncestorCascade = true) was introduced by #29322 and entered this branch only through the merge of main. The orphan-cleanup concern is also already addressed by #29549 via deleteDescendantColumns, which handles TABLE_COLUMN docs for database/schema/service-level recursive deletes independently of the per-table skip.

The finding will be dismissed as out-of-scope for this PR. If a residual gap exists, it belongs against #29322 or #29549.

@sonarqubecloud

sonarqubecloud Bot commented Jul 1, 2026

Copy link
Copy Markdown

@sonarqubecloud

sonarqubecloud Bot commented Jul 1, 2026

Copy link
Copy Markdown

@gitar-bot

gitar-bot Bot commented Jul 1, 2026

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

Moves column tag and glossary filtering to the backend to ensure accurate cross-page results and proper tree structure. Resolved issues regarding derived tag inheritance, filter-triggered pagination resets, and unintended tree mutation.

✅ 4 resolved
Edge Case: Tag filter may miss columns whose tag is derived/inherited

📄 openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TableRepository.java:3405-3419
filterColumnsByTags resolves column tags via getTagsByPrefix(table.getFullyQualifiedName(), ".%"), which reads tag_usage directly and (unlike EntityRepository.getTags, which calls addDerivedTagsGracefully) does NOT include derived tags. Meanwhile the UI dropdown options come from getAllTags(table.columns) / extractTags, which iterate column.tags as loaded on the detail page — and those tags can include derived tags. As a result, a user can select a derived tag in the dropdown that exists on a column's loaded tags array but is not stored as a direct tag_usage row, so the server-side filter returns 0 matches even though the UI shows the column as tagged. The free-text q filter and the no-filter path are unaffected. Consider matching against the same derived-tag-aware resolution used elsewhere (e.g. include derived tags when building tagsByHash), or document that only directly-applied tags are filterable. Low likelihood but produces a confusing empty result.

Quality: onChange resets to page 1 on any antd table change, not just filters

📄 openmetadata-ui/src/main/resources/ui/src/components/Database/SchemaTable/SchemaTable.component.tsx:329-343
handleColumnFilterChange is wired to the antd Table's onChange, which fires for sorter and pagination changes as well as filter changes. It unconditionally calls handlePageChange(INITIAL_PAGING_VALUE, ...) on every invocation. If the table ever emits an onChange for a non-filter reason (e.g. an internal sorter/pagination interaction), the user would be silently bounced back to page 1. Since column sort is handled separately via handleColumnHeaderSortToggle this is likely benign today, but guarding the page reset to only run when the tag/glossary filter values actually changed (compare against current activeTagFilter) would make the behavior robust against future column config changes.

Quality: pruneColumnsToMatches mutates the loaded table's column tree in place

📄 openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TableRepository.java:3389-3403
pruneColumnsToMatches calls column.setChildren(prunedChildren) on the Column objects returned by table.getColumns(), mutating the in-memory entity tree as a side effect of a read/search operation. In practice this is currently safe because the entity name cache (CACHE_WITH_NAME) stores serialized JSON and getByName(..., fromCache=false) deserializes a fresh copy per call, so there is no cross-request corruption. However, the mutation is a latent footgun: if the same Table instance is ever served from a request-scoped/in-memory cache (e.g. RequestEntityCache) and reused later in the same request, downstream readers would observe a pruned/filtered column tree (matched children only) rather than the full set. Consider building new Column nodes (or shallow copies) instead of mutating the loaded entity's children so the search path has no observable side effects on the entity object.

Bug: Table column search docs may orphan on ancestor cascade delete

📄 openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/TableRepository.java:184-188
TableRepository now sets descendantsCoveredByAncestorCascade = true. In EntityRepository.bulkHardDeleteSubtreeChunk (EntityRepository.java:6741-6747) this skips the per-entity deleteFromSearch(entity, true) when a Table is deleted as a descendant of a recursive hard-delete of a databaseService / database / databaseSchema, relying on the ancestor's deleteOrUpdateChildren cascade to remove the table's search docs.

However, SearchRepository.deleteEntityIndex does TWO things for tables: (1) deleteOrUpdateChildren and (2) deleteTableColumns((Table) entity) (SearchRepository.java:2607-2608). deleteTableColumns removes documents from the separate TABLE_COLUMN index using the field table.id (per-table id). The ancestor cascade in deleteOrUpdateChildren for the DATABASE_SERVICE case deletes child docs by service.id (SearchRepository.java:2808-2810) and the default case by <entityType>.id (e.g. database.id, databaseSchema.id) (line 2817-2823).

Unless the TABLE_COLUMN index documents (a) are included in the ancestor's childAliases AND (b) carry a service.id / database.id / databaseSchema.id field, the column-level search documents will NOT be matched by the ancestor cascade. Since the per-table deleteFromSearch (which is the only caller of deleteTableColumns) is now skipped, this can leave orphaned column search documents in the TABLE_COLUMN index after a recursive hard delete of a database/schema/service — stale columns that reappear in search until a full reindex.

The other repos that set this flag (Chart, Dashboard, Pipeline, Topic, etc.) do not have an analogous secondary child index like TABLE_COLUMN, so Table is a special case here. Please verify that the database/databaseSchema/databaseService cascade actually removes TABLE_COLUMN docs (e.g. that column docs carry the ancestor id fields and the column index is in the child aliases). If not, the per-entity column cleanup must still run (e.g. delete column docs by FQN prefix as part of the ancestor cascade, or keep the per-table search dispatch for tables).

Options

Display: compact → Showing less information.

Comment with these commands to change:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

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

Labels

backend safe to test Add this label to run secure Github workflows on PRs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Column tag filter works only per page instead of across entire table

4 participants