feat: unify filter sidebar and query input into single where clause - #2795
feat: unify filter sidebar and query input into single where clause#2795Official-Krish wants to merge 3 commits into
Conversation
🦋 Changeset detectedLatest commit: e4e377f The changes in this PR will be included in the next version bump. This PR includes changesets to release 4 packages
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 |
|
@Official-Krish is attempting to deploy a commit to the HyperDX Team on Vercel. A member of the Team first needs to authorize it. |
Greptile SummaryThe PR unifies search-sidebar filters and query text around the
Confidence Score: 4/5The PR is not yet safe to merge because same-field predicates that the sidebar cannot faithfully represent remain active when users select replacement values. Repeated SQL predicates are preserved and then combined with the newly selected value, while Lucene modifier terms and negated ranges likewise remain alongside replacement predicates; the sidebar can therefore display a selection that does not reflect the effective query and can unexpectedly return no rows. Files Needing Attention: packages/common-utils/src/filters.ts
|
| Filename | Overview |
|---|---|
| packages/common-utils/src/filters.ts | Adds the core Lucene/SQL parsing and rewriting machinery, but unrepresentable and repeated same-field predicates can remain active alongside sidebar replacements. |
| packages/app/src/DBSearchPage.tsx | Makes where canonical, migrates legacy filters, and wires query parsing, language translation, and sidebar rewrites into search-page state. |
| packages/app/src/searchFilters.tsx | Adds adapters between where-clause facet state and the existing sidebar filter API. |
| packages/common-utils/src/tests/filterRoundTrip.test.ts | Adds broad regression coverage for parsing, replacement, migration, Boolean grouping, modifiers, ranges, and repeated SQL predicates. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart LR
W[Where clause] --> P[Parse facet state]
P --> S[Filter sidebar]
S --> R[Replace facet clauses]
R --> W
L[Legacy filters] --> M[Merge into where]
M --> W
Reviews (4): Last reviewed commit: "fix(search): unified sidebar-where claus..." | Re-trigger Greptile
pulpdrew
left a comment
There was a problem hiding this comment.
Hey @Official-Krish, thanks for the PR, this is definitely a feature we'd love to see!
However, there are some things that I think will need to be addressed.
Bugs in WHERE string generation (Lucene)
NOT term AND ServiceName:"api"→ click a filter →term AND (ServiceName:"api" OR ServiceName:"accounting"). TheNOTis stripped, so the query is inverted.ServiceName:"api" OR SeverityText:"error"→ click a filter →ServiceName:"api" AND SeverityText:"error".ORsilently becomesAND.ServiceName:("api" OR "web") AND term→ click a filter →("api" OR "web") AND term AND (ServiceName:"api" OR ServiceName:"web"). The field name is dropped, so a field-scoped search becomes a full-text search, and the clause is duplicated.Duration:[* TO 100] AND ServiceName:"api"→ click a filter →ServiceName:"web". The range clause is deleted because it starts at character offset 0 (falsy-offset check).Duration:{10 TO 20}→ click a filter →Duration:[10 TO 20]. Exclusive bounds silently become inclusive.msg:"hello"~2 AND ServiceName:"api"→ click a filter → the~2proximity modifier is dropped (same for^3boost).level:error(unquoted, the idiomatic Lucene form) → clickwarnin the sidebar →level:error AND level:"warn". Always returns zero rows.- Click a sidebar value for an attribute key containing
(,",{or[(e.g.LogAttributes['a(b)']) → the emitted text fails to parse, so the query breaks and cannot be fixed from the sidebar. A key containing a space (LogAttributes['my key']) parses but becomes free text plus a nonexistent field. "timeout)" OR "error"→ click a filter →"timeout)" OR "error" AND level:"x". The paren inside the quoted value defeats the top-level-OR check, so the parens thatf89fc39c9added are omitted andANDbinds tighter.term1 OR term2→ click a filter four times →(term1 OR term2) AND .... One space is added per interaction, growing the query text and URL without bound.
Bugs in WHERE string generation (SQL)
ServiceName = 'a' OR ServiceName = 'b'→ click a filter →ServiceName = 'a' OR ServiceName = 'b' AND SeverityText IN ('error'). Same precedence bugf89fc39c9fixed for Lucene; the filter applies only to the'b'branch.ServiceName = 'a' -- temp note→ click a filter →ServiceName = 'a' -- temp note AND SeverityText IN ('error'). The new predicate lands inside the comment: the checkbox looks applied but nothing is filtered.msg = 'x AND y IN ('(unbalanced paren in a string) or`it's` = 1(quote in a quoted identifier) → click the same filter twice →... AND ServiceName IN ('a') AND ServiceName IN ('b'). Paren counting runs before the in-string guard, so conjunct splitting stops and clauses pile up. Unchecking stops working; new selections have no effect.- Same duplication for any column needing backticks: click a value on a
service-namecolumn twice →`service-name` IN ('a') AND `service-name` IN ('a', 'b'). The matcher compares quoted keys against unquoted ones. ServiceName IN (SELECT name FROM t) AND foo = 1→ the sidebar renders a checkbox labelledSELECT name FROM t; click any filter →foo = 1 AND ServiceName IN ('b'). The subquery is destroyed.
Backwards incompatibility (existing URLs and saved searches)
- Open a bookmarked URL / saved search with
whereLanguage=lucene,where=ServiceName:"api"andfilters=[SeverityText IN ('error')]→ migration producesSeverityText:"error". Thewhereclause's own filter is destroyed and written back to the URL.
UX regressions
- It's common to start querying in lucene, potentially adding filters, then switch to SQL when a more complex condition is needed. Previously, all filters persisted when switching languages. Now all filters are lost until the user re-selects them or re-writes the WHERE input, which is stuck in the previous language. Ideally we should not lose filters when switching languages, the filters should transfer over to the new language.
- Type
service:"(any incomplete query) → all checkboxes clear, and clicking a filter does nothing at all while still triggering a re-query. No error or explanation is shown. NOT ServiceName:"api"orterm AND NOT ServiceName:"api"→ the sidebar showsapias checked, i.e. the opposite of what the query does.ServiceName:"api" OR SeverityText:"error"→ the sidebar shows both as checked, implying anAND.
Performance regressions
- Type in the search input → the whole filter sidebar re-renders per keystroke.
handleSetFiltersnow depends on the watchedwherevalue, so all eight mutators get new identities and defeatmemo(DBSearchPageFiltersComponent). The search page is noticeably laggy when typing, with these changes.
|
Thanks for the review @pulpdrew! I'll work through these issues, push a revised implementation that addresses them, and update the PR shortly. |
38bc87a to
e4e377f
Compare
| // A key is only managed when it appears *exactly once* as a facet conjunct. | ||
| // If the same key appears in multiple conjuncts (e.g. `host IN ('a') AND host | ||
| // IN ('b')`) the user intentionally wrote a conjunction of two IN lists. | ||
| // Merging them into one IN list would change the semantics (intersection → | ||
| // union for scalar columns), so we leave both conjuncts untouched instead. |
There was a problem hiding this comment.
Replacement retains old SQL predicates
When a user changes a sidebar value for a field with repeated SQL predicates such as host IN ('a') AND host IN ('b'), the duplicate count excludes that field from replacement and appends the new predicate instead. The query becomes host IN ('a') AND host IN ('b') AND host IN ('c'), so the old restrictions remain active and the sidebar selection can return zero rows.
Summary
The filter sidebar and the query input box previously held independent state — selecting a value in the sidebar applied it to the search but never appeared in the query input, so the two could silently drift out of sync. This PR makes the where clause the single source of truth for both.
How it works:
New internals in common-utils/filters.ts:
level:"error").How to test on Vercel preview
Preview routes: /search
Steps:
References
Fixes #2751