feat(tesseract): default value filters for views (CORE-357)#10892
Conversation
Adds a view-only top-level `filters` field accepting entries with `member`, `operator`, `values`, and optional `unless`. Wires the new member/values/unless paths into the JS and YAML transpilers, and rejects `filters` on regular cubes via Joi's unknown-key validation.
Wires the view-only `filters` field (added to the Joi schema in a703f81) end-to-end: - CubeEvaluator.prepareViewFilters resolves `member`/`unless` against the view's includedMembers (short, real-cube and view-prefixed paths all normalize to the real cube path) and stringifies `values` to match the existing FilterItem.values contract. - New ViewFilterDefinition bridge in cubesqlplanner (static-only, no trait fields) plus an optional `filters()` getter on CubeDefinition. - MockViewFilterDefinition and `filters` field on MockCubeDefinition for Rust-side test fixtures. - backend-native bridge_registry registration with fixture, expected field set and round-trip coverage for the new struct.
…ompiler Default-value filters declared on a view (CORE-357) are now materialized into the query during `QueryPropertiesCompiler::compile_filters`: - Active views are detected from compiled MemberSymbols (`compiled_path().cube_name()` for every dimension / time-dim / measure / filter-member). Each view's `filters()` becomes a candidate. - A candidate is dispatched through the existing `FilterCompiler::add_item` so default filters share the same operator/typing path as explicit ones, and get routed into the right bucket (dimension / measure / time-dim). - `unless` is filter-only: a member referenced only by projection does not release the default, since changing what columns the user selects must not silently change the row set. Only an explicit filter on the same member overrides the default. JS evaluator (CubeEvaluator.prepareViewFilters) resolves `member` / `unless` to view-scoped paths (`<view>.<member>`) so they line up with `MemberSymbol::full_name` on the Rust side. Test coverage: - Unit (cube_evaluator-level): default filter applies when view is active; projection alone does not trigger `unless`; explicit filter on the unless-member releases the default; filter applies when unless-member is absent; filter applies even when member is in dimensions if `unless` is not declared. - Postgres integration: virtual `type: switch` currency dim plus a `case`-switch measure on `country`; covers union-collapse via default filter, projection-not-triggering-unless, explicit-filter-overrides, default-filter-with-switch-measure, default-filter-when-unless-member- is-absent. The Postgres seed (`view_default_filters_tables.sql`) deliberately ships without a `currency` column — currency is purely virtual; `country` drives the switch-case measure.
New file scaffolded on calendars.test.ts: a single inline YAML model with both a real string dimension (`country`) and a virtual `type: switch` dimension (`currency`), each exposed through two views — `_unconditional` and `_with_unless`. Tests run through the full JS pipeline (Joi schema, YAML transpiler, CubeEvaluator.prepareViewFilters, BaseQuery, Tesseract planner) against testcontainers Postgres, gated on `nativeSqlPlanner`. Six cases — three per filter flavour: default applies without `unless`, projection alone does not release the default, an explicit filter on the unless-member overrides the default. YamlCompiler fix in the same commit: `filters.[N].member` (string) and `filters.[N].unless` (array) are now wrapped as f-string literals, like `values`. They are member references in the view's own namespace, not Python expressions — and the view's `includedMembers` are not resolvable at transpile time, so the previous default path treated names like `country` as undefined identifiers and blew up at runtime with `country is not defined`.
|
Claude finished @waralexrom's task in 3m 32s —— View job Code Review: feat(tesseract): default value filters for views (CORE-357)
Overall AssessmentThis is a well-structured feature addition with clean layer separation — schema validation (Joi), reference resolution (CubeEvaluator), bridge definition (Rust What Looks Good
Issues Found1. Segments not included in view activation (filed as inline comment)
2. Missing The method relies on Minor Observations
Test Coverage Summary
Solid work overall. The segments gap is the one functional concern worth addressing before merge. |
|
Claude finished @waralexrom's task in 2m 59s —— View job Code Review: feat(tesseract): default value filters for views (CORE-357)
This is a well-structured PR that adds view-level default value filters across the full stack (schema validation → JS evaluator → Rust bridge → Tesseract planner). The implementation is clean, the design decisions are well-reasoned, and the test coverage is thorough. Below are my findings. Architecture & DesignThe overall design is sound:
The Issues & Suggestions1.
|
| Layer | Tests | Verdict |
|---|---|---|
| Schema validation (Joi) | 6 tests: valid filter, valid with unless, set operator, missing values, missing member, invalid operator, non-view cube rejection | Thorough |
| JS evaluator (CubeEvaluator) | 5 tests: reference resolution (short/real-path/view-path), member-not-included error, unless-not-included error, fully-qualified non-included path error, mixed-type value coercion | Thorough |
| Rust unit tests | 5 tests: default applies, unless + projection keeps filter, unless + explicit filter releases, unless member absent, no-unless + projection | Thorough |
| Rust integration (snapshot) | 5 tests with Postgres execution: switch union collapse, unless projection, explicit override, unless member untouched, switch-case measure | Thorough |
| E2E Postgres | 6 tests: real dimension + switch dimension, each with unconditional / unless+projection / unless+explicit filter | Thorough |
| Bridge coverage | Fixture + fields meta for viewFilterDefinition added |
Complete |
Edge cases covered:
- Null values in filter values array (coerced correctly)
- Mixed types (numbers, booleans, strings, null) in values
set/notSetoperators without values- Members referenced by short name, cube-qualified path, and view-qualified path
- Members not included in the view (error)
- Unless references not included in the view (error)
One scenario not explicitly tested: Multiple default filters from the same view where one filter's unless member is also another filter's member. The current code handles this correctly (unless only checks explicit user filters, not other defaults), but an explicit test would document this behavior.
Security
No security concerns. The feature:
- Only operates on server-side schema definitions, not user input
- Values are coerced to strings matching the existing
FilterItem.valuescontract - Member references are validated against
includedMembersbefore reaching the planner - No SQL injection risk — filters go through the same
FilterCompilerpath as regular filters
Summary
This is a high-quality PR. The implementation is well-layered, the design decisions (especially filter-only unless) are clearly documented with inline comments explaining the why, and the test coverage is comprehensive across all layers. The code is clean and follows existing patterns in both the TypeScript and Rust codebases.
Verdict: Looks good to merge. The issues noted above are low-severity observations rather than blockers.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #10892 +/- ##
========================================
Coverage 78.86% 78.87%
========================================
Files 470 470
Lines 92289 92483 +194
Branches 3435 3448 +13
========================================
+ Hits 72788 72948 +160
- Misses 18999 19031 +32
- Partials 502 504 +2
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Summary
Adds support for view-level default value filters (CORE-357). A view can now declare:
Whenever the view is touched by a query, the default filter is added; an explicit user filter on the same member can release it via
unless. The motivating customer case is aswitchdimension where the planner otherwise rolls up across all switch values.