Release notes¶
0.1.13¶
New features
- Levels of detail. An aggregate can now be computed at a grain other than
the query's. A field pins a fixed grain, such as
COUNT(DISTINCT event_date)per user, and becomes an axis, so "users active exactly N days" is one query. A metric pinsinclude,excludeorfixedrelative to the query's grouping: an average per user reported by channel, or a share whose total no filter narrows. See D-LOD. - Two facts aligned on a level of detail. When each fact has its own fixed field — how many days a user had a session, how many days they ordered — a conformed pairing (D-CONFORM) between the two makes them one axis, so a metric from either fact can be grouped by it.
- A level of detail over another dataset. A fixed field's base can now
aggregate a related dataset, such as each flight's open alerts, when the keys
meet through a declared many-to-one relationship or a conformed pairing. A
row with nothing on the other side counts 0, so
> 0and= 0in awhereanswer "has one" and "has none". See D-LOD across datasets. - A level of detail with a window of its own. When a fixed field's base is
a parameterized filter metric, the query's
paramsbind it, so "January actives with no February login" is one query: the model declares the window as parameters and the query supplies the days. See A level-of-detail field's base. - Detail queries read levels of detail.
dosi selectcan project, filter and order by a fixed field of its--fromdataset, and--parambinds that field's window. It also gains--distinctand--context-filter, which scopes the rollup the way a time range does. See Level-of-detail fields.
Enhancements
- The DATUS extension version is now 1.11. Models that declare an older version load as before.
- A
stringparameter may leave its domain open instead of listingallowed, for a date spelled the way its column stores it or a code the model cannot enumerate. It compiles with adatus_param_unboundedwarning and still binds as a quoted literal. See Parameterized metrics. - When every value a fixed rollup computes comes from a filter metric, such as
a field with a window of its own, the filter is now also the rollup's
WHERE, so a warehouse can prune partitions instead of scanning the whole table. The values are unchanged. list_dimensionsfor a metric now reports the metric's time column once, asmetric_time, instead of listing the column beside it. Other time columns are still listed, and the column remains a legalgroup_by.
Bug fixes
dosi validate --osi-basicreported a model as invalid when the upstream Ossie validator could not run at all, for instance because its Python libraries were missing. Anything that stops the validator from running is a notice, not a verdict, and the validator now provisions its own dependencies throughuvwhen it is installed.ROUNDin a field or metric expression rounded halves to even on PostgreSQL, MySQL, TiDB and ClickHouse, soROUND(0.5)was 0 there and 1 everywhere else, and a binned dimension put its .5 rows in a different bucket. It now rounds halves away from zero on every warehouse.- A
DATE_TRUNC('MONTH', ...)written into a model expression failed on MySQL and TiDB, which have no such function. It is now rewritten for them the way a query's own time grain is. - On ClickHouse, a time dimension stored as text failed the whole query when any of its values was NULL. NULL values now land in the NULL group, as on other warehouses.
list_dimensionsfor a metric hid the columns of a D-CONFORM pairing as foreign keys and listed a conformed dimension once per spelling. A paired column is now judged like any other field and listed once, under the metric's spelling.- A count metric used as a member of a compose with a window member read 0,
not NULL, for a group its fact had no rows for, so a weighted score such as
(… ) / store_countbecame0 / 0— NaN on DuckDB, an error on PostgreSQL. Only a count that is itself selected fills 0 (S-FAN-6); a count inside an expression now keeps NULL under a window stage too.
Behavior changes
- A metric or detail query that carries a key its type does not define is now
refused with
unknown_query_key, naming the keys it does define. Before, the key was silently ignored, so a misspelledcontext_filteror awheremeant aswhere_sqlreturned unfiltered numbers.
Known issues
- A division with arithmetic on either side, such as
(a + b) / cora / NULLIF(b, 0), still runs with the warehouse's own/: PostgreSQL, Trino and Redshift truncate integer division, and MySQL and TiDB keep four decimal places. Only a plainSUM(a) / SUM(b)is converted to floating point; elsewhere writeCAST(... AS DOUBLE)on the numerator. - 0.1.13 still reads only the older document format, the
semantic_model:list. See the 0.1.11 note below.
0.1.12¶
New features
- Ranked scorecards. A window metric can now rank a ratio or a composed
metric, and it can count its whole partition with
frame.scope: "partition"(the N beside a rank). Windows can stack up to three levels, such as ranking markets within a brand and then weighting scores within a market.order.nullspins where NULLs sort, which otherwise differs between warehouses. See D-WINDOW. - Filtering on metrics.
where_sqlcan now name a selected metric (revenue > 1000). It filters the result rows, like SQLHAVING. The newcontext_filter(CLI--context-filter, REST, MCP and Python) always filters the input rows. Use it to rank within a subset. See Filters. - How a metric is built, and what a query must group by. The metric catalog
in the CLI, REST, MCP
describe_metricand Python now has two new fields.derive_exprgives a composed metric's formula.required_dimensionslists the fields a query has to group by for the metric's windows to mean what they define.
Enhancements
- A composed metric whose members use different time columns on the same table
used to be refused with
MetricTimeConflict. Now each time column gets its own branch, so every metric returns what it returns when queried alone. See D-TIME. - Oracle: a missing or incompatible Instant Client now fails at once with
DPI-1047and a link to the setup guide, instead of a 30-second pool timeout. The Oracle connector guide has been rewritten around installation and troubleshooting. - The DATUS extension version is now 1.9. Models that declare an older version load as before.
Bug fixes
- A window's partition could change depending on the other metrics in the query. Adding a cumulative metric, for example, made another metric's daily rank span every day.
- A dimension-table measure placed on two fact tables in one window query read the wrong fact's numbers on DuckDB and was rejected by StarRocks.
- The
dosi lineage viewlegend now draws each edge kind the same way the graph does.
Behavior changes
- In a window query, a
where_sqlcondition on group-by fields now filters the result instead of the rows being ranked. The rows you keep also keep the ranks and partition counts of the whole population. To get the 0.1.11 numbers, move the condition tocontext_filter. - A window whose
partition.excludenames a field the query does not group by is now refused withwindow_exclude_not_grouped. Before, every row ranked first in a partition of one. Add the field togroup_by;required_dimensionslists the fields a query needs. - A dimension count beside a fact measure, such as
COUNT(DISTINCT stores.store_id)in repairs per store, now counts every store, not only the stores that have a repair. The ratio's value changes. To count only the entities seen in the fact, count the fact's key:COUNT(DISTINCT repairs.store_id). See Fan-out protection.
Known issues
- A division with arithmetic on either side, such as
(a + b) / cora / NULLIF(b, 0), runs with the warehouse's own/. PostgreSQL, Trino and Redshift truncate integer division, and MySQL and TiDB keep four decimal places. Only a plainSUM(a) / SUM(b)is converted to floating point for you. Until that changes, writeCAST(... AS DOUBLE)on the numerator. - 0.1.12 still reads only the older document format, the
semantic_model:list. See the 0.1.11 note below.
0.1.11¶
New features
- Recommended dimensions for each metric.
dosi list dimensions --metriclists only the fields worth grouping that metric by, recommended ones first. The REST API, MCP tool, PythonEngine.dimensions(metric=)and the lineage graph return the same list. Dosi leaves out keys, join columns and the columns a metric aggregates. A field can override that withis_dimension(D-DIM):falseto hide an id or free-text column,trueto include a numeric column. The list is only a recommendation:--group-bystill accepts any reachable field, and the generated SQL is unchanged.dosi validatesuggests where a declaration is missing or contradicts the model;--strictturns those suggestions into errors. - Ratios across two fact tables. Two fact tables that share a date or other
column without one belonging to the other, such as logins and payments, can
now be paired with
cardinality: many_to_many(D-CONFORM). A metric like payers per active player then groups and filters by the shared column, and Dosi never joins the two tables directly. dosi lineage.dosi lineage viewopens the metric lineage graph in your browser, with derived metrics laid out left to right in the order they are built.dosi lineage dumpprints the graph as JSON. See Lineage.- Ontologies (experimental).
--ontologyloads an Apache Ossie ontology, with or without a core model. You can query by its concepts, browse them withdosi describeanddosi paths, and see them on the lineage graph. Flags and output may still change.
Enhancements
- Attribution now requires
dimensions. Without them it stops before querying the warehouse and suggests the metric's recommended dimensions, ready to paste. See Which dimensions?. - The DATUS extension version is now 1.8. Models that declare an older version load as before.
Bug fixes
- Arrow Flight reads from StarRocks failed every time. On StarRocks 4.0.14, also
set
arrow_flight_proxy_enabled = false. See Arrow-native results.
Known issue
- Upstream Ossie changed its document format after this release: a document
now holds one model, with
nameanddatasetsat the top level instead of asemantic_model:list. 0.1.11 cannot load the new format. A later release reads both. With--osi-basic, use the upstream validator at the commit named indosi validate; upstreammainrejects every model 0.1.11 accepts.
0.1.10¶
New features
- Detail queries.
dosi selectreturns rows instead of aggregated metrics — "the 100 largest orders from Enterprise customers". Name the fields you want; Dosi works out the joins, and refuses rather than hand you duplicated rows. See Detail queries. - Time columns stored as text or numbers. A time column no longer has to be
a real
DATE. Declare what it holds —20240131,2024-01,2024年01月31日— and Dosi groups and filters it correctly, without losing partition pruning. Queries are unchanged: you always write2024-01-31. See D-FORMAT. - A composed metric can build on another composed metric. A formula is written once and reused, instead of restated in every metric that uses it — and a restatement that had drifted used to compile silently.
Enhancements
- Models using the standard OSI
datatypeproperty now load, Apache Ossie's TPC-DS reference model included. - An
--orderkey names a metric or a field you grouped by, never a column name the engine generated.
Bug fixes
- A parameter whose values were Chinese, or any non-ASCII text, produced the same column name for every value; binding several at once failed the query.
- Time columns stored as integers failed on MySQL, TiDB and ClickHouse when rolled up to month or year.
0.1.9¶
Dosi is now under the Elastic License 2.0: free to use and redistribute, with no hosted-service resale. It replaces the proprietary licence 0.1.5 introduced, which granted no rights at all without a separate agreement.
New features
- BigQuery runs queries. It was compile-only — Dosi generated the SQL and refused to execute it. It now passes the full test corpus against a live project. See BigQuery.
- Metric lineage (Python only) —
Engine.lineage()returns the graph behind your metrics: table → dataset → metric → derived metric. Passredact_sql=Trueif the graph leaves your trust boundary. - Model authoring contract (Python only) —
render_datus_authoring_spec()returns the rules this engine enforces, with a digest, so a tool that generates models can check it is authoring against the same version. - Installing from a mirror.
install.shtakesDOSI_SOURCE=github|oss|cloudflare. UseDOSI_SOURCE=ossfrom mainland China, where the GitHub download runs at tens of KB/s and often stalls. Archives are checksum-verified either way. The Cloudflare mirror is not populated in this release.
Enhancements
install.shworks on macOS, where it used to refuse any non-Linux host.- Validation returns what each metric actually compiled to, and Python connection settings can be passed in memory without the plaintext-secret warning — which only ever made sense for a file on disk.
- A new BigQuery docs page, plus a 200k-row example
database at
https://dosi.datus.ai/orders-200k.duckdb.
Bug fixes
- A rolling window of 1 period, or a
precedingbound of 0, generated SQL that Doris and StarRocks reject outright, so an ordinary--param n=1,3,6failed on those two engines.
0.1.8¶
No wheel was published for this version, so dosi-engine users go from
0.1.7 straight to 0.1.9, which includes everything here. The binaries shipped
for Linux x86_64, Linux aarch64 and Apple Silicon only — on an Intel Mac,
install 0.1.9.
New features
- Attribution: why did this metric change? Give Dosi a metric, the dimensions to look at and two time windows; it runs the queries itself and returns a ranked breakdown of where the change came from, each segment with a ready-made filter for the next drill-down. The method comes from the metric's own algebra, and a metric it cannot decompose returns a structured "unsupported" with the reason rather than a wrong number. See Explain metric changes.
- Parameterized metrics. A metric's window width, offset, rank bucket or
filter value can be a query-time parameter: one
moving_avg(n)replacesr7/r30/r90, and one query can ask for several values at once, a column each. See Parameterized metrics.
Enhancements
dosi list metricssays whether a metric can be attributed, and how, so you can check before running an analysis.- A query can carry several named time windows and get them all back from a single statement.
Behaviour change
- If you call
Engine.attribute()from the 0.1.7 wheel: the share fields can now be absent for a segment whose denominator is not positive, so code that reads them unconditionally needs to handle that.
0.1.7¶
This release is split: the binaries and the wheel were built three days apart, from different commits, so the wheel contains more.
New features
- A second, smaller download —
dosi-<version>-<target>-lean.tar.gzis the executables alone. It compiles queries and reaches every warehouse; local DuckDB execution uses aduckdbCLI on your PATH. - Derived metrics (D-DERIVE) M2 — windows over derived metrics, and composing over window members.
- SQLite files are readable through DuckDB.
- An error-code reference, with a catalog of what each rejection means.
- Attribution, first version (wheel only) —
Engine.attribute(). 0.1.8 extends it; read its behaviour change above before upgrading.
Enhancements
- DuckDB starts about 200 ms faster per process — a one-shot
dosi query --executewent from 249 ms to 41 ms of CPU. Query speed itself is unchanged; the cost was start-up.
Bug fixes
- Redshift now renders as Redshift. It had been falling through to the generic generator and emitting Postgres SQL.
- The wheel builds under cargo 1.93 and later.
0.1.6¶
Wheel only — dosi and dosi-server users go from 0.1.5 to 0.1.7.
New features
- A Linux aarch64 wheel, so Graviton and arm64 containers no longer need a source build.
- SQLite files are readable through DuckDB, which makes a local
.sqlitefile a usable connection for tests and small models. CONNECTION_TYPESis exported, so a caller can list which warehouses a given wheel was built with.
0.1.5¶
The first public release of Dosi. From here the project ships as binaries
and wheels only; no source archive is published. Install with
curl -fsSL https://dosi.datus.ai/install.sh | sh for the dosi and
dosi-server executables, or pip install dosi-engine for the Python
bindings.
It loads an OSI semantic model, validates it, compiles a metric query to SQL
for your warehouse, and runs it. Fifteen warehouses — DuckDB · StarRocks ·
ClickHouse · Doris · TiDB · Trino · Postgres · MySQL · Snowflake · BigQuery ·
Databricks · Redshift · Hologres · GaussDB · Oracle. Four ways in: the dosi
CLI, REST, MCP, and the Python wheel. Two modes: --osi-datus honours the
Datus extensions, --osi-basic is strict standard OSI.
Nothing before 0.1.5 was published.