Skip to content

fix(app): serialize JSON column filters with dot access (HDX-5085) - #2864

Closed
pulpdrew wants to merge 1 commit into
mainfrom
cursor/fix-json-column-filter-bracket-notation-9f7b
Closed

fix(app): serialize JSON column filters with dot access (HDX-5085)#2864
pulpdrew wants to merge 1 commit into
mainfrom
cursor/fix-json-column-filter-bracket-notation-9f7b

Conversation

@pulpdrew

Copy link
Copy Markdown
Contributor

Summary

Search page filters on a JSON ResourceAttributes (or any JSON-typed) column were being serialized with bracket access — e.g. ResourceAttributes['region'] NOT IN ('eu-central-1'). ClickHouse cannot subscript a JSON column, so the query failed with:

First argument for function 'arrayElement' must be array, got 'JSON' instead

This reproduced by clicking Add to Filters and then Exclude (or Include) on a value from a JSON column.

Root cause

Filter keys live in a clean, in-memory FilterState (dot form, e.g. ResourceAttributes.region) and are converted to raw SQL keys at serialization time by escapeFilterStateKeystoQuotedClickHouseKeyExpressiontoClickHouseKeyExpression. That serialization step had no knowledge of column types and unconditionally rewrote every dot-form sub-key to Map bracket form (Col['key']). For a Map column that is correct; for a JSON column it produces SQL ClickHouse rejects.

Fix

Thread the set of JSON-typed columns through the serialization path so a sub-key of a JSON column renders as dot access with backtick-quoted segments (ResourceAttributes. + `region`) instead of bracket access:

  • toClickHouseKeyExpression / toQuotedClickHouseKeyExpression now accept an optional jsonColumns set; a JSON base column routes through mergePath's JSON branch. Map columns keep their existing bracket behavior (including the HDX-4369 numeric-sub-key guard).
  • escapeFilterStateKeys and useSearchPageFilterState accept and forward jsonColumns (via a ref, matching the existing knownColumns pattern).
  • DBSearchPage derives the JSON column set from the active source's columns and passes it in.
  • The sidebar facet-value queries (DBSearchPageFilters and its hooks.ts) are made JSON-aware too, so facet counts / "Load more" respect an active JSON filter without emitting invalid SQL.

The value round-trips: the persisted ResourceAttributes. + `region` cleans back to ResourceAttributes.region in FilterState, so re-serialization is stable.

Tests

  • utils.test.ts: JSON-aware cases for toClickHouseKeyExpression / toQuotedClickHouseKeyExpression (dot access, nested paths, fallback to bracket when not JSON / no set provided).
  • jsonAddToFilter.pipeline.test.ts: end-to-end pipeline coverage for the reported HDX-5085 exclude scenario, asserting the exact corrected condition, that the illegal ResourceAttributes[' bracket subscript is never emitted, valid-SQL checks, and a parseQuery round-trip.

All app unit tests for the affected areas pass (146 tests across 4 suites); yarn lint reports 0 errors and tsc --noEmit is clean.

Screenshots or video

N/A — no visual UI changes; this fixes the generated query for JSON-column filters.

How to test on Vercel preview

Preview routes: /search

Steps:

  1. Open /search against a source whose table has a native JSON column (e.g. ResourceAttributes).
  2. Expand a row, find a JSON sub-attribute (e.g. ResourceAttributes.region), and click Add to Filters.
  3. In the filters sidebar, click Exclude on one of that field's values.
  4. Verify the results query runs without a ClickHouse arrayElement/JSON error, and that the generated SQL uses ResourceAttributes. + `region` (dot access) rather than ResourceAttributes['region'].

References

  • Linear Issue: HDX-5085
  • Related PRs:

Linear Issue: HDX-5085

Open in Web Open in Cursor 

Co-authored-by: Drew Davis <pulpdrew@gmail.com>
@vercel

vercel Bot commented Aug 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hyperdx-oss Ready Ready Preview Aug 11, 2026 3:18pm
hyperdx-storybook Ready Ready Preview Aug 11, 2026 3:18pm

Request Review

@changeset-bot

changeset-bot Bot commented Aug 11, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: fc3afef

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@hyperdx/app Patch
@hyperdx/api Patch
@hyperdx/otel-collector Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@greptile-apps

greptile-apps Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR threads native JSON-column metadata through search-filter and facet serialization so JSON sub-keys use ClickHouse dot access while Map sub-keys retain bracket access.

  • Derives JSON-column sets from active source metadata.
  • Extends filter and facet serialization helpers with JSON-aware behavior.
  • Adds utility and pipeline coverage for JSON sub-key filtering and round trips.
  • Leaves facet serialization able to run before JSON metadata resolves.

Confidence Score: 4/5

The facet query paths should be gated on successful JSON-column metadata loading before this PR is merged, because they can still emit the invalid SQL this change is intended to eliminate.

Facet and Load more queries execute while JSON metadata can still be undefined, causing native JSON sub-keys to fall back to rejected Map bracket syntax; the test-placement issue is independently non-blocking.

Files Needing Attention: packages/app/src/components/DBSearchPageFilters/hooks.ts, packages/app/src/components/DBSearchPageFilters.tsx, and the changed DBSearchPageFilters test files

Important Files Changed

Filename Overview
packages/app/src/components/DBSearchPageFilters/hooks.ts Makes facet and Load more serialization JSON-aware, but conflates loading or failed metadata with a confirmed empty JSON-column set.
packages/app/src/components/DBSearchPageFilters/utils.ts Adds schema-directed JSON dot-access serialization while preserving existing Map bracket behavior.
packages/app/src/searchFilters.tsx Threads JSON-column metadata through filter-state serialization using stable refs.
packages/app/src/DBSearchPage.tsx Derives JSON columns from active source metadata and supplies them to search-filter state.
packages/app/src/components/DBSearchPageFilters.tsx Supplies JSON metadata to displayed facet serialization, with the same unresolved-metadata fallback used by the hooks.
packages/app/src/components/DBSearchPageFilters/jsonAddToFilter.pipeline.test.ts Adds end-to-end serialization and round-trip coverage but remains outside the required test directory.
packages/app/src/components/DBSearchPageFilters/utils.test.ts Adds focused JSON-versus-Map accessor tests but remains outside the required test directory.

Sequence Diagram

sequenceDiagram
  participant UI as Search filters
  participant Meta as Column metadata
  participant Serializer as Filter serializer
  participant CH as ClickHouse
  UI->>Meta: Request JSON column names
  Meta-->>UI: Loading / undefined
  UI->>Serializer: Serialize facet key with empty JSON set
  Serializer-->>UI: ResourceAttributes['region']
  UI->>CH: Execute facet query
  CH-->>UI: Reject bracket access on JSON
  Meta-->>UI: ResourceAttributes is JSON
  UI->>Serializer: Re-serialize with JSON set
  Serializer-->>UI: ResourceAttributes.`region`
Loading

Fix All in Claude Code Fix All in Conductor Fix All in Cursor Fix All in Codex

Reviews (1): Last reviewed commit: "fix(app): serialize JSON column filters ..." | Re-trigger Greptile

Comment on lines +64 to +67
// render as dot access rather than the bracket access ClickHouse rejects on a
// JSON column. HDX-5085.
const jsonColumnsSet = useMemo(
() => new Set(jsonColumns ?? []),

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.

P1 Unresolved metadata restores invalid access

When JSON-column metadata is loading or fails, jsonColumns ?? [] treats every column as non-JSON while facet and Load more queries remain enabled. Native JSON sub-keys are therefore serialized with Map bracket access, causing ClickHouse to reject the facet query with an arrayElement-on-JSON error; the initial request shows an error notification, while Load more silently returns no additional values.

Fix in Claude Code Fix in Conductor Fix in Cursor Fix in Codex


// HDX-5085: filtering a sub-key of a *native* JSON column (e.g. from clicking
// "Add to Filters" then "Exclude" in the sidebar) must serialize as dot access,
// not the bracket access ClickHouse rejects on a JSON column with "First

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.

P2 Tests remain outside test directory

The new pipeline tests, along with the added cases in utils.test.ts, are located beside the component implementation rather than in the repository-required __tests__ directory. This conflicts with the prescribed Jest test organization and makes test discovery less consistent for maintainers.

Context Used: AGENTS.md (source)

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!

Fix in Claude Code Fix in Conductor Fix in Cursor Fix in Codex

@github-actions

Copy link
Copy Markdown
Contributor

E2E Test Results

All tests passed • 277 passed • 1 skipped • 1001s

Status Count
✅ Passed 277
❌ Failed 0
⚠️ Flaky 0
⏭️ Skipped 1

Tests ran across 4 shards in parallel.

View full report →

@pulpdrew pulpdrew closed this Aug 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants