Skip to content

feat(app): add faceted filters to the sessions page - #2992

Open
vinzee wants to merge 1 commit into
hyperdxio:mainfrom
vinzee:add-session-filters
Open

feat(app): add faceted filters to the sessions page#2992
vinzee wants to merge 1 commit into
hyperdxio:mainfrom
vinzee:add-session-filters

Conversation

@vinzee

@vinzee vinzee commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Bring the search page's faceted filter sidebar to /sessions and wire the selected filters into the session aggregation query.

  • Add a SessionFilters wrapper around DBSearchPageFilters that hides the analysis-mode tabs, denoise, and root-spans-only toggles that don't apply to sessions.
  • Persist sidebar filters in the URL and thread them through useSessions so they constrain the trace aggregation alongside the free-text query.
  • Scope both the facet sidebar and the aggregation to RUM session spans:
  • Add unit tests covering the new sessions behavior.

Screenshots or video

The Sessions Tab now has a sidebar with filters:
image

How to test on Vercel preview

Preview routes: /sessions

Steps (/sessions):

  1. Open /sessions.
  2. In the "Data Source" dropdown (combobox labeled "Data Source"), confirm a session source (labeled "Sessions") is selected; if it is not, open the dropdown and choose the "Sessions" source.
  3. Wait for the results area (data-testid "session-card-list") to render at least one session card (each card has a data-testid beginning with "session-card-").
  4. Confirm the filters sidebar is visible to the left of the results with a "Filters" heading.
  5. Confirm the sidebar is populated: at least one facet group is listed (for example a group labeled "ServiceName") and the text "No filters available" is NOT shown.
  6. Click the "ServiceName" facet group header to expand it, then click the first value's checkbox inside its panel.
  7. Confirm an active filter pill appears above the results list and the page URL gains a "filters=" query parameter.
  8. Click "Hide filters", confirm the sidebar collapses, then click "Show filters" and confirm the sidebar reappears.
  9. Verify the results area (data-testid "session-card-list") re-rendered after the filter was applied and no red error notification was shown.

References

  • Linear Issue: n/a
  • Related PRs: n/a

@changeset-bot

changeset-bot Bot commented Aug 25, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 1901232

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

@vercel

vercel Bot commented Aug 25, 2026

Copy link
Copy Markdown

@vinzee is attempting to deploy a commit to the HyperDX Team on Vercel.

A member of the Team first needs to authorize it.

@greptile-apps

greptile-apps Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR adds a sessions-specific faceted-filter sidebar, persists selected filters in the URL, and applies them to RUM session aggregation.

  • Reuses the search-page facet machinery while hiding analysis-only controls.
  • Builds facet queries from the selected trace source and scopes them to RUM session spans.
  • Adds URL-filter validation and unit coverage for the wrapper and service-scope helper.
  • Introduces a session-source-derived service scope intended to reduce trace-query cost.

Confidence Score: 3/5

The PR is not yet safe to merge because service scoping can hide valid sessions and source changes can apply stale filters to the newly selected source.

The trace aggregation still treats a successful but incomplete session-derived service list as authoritative, while the source-switch path preserves filters generated for the previous trace schema; both paths can return missing or incorrect session results.

Files Needing Attention: packages/app/src/sessions.ts, packages/app/src/SessionsPage.tsx

Important Files Changed

Filename Overview
packages/app/src/SessionsPage.tsx Adds URL-backed facet state and the sessions sidebar, but source changes still carry filters from the previously selected trace source.
packages/app/src/sessions.ts Applies faceted filters and a service-scope optimization to session aggregation, while the optimization can still exclude valid trace-derived sessions when its lookup is partially incomplete.
packages/app/src/components/DBSearchPageFilters.tsx Adds optional presentation and exact-facet-mode controls used by the sessions wrapper.
packages/app/src/components/SessionFilters.tsx Provides a thin sessions-specific configuration of the shared faceted-filter sidebar.
packages/app/src/tests/sessions.test.ts Covers filter URL validation and service-scope serialization, but not the outstanding partial-scope behavior.
packages/app/src/components/tests/SessionFilters.test.tsx Verifies that the sessions wrapper forces the intended sidebar options.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  User[Session filters] --> URL[URL and form filter state]
  URL --> Page[SessionsPage]
  Page --> Facets[Session facet query]
  Page --> Hook[useSessions]
  SessionSource[Session source service lookup] --> Scope[ServiceName scope]
  Scope --> Hook
  Hook --> TraceQuery[RUM trace aggregation]
  TraceQuery --> Results[Session cards]
Loading

Reviews (4): Last reviewed commit: "feat(app): add faceted filters to the se..." | Re-trigger Greptile

Comment thread packages/app/src/sessions.ts Outdated
Comment thread packages/app/src/SessionsPage.tsx
@github-actions

github-actions Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Deep Review

Faceted filters for /sessions: URL-persisted sidebar filters threaded into the RUM trace aggregation, plus a new service-scope optimization on the ClickHouse query and a shared-component refactor of DBSearchPageFilters. Reviewed the single PR commit (7 files). SQL escaping in buildServiceScopeFilters was independently verified safe — no injection finding.

🔴 P0/P1 -- must fix

  • packages/app/src/sessions.ts:234 -- The service-scope optimization turns a non-empty DISTINCT ServiceName list from the session source into a hard ServiceName IN (...) predicate on the trace aggregation, and the try/catch only falls back to an unscoped scan on an empty result or thrown query — a partial-but-non-empty enumeration silently excludes any service that has qualifying RUM trace spans but no session-source row in the 30-day window (trace retention > session retention, projection lag, or a divergent service-name column), dropping those sessions with no error.
    • Fix: Derive the service scope from the trace source itself, or apply it as a non-excluding index hint rather than a filtering predicate, and skip the scope when the enumeration window is not fully covered by session-source retention.
    • correctness, adversarial, previous-comments

🟡 P2 -- recommended

  • packages/app/src/SessionsPage.tsx:492 -- On a session-source switch the correlated trace source changes, but submitOnSourceChangeonSubmit forwards the persisted appliedConfig.filters verbatim to the new trace query; a filter referencing a column the new source lacks yields a mismatched/empty result, and since the useSessions error is never destructured or rendered the list shows empty, indistinguishable from "no matches".
    • Fix: Clear or re-validate persisted filters against the new trace source's known columns when the session source changes, and surface query errors instead of rendering a silent empty list.
    • correctness, adversarial, previous-comments
  • packages/app/src/sessions.ts:230 -- The DISTINCT ServiceName enumeration lives inside the useSessions queryFn whose queryKey includes where/whereLanguage/filters, yet its inputs depend only on the session source and date range, so every facet toggle or WHERE edit re-runs an identical serialized 30-day scan before the main aggregation can start.
    • Fix: Hoist the enumeration into its own useQuery keyed only on session-source id and bucketed date range with a longer staleTime so facet/where changes reuse the cached scope.
    • performance
  • packages/app/src/sessions.ts:230 -- The new query orchestration is untested: the service-scope try/catch fallback to an unscoped scan, the 30-day lookback window, the empty-result path, and the hasSearchQuery change that now flips the HAVING/CTE branch for facet-only queries all lack coverage, while only the pure buildServiceScopeFilters helper and the URL parser are tested.
    • Fix: Add queryFn-level tests (mocking renderChartConfig and the ClickHouse client) asserting the unscoped fallback on enumeration failure, the widened lookback range, the empty-list path, and the filters-only HAVING branch.
    • testing, correctness, adversarial
🔵 P3 nitpicks (3)
  • packages/app/src/components/DBSearchPageFilters.tsx:1065 -- Making analysisMode, setAnalysisMode, showDelta, denoiseResults, and setDenoiseResults individually optional with silent defaults (voidFunc) lets a future caller render the analysis-mode tabs while omitting setAnalysisMode, so onChange no-ops with no compile error.
    • Fix: Keep the analysis-mode props required together or group them under a single optional object so they can only be supplied or omitted as a unit.
    • kieran-typescript, maintainability
  • packages/app/src/sessions.ts:184 -- The DISTINCT ServiceName query has no LIMIT, so an unexpectedly high service cardinality (misconfigured expression or shared table) produces a large IN predicate that widens rather than prunes the trace range.
    • Fix: Add a defensive LIMIT and skip the scope filter when the count hits the cap, falling back to the unscoped scan.
  • packages/app/src/sessions.ts:155 -- The ~80-line inline service-scope enumeration block sits inside queryFn and is not independently testable.
    • Fix: Extract it into a module-level helper (mirroring the already-extracted buildServiceScopeFilters) so the fallback path becomes unit-testable.

Reviewers (8): correctness, security, performance, testing, maintainability, kieran-typescript, adversarial, previous-comments.

Testing gaps:

  • No coverage of the service-scope fallback, 30-day lookback, or empty-result paths in useSessions.
  • No coverage of the filters-only hasSearchQuery branch flipping the HAVING/CTE behavior.
  • No coverage of source-switch behavior with a persisted filter incompatible with the new trace source.

@vinzee
vinzee force-pushed the add-session-filters branch from fa84b72 to d81257b Compare August 25, 2026 01:22
Comment thread packages/app/src/sessions.ts
@vinzee
vinzee force-pushed the add-session-filters branch from d81257b to 2b5bdd1 Compare August 25, 2026 01:30
Bring the search page's faceted filter sidebar to /sessions and wire the
selected filters into the session aggregation query.

- Add a SessionFilters wrapper around DBSearchPageFilters that hides the
  analysis-mode tabs, denoise, and root-spans-only toggles that don't
  apply to sessions.
- Persist sidebar filters in the URL and thread them through useSessions
  so they constrain the trace aggregation alongside the free-text query.
  Validate the ?filters= param against FilterSchema with a [] default so a
  stale/hand-edited param resolves to the default instead of white-screening.
- Speed up the aggregation: resolve the RUM service name(s) from the
  session source and constrain the otel_traces scan by ServiceName,
  turning a ~100k-mark full scan into a small primary-key range. Enumerate
  those services over a window wider than the selected range so sessions
  that started just before the range aren't dropped, and on any failure or
  when no services resolve, fall back to the unscoped (still correct) scan.
- Harden the ServiceName scope: escape ingested names with escapeSqlString
  (backslash then quote) in a dedicated buildServiceScopeFilters helper and
  build it inside the try/catch, so a crafted/backslash name can't inject
  into or break the aggregation.
- Fix the empty filter sidebar: scope facet queries to RUM session spans
  via a rum.sessionId indexHint and force exact facet mode, so the
  'show all values' path can't strip the scope and sample the whole trace
  table (which timed out and left the sidebar empty).
- Portal the source-select dropdown so it renders above the filter
  sidebar instead of being clipped by its stacking context.
- Add unit tests: SessionFilters prop forwarding, buildServiceScopeFilters
  escaping/fallback, and the ?filters= param validator.

Co-authored-by: Cursor <cursoragent@cursor.com>
@vinzee
vinzee force-pushed the add-session-filters branch from 2b5bdd1 to 1901232 Compare August 25, 2026 14:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant