Releases: boringdata/boring-semantic-layer
v0.3.14
boring-semantic-layer v0.3.14
Highlights
This release fixes joins on plain ibis backends (Snowflake, Databricks, BigQuery), unifies calc-measure compilation on an ibis-native classifier (ADR 0001), and includes several correctness fixes around joins and serialization.
- Fixed
ValueError: Don't know how to handle type <class 'ibis.expr.types.relations.Table'>when joining models on Snowflake / Databricks / BigQuery (#222, fixes #221) - Unified calc measures on the new ibis-native classifier (#262, ADR 0001 phases 1+2)
- Fixed grain mismatch when chaining
with_measuresover joined models (#261) - Fixed serialization content hashing to include BSL metadata (#264)
Why upgrade
- If you're on Snowflake, Databricks, BigQuery, or any other backend xorq doesn't wrap, joined-model queries now execute natively instead of crashing
- The new calc-measure compiler is faster, surfaces clearer errors on type-inference failures, and correctly preserves user casts
- Catalog workflows relying on tag content hashes will now invalidate correctly when BSL metadata changes
Included changes
Fixes
- #222 — handle plain ibis backends in join backend rebinding (fixes #221)
- #261 — preserve preagg + canonical backend through
with_measureson joins - #264 — use
HashingTagso BSL metadata contributes to content hash
Features / refactors
- #262 — unify calc measures on ibis-native classifier (ADR 0001 phases 1+2)
- New
calc_analyzerandcalc_compilermodules - Inline-reduction lift and runtime orchestration
- Windowed totals carried through aggregation (replaces cross-join totals)
- Tighter bare-ref detection, surfaced lift failures, asserted totals substitutions
- New
Upgrade
pip install --upgrade boring-semantic-layer==0.3.14
v0.3.13
boring-semantic-layer v0.3.13
Highlights
This release adds metadata support on model fields, expands xorq/TagHandler integration, improves MCP tooling, and fixes several round-trip and serialization correctness issues.
- Added
metadatafield onDimensionandMeasurefor arbitrary annotations (#245, #246) - Exposed BSL
TagHandlerviaxorq.from_tag_nodeentry point for catalog workflows (#235) - Added
reemitsupport onTagHandlerfor full catalog rebuild (#244) - Added MCP period comparison workflow (#238)
- Fixed join rebind guard for non-xorq backends (#242, #243)
- Fixed multiple round-trip and serialization correctness issues in xorq boundary and calc-measure handling
Why upgrade
- If you use
metadataon dimensions or measures, this is the release that adds it - If you're integrating BSL with xorq catalog workflows, the new
TagHandlerentry points make that cleaner - Several xorq serialization round-trip bugs are fixed — recommended if you serialize/deserialize semantic models
Included changes
Features
- #238 — MCP period comparison workflow
- #244 — TagHandler
reemitfor catalog rebuild - #245/#246 —
metadatafield onDimensionandMeasure - #235 — expose BSL
TagHandlerviaxorq.from_tag_node
Fixes
- #242/#243 — guard join rebind against non-xorq backends
- Fix: walk join branches in
tag_handler.extract_metadata - Fix: roundtrip model-level description through
to_tagged/from_tagged - Fix: preserve join cardinality on round-trip; fix pre-agg grain for derived dims
- Fix: treat
UnboundTableas leaf node in_reconstruct_table - Fix: use matching ibis module for
desc()in MCPsearch_dimension_values - Fix: broaden agg-method recognition, surface typos, pin ibis workaround
Refactors
- Introduce Predicate AST for filter expressions
- Tighten calc-measure AST and surface type-inference failures
- Route all xorq imports through single
_xorqshim - Convert ibis→xorq once at
SemanticModelconstruction - Dispatch reduction-type check on expr's ibis flavor
Install
pip install -U boring-semantic-layer==0.3.13v0.3.12
boring-semantic-layer v0.3.12
Highlights
This release focuses on join planning and reliability.
- Added deferred
join_onesupport for dimension lookups after aggregation (#220) - Added grain-aware
join_onebehavior viais_entitydimensions (#219) - Pruned unused dimension joins from generated queries for leaner SQL (#228)
- Fixed
demo_bsl_v2.pyso the example script works again (#236) - Added regression coverage for read-only DuckDB joined dimension introspection (#237, refs #232)
Why upgrade
If you use joined semantic models, this release improves correctness and efficiency:
- better handling of dimension lookups across joins
- better join behavior when entity grain is defined explicitly
- fewer unnecessary joins in compiled queries
- better protection against regressions around read-only DuckDB joined introspection (#232)
Included changes
- #219 — grain-aware
join_oneviais_entitydimensions - #220 — deferred
join_onefor dimension lookups after aggregation - #228 — prune unused dimension joins from generated queries
- #232 — regression coverage for read-only DuckDB joined introspection
- #236 — repair
demo_bsl_v2.py - #237 — add regression test coverage for read-only joined dimensions
Install
pip install -U boring-semantic-layer==0.3.12v0.3.11
What's Changed
- fix: route dimension-only queries to source table on joins (#225)
- Dimension-only queries (no measures) on joined models now route directly to the source dimension table
- Ensures dimension members with no matching fact rows are still returned
- Supports filters on the target dimension table
- Falls back to standard join path when filters reference other tables
Full Changelog: v0.3.10...v0.3.11
v0.3.10
What's New
Features
- HTTP server transport — Optional
bsl servecommand exposes BSL models over REST via FastAPI (pip install boring-semantic-layer[server]) (#214) - Per-dimension time grains —
time_grainsparameter lets each time dimension use a different grain (e.g.,{"order_date": "month", "ship_date": "quarter"}) (#214) - Short grain names — Core
query()now accepts"month","quarter", etc. alongside"TIME_GRAIN_MONTH"(#214) - Derived time dimensions — Auto-generate year/month/day parts from time dimensions via
derived_dimensions(#215, closes #213) - Plain ibis backend support — BSL works with any ibis backend (Databricks, ClickHouse, MySQL, etc.) without xorq wrapping (#217)
- Self-joins with aliases — Join the same model multiple times in YAML with different aliases (#205, addresses #114)
- YAML calculated_measures — Support
calculated_measuresand.all()in YAML definitions (#211)
Fixes
- Fix content hashing so models with different measures/dimensions produce different hashes (#209)
- Fix 15 broken links across documentation (#208)
- Fix model prefix behavior in
with_dimensions/with_measuresafter joins (#211)
Internal
- Convert
to_untagged()to return xorq-vendored ibis types consistently (#203)
v0.3.9
What's Changed
Features
- Measure filtering: Support filtering by measures in
query()(#178) - MCP tool: Add
search_dimension_valuesMCP tool for discovering dimension values
Refactoring
- Serialization: Move serialization into dedicated package with singledispatch architecture
- xorq vendored types: Convert
to_untagged()to return xorq-vendored ibis types at the leaf (#203) - Eliminate cloudpickle: Remove cloudpickle from xorq tag serialization (v2.0)
- Deduplicate: Consolidate deserialization, join deprecation, and xorq unwrapping
Bug Fixes
- Preserve mutated group-by columns in
join_manypre-aggregation (#187) - Add join methods to
SemanticFilterforfrom_configcompatibility (#186) - Support multi-level derived dimensions in filters (#182)
- Strip
VARCHAR(N)length from dtype strings in_make_schema(#183) - Make
join_oneuse left outer join by default (Malloy semantics) - Preserve unmatched left-join rows in pre-aggregation
- Resolve ambiguous field reference in multi-way joins with shared column names
- Propagate
is_right_of_manythrough nested joins in pre-aggregation - Compute actual ibis join depth for column rename suffix
- Resolve unprefixed measure names via suffix matching on joined models
- Split join expression in
from_taggedto prevent cross-join on round-trip
Performance
- Compute
COUNT DISTINCTon raw source table when possible
Full Changelog: v0.3.8...v0.3.9
v0.3.8
What's Changed
- fix: dim bridge for 3+ join_many arms by @hussainsultan in #165
- feat: deferred group by aggregate + fan/chasm trap by @hussainsultan in #164
- feat: structured resolver serialization by @hussainsultan in #170
- fix: resolve prefixed dimension/measure names on standalone models by @boringdata in #167
- feat: fix repr and require FDQN names by @hussainsultan in #171
- Fix multi-layer derived query resolution (dims, calcs, and t.all chaining) by @boringdata in #168
- fix: non-additive measure re-aggregation by @hussainsultan in #173
- fix: unwrap nested chart spec in dashboard renderer by @boringdata in #158
- fix: support coalesce and post-ops in YAML measure definitions by @Angel-RC in #160
- fix: SemanticAggregateOp.values/.schema fails with calc measures by @hussainsultan in #174
- fix: resolve .type() on derived dimensions and measures by @boringdata in #176
New Contributors
Full Changelog: v0.3.7...v0.3.8
v0.3.7
What's Changed
- feat: add CSS theming support via YAML frontmatter (#157)
Full Changelog: v0.3.6...v0.3.7
v0.3.6
What's Changed
- ECharts chart backend - New visualization backend with bar, line, pie, scatter, area charts and auto chart detection
- Dashboard renderer - 16-column CSS grid layout for markdown-based dashboards
- Altair remains default - ECharts available via
backend="echarts"
Full Changelog: v0.3.5...v0.3.6
v0.3.5
What's Changed
- fix: resolve time_grain infinite recursion with Deferred expressions by @benjaminsicard in #152
- Fix infinite recursion when using
time_grainwith Deferred dimension expressions - Fix timestamp comparison errors when using
time_rangewith certain database backends - Use
Dimension.__call__for time_range filters (properly resolves Deferred) - Add regression tests for Deferred expressions with
time_grainandtime_range
- Fix infinite recursion when using
Full Changelog: v0.3.4...v0.3.5