5.4 FilterState
学习目标
完成本节后,你将能够:
- 解释 Search Bar、facet sidebar、URL 和 query builder 为什么共享同一个
FilterState。 - 读懂
singleFilterdiscriminated union 和 operator contract。 - 修改筛选语法前知道要同步 grammar、validate、lower、reverse adapter 和 SQL lowering。
5.4.1 先给结论
FilterState 是 UI 筛选和服务端查询之间的结构化契约。它不是某个组件的本地状态。
当前 v4 events 的状态关系是:
text
URL state = FilterState + searchQuery + searchTypeSearch Bar 和 facet sidebar 都只是这个状态的编辑器。最终查询只认结构化状态,不认 UI 文本,也不认某个侧边栏内部对象。
5.4.2 数据流
Search Bar 有 draft state,但 committed state 仍然来自 URL/filter state。这样两个编辑器不会各自维护最终筛选真相。
5.4.3 singleFilter 结构
源码:packages/shared/src/interfaces/filters.ts
singleFilter 是按 type 区分的 union:
| type | value 形状 | operator |
|---|---|---|
datetime | Date | >、<、>=、<= |
string | string | =、contains、does not contain、starts with、ends with |
number | number | =、>、<、>=、<= |
stringOptions | string[] | any of、none of |
categoryOptions | string[] + key | any of、none of |
arrayOptions | string[] | any of、none of、all of |
stringObject | column + key + string | string operators |
numberObject | column + key + number | number operators |
boolean | boolean | =、<> |
null | empty string value | is null、is not null |
positionInTrace | root/first/last/nth | = |
eventsTableSingleFilter 又扩展了 events table 的 full-text matches operator,用在 string 和 stringObject filter 上。
5.4.4 Search Bar 不是第二套筛选系统
web/src/features/search-bar/README.md 给出的设计非常明确:
- Search Bar 是 grammar editor,不替代 facet sidebar;
- sidebar 的
FilterState加上 full-text search 仍是单一事实源; - bar 的 local store 只保存 draft;
filterStateToQueryText从 committed state 派生展示文本;- commit 时通过
planCommit -> validateQuery -> astToFilterState写回 FilterState; - 不创建新的 URL param,也不维护第二份 committed copy。
这解决的是“两个筛选入口如何保持一致”的问题。
5.4.5 Search Bar 不变量
| 不变量 | 含义 |
|---|---|
| 不静默丢弃 filter | grammar 不能表达的 filter 要保留或阻止 commit,不能偷偷删除。 |
| validate 和 lower parity | 红色错误状态、commit gate、AST lowering 必须用同一上下文。 |
| negation 不是 primitive | - 要 lower 到已有 inverse operator,如 none of、does not contain、is null。 |
| URL 是 canonical source | bar 和 sidebar 都从 URL/filter state 派生。 |
| 用户显式 filter 不被自动删除 | managed environment default 不能误删用户手写 filter。 |
| skipped filters 要可解释 | grammar 暂不支持的 shape 不能假装支持。 |
这些不变量比 UI 交互细节更重要。修改 grammar 时先验证这些规则。
5.4.6 FilterState 如何进入查询
FilterState 本身不是 SQL。它要经过服务端 lowering:
query builder 还会额外做:
- 自动 project filter;
- trace_id equality 的
xxHash32优化; events_core/events_full选择;- field set 选择;
- 排序与 primary key 贴合。
所以新增筛选字段时,不是只改 UI,还要让服务端知道字段类型、operator、ClickHouse 表达式和性能影响。
5.4.7 修改筛选能力的同步面
| 改动 | 同步位置 |
|---|---|
| 新增基础 filter 类型 | filters.ts、InMemoryFilterService、query lowering。 |
| 新增 events 字段 | search-bar field registry、FilterState column、query builder field map。 |
| 新增 operator | filterOperators、validate、adapter、reverse adapter、SQL lowering。 |
| 新增 score/metadata key 语法 | quoted segment、completion、adapter、reverse adapter。 |
| 改 full-text 行为 | searchQuery、searchType、ClickHouse search lowering。 |
| 改 URL shape | sidebar、search bar、saved views、recent searches、tests。 |
5.4.8 常见错误
| 错误 | 后果 |
|---|---|
| Search Bar 自己维护 committed filters | sidebar 和 URL 不一致。 |
| validate 支持但 lower 不支持 | Enter 无效或查询语义错。 |
| lower 支持但 reverse adapter 不支持 | URL reload 后 filter 消失或文本不一致。 |
| UI 新增 operator,SQL 没同步 | 查询结果和界面承诺不一致。 |
| 手写 SQL filter | 绕过 tenant filter 和统一 operator 语义。 |