[Hackathon] Interactive and Explainable Result Pane#5099
Open
tanishqgandhi1908 wants to merge 1 commit into
Open
[Hackathon] Interactive and Explainable Result Pane#5099tanishqgandhi1908 wants to merge 1 commit into
tanishqgandhi1908 wants to merge 1 commit into
Conversation
…rmation diff Upgrade the operator result pane from a static 5-row nz-table into an interactive, full-dataset spreadsheet view with row-level filtering, sorting, search, and a per-operator transformation summary. Frontend - Replace nz-table with ag-grid Community (MIT) using the Infinite Row Model; pagination with auto-fit page size; column reorder/hide/pin/resize. - Custom header component restores inline column stats (Min / Max / Non-Null / category %). - Per-cell renderer keeps image preview + hover-only download icon. - Row inspector docks inline below the grid (replaces the prior popup modal): shows the row as a JSON tree with prev/next/close. - Row search input above the grid, debounced 250 ms. - Transformation diff strip above the grid: upstream-vs-current row delta and column diff (added / removed / kept / type-changed) with an expandable detail drawer. - Result panel itself is now a fixed bottom dock (top-edge resize only) — no more floating popup. Backend - Extend ResultPaginationRequest with optional filters / sorts / rowSearch. - New VirtualDocument query methods: getRangeWithQuery, countWithQuery (defaulted to no-op fallback). - IcebergPredicateBuilder compiles ColumnFilter into Iceberg Expressions with type-aware value parsing (eq/ne/lt/le/gt/ge/startsWith/isNull/isNotNull/in pushed down; contains/endsWith handled as residual). - IcebergDocument.getRangeWithQuery / countWithQuery: pushdown + residual filter + rowSearch + in-memory sort with a configurable cap (storage.result.sort.max-rows, default 100k); responses include a sortSkipped flag so the UI can prompt the user to narrow the filter. - PaginatedResultEvent carries totalNumTuples and sortSkipped for the filtered/sorted path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
video submission -
https://youtu.be/15F18ZQe-OA
Motivation
Texera's result pane has historically been a static, page-by-page table viewer with a default page size of five rows. Users could glance at operator outputs and search by column name, but they could not interact with the data the way they would in a modern spreadsheet tool — no row-level filtering, no sorting, no full-data search, and no way to see, at a glance, what an operator actually did to its input.
That meant every debugging or exploration session looked roughly the same:
It worked, but it was slow, fiddly, and easy to get wrong on wide or large tables.
This PR rethinks the result pane around two ideas:
Interactive result pane with pagination
Search rows
Search with text in columns
Whats changed since last operator
What changed (story version)
Phase 1 — From
nz-tableto ag-grid CommunityThe old
nz-tableview rendered every column to the DOM and capped at five rows per page. That worked for toy data but felt cramped, didn't sort or filter, and couldn't survive a 200-column table.We swapped it for ag-grid Community (MIT-licensed, Apache-compatible) using the Infinite Row Model wired into Texera's existing WebSocket pagination protocol via a custom
IDatasource. Out of the box, the user now gets:The grid is themed against Texera's existing Ant Design palette (no garish ag-grid defaults), and the per-column stats (Min / Max / Non-Null / category %) that lived in the old header are restored via a custom header component — same data, better layout.
Phase 2 — Backend pushdown
Spreadsheet UX is only useful if it scales. Texera stores operator results as Iceberg / Parquet, which can prune entire data files by partition + min/max stats and push predicates into the Parquet reader. We extended the protocol and the storage layer to take advantage of that:
ResultPaginationRequestnow carries optionalfilters,sorts, androwSearchfields.VirtualDocumentgainsgetRangeWithQuery+countWithQuery(defaulted to safe fallbacks so non-Iceberg documents keep working).IcebergPredicateBuildertranslates the wire-formatColumnFilterinto IcebergExpressionswith type-aware value parsing per column type (no silent string-coercion bugs).IcebergDocumentimplements both methods: predicate pushdown for ops Iceberg supports natively, residual evaluation in memory forcontains/endsWith/rowSearch, and an in-memory sort capped atstorage.result.sort.max-rows(default 100k).When sort is requested but the matched count exceeds the cap, the backend returns rows in scan order with a
sortSkippedflag, and the UI shows a friendly banner explaining how to narrow the filter. (Iceberg cannot push ORDER BY into the Parquet reader — sort is the one place we have to spend JVM heap.)Phase 3 — Full-data row search
A debounced
Search rows...input above the grid sends arowSearchstring down to the backend, which compiles it into a multi-columncontainspredicate over all string columns. This is the first real "search inside the data" experience in the result pane — the existing column-name search continues to work alongside it.Phase 4 — The transformation diff
This is the most ambitious idea: every operator, at a glance, tells you what it did.
A compact strip above the grid renders:
↓ -149 rows (-99.3%), color-coded green/red/neutral) and column delta (e.g.+2 -1 ⇄1 colsor5 cols · unchanged).Click the strip and it expands inline (no popup) into a detail drawer with:
For source operators with no input, the strip shows a friendly
▶ Source operatorchip. For multi-input operators (joins, unions), it collapses to⛙ Combined from N inputsand defers the pairwise diff for a future iteration. All of this is computed from the data the frontend already maintains inWorkflowResultService— zero new backend round trips.Layout — bottom dock instead of floating modal
The result panel itself was a draggable floating popup. We turned it into a fixed bottom dock: full viewport width, top-edge resize handle for height, no drag-to-move, no "return to corner" widget. Clicking a row no longer opens a modal — instead an inline row inspector slides in below the grid with a JSON tree view, prev/next/close, and visual selection on the corresponding row in the grid.
Architectural notes
columnOffset/columnLimit/columnSearchare kept onResultPaginationRequestwith their defaults for the Python SDK and any external callers. New frontend simply stops setting them; the bare-minimum payload also avoids a Jackson edge case where JSNumber.MAX_SAFE_INTEGERoverflows Scala'sInt.🤖 Generated with Claude Code