You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@ossie.apache.org by Klahr Joshua via dev <de...@ossie.apache.org> on 2026/08/27 16:57:13 UTC
[DISCUSS] Dataset-scoped metrics
Hi all,
I'd like to propose allowing metrics to be declared on an individual dataset (datasets[].metrics) in addition to the semantic model (semantic_model.metrics), using the same metric structure in both placements.
PR: https://github.com/apache/ossie/pull/343
Motivation
Today every metric must be declared at the semantic model level, even when it aggregates a single dataset's own fields. Three costs follow from that.
First, a dataset is not independently interpretable — you cannot tell from a dataset what metrics exist over it without scanning the whole model, and simple aggregations end up separated from the fields they depend on.
Second, and more concretely: round-trip fidelity through Ossie is reduced. Several formats we convert from attach metrics to a specific entity — Omni has per-view measures, Honeydew has per-entity metric files, WisdomAI has per-table measures, and dbt MetricFlow binds metrics to a semantic model. Because Ossie has no dataset-scoped placement, the converters in this repository flatten all of those to the model level on import, and the entity association is not represented in the resulting document.
On export, four converters independently reconstruct that association by scanning expressions for dataset.column qualifiers and falling back to the first dataset when none is found. The Honeydew converter emits a runtime warning when it does so. Two converters stash the owning entity in vendor-specific custom_extensions, which round-trips within a single vendor's toolchain but is not something a generic Ossie consumer can produce or interpret.
Four converters reimplementing the same heuristic suggests the spec is missing a place to put information the source formats already carry.
Third, export placement can be coarser than the target supports. The Snowflake converter maps every Ossie model-level metric to Snowflake's top-level metrics block, including metrics referencing only one table that would fit the narrower tables[].metrics position. The converter cannot distinguish the two cases because the Ossie document does not record the distinction.
What's proposed
datasets[].metrics, using the identical Metric structure. Scoping rules:
-
A dataset-scoped metric's expression MUST only reference fields of its declaring dataset, and MUST NOT traverse relationships. Cross-dataset metrics remain model-scoped.
-
Names MUST be unique within their dataset — two datasets MAY each declare item_count.
-
A dataset-scoped name MUST NOT collide with any model-scoped metric name.
-
Referenced from outside the dataset as dataset_name.metric_name.
Worth stating explicitly because it's easy to misread: rule 1 constrains the expression, not the query. A dataset-scoped metric can still be grouped by or filtered on dimensions from other datasets via relationships, since grouping dimensions come from the consumer at query time.
Prior art
Separating single-entity aggregations from cross-entity calculations is established practice. Four of the five systems I looked at do it — AtScale SML (metric bound to one dataset + column, with metric_calc as a separate type), Cube (measures within cubes), dbt MetricFlow (in-model vs top-level), and Snowflake semantic views (tables[].metrics vs top-level derived). Databricks UC metric views take a different approach, with one flat scope per view and joins declared inside it.
On naming, the proposal extends Ossie's own existing convention rather than importing one: field names are already unique within a dataset rather than globally, and metric expressions already reference them as dataset.field. Applying the same scoped-uniqueness and qualified-reference pattern to metrics keeps the model internally consistent.
On strictness it sits within the range the ecosystem spans — SML is more restrictive (one column, one aggregation method), Snowflake semantic views more permissive (table-level metrics may traverse relationships with a declared join path).
Compatibility
Additive and backward compatible. datasets[].metrics is optional and existing models remain valid unchanged. Because a dataset-scoped expression resolves within its own dataset, it is already valid as a model-scoped metric, so consumers wanting a single flat namespace can hoist without rewriting expressions. No converter changes are included; existing converters read semantic_model.metrics and that behaviour is untouched.
Two open questions I'd particularly like input on
-
Should a dataset-scoped metric eventually be allowed to traverse relationships when the join path is declared explicitly? The PR takes the conservative position, since relaxing later is backward compatible while tightening is not.
-
Separately, I noticed the spec does not say whether one metric expression may reference another metric — there's no mechanism and no prohibition, while every system above has one. It's ambiguous rather than merely absent: SUM(orders.amount) / total_orders passes validation today because total_orders parses as an unqualified column. My inclination is to handle that separately since it needs aggregation-composition semantics settled, but the two features interact. The PR includes a sketch of what the reference-scoping rules would be, in case it's worth fixing those now for forward compatibility.
Validation
Three of the rules are semantic constraints JSON Schema cannot express, so validate.py gains checks for duplicate names within a dataset, collisions with model-scoped names, and cross-dataset references (table qualifiers extracted via sqlglot). Scope checking skips rather than guesses for dialects sqlglot cannot parse. The TPC-DS example is updated to demonstrate both placements.
AI disclosure
This contribution was developed with AI assistance (Snowflake Cortex Code). I have reviewed all changes and am personally responsible for the content per the ASF Generative Tooling Guidance.
Feedback and pushback welcome. Minimum discussion period is 7 days per the contribution guidelines.
Best,
Josh Klahr
Re: [DISCUSS] Dataset-scoped metrics
Posted by Chris Eubank via dev <de...@ossie.apache.org>.
Thanks for the feedback, JB!
Agreed on tightening up the "source fields" terminology (I was referring to
the physical columns underneath but this should be explicit). That's a fair
point regarding the validation extensions as well. The cases you enumerate
should be fairly straightforward to implement.
Josh, how would you like to proceed on the PR?
Kind regards,
Chris
On Tue, Sep 29, 2026 at 9:04 PM Jean-Baptiste Onofré <jb...@nanthrax.net>
wrote:
> Hi all,
>
> I completely agree with Chris's proposal to tighten the scope here.
> Decoupling the placement (datasets[].metrics) from the larger,
> in-flight semantic questions (expression composition, traversal paths,
> grain locking) is the pragmatic way to get a clean spec vote (I love
> pragmatic approaches :)).
>
> A few specific thoughts on the three restrictions and the rollout:
> 1. Unqualified references and unified namespace are spot on.
> Single-part references (SUM(amount) instead of orders.amount) neatly
> eliminate the circularity issue in DDL/parser contexts that Justin
> pointed out. Enforcing a single namespace across fields and metrics
> within the dataset also elliminites the naming collision ambiguity.
> 2. In the wording, we should be explicit about whether "source fields"
> refers to the underlying physical table columns or the dataset's
> declared fields (datasets[].fields). In Ossie, metrics should
> aggregate the dataset's declared/modeled fields (or we need clear
> precedence if both are permitted). Tightening this phrasing will avoid
> confusion during the vote.
> 3. While I support deferring complex AST evaluation or cross-dialect
> query semantics to follow-up, I would prefer we do not strip the
> validation entirely from this PR. The JSON schema update
> (ossie-schema.json) and basic structural validation in validate.py
> (uniqueness across fields + metrics, checking for unqualified
> references, and null guards) should land together with the spec. In
> Ossie, the schema and validator act as our executable spec: keeping
> baseline structural checks ensures the vote lands on a solid,
> verifiable foundation.
>
> Overall, this narrowed scope gives us a safe, backward-compatible
> baseline that we can relax later one expression composition lands.
>
> Happy to review the updated PR and spec wording once ready.
>
> Regards
> JB
>
> On Tue, Sep 29, 2026 at 11:50 PM Chris Eubank
> <ch...@databricks.com> wrote:
> >
> > Hi Josh,
> >
> > Thank you for putting together this proposal for dataset-scoped metrics.
> It's a fundamental building block for several in-flight extensions:
> expression resolution / composability (thread), relational query semantics
> (thread) and dimensional query semantics (thread).
> >
> > Several open comments remain, mostly concerning those follow-up features
> (e.g. expression composition). I'm wondering if we can tighten this PR's
> scope to land the minimal spec extension needed for a spec vote, and defer
> the rest. The validation logic could even move to a follow-up PR.
> >
> > Concretely, I'd propose these three restrictions:
> >
> > 1. Datasets may declare metrics. Within a dataset, names must be unique
> across fields and metrics.
> > 2. A dataset's metric expression may reference only that dataset's
> source fields. We can lift this restriction once the expression-composition
> rules land.
> > 3. Those references must be single-part (unqualified) within the
> expression and must resolve to the dataset's source fields.
> >
> > Would this narrower scope work for you as a path to a spec vote? If so,
> I'd be glad to help however is most useful - happy to pair on the scoped
> wording or take a first pass for us to iterate on.
> >
> > Kind regards,
> > Chris
> >
> > On Thu, Aug 27, 2026 at 2:20 PM Khushboo Bhatia <kb...@apache.org>
> wrote:
> >>
> >> Hey Josh,
> >> Thanks for starting this discussion. This issue has been raised in
> multiple discussions on the repo.
> >>
> >> I am generally supportive of adding these. Leaving some comments on the
> PR for the subtle differences between semantic model scoped metrics vs
> dataset scoped.
> >>
> >> Khushboo
> >>
> >> On 2026/08/27 16:57:13 Klahr Joshua via dev wrote:
> >> >
> >> >
> >> >
> >> > Hi all,
> >> >
> >> > I'd like to propose allowing metrics to be declared on an individual
> dataset (datasets[].metrics) in addition to the semantic model
> (semantic_model.metrics), using the same metric structure in both
> placements.
> >> >
> >> > PR: https://github.com/apache/ossie/pull/343
> >> >
> >> > Motivation
> >> >
> >> > Today every metric must be declared at the semantic model level, even
> when it aggregates a single dataset's own fields. Three costs follow from
> that.
> >> >
> >> > First, a dataset is not independently interpretable — you cannot tell
> from a dataset what metrics exist over it without scanning the whole model,
> and simple aggregations end up separated from the fields they depend on.
> >> >
> >> > Second, and more concretely: round-trip fidelity through Ossie is
> reduced. Several formats we convert from attach metrics to a specific
> entity — Omni has per-view measures, Honeydew has per-entity metric files,
> WisdomAI has per-table measures, and dbt MetricFlow binds metrics to a
> semantic model. Because Ossie has no dataset-scoped placement, the
> converters in this repository flatten all of those to the model level on
> import, and the entity association is not represented in the resulting
> document.
> >> >
> >> > On export, four converters independently reconstruct that association
> by scanning expressions for dataset.column qualifiers and falling back to
> the first dataset when none is found. The Honeydew converter emits a
> runtime warning when it does so. Two converters stash the owning entity in
> vendor-specific custom_extensions, which round-trips within a single
> vendor's toolchain but is not something a generic Ossie consumer can
> produce or interpret.
> >> >
> >> > Four converters reimplementing the same heuristic suggests the spec
> is missing a place to put information the source formats already carry.
> >> >
> >> > Third, export placement can be coarser than the target supports. The
> Snowflake converter maps every Ossie model-level metric to Snowflake's
> top-level metrics block, including metrics referencing only one table that
> would fit the narrower tables[].metrics position. The converter cannot
> distinguish the two cases because the Ossie document does not record the
> distinction.
> >> >
> >> > What's proposed
> >> >
> >> > datasets[].metrics, using the identical Metric structure. Scoping
> rules:
> >> >
> >> > -
> >> > A dataset-scoped metric's expression MUST only reference fields of
> its declaring dataset, and MUST NOT traverse relationships. Cross-dataset
> metrics remain model-scoped.
> >> >
> >> > -
> >> > Names MUST be unique within their dataset — two datasets MAY each
> declare item_count.
> >> >
> >> > -
> >> > A dataset-scoped name MUST NOT collide with any model-scoped metric
> name.
> >> >
> >> > -
> >> > Referenced from outside the dataset as dataset_name.metric_name.
> >> >
> >> >
> >> > Worth stating explicitly because it's easy to misread: rule 1
> constrains the expression, not the query. A dataset-scoped metric can still
> be grouped by or filtered on dimensions from other datasets via
> relationships, since grouping dimensions come from the consumer at query
> time.
> >> >
> >> > Prior art
> >> >
> >> > Separating single-entity aggregations from cross-entity calculations
> is established practice. Four of the five systems I looked at do it —
> AtScale SML (metric bound to one dataset + column, with metric_calc as a
> separate type), Cube (measures within cubes), dbt MetricFlow (in-model vs
> top-level), and Snowflake semantic views (tables[].metrics vs top-level
> derived). Databricks UC metric views take a different approach, with one
> flat scope per view and joins declared inside it.
> >> >
> >> > On naming, the proposal extends Ossie's own existing convention
> rather than importing one: field names are already unique within a dataset
> rather than globally, and metric expressions already reference them as
> dataset.field. Applying the same scoped-uniqueness and qualified-reference
> pattern to metrics keeps the model internally consistent.
> >> >
> >> > On strictness it sits within the range the ecosystem spans — SML is
> more restrictive (one column, one aggregation method), Snowflake semantic
> views more permissive (table-level metrics may traverse relationships with
> a declared join path).
> >> >
> >> > Compatibility
> >> >
> >> > Additive and backward compatible. datasets[].metrics is optional and
> existing models remain valid unchanged. Because a dataset-scoped expression
> resolves within its own dataset, it is already valid as a model-scoped
> metric, so consumers wanting a single flat namespace can hoist without
> rewriting expressions. No converter changes are included; existing
> converters read semantic_model.metrics and that behaviour is untouched.
> >> >
> >> > Two open questions I'd particularly like input on
> >> >
> >> > -
> >> > Should a dataset-scoped metric eventually be allowed to traverse
> relationships when the join path is declared explicitly? The PR takes the
> conservative position, since relaxing later is backward compatible while
> tightening is not.
> >> >
> >> > -
> >> > Separately, I noticed the spec does not say whether one metric
> expression may reference another metric — there's no mechanism and no
> prohibition, while every system above has one. It's ambiguous rather than
> merely absent: SUM(orders.amount) / total_orders passes validation today
> because total_orders parses as an unqualified column. My inclination is to
> handle that separately since it needs aggregation-composition semantics
> settled, but the two features interact. The PR includes a sketch of what
> the reference-scoping rules would be, in case it's worth fixing those now
> for forward compatibility.
> >> >
> >> >
> >> > Validation
> >> >
> >> > Three of the rules are semantic constraints JSON Schema cannot
> express, so validate.py gains checks for duplicate names within a dataset,
> collisions with model-scoped names, and cross-dataset references (table
> qualifiers extracted via sqlglot). Scope checking skips rather than guesses
> for dialects sqlglot cannot parse. The TPC-DS example is updated to
> demonstrate both placements.
> >> >
> >> > AI disclosure
> >> >
> >> > This contribution was developed with AI assistance (Snowflake Cortex
> Code). I have reviewed all changes and am personally responsible for the
> content per the ASF Generative Tooling Guidance.
> >> >
> >> > Feedback and pushback welcome. Minimum discussion period is 7 days
> per the contribution guidelines.
> >> >
> >> > Best,
> >> > Josh Klahr
> >> >
> >> >
> >> >
> >> >
> >> >
>
Re: [DISCUSS] Dataset-scoped metrics
Posted by Chris Eubank via dev <de...@ossie.apache.org>.
Hi Josh,
I think we'd ultimately want to support both. To your point, though, that
introduces some naming ambiguity. Damian raised a related issue in this
thread <https://lists.apache.org/thread/bc9v9o0d2702gwm978km13mg6v16hzv1>
around
naming for model-level metrics as well.
Naming rules - especially as we define how expressions can be composed -
feel like a broader topic that deserves its own PR. My understanding from
that thread is that Damian and Justin plan to create a joint PR covering
this area.
For this PR, I proposed restricting references to source columns for
consistency with fields, and getting the core concept of dataset-level
metrics into the spec without resolving the broader naming questions. The
naming work can then build on and reference the concepts established here.
Kind regards,
Chris
On Thu, Oct 1, 2026 at 10:44 AM Klahr Joshua <jk...@yahoo.com> wrote:
> I think this makes sense, and agree it would be good to get this out
> soon. The one question I have is how/if we can allow data set scoped
> metrics to refer to either fields (defined in the dataset) OR columns (from
> the data set's source columns). Is this something that could be supported?
> I think this would be desirable but can create ambiguity if field_names and
> column_names collide...
>
> Thoughts?
>
> On Wednesday, September 30, 2026 at 05:04:28 AM GMT+1, Jean-Baptiste
> Onofré <jb...@nanthrax.net> wrote:
>
>
> Hi all,
>
> I completely agree with Chris's proposal to tighten the scope here.
> Decoupling the placement (datasets[].metrics) from the larger,
> in-flight semantic questions (expression composition, traversal paths,
> grain locking) is the pragmatic way to get a clean spec vote (I love
> pragmatic approaches :)).
>
> A few specific thoughts on the three restrictions and the rollout:
> 1. Unqualified references and unified namespace are spot on.
> Single-part references (SUM(amount) instead of orders.amount) neatly
> eliminate the circularity issue in DDL/parser contexts that Justin
> pointed out. Enforcing a single namespace across fields and metrics
> within the dataset also elliminites the naming collision ambiguity.
> 2. In the wording, we should be explicit about whether "source fields"
> refers to the underlying physical table columns or the dataset's
> declared fields (datasets[].fields). In Ossie, metrics should
> aggregate the dataset's declared/modeled fields (or we need clear
> precedence if both are permitted). Tightening this phrasing will avoid
> confusion during the vote.
> 3. While I support deferring complex AST evaluation or cross-dialect
> query semantics to follow-up, I would prefer we do not strip the
> validation entirely from this PR. The JSON schema update
> (ossie-schema.json) and basic structural validation in validate.py
> (uniqueness across fields + metrics, checking for unqualified
> references, and null guards) should land together with the spec. In
> Ossie, the schema and validator act as our executable spec: keeping
> baseline structural checks ensures the vote lands on a solid,
> verifiable foundation.
>
> Overall, this narrowed scope gives us a safe, backward-compatible
> baseline that we can relax later one expression composition lands.
>
> Happy to review the updated PR and spec wording once ready.
>
> Regards
> JB
>
> On Tue, Sep 29, 2026 at 11:50 PM Chris Eubank
> <ch...@databricks.com> wrote:
> >
> > Hi Josh,
> >
> > Thank you for putting together this proposal for dataset-scoped metrics.
> It's a fundamental building block for several in-flight extensions:
> expression resolution / composability (thread), relational query semantics
> (thread) and dimensional query semantics (thread).
> >
> > Several open comments remain, mostly concerning those follow-up features
> (e.g. expression composition). I'm wondering if we can tighten this PR's
> scope to land the minimal spec extension needed for a spec vote, and defer
> the rest. The validation logic could even move to a follow-up PR.
> >
> > Concretely, I'd propose these three restrictions:
> >
> > 1. Datasets may declare metrics. Within a dataset, names must be unique
> across fields and metrics.
> > 2. A dataset's metric expression may reference only that dataset's
> source fields. We can lift this restriction once the expression-composition
> rules land.
> > 3. Those references must be single-part (unqualified) within the
> expression and must resolve to the dataset's source fields.
> >
> > Would this narrower scope work for you as a path to a spec vote? If so,
> I'd be glad to help however is most useful - happy to pair on the scoped
> wording or take a first pass for us to iterate on.
> >
> > Kind regards,
> > Chris
> >
> > On Thu, Aug 27, 2026 at 2:20 PM Khushboo Bhatia <kb...@apache.org>
> wrote:
> >>
> >> Hey Josh,
> >> Thanks for starting this discussion. This issue has been raised in
> multiple discussions on the repo.
> >>
> >> I am generally supportive of adding these. Leaving some comments on the
> PR for the subtle differences between semantic model scoped metrics vs
> dataset scoped.
> >>
> >> Khushboo
> >>
> >> On 2026/08/27 16:57:13 Klahr Joshua via dev wrote:
> >> >
> >> >
> >> >
> >> > Hi all,
> >> >
> >> > I'd like to propose allowing metrics to be declared on an individual
> dataset (datasets[].metrics) in addition to the semantic model
> (semantic_model.metrics), using the same metric structure in both
> placements.
> >> >
> >> > PR: https://github.com/apache/ossie/pull/343
> >> >
> >> > Motivation
> >> >
> >> > Today every metric must be declared at the semantic model level, even
> when it aggregates a single dataset's own fields. Three costs follow from
> that.
> >> >
> >> > First, a dataset is not independently interpretable — you cannot tell
> from a dataset what metrics exist over it without scanning the whole model,
> and simple aggregations end up separated from the fields they depend on.
> >> >
> >> > Second, and more concretely: round-trip fidelity through Ossie is
> reduced. Several formats we convert from attach metrics to a specific
> entity — Omni has per-view measures, Honeydew has per-entity metric files,
> WisdomAI has per-table measures, and dbt MetricFlow binds metrics to a
> semantic model. Because Ossie has no dataset-scoped placement, the
> converters in this repository flatten all of those to the model level on
> import, and the entity association is not represented in the resulting
> document.
> >> >
> >> > On export, four converters independently reconstruct that association
> by scanning expressions for dataset.column qualifiers and falling back to
> the first dataset when none is found. The Honeydew converter emits a
> runtime warning when it does so. Two converters stash the owning entity in
> vendor-specific custom_extensions, which round-trips within a single
> vendor's toolchain but is not something a generic Ossie consumer can
> produce or interpret.
> >> >
> >> > Four converters reimplementing the same heuristic suggests the spec
> is missing a place to put information the source formats already carry.
> >> >
> >> > Third, export placement can be coarser than the target supports. The
> Snowflake converter maps every Ossie model-level metric to Snowflake's
> top-level metrics block, including metrics referencing only one table that
> would fit the narrower tables[].metrics position. The converter cannot
> distinguish the two cases because the Ossie document does not record the
> distinction.
> >> >
> >> > What's proposed
> >> >
> >> > datasets[].metrics, using the identical Metric structure. Scoping
> rules:
> >> >
> >> > -
> >> > A dataset-scoped metric's expression MUST only reference fields of
> its declaring dataset, and MUST NOT traverse relationships. Cross-dataset
> metrics remain model-scoped.
> >> >
> >> > -
> >> > Names MUST be unique within their dataset — two datasets MAY each
> declare item_count.
> >> >
> >> > -
> >> > A dataset-scoped name MUST NOT collide with any model-scoped metric
> name.
> >> >
> >> > -
> >> > Referenced from outside the dataset as dataset_name.metric_name.
> >> >
> >> >
> >> > Worth stating explicitly because it's easy to misread: rule 1
> constrains the expression, not the query. A dataset-scoped metric can still
> be grouped by or filtered on dimensions from other datasets via
> relationships, since grouping dimensions come from the consumer at query
> time.
> >> >
> >> > Prior art
> >> >
> >> > Separating single-entity aggregations from cross-entity calculations
> is established practice. Four of the five systems I looked at do it —
> AtScale SML (metric bound to one dataset + column, with metric_calc as a
> separate type), Cube (measures within cubes), dbt MetricFlow (in-model vs
> top-level), and Snowflake semantic views (tables[].metrics vs top-level
> derived). Databricks UC metric views take a different approach, with one
> flat scope per view and joins declared inside it.
> >> >
> >> > On naming, the proposal extends Ossie's own existing convention
> rather than importing one: field names are already unique within a dataset
> rather than globally, and metric expressions already reference them as
> dataset.field. Applying the same scoped-uniqueness and qualified-reference
> pattern to metrics keeps the model internally consistent.
> >> >
> >> > On strictness it sits within the range the ecosystem spans — SML is
> more restrictive (one column, one aggregation method), Snowflake semantic
> views more permissive (table-level metrics may traverse relationships with
> a declared join path).
> >> >
> >> > Compatibility
> >> >
> >> > Additive and backward compatible. datasets[].metrics is optional and
> existing models remain valid unchanged. Because a dataset-scoped expression
> resolves within its own dataset, it is already valid as a model-scoped
> metric, so consumers wanting a single flat namespace can hoist without
> rewriting expressions. No converter changes are included; existing
> converters read semantic_model.metrics and that behaviour is untouched.
> >> >
> >> > Two open questions I'd particularly like input on
> >> >
> >> > -
> >> > Should a dataset-scoped metric eventually be allowed to traverse
> relationships when the join path is declared explicitly? The PR takes the
> conservative position, since relaxing later is backward compatible while
> tightening is not.
> >> >
> >> > -
> >> > Separately, I noticed the spec does not say whether one metric
> expression may reference another metric — there's no mechanism and no
> prohibition, while every system above has one. It's ambiguous rather than
> merely absent: SUM(orders.amount) / total_orders passes validation today
> because total_orders parses as an unqualified column. My inclination is to
> handle that separately since it needs aggregation-composition semantics
> settled, but the two features interact. The PR includes a sketch of what
> the reference-scoping rules would be, in case it's worth fixing those now
> for forward compatibility.
> >> >
> >> >
> >> > Validation
> >> >
> >> > Three of the rules are semantic constraints JSON Schema cannot
> express, so validate.py gains checks for duplicate names within a dataset,
> collisions with model-scoped names, and cross-dataset references (table
> qualifiers extracted via sqlglot). Scope checking skips rather than guesses
> for dialects sqlglot cannot parse. The TPC-DS example is updated to
> demonstrate both placements.
> >> >
> >> > AI disclosure
> >> >
> >> > This contribution was developed with AI assistance (Snowflake Cortex
> Code). I have reviewed all changes and am personally responsible for the
> content per the ASF Generative Tooling Guidance.
> >> >
> >> > Feedback and pushback welcome. Minimum discussion period is 7 days
> per the contribution guidelines.
> >> >
> >> > Best,
> >> > Josh Klahr
> >> >
> >> >
> >> >
> >> >
> >> >
>
Re: [DISCUSS] Dataset-scoped metrics
Posted by Klahr Joshua via dev <de...@ossie.apache.org>.
I think this makes sense, and agree it would be good to get this out soon. The one question I have is how/if we can allow data set scoped metrics to refer to either fields (defined in the dataset) OR columns (from the data set's source columns). Is this something that could be supported? I think this would be desirable but can create ambiguity if field_names and column_names collide...
Thoughts?
On Wednesday, September 30, 2026 at 05:04:28 AM GMT+1, Jean-Baptiste Onofré <jb...@nanthrax.net> wrote:
Hi all,
I completely agree with Chris's proposal to tighten the scope here.
Decoupling the placement (datasets[].metrics) from the larger,
in-flight semantic questions (expression composition, traversal paths,
grain locking) is the pragmatic way to get a clean spec vote (I love
pragmatic approaches :)).
A few specific thoughts on the three restrictions and the rollout:
1. Unqualified references and unified namespace are spot on.
Single-part references (SUM(amount) instead of orders.amount) neatly
eliminate the circularity issue in DDL/parser contexts that Justin
pointed out. Enforcing a single namespace across fields and metrics
within the dataset also elliminites the naming collision ambiguity.
2. In the wording, we should be explicit about whether "source fields"
refers to the underlying physical table columns or the dataset's
declared fields (datasets[].fields). In Ossie, metrics should
aggregate the dataset's declared/modeled fields (or we need clear
precedence if both are permitted). Tightening this phrasing will avoid
confusion during the vote.
3. While I support deferring complex AST evaluation or cross-dialect
query semantics to follow-up, I would prefer we do not strip the
validation entirely from this PR. The JSON schema update
(ossie-schema.json) and basic structural validation in validate.py
(uniqueness across fields + metrics, checking for unqualified
references, and null guards) should land together with the spec. In
Ossie, the schema and validator act as our executable spec: keeping
baseline structural checks ensures the vote lands on a solid,
verifiable foundation.
Overall, this narrowed scope gives us a safe, backward-compatible
baseline that we can relax later one expression composition lands.
Happy to review the updated PR and spec wording once ready.
Regards
JB
On Tue, Sep 29, 2026 at 11:50 PM Chris Eubank
<ch...@databricks.com> wrote:
>
> Hi Josh,
>
> Thank you for putting together this proposal for dataset-scoped metrics. It's a fundamental building block for several in-flight extensions: expression resolution / composability (thread), relational query semantics (thread) and dimensional query semantics (thread).
>
> Several open comments remain, mostly concerning those follow-up features (e.g. expression composition). I'm wondering if we can tighten this PR's scope to land the minimal spec extension needed for a spec vote, and defer the rest. The validation logic could even move to a follow-up PR.
>
> Concretely, I'd propose these three restrictions:
>
> 1. Datasets may declare metrics. Within a dataset, names must be unique across fields and metrics.
> 2. A dataset's metric expression may reference only that dataset's source fields. We can lift this restriction once the expression-composition rules land.
> 3. Those references must be single-part (unqualified) within the expression and must resolve to the dataset's source fields.
>
> Would this narrower scope work for you as a path to a spec vote? If so, I'd be glad to help however is most useful - happy to pair on the scoped wording or take a first pass for us to iterate on.
>
> Kind regards,
> Chris
>
> On Thu, Aug 27, 2026 at 2:20 PM Khushboo Bhatia <kb...@apache.org> wrote:
>>
>> Hey Josh,
>> Thanks for starting this discussion. This issue has been raised in multiple discussions on the repo.
>>
>> I am generally supportive of adding these. Leaving some comments on the PR for the subtle differences between semantic model scoped metrics vs dataset scoped.
>>
>> Khushboo
>>
>> On 2026/08/27 16:57:13 Klahr Joshua via dev wrote:
>> >
>> >
>> >
>> > Hi all,
>> >
>> > I'd like to propose allowing metrics to be declared on an individual dataset (datasets[].metrics) in addition to the semantic model (semantic_model.metrics), using the same metric structure in both placements.
>> >
>> > PR: https://github.com/apache/ossie/pull/343
>> >
>> > Motivation
>> >
>> > Today every metric must be declared at the semantic model level, even when it aggregates a single dataset's own fields. Three costs follow from that.
>> >
>> > First, a dataset is not independently interpretable — you cannot tell from a dataset what metrics exist over it without scanning the whole model, and simple aggregations end up separated from the fields they depend on.
>> >
>> > Second, and more concretely: round-trip fidelity through Ossie is reduced. Several formats we convert from attach metrics to a specific entity — Omni has per-view measures, Honeydew has per-entity metric files, WisdomAI has per-table measures, and dbt MetricFlow binds metrics to a semantic model. Because Ossie has no dataset-scoped placement, the converters in this repository flatten all of those to the model level on import, and the entity association is not represented in the resulting document.
>> >
>> > On export, four converters independently reconstruct that association by scanning expressions for dataset.column qualifiers and falling back to the first dataset when none is found. The Honeydew converter emits a runtime warning when it does so. Two converters stash the owning entity in vendor-specific custom_extensions, which round-trips within a single vendor's toolchain but is not something a generic Ossie consumer can produce or interpret.
>> >
>> > Four converters reimplementing the same heuristic suggests the spec is missing a place to put information the source formats already carry.
>> >
>> > Third, export placement can be coarser than the target supports. The Snowflake converter maps every Ossie model-level metric to Snowflake's top-level metrics block, including metrics referencing only one table that would fit the narrower tables[].metrics position. The converter cannot distinguish the two cases because the Ossie document does not record the distinction.
>> >
>> > What's proposed
>> >
>> > datasets[].metrics, using the identical Metric structure. Scoping rules:
>> >
>> > -
>> > A dataset-scoped metric's expression MUST only reference fields of its declaring dataset, and MUST NOT traverse relationships. Cross-dataset metrics remain model-scoped.
>> >
>> > -
>> > Names MUST be unique within their dataset — two datasets MAY each declare item_count.
>> >
>> > -
>> > A dataset-scoped name MUST NOT collide with any model-scoped metric name.
>> >
>> > -
>> > Referenced from outside the dataset as dataset_name.metric_name.
>> >
>> >
>> > Worth stating explicitly because it's easy to misread: rule 1 constrains the expression, not the query. A dataset-scoped metric can still be grouped by or filtered on dimensions from other datasets via relationships, since grouping dimensions come from the consumer at query time.
>> >
>> > Prior art
>> >
>> > Separating single-entity aggregations from cross-entity calculations is established practice. Four of the five systems I looked at do it — AtScale SML (metric bound to one dataset + column, with metric_calc as a separate type), Cube (measures within cubes), dbt MetricFlow (in-model vs top-level), and Snowflake semantic views (tables[].metrics vs top-level derived). Databricks UC metric views take a different approach, with one flat scope per view and joins declared inside it.
>> >
>> > On naming, the proposal extends Ossie's own existing convention rather than importing one: field names are already unique within a dataset rather than globally, and metric expressions already reference them as dataset.field. Applying the same scoped-uniqueness and qualified-reference pattern to metrics keeps the model internally consistent.
>> >
>> > On strictness it sits within the range the ecosystem spans — SML is more restrictive (one column, one aggregation method), Snowflake semantic views more permissive (table-level metrics may traverse relationships with a declared join path).
>> >
>> > Compatibility
>> >
>> > Additive and backward compatible. datasets[].metrics is optional and existing models remain valid unchanged. Because a dataset-scoped expression resolves within its own dataset, it is already valid as a model-scoped metric, so consumers wanting a single flat namespace can hoist without rewriting expressions. No converter changes are included; existing converters read semantic_model.metrics and that behaviour is untouched.
>> >
>> > Two open questions I'd particularly like input on
>> >
>> > -
>> > Should a dataset-scoped metric eventually be allowed to traverse relationships when the join path is declared explicitly? The PR takes the conservative position, since relaxing later is backward compatible while tightening is not.
>> >
>> > -
>> > Separately, I noticed the spec does not say whether one metric expression may reference another metric — there's no mechanism and no prohibition, while every system above has one. It's ambiguous rather than merely absent: SUM(orders.amount) / total_orders passes validation today because total_orders parses as an unqualified column. My inclination is to handle that separately since it needs aggregation-composition semantics settled, but the two features interact. The PR includes a sketch of what the reference-scoping rules would be, in case it's worth fixing those now for forward compatibility.
>> >
>> >
>> > Validation
>> >
>> > Three of the rules are semantic constraints JSON Schema cannot express, so validate.py gains checks for duplicate names within a dataset, collisions with model-scoped names, and cross-dataset references (table qualifiers extracted via sqlglot). Scope checking skips rather than guesses for dialects sqlglot cannot parse. The TPC-DS example is updated to demonstrate both placements.
>> >
>> > AI disclosure
>> >
>> > This contribution was developed with AI assistance (Snowflake Cortex Code). I have reviewed all changes and am personally responsible for the content per the ASF Generative Tooling Guidance.
>> >
>> > Feedback and pushback welcome. Minimum discussion period is 7 days per the contribution guidelines.
>> >
>> > Best,
>> > Josh Klahr
>> >
>> >
>> >
>> >
>> >
Re: [DISCUSS] Dataset-scoped metrics
Posted by Chris Eubank via dev <de...@ossie.apache.org>.
Hi all,
Thanks JB - that's a great summary of the proposed scope.
@Josh, would you be comfortable with me taking on the spec-wording update?
I'd be happy to take a first pass to help move this forward, preserving
your set of commits as the base. If there are no objections by Thursday,
I'll prepare an updated branch for us to iterate on. Hopefully, I'll have a
draft ready for a review pass later this week, and we can move toward a
vote from there.
@Kyoung Min, happy to coordinate on the validator changes you offered to
make - we can figure out the cleanest way to land them together.
Kind regards,
Chris
On Sun, Oct 4, 2026 at 10:46 PM Jean-Baptiste Onofré <jb...@nanthrax.net>
wrote:
> Hi all,
>
> Thanks Kyoung Min, that is spot on.
>
> The stat on passthrough fields (228 out of 306) illustrates the boundary
> separation perfectly: field expressions map physical source columns to
> logical fields, while metric expressions aggregate declared fields. Keeping
> that distinction clear and explicit in the spec text makes complete sense.
>
> I also fully agree with the single-part restriction in v1. Disallowing
> qualified identifiers in dataset-scoped metrics keeps things clean, and
> avoids any syntax collision with #486. It also leaves the door open for
> future extensions without breaking backward compatibility. As you noted, it
> also makes the validator check trivial and 100% verifiable offline.
>
> It seems we have a strong consensus on the narrowed scope:
> - Datasets may declare metrics (datasets[].metrics), sharing a unique
> namespace with fields within the dataset.
> - Dataset-scoped metric expressions reference declared fields only.
> - References without dataset-scoped metrics must be single-part
> (unqualified). The qualified references are rejected in v1.
>
> @Josh, if you are good with this direction, could you update the spec
> wording in #343 accordingly?
>
> @Kyoung Min, thanks for your offer to prepare commit against Josh's branch
> with the trimmed validator rules and test adjustements.
>
> Once the PR is updated and rebased, I will do a review pass and we should
> be in great shape to move to a formal spec vote.
>
> Regards
> JB
>
> On Sat, Oct 3, 2026 at 4:46 AM Kyoung Min Kim <ka...@gmail.com> wrote:
>
> > Chris, yes from me. Fields first, unqualified, with the precedence Justin
> > wrote down.
> >
> > On Justin's last two questions, the repository already answers the second
> > one in practice. In the example and fixture documents checked into the
> > repo,
> > 228 of the 306 field expressions are exactly the field's own name, the
> > passthrough field that exposes one source column under the same name
> > (30 of 31 in examples/tpcds_semantic_model.yaml). If field expressions
> took
> > the same field-first precedence as dataset metrics, every one of those
> > would
> > turn into a reference to itself. So field expressions have to keep
> > resolving
> > to source columns, which is what #486 states, and dataset metrics get the
> > field-first rule. The two kinds of expression end up with different rules
> > because they sit on different sides of the dataset boundary, not by
> > accident,
> > and I think that is fine as long as each rule is written next to the
> thing
> > it
> > governs. Field-to-field references, if they ever come, would need their
> own
> > spelling for the same reason.
> >
> > One thing I would pin down in the v1 text so the future extension stays
> > non-breaking. Justin's rule reaches a shadowed source column by
> qualifying
> > it, but the qualified spelling is already taken. Today #343 uses
> > `orders.foo`
> > for the declared field and bare `foo` for the column, the inverse of the
> > new
> > rule, and in #486 `dataset.field` means the field in a metric expression.
> > Whatever spelling a shadowed source column gets later cannot be that one.
> > For v1 the simplest guard is to say that a dataset-scoped metric
> > expression uses single-part identifiers only, each naming a declared
> field
> > of
> > that dataset, and that a qualified identifier there is an error until the
> > source-column form is defined. That is also the version the validator can
> > check completely: one lookup against the field list, same as the one #486
> > asks for in model-scoped metrics.
> >
> > On the validator itself, this narrows validate_metric_scoping rather than
> > extending it. The existence check moves from qualified references to bare
> > ones, the "qualified reference must name a declared field" check becomes
> > "no qualified reference", and the uniqueness and field-collision checks
> > stay
> > as they are. Josh, once the spec text in the PR reflects the above I can
> > send
> > that as a commit against your branch, with the tests adjusted to match.
> >
> > Kyoung Min Kim
> >
> >
> >
> > > 2026. 10. 2. 오후 1:16, Jean-Baptiste Onofré <jb...@nanthrax.net> wrote:
> > >
> > > Hi guys,
> > >
> > > Thanks Kyoung Min for the summary, and it's good to know that most of
> > > the validation is already in #343 (I did a couple of review already, I
> > > will do a new pass).
> > >
> > > Before we rework the validator, I think we need to close the point
> > > Chris and I left open: do the unqualified references resolve to source
> > > columns or to the dataset's declared fields? @Chris, I understand you
> > > meant source columns, for consistency with field expressions. I would
> > > still go with declared fields for this PR, for three reasons:
> > > 1. #486 makes model-scoped metrics see fields only. A dataset-scoped
> > > metric over a raw column would then have no model-level equivalent, so
> > > Josh's proposal doesn't work. With fields, we qualify each name with
> > > the dataset.
> > > 2. The reference becomes checkable. The field list is in the document,
> > > the source column's are not.
> > > 3. It answers Josh's collision question with the approach in #486: in
> > > a metric, a bare name is the field. If we later allow undeclared
> > > source columns as well, the field still wins, so that relax is
> > > backward compatible.
> > >
> > > The cost is real: a column used only in a metric has to be declared as
> > > a field, and importers from formats with per-entity measures would
> > > have to synthetise these fields. I think it's acceptable for a first
> > > version.
> > >
> > > @Kyoung Min, a commit against Josh's branch once the wording settles
> > > sounds good to me, if Josh is fine with it. Happy to review :)
> > >
> > > Regards
> > > JB
> > >
> > > On Fri, Oct 2, 2026 at 4:53 AM Kyoung Min Kim <ka...@gmail.com>
> > wrote:
> > >>
> > >> Two things from the validate.py side, since JB's third point and
> Josh's
> > >> question meet there.
> > >>
> > >> The current revision of #343 already carries most of what JB listed.
> > >> validate_metric_scoping reports a duplicate metric name within a
> > dataset and
> > >> a dataset-scoped metric that collides with a field as errors, a
> > model-scoped
> > >> metric that shadows a dataset-scoped one as a warning, and a qualified
> > >> reference that does not name a declared field as an error, with about
> > 300
> > >> lines of tests behind it. Keeping validation in the PR is therefore a
> > matter
> > >> of trimming those checks to whatever the narrowed rules end up being,
> > not of
> > >> writing them. The branch is a month behind main and conflicts in
> > validate.py,
> > >> spec.md, spec.yaml and both test files, so the rebase touches that
> code
> > >> anyway.
> > >>
> > >> On fields versus source columns, the validator can only ever check one
> > of the
> > >> two. The document lists a dataset's fields, so a reference to a
> > declared field
> > >> can be checked for existence. It does not list the columns of the
> > source, and
> > >> #343 says as much: "not checked, because that requires catalog
> metadata
> > the
> > >> model does not carry". If the single-part references in Chris's third
> > >> restriction resolve to source columns, the structural check shrinks to
> > >> uniqueness plus "is single-part". If they resolve to the dataset's
> > declared
> > >> fields, as JB read it, the existence check is the same lookup #486
> asks
> > for in
> > >> model-scoped metrics, where dataset.field must name a declared field
> > and a
> > >> source column is never visible to a metric expression. That reading
> also
> > >> removes the collision Josh is asking about, because a bare name in a
> > metric
> > >> would mean the field and nothing else, the way the tie-break sentence
> > in #486
> > >> settles it for qualified names.
> > >>
> > >> Josh, if trimming the validator to the narrowed rules is useful to
> hand
> > off, I
> > >> can prepare it as a commit against your branch or as a follow-up PR
> the
> > day
> > >> the spec text settles. If you would rather keep it in one PR, I will
> > review
> > >> that part.
> > >>
> > >> Kyoung Min Kim
> > >>
> > >>
> > >>>
> > >>> Hi all,
> > >>>
> > >>> I completely agree with Chris's proposal to tighten the scope here.
> > >>> Decoupling the placement (datasets[].metrics) from the larger,
> > >>> in-flight semantic questions (expression composition, traversal
> paths,
> > >>> grain locking) is the pragmatic way to get a clean spec vote (I love
> > >>> pragmatic approaches :)).
> > >>>
> > >>> A few specific thoughts on the three restrictions and the rollout:
> > >>> 1. Unqualified references and unified namespace are spot on.
> > >>> Single-part references (SUM(amount) instead of orders.amount) neatly
> > >>> eliminate the circularity issue in DDL/parser contexts that Justin
> > >>> pointed out. Enforcing a single namespace across fields and metrics
> > >>> within the dataset also elliminites the naming collision ambiguity.
> > >>> 2. In the wording, we should be explicit about whether "source
> fields"
> > >>> refers to the underlying physical table columns or the dataset's
> > >>> declared fields (datasets[].fields). In Ossie, metrics should
> > >>> aggregate the dataset's declared/modeled fields (or we need clear
> > >>> precedence if both are permitted). Tightening this phrasing will
> avoid
> > >>> confusion during the vote.
> > >>> 3. While I support deferring complex AST evaluation or cross-dialect
> > >>> query semantics to follow-up, I would prefer we do not strip the
> > >>> validation entirely from this PR. The JSON schema update
> > >>> (ossie-schema.json) and basic structural validation in validate.py
> > >>> (uniqueness across fields + metrics, checking for unqualified
> > >>> references, and null guards) should land together with the spec. In
> > >>> Ossie, the schema and validator act as our executable spec: keeping
> > >>> baseline structural checks ensures the vote lands on a solid,
> > >>> verifiable foundation.
> > >>>
> > >>> Overall, this narrowed scope gives us a safe, backward-compatible
> > >>> baseline that we can relax later one expression composition lands.
> > >>>
> > >>> Happy to review the updated PR and spec wording once ready.
> > >>>
> > >>> Regards
> > >>> JB
> > >>>
> > >>> On Tue, Sep 29, 2026 at 11:50 PM Chris Eubank
> > >>> <ch...@databricks.com> wrote:
> > >>>>
> > >>>> Hi Josh,
> > >>>>
> > >>>> Thank you for putting together this proposal for dataset-scoped
> > metrics. It's a fundamental building block for several in-flight
> > extensions: expression resolution / composability (thread), relational
> > query semantics (thread) and dimensional query semantics (thread).
> > >>>>
> > >>>> Several open comments remain, mostly concerning those follow-up
> > features (e.g. expression composition). I'm wondering if we can tighten
> > this PR's scope to land the minimal spec extension needed for a spec
> vote,
> > and defer the rest. The validation logic could even move to a follow-up
> PR.
> > >>>>
> > >>>> Concretely, I'd propose these three restrictions:
> > >>>>
> > >>>> 1. Datasets may declare metrics. Within a dataset, names must be
> > unique across fields and metrics.
> > >>>> 2. A dataset's metric expression may reference only that dataset's
> > source fields. We can lift this restriction once the
> expression-composition
> > rules land.
> > >>>> 3. Those references must be single-part (unqualified) within the
> > expression and must resolve to the dataset's source fields.
> > >>>>
> > >>>> Would this narrower scope work for you as a path to a spec vote? If
> > so, I'd be glad to help however is most useful - happy to pair on the
> > scoped wording or take a first pass for us to iterate on.
> > >>>>
> > >>>> Kind regards,
> > >>>> Chris
> > >>>>
> > >>>> On Thu, Aug 27, 2026 at 2:20 PM Khushboo Bhatia <kbhatia@apache.org
> >
> > wrote:
> > >>>>>
> > >>>>> Hey Josh,
> > >>>>> Thanks for starting this discussion. This issue has been raised in
> > multiple discussions on the repo.
> > >>>>>
> > >>>>> I am generally supportive of adding these. Leaving some comments on
> > the PR for the subtle differences between semantic model scoped metrics
> vs
> > dataset scoped.
> > >>>>>
> > >>>>> Khushboo
> > >>>>>
> > >>>>> On 2026/08/27 16:57:13 Klahr Joshua via dev wrote:
> > >>>>>>
> > >>>>>>
> > >>>>>>
> > >>>>>> Hi all,
> > >>>>>>
> > >>>>>> I'd like to propose allowing metrics to be declared on an
> > individual dataset (datasets[].metrics) in addition to the semantic model
> > (semantic_model.metrics), using the same metric structure in both
> > placements.
> > >>>>>>
> > >>>>>> PR: https://github.com/apache/ossie/pull/343
> > >>>>>>
> > >>>>>> Motivation
> > >>>>>>
> > >>>>>> Today every metric must be declared at the semantic model level,
> > even when it aggregates a single dataset's own fields. Three costs follow
> > from that.
> > >>>>>>
> > >>>>>> First, a dataset is not independently interpretable — you cannot
> > tell from a dataset what metrics exist over it without scanning the whole
> > model, and simple aggregations end up separated from the fields they
> depend
> > on.
> > >>>>>>
> > >>>>>> Second, and more concretely: round-trip fidelity through Ossie is
> > reduced. Several formats we convert from attach metrics to a specific
> > entity — Omni has per-view measures, Honeydew has per-entity metric
> files,
> > WisdomAI has per-table measures, and dbt MetricFlow binds metrics to a
> > semantic model. Because Ossie has no dataset-scoped placement, the
> > converters in this repository flatten all of those to the model level on
> > import, and the entity association is not represented in the resulting
> > document.
> > >>>>>>
> > >>>>>> On export, four converters independently reconstruct that
> > association by scanning expressions for dataset.column qualifiers and
> > falling back to the first dataset when none is found. The Honeydew
> > converter emits a runtime warning when it does so. Two converters stash
> the
> > owning entity in vendor-specific custom_extensions, which round-trips
> > within a single vendor's toolchain but is not something a generic Ossie
> > consumer can produce or interpret.
> > >>>>>>
> > >>>>>> Four converters reimplementing the same heuristic suggests the
> spec
> > is missing a place to put information the source formats already carry.
> > >>>>>>
> > >>>>>> Third, export placement can be coarser than the target supports.
> > The Snowflake converter maps every Ossie model-level metric to
> Snowflake's
> > top-level metrics block, including metrics referencing only one table
> that
> > would fit the narrower tables[].metrics position. The converter cannot
> > distinguish the two cases because the Ossie document does not record the
> > distinction.
> > >>>>>>
> > >>>>>> What's proposed
> > >>>>>>
> > >>>>>> datasets[].metrics, using the identical Metric structure. Scoping
> > rules:
> > >>>>>>
> > >>>>>> -
> > >>>>>> A dataset-scoped metric's expression MUST only reference fields of
> > its declaring dataset, and MUST NOT traverse relationships. Cross-dataset
> > metrics remain model-scoped.
> > >>>>>>
> > >>>>>> -
> > >>>>>> Names MUST be unique within their dataset — two datasets MAY each
> > declare item_count.
> > >>>>>>
> > >>>>>> -
> > >>>>>> A dataset-scoped name MUST NOT collide with any model-scoped
> metric
> > name.
> > >>>>>>
> > >>>>>> -
> > >>>>>> Referenced from outside the dataset as dataset_name.metric_name.
> > >>>>>>
> > >>>>>>
> > >>>>>> Worth stating explicitly because it's easy to misread: rule 1
> > constrains the expression, not the query. A dataset-scoped metric can
> still
> > be grouped by or filtered on dimensions from other datasets via
> > relationships, since grouping dimensions come from the consumer at query
> > time.
> > >>>>>>
> > >>>>>> Prior art
> > >>>>>>
> > >>>>>> Separating single-entity aggregations from cross-entity
> > calculations is established practice. Four of the five systems I looked
> at
> > do it — AtScale SML (metric bound to one dataset + column, with
> metric_calc
> > as a separate type), Cube (measures within cubes), dbt MetricFlow
> (in-model
> > vs top-level), and Snowflake semantic views (tables[].metrics vs
> top-level
> > derived). Databricks UC metric views take a different approach, with one
> > flat scope per view and joins declared inside it.
> > >>>>>>
> > >>>>>> On naming, the proposal extends Ossie's own existing convention
> > rather than importing one: field names are already unique within a
> dataset
> > rather than globally, and metric expressions already reference them as
> > dataset.field. Applying the same scoped-uniqueness and
> qualified-reference
> > pattern to metrics keeps the model internally consistent.
> > >>>>>>
> > >>>>>> On strictness it sits within the range the ecosystem spans — SML
> is
> > more restrictive (one column, one aggregation method), Snowflake semantic
> > views more permissive (table-level metrics may traverse relationships
> with
> > a declared join path).
> > >>>>>>
> > >>>>>> Compatibility
> > >>>>>>
> > >>>>>> Additive and backward compatible. datasets[].metrics is optional
> > and existing models remain valid unchanged. Because a dataset-scoped
> > expression resolves within its own dataset, it is already valid as a
> > model-scoped metric, so consumers wanting a single flat namespace can
> hoist
> > without rewriting expressions. No converter changes are included;
> existing
> > converters read semantic_model.metrics and that behaviour is untouched.
> > >>>>>>
> > >>>>>> Two open questions I'd particularly like input on
> > >>>>>>
> > >>>>>> -
> > >>>>>> Should a dataset-scoped metric eventually be allowed to traverse
> > relationships when the join path is declared explicitly? The PR takes the
> > conservative position, since relaxing later is backward compatible while
> > tightening is not.
> > >>>>>>
> > >>>>>> -
> > >>>>>> Separately, I noticed the spec does not say whether one metric
> > expression may reference another metric — there's no mechanism and no
> > prohibition, while every system above has one. It's ambiguous rather than
> > merely absent: SUM(orders.amount) / total_orders passes validation today
> > because total_orders parses as an unqualified column. My inclination is
> to
> > handle that separately since it needs aggregation-composition semantics
> > settled, but the two features interact. The PR includes a sketch of what
> > the reference-scoping rules would be, in case it's worth fixing those now
> > for forward compatibility.
> > >>>>>>
> > >>>>>>
> > >>>>>> Validation
> > >>>>>>
> > >>>>>> Three of the rules are semantic constraints JSON Schema cannot
> > express, so validate.py gains checks for duplicate names within a
> dataset,
> > collisions with model-scoped names, and cross-dataset references (table
> > qualifiers extracted via sqlglot). Scope checking skips rather than
> guesses
> > for dialects sqlglot cannot parse. The TPC-DS example is updated to
> > demonstrate both placements.
> > >>>>>>
> > >>>>>> AI disclosure
> > >>>>>>
> > >>>>>> This contribution was developed with AI assistance (Snowflake
> > Cortex Code). I have reviewed all changes and am personally responsible
> for
> > the content per the ASF Generative Tooling Guidance.
> > >>>>>>
> > >>>>>> Feedback and pushback welcome. Minimum discussion period is 7 days
> > per the contribution guidelines.
> > >>>>>>
> > >>>>>> Best,
> > >>>>>> Josh Klahr
> > >>>>>>
> > >>>>>>
> > >>>>>>
> > >>>>>>
> > >>>>>>
> > >>
> >
> >
>
Re: [DISCUSS] Dataset-scoped metrics
Posted by Chris Eubank via dev <de...@ossie.apache.org>.
Hi all,
I've prepared https://github.com/apache/ossie/pull/591, which builds on top
of #343 to reflect the consensus in the thread (add dataset-scoped metrics,
require them to reference dataset fields using unqualified names).
Kind regards,
Chris
On Tue, Oct 13, 2026 at 12:35 AM Jean-Baptiste Onofré <jb...@nanthrax.net>
wrote:
> Hi everyone,
>
> I'm completely fine narrowing the validator. It makes sense to move the
> existing check to bare references and making qualified references an error.
> Since this tightens what the spec accepts, could we:
> - state the rule explicitly in the spec text
> - add a conformance test for the qualified-reference error case?
>
> Imlho, about the [Unique] Warning, strings would be treated as errors now
> that warnings are ValidationWarning instances, so validate.py should exit
> 1. It would be great to tackle that before merging :)
> Regarding the double parsing, I would prefer merging _parse_expression into
> validate_sql_expression over mirroring the multi-statement handling from
> #527. That way, there's one parsing path to keep correct.
>
> Happy to review PRs once ready ;)
>
> Regards
> JB
>
> On Tue, Oct 13, 2026 at 6:27 AM Kyoung Min Kim <ka...@gmail.com> wrote:
>
> > Chris, that works for me, and thanks for picking up the wording.
> >
> > Once your branch is up I will send the validator narrowing as a pull
> > request against that branch, with the tests adjusted, so it lands in #343
> > as one commit on top of yours rather than a parallel PR. The change
> itself
> > is the one from my earlier note: the existence check moves from qualified
> > references to bare ones, qualified references become an error, uniqueness
> > and field-collision checks stay.
> >
> > One thing to fold into the same pass, since the branch is being rebuilt
> > anyway. The validator half of #343 predates two changes on main. Warnings
> > are now instances of ValidationWarning rather than a "Warning:" prefix in
> > the message text, so the "[Unique] Warning:" strings in the PR would
> count
> > as errors and make validate.py exit 1. The PR also parses each expression
> > a second time in its own _parse_expression, so the multi-statement
> handling
> > #527 added to validate_sql_expression needs mirroring there, or the two
> > helpers merged. I can take both along with the narrowing so the validator
> > commit is the only place that has to know.
> >
> > Kyoung Min Kim
> >
> >
>
Re: [DISCUSS] Dataset-scoped metrics
Posted by Jean-Baptiste Onofré <jb...@nanthrax.net>.
Hi everyone,
I'm completely fine narrowing the validator. It makes sense to move the
existing check to bare references and making qualified references an error.
Since this tightens what the spec accepts, could we:
- state the rule explicitly in the spec text
- add a conformance test for the qualified-reference error case?
Imlho, about the [Unique] Warning, strings would be treated as errors now
that warnings are ValidationWarning instances, so validate.py should exit
1. It would be great to tackle that before merging :)
Regarding the double parsing, I would prefer merging _parse_expression into
validate_sql_expression over mirroring the multi-statement handling from
#527. That way, there's one parsing path to keep correct.
Happy to review PRs once ready ;)
Regards
JB
On Tue, Oct 13, 2026 at 6:27 AM Kyoung Min Kim <ka...@gmail.com> wrote:
> Chris, that works for me, and thanks for picking up the wording.
>
> Once your branch is up I will send the validator narrowing as a pull
> request against that branch, with the tests adjusted, so it lands in #343
> as one commit on top of yours rather than a parallel PR. The change itself
> is the one from my earlier note: the existence check moves from qualified
> references to bare ones, qualified references become an error, uniqueness
> and field-collision checks stay.
>
> One thing to fold into the same pass, since the branch is being rebuilt
> anyway. The validator half of #343 predates two changes on main. Warnings
> are now instances of ValidationWarning rather than a "Warning:" prefix in
> the message text, so the "[Unique] Warning:" strings in the PR would count
> as errors and make validate.py exit 1. The PR also parses each expression
> a second time in its own _parse_expression, so the multi-statement handling
> #527 added to validate_sql_expression needs mirroring there, or the two
> helpers merged. I can take both along with the narrowing so the validator
> commit is the only place that has to know.
>
> Kyoung Min Kim
>
>
Re: [DISCUSS] Dataset-scoped metrics
Posted by Kyoung Min Kim <ka...@gmail.com>.
Chris, that works for me, and thanks for picking up the wording.
Once your branch is up I will send the validator narrowing as a pull
request against that branch, with the tests adjusted, so it lands in #343
as one commit on top of yours rather than a parallel PR. The change itself
is the one from my earlier note: the existence check moves from qualified
references to bare ones, qualified references become an error, uniqueness
and field-collision checks stay.
One thing to fold into the same pass, since the branch is being rebuilt
anyway. The validator half of #343 predates two changes on main. Warnings
are now instances of ValidationWarning rather than a "Warning:" prefix in
the message text, so the "[Unique] Warning:" strings in the PR would count
as errors and make validate.py exit 1. The PR also parses each expression
a second time in its own _parse_expression, so the multi-statement handling
#527 added to validate_sql_expression needs mirroring there, or the two
helpers merged. I can take both along with the narrowing so the validator
commit is the only place that has to know.
Kyoung Min Kim
Re: [DISCUSS] Dataset-scoped metrics
Posted by Jean-Baptiste Onofré <jb...@nanthrax.net>.
Hi all,
Thanks Kyoung Min, that is spot on.
The stat on passthrough fields (228 out of 306) illustrates the boundary
separation perfectly: field expressions map physical source columns to
logical fields, while metric expressions aggregate declared fields. Keeping
that distinction clear and explicit in the spec text makes complete sense.
I also fully agree with the single-part restriction in v1. Disallowing
qualified identifiers in dataset-scoped metrics keeps things clean, and
avoids any syntax collision with #486. It also leaves the door open for
future extensions without breaking backward compatibility. As you noted, it
also makes the validator check trivial and 100% verifiable offline.
It seems we have a strong consensus on the narrowed scope:
- Datasets may declare metrics (datasets[].metrics), sharing a unique
namespace with fields within the dataset.
- Dataset-scoped metric expressions reference declared fields only.
- References without dataset-scoped metrics must be single-part
(unqualified). The qualified references are rejected in v1.
@Josh, if you are good with this direction, could you update the spec
wording in #343 accordingly?
@Kyoung Min, thanks for your offer to prepare commit against Josh's branch
with the trimmed validator rules and test adjustements.
Once the PR is updated and rebased, I will do a review pass and we should
be in great shape to move to a formal spec vote.
Regards
JB
On Sat, Oct 3, 2026 at 4:46 AM Kyoung Min Kim <ka...@gmail.com> wrote:
> Chris, yes from me. Fields first, unqualified, with the precedence Justin
> wrote down.
>
> On Justin's last two questions, the repository already answers the second
> one in practice. In the example and fixture documents checked into the
> repo,
> 228 of the 306 field expressions are exactly the field's own name, the
> passthrough field that exposes one source column under the same name
> (30 of 31 in examples/tpcds_semantic_model.yaml). If field expressions took
> the same field-first precedence as dataset metrics, every one of those
> would
> turn into a reference to itself. So field expressions have to keep
> resolving
> to source columns, which is what #486 states, and dataset metrics get the
> field-first rule. The two kinds of expression end up with different rules
> because they sit on different sides of the dataset boundary, not by
> accident,
> and I think that is fine as long as each rule is written next to the thing
> it
> governs. Field-to-field references, if they ever come, would need their own
> spelling for the same reason.
>
> One thing I would pin down in the v1 text so the future extension stays
> non-breaking. Justin's rule reaches a shadowed source column by qualifying
> it, but the qualified spelling is already taken. Today #343 uses
> `orders.foo`
> for the declared field and bare `foo` for the column, the inverse of the
> new
> rule, and in #486 `dataset.field` means the field in a metric expression.
> Whatever spelling a shadowed source column gets later cannot be that one.
> For v1 the simplest guard is to say that a dataset-scoped metric
> expression uses single-part identifiers only, each naming a declared field
> of
> that dataset, and that a qualified identifier there is an error until the
> source-column form is defined. That is also the version the validator can
> check completely: one lookup against the field list, same as the one #486
> asks for in model-scoped metrics.
>
> On the validator itself, this narrows validate_metric_scoping rather than
> extending it. The existence check moves from qualified references to bare
> ones, the "qualified reference must name a declared field" check becomes
> "no qualified reference", and the uniqueness and field-collision checks
> stay
> as they are. Josh, once the spec text in the PR reflects the above I can
> send
> that as a commit against your branch, with the tests adjusted to match.
>
> Kyoung Min Kim
>
>
>
> > 2026. 10. 2. 오후 1:16, Jean-Baptiste Onofré <jb...@nanthrax.net> wrote:
> >
> > Hi guys,
> >
> > Thanks Kyoung Min for the summary, and it's good to know that most of
> > the validation is already in #343 (I did a couple of review already, I
> > will do a new pass).
> >
> > Before we rework the validator, I think we need to close the point
> > Chris and I left open: do the unqualified references resolve to source
> > columns or to the dataset's declared fields? @Chris, I understand you
> > meant source columns, for consistency with field expressions. I would
> > still go with declared fields for this PR, for three reasons:
> > 1. #486 makes model-scoped metrics see fields only. A dataset-scoped
> > metric over a raw column would then have no model-level equivalent, so
> > Josh's proposal doesn't work. With fields, we qualify each name with
> > the dataset.
> > 2. The reference becomes checkable. The field list is in the document,
> > the source column's are not.
> > 3. It answers Josh's collision question with the approach in #486: in
> > a metric, a bare name is the field. If we later allow undeclared
> > source columns as well, the field still wins, so that relax is
> > backward compatible.
> >
> > The cost is real: a column used only in a metric has to be declared as
> > a field, and importers from formats with per-entity measures would
> > have to synthetise these fields. I think it's acceptable for a first
> > version.
> >
> > @Kyoung Min, a commit against Josh's branch once the wording settles
> > sounds good to me, if Josh is fine with it. Happy to review :)
> >
> > Regards
> > JB
> >
> > On Fri, Oct 2, 2026 at 4:53 AM Kyoung Min Kim <ka...@gmail.com>
> wrote:
> >>
> >> Two things from the validate.py side, since JB's third point and Josh's
> >> question meet there.
> >>
> >> The current revision of #343 already carries most of what JB listed.
> >> validate_metric_scoping reports a duplicate metric name within a
> dataset and
> >> a dataset-scoped metric that collides with a field as errors, a
> model-scoped
> >> metric that shadows a dataset-scoped one as a warning, and a qualified
> >> reference that does not name a declared field as an error, with about
> 300
> >> lines of tests behind it. Keeping validation in the PR is therefore a
> matter
> >> of trimming those checks to whatever the narrowed rules end up being,
> not of
> >> writing them. The branch is a month behind main and conflicts in
> validate.py,
> >> spec.md, spec.yaml and both test files, so the rebase touches that code
> >> anyway.
> >>
> >> On fields versus source columns, the validator can only ever check one
> of the
> >> two. The document lists a dataset's fields, so a reference to a
> declared field
> >> can be checked for existence. It does not list the columns of the
> source, and
> >> #343 says as much: "not checked, because that requires catalog metadata
> the
> >> model does not carry". If the single-part references in Chris's third
> >> restriction resolve to source columns, the structural check shrinks to
> >> uniqueness plus "is single-part". If they resolve to the dataset's
> declared
> >> fields, as JB read it, the existence check is the same lookup #486 asks
> for in
> >> model-scoped metrics, where dataset.field must name a declared field
> and a
> >> source column is never visible to a metric expression. That reading also
> >> removes the collision Josh is asking about, because a bare name in a
> metric
> >> would mean the field and nothing else, the way the tie-break sentence
> in #486
> >> settles it for qualified names.
> >>
> >> Josh, if trimming the validator to the narrowed rules is useful to hand
> off, I
> >> can prepare it as a commit against your branch or as a follow-up PR the
> day
> >> the spec text settles. If you would rather keep it in one PR, I will
> review
> >> that part.
> >>
> >> Kyoung Min Kim
> >>
> >>
> >>>
> >>> Hi all,
> >>>
> >>> I completely agree with Chris's proposal to tighten the scope here.
> >>> Decoupling the placement (datasets[].metrics) from the larger,
> >>> in-flight semantic questions (expression composition, traversal paths,
> >>> grain locking) is the pragmatic way to get a clean spec vote (I love
> >>> pragmatic approaches :)).
> >>>
> >>> A few specific thoughts on the three restrictions and the rollout:
> >>> 1. Unqualified references and unified namespace are spot on.
> >>> Single-part references (SUM(amount) instead of orders.amount) neatly
> >>> eliminate the circularity issue in DDL/parser contexts that Justin
> >>> pointed out. Enforcing a single namespace across fields and metrics
> >>> within the dataset also elliminites the naming collision ambiguity.
> >>> 2. In the wording, we should be explicit about whether "source fields"
> >>> refers to the underlying physical table columns or the dataset's
> >>> declared fields (datasets[].fields). In Ossie, metrics should
> >>> aggregate the dataset's declared/modeled fields (or we need clear
> >>> precedence if both are permitted). Tightening this phrasing will avoid
> >>> confusion during the vote.
> >>> 3. While I support deferring complex AST evaluation or cross-dialect
> >>> query semantics to follow-up, I would prefer we do not strip the
> >>> validation entirely from this PR. The JSON schema update
> >>> (ossie-schema.json) and basic structural validation in validate.py
> >>> (uniqueness across fields + metrics, checking for unqualified
> >>> references, and null guards) should land together with the spec. In
> >>> Ossie, the schema and validator act as our executable spec: keeping
> >>> baseline structural checks ensures the vote lands on a solid,
> >>> verifiable foundation.
> >>>
> >>> Overall, this narrowed scope gives us a safe, backward-compatible
> >>> baseline that we can relax later one expression composition lands.
> >>>
> >>> Happy to review the updated PR and spec wording once ready.
> >>>
> >>> Regards
> >>> JB
> >>>
> >>> On Tue, Sep 29, 2026 at 11:50 PM Chris Eubank
> >>> <ch...@databricks.com> wrote:
> >>>>
> >>>> Hi Josh,
> >>>>
> >>>> Thank you for putting together this proposal for dataset-scoped
> metrics. It's a fundamental building block for several in-flight
> extensions: expression resolution / composability (thread), relational
> query semantics (thread) and dimensional query semantics (thread).
> >>>>
> >>>> Several open comments remain, mostly concerning those follow-up
> features (e.g. expression composition). I'm wondering if we can tighten
> this PR's scope to land the minimal spec extension needed for a spec vote,
> and defer the rest. The validation logic could even move to a follow-up PR.
> >>>>
> >>>> Concretely, I'd propose these three restrictions:
> >>>>
> >>>> 1. Datasets may declare metrics. Within a dataset, names must be
> unique across fields and metrics.
> >>>> 2. A dataset's metric expression may reference only that dataset's
> source fields. We can lift this restriction once the expression-composition
> rules land.
> >>>> 3. Those references must be single-part (unqualified) within the
> expression and must resolve to the dataset's source fields.
> >>>>
> >>>> Would this narrower scope work for you as a path to a spec vote? If
> so, I'd be glad to help however is most useful - happy to pair on the
> scoped wording or take a first pass for us to iterate on.
> >>>>
> >>>> Kind regards,
> >>>> Chris
> >>>>
> >>>> On Thu, Aug 27, 2026 at 2:20 PM Khushboo Bhatia <kb...@apache.org>
> wrote:
> >>>>>
> >>>>> Hey Josh,
> >>>>> Thanks for starting this discussion. This issue has been raised in
> multiple discussions on the repo.
> >>>>>
> >>>>> I am generally supportive of adding these. Leaving some comments on
> the PR for the subtle differences between semantic model scoped metrics vs
> dataset scoped.
> >>>>>
> >>>>> Khushboo
> >>>>>
> >>>>> On 2026/08/27 16:57:13 Klahr Joshua via dev wrote:
> >>>>>>
> >>>>>>
> >>>>>>
> >>>>>> Hi all,
> >>>>>>
> >>>>>> I'd like to propose allowing metrics to be declared on an
> individual dataset (datasets[].metrics) in addition to the semantic model
> (semantic_model.metrics), using the same metric structure in both
> placements.
> >>>>>>
> >>>>>> PR: https://github.com/apache/ossie/pull/343
> >>>>>>
> >>>>>> Motivation
> >>>>>>
> >>>>>> Today every metric must be declared at the semantic model level,
> even when it aggregates a single dataset's own fields. Three costs follow
> from that.
> >>>>>>
> >>>>>> First, a dataset is not independently interpretable — you cannot
> tell from a dataset what metrics exist over it without scanning the whole
> model, and simple aggregations end up separated from the fields they depend
> on.
> >>>>>>
> >>>>>> Second, and more concretely: round-trip fidelity through Ossie is
> reduced. Several formats we convert from attach metrics to a specific
> entity — Omni has per-view measures, Honeydew has per-entity metric files,
> WisdomAI has per-table measures, and dbt MetricFlow binds metrics to a
> semantic model. Because Ossie has no dataset-scoped placement, the
> converters in this repository flatten all of those to the model level on
> import, and the entity association is not represented in the resulting
> document.
> >>>>>>
> >>>>>> On export, four converters independently reconstruct that
> association by scanning expressions for dataset.column qualifiers and
> falling back to the first dataset when none is found. The Honeydew
> converter emits a runtime warning when it does so. Two converters stash the
> owning entity in vendor-specific custom_extensions, which round-trips
> within a single vendor's toolchain but is not something a generic Ossie
> consumer can produce or interpret.
> >>>>>>
> >>>>>> Four converters reimplementing the same heuristic suggests the spec
> is missing a place to put information the source formats already carry.
> >>>>>>
> >>>>>> Third, export placement can be coarser than the target supports.
> The Snowflake converter maps every Ossie model-level metric to Snowflake's
> top-level metrics block, including metrics referencing only one table that
> would fit the narrower tables[].metrics position. The converter cannot
> distinguish the two cases because the Ossie document does not record the
> distinction.
> >>>>>>
> >>>>>> What's proposed
> >>>>>>
> >>>>>> datasets[].metrics, using the identical Metric structure. Scoping
> rules:
> >>>>>>
> >>>>>> -
> >>>>>> A dataset-scoped metric's expression MUST only reference fields of
> its declaring dataset, and MUST NOT traverse relationships. Cross-dataset
> metrics remain model-scoped.
> >>>>>>
> >>>>>> -
> >>>>>> Names MUST be unique within their dataset — two datasets MAY each
> declare item_count.
> >>>>>>
> >>>>>> -
> >>>>>> A dataset-scoped name MUST NOT collide with any model-scoped metric
> name.
> >>>>>>
> >>>>>> -
> >>>>>> Referenced from outside the dataset as dataset_name.metric_name.
> >>>>>>
> >>>>>>
> >>>>>> Worth stating explicitly because it's easy to misread: rule 1
> constrains the expression, not the query. A dataset-scoped metric can still
> be grouped by or filtered on dimensions from other datasets via
> relationships, since grouping dimensions come from the consumer at query
> time.
> >>>>>>
> >>>>>> Prior art
> >>>>>>
> >>>>>> Separating single-entity aggregations from cross-entity
> calculations is established practice. Four of the five systems I looked at
> do it — AtScale SML (metric bound to one dataset + column, with metric_calc
> as a separate type), Cube (measures within cubes), dbt MetricFlow (in-model
> vs top-level), and Snowflake semantic views (tables[].metrics vs top-level
> derived). Databricks UC metric views take a different approach, with one
> flat scope per view and joins declared inside it.
> >>>>>>
> >>>>>> On naming, the proposal extends Ossie's own existing convention
> rather than importing one: field names are already unique within a dataset
> rather than globally, and metric expressions already reference them as
> dataset.field. Applying the same scoped-uniqueness and qualified-reference
> pattern to metrics keeps the model internally consistent.
> >>>>>>
> >>>>>> On strictness it sits within the range the ecosystem spans — SML is
> more restrictive (one column, one aggregation method), Snowflake semantic
> views more permissive (table-level metrics may traverse relationships with
> a declared join path).
> >>>>>>
> >>>>>> Compatibility
> >>>>>>
> >>>>>> Additive and backward compatible. datasets[].metrics is optional
> and existing models remain valid unchanged. Because a dataset-scoped
> expression resolves within its own dataset, it is already valid as a
> model-scoped metric, so consumers wanting a single flat namespace can hoist
> without rewriting expressions. No converter changes are included; existing
> converters read semantic_model.metrics and that behaviour is untouched.
> >>>>>>
> >>>>>> Two open questions I'd particularly like input on
> >>>>>>
> >>>>>> -
> >>>>>> Should a dataset-scoped metric eventually be allowed to traverse
> relationships when the join path is declared explicitly? The PR takes the
> conservative position, since relaxing later is backward compatible while
> tightening is not.
> >>>>>>
> >>>>>> -
> >>>>>> Separately, I noticed the spec does not say whether one metric
> expression may reference another metric — there's no mechanism and no
> prohibition, while every system above has one. It's ambiguous rather than
> merely absent: SUM(orders.amount) / total_orders passes validation today
> because total_orders parses as an unqualified column. My inclination is to
> handle that separately since it needs aggregation-composition semantics
> settled, but the two features interact. The PR includes a sketch of what
> the reference-scoping rules would be, in case it's worth fixing those now
> for forward compatibility.
> >>>>>>
> >>>>>>
> >>>>>> Validation
> >>>>>>
> >>>>>> Three of the rules are semantic constraints JSON Schema cannot
> express, so validate.py gains checks for duplicate names within a dataset,
> collisions with model-scoped names, and cross-dataset references (table
> qualifiers extracted via sqlglot). Scope checking skips rather than guesses
> for dialects sqlglot cannot parse. The TPC-DS example is updated to
> demonstrate both placements.
> >>>>>>
> >>>>>> AI disclosure
> >>>>>>
> >>>>>> This contribution was developed with AI assistance (Snowflake
> Cortex Code). I have reviewed all changes and am personally responsible for
> the content per the ASF Generative Tooling Guidance.
> >>>>>>
> >>>>>> Feedback and pushback welcome. Minimum discussion period is 7 days
> per the contribution guidelines.
> >>>>>>
> >>>>>> Best,
> >>>>>> Josh Klahr
> >>>>>>
> >>>>>>
> >>>>>>
> >>>>>>
> >>>>>>
> >>
>
>
Re: [DISCUSS] Dataset-scoped metrics
Posted by Kyoung Min Kim <ka...@gmail.com>.
Chris, yes from me. Fields first, unqualified, with the precedence Justin
wrote down.
On Justin's last two questions, the repository already answers the second
one in practice. In the example and fixture documents checked into the repo,
228 of the 306 field expressions are exactly the field's own name, the
passthrough field that exposes one source column under the same name
(30 of 31 in examples/tpcds_semantic_model.yaml). If field expressions took
the same field-first precedence as dataset metrics, every one of those would
turn into a reference to itself. So field expressions have to keep resolving
to source columns, which is what #486 states, and dataset metrics get the
field-first rule. The two kinds of expression end up with different rules
because they sit on different sides of the dataset boundary, not by accident,
and I think that is fine as long as each rule is written next to the thing it
governs. Field-to-field references, if they ever come, would need their own
spelling for the same reason.
One thing I would pin down in the v1 text so the future extension stays
non-breaking. Justin's rule reaches a shadowed source column by qualifying
it, but the qualified spelling is already taken. Today #343 uses `orders.foo`
for the declared field and bare `foo` for the column, the inverse of the new
rule, and in #486 `dataset.field` means the field in a metric expression.
Whatever spelling a shadowed source column gets later cannot be that one.
For v1 the simplest guard is to say that a dataset-scoped metric
expression uses single-part identifiers only, each naming a declared field of
that dataset, and that a qualified identifier there is an error until the
source-column form is defined. That is also the version the validator can
check completely: one lookup against the field list, same as the one #486
asks for in model-scoped metrics.
On the validator itself, this narrows validate_metric_scoping rather than
extending it. The existence check moves from qualified references to bare
ones, the "qualified reference must name a declared field" check becomes
"no qualified reference", and the uniqueness and field-collision checks stay
as they are. Josh, once the spec text in the PR reflects the above I can send
that as a commit against your branch, with the tests adjusted to match.
Kyoung Min Kim
> 2026. 10. 2. 오후 1:16, Jean-Baptiste Onofré <jb...@nanthrax.net> wrote:
>
> Hi guys,
>
> Thanks Kyoung Min for the summary, and it's good to know that most of
> the validation is already in #343 (I did a couple of review already, I
> will do a new pass).
>
> Before we rework the validator, I think we need to close the point
> Chris and I left open: do the unqualified references resolve to source
> columns or to the dataset's declared fields? @Chris, I understand you
> meant source columns, for consistency with field expressions. I would
> still go with declared fields for this PR, for three reasons:
> 1. #486 makes model-scoped metrics see fields only. A dataset-scoped
> metric over a raw column would then have no model-level equivalent, so
> Josh's proposal doesn't work. With fields, we qualify each name with
> the dataset.
> 2. The reference becomes checkable. The field list is in the document,
> the source column's are not.
> 3. It answers Josh's collision question with the approach in #486: in
> a metric, a bare name is the field. If we later allow undeclared
> source columns as well, the field still wins, so that relax is
> backward compatible.
>
> The cost is real: a column used only in a metric has to be declared as
> a field, and importers from formats with per-entity measures would
> have to synthetise these fields. I think it's acceptable for a first
> version.
>
> @Kyoung Min, a commit against Josh's branch once the wording settles
> sounds good to me, if Josh is fine with it. Happy to review :)
>
> Regards
> JB
>
> On Fri, Oct 2, 2026 at 4:53 AM Kyoung Min Kim <ka...@gmail.com> wrote:
>>
>> Two things from the validate.py side, since JB's third point and Josh's
>> question meet there.
>>
>> The current revision of #343 already carries most of what JB listed.
>> validate_metric_scoping reports a duplicate metric name within a dataset and
>> a dataset-scoped metric that collides with a field as errors, a model-scoped
>> metric that shadows a dataset-scoped one as a warning, and a qualified
>> reference that does not name a declared field as an error, with about 300
>> lines of tests behind it. Keeping validation in the PR is therefore a matter
>> of trimming those checks to whatever the narrowed rules end up being, not of
>> writing them. The branch is a month behind main and conflicts in validate.py,
>> spec.md, spec.yaml and both test files, so the rebase touches that code
>> anyway.
>>
>> On fields versus source columns, the validator can only ever check one of the
>> two. The document lists a dataset's fields, so a reference to a declared field
>> can be checked for existence. It does not list the columns of the source, and
>> #343 says as much: "not checked, because that requires catalog metadata the
>> model does not carry". If the single-part references in Chris's third
>> restriction resolve to source columns, the structural check shrinks to
>> uniqueness plus "is single-part". If they resolve to the dataset's declared
>> fields, as JB read it, the existence check is the same lookup #486 asks for in
>> model-scoped metrics, where dataset.field must name a declared field and a
>> source column is never visible to a metric expression. That reading also
>> removes the collision Josh is asking about, because a bare name in a metric
>> would mean the field and nothing else, the way the tie-break sentence in #486
>> settles it for qualified names.
>>
>> Josh, if trimming the validator to the narrowed rules is useful to hand off, I
>> can prepare it as a commit against your branch or as a follow-up PR the day
>> the spec text settles. If you would rather keep it in one PR, I will review
>> that part.
>>
>> Kyoung Min Kim
>>
>>
>>>
>>> Hi all,
>>>
>>> I completely agree with Chris's proposal to tighten the scope here.
>>> Decoupling the placement (datasets[].metrics) from the larger,
>>> in-flight semantic questions (expression composition, traversal paths,
>>> grain locking) is the pragmatic way to get a clean spec vote (I love
>>> pragmatic approaches :)).
>>>
>>> A few specific thoughts on the three restrictions and the rollout:
>>> 1. Unqualified references and unified namespace are spot on.
>>> Single-part references (SUM(amount) instead of orders.amount) neatly
>>> eliminate the circularity issue in DDL/parser contexts that Justin
>>> pointed out. Enforcing a single namespace across fields and metrics
>>> within the dataset also elliminites the naming collision ambiguity.
>>> 2. In the wording, we should be explicit about whether "source fields"
>>> refers to the underlying physical table columns or the dataset's
>>> declared fields (datasets[].fields). In Ossie, metrics should
>>> aggregate the dataset's declared/modeled fields (or we need clear
>>> precedence if both are permitted). Tightening this phrasing will avoid
>>> confusion during the vote.
>>> 3. While I support deferring complex AST evaluation or cross-dialect
>>> query semantics to follow-up, I would prefer we do not strip the
>>> validation entirely from this PR. The JSON schema update
>>> (ossie-schema.json) and basic structural validation in validate.py
>>> (uniqueness across fields + metrics, checking for unqualified
>>> references, and null guards) should land together with the spec. In
>>> Ossie, the schema and validator act as our executable spec: keeping
>>> baseline structural checks ensures the vote lands on a solid,
>>> verifiable foundation.
>>>
>>> Overall, this narrowed scope gives us a safe, backward-compatible
>>> baseline that we can relax later one expression composition lands.
>>>
>>> Happy to review the updated PR and spec wording once ready.
>>>
>>> Regards
>>> JB
>>>
>>> On Tue, Sep 29, 2026 at 11:50 PM Chris Eubank
>>> <ch...@databricks.com> wrote:
>>>>
>>>> Hi Josh,
>>>>
>>>> Thank you for putting together this proposal for dataset-scoped metrics. It's a fundamental building block for several in-flight extensions: expression resolution / composability (thread), relational query semantics (thread) and dimensional query semantics (thread).
>>>>
>>>> Several open comments remain, mostly concerning those follow-up features (e.g. expression composition). I'm wondering if we can tighten this PR's scope to land the minimal spec extension needed for a spec vote, and defer the rest. The validation logic could even move to a follow-up PR.
>>>>
>>>> Concretely, I'd propose these three restrictions:
>>>>
>>>> 1. Datasets may declare metrics. Within a dataset, names must be unique across fields and metrics.
>>>> 2. A dataset's metric expression may reference only that dataset's source fields. We can lift this restriction once the expression-composition rules land.
>>>> 3. Those references must be single-part (unqualified) within the expression and must resolve to the dataset's source fields.
>>>>
>>>> Would this narrower scope work for you as a path to a spec vote? If so, I'd be glad to help however is most useful - happy to pair on the scoped wording or take a first pass for us to iterate on.
>>>>
>>>> Kind regards,
>>>> Chris
>>>>
>>>> On Thu, Aug 27, 2026 at 2:20 PM Khushboo Bhatia <kb...@apache.org> wrote:
>>>>>
>>>>> Hey Josh,
>>>>> Thanks for starting this discussion. This issue has been raised in multiple discussions on the repo.
>>>>>
>>>>> I am generally supportive of adding these. Leaving some comments on the PR for the subtle differences between semantic model scoped metrics vs dataset scoped.
>>>>>
>>>>> Khushboo
>>>>>
>>>>> On 2026/08/27 16:57:13 Klahr Joshua via dev wrote:
>>>>>>
>>>>>>
>>>>>>
>>>>>> Hi all,
>>>>>>
>>>>>> I'd like to propose allowing metrics to be declared on an individual dataset (datasets[].metrics) in addition to the semantic model (semantic_model.metrics), using the same metric structure in both placements.
>>>>>>
>>>>>> PR: https://github.com/apache/ossie/pull/343
>>>>>>
>>>>>> Motivation
>>>>>>
>>>>>> Today every metric must be declared at the semantic model level, even when it aggregates a single dataset's own fields. Three costs follow from that.
>>>>>>
>>>>>> First, a dataset is not independently interpretable — you cannot tell from a dataset what metrics exist over it without scanning the whole model, and simple aggregations end up separated from the fields they depend on.
>>>>>>
>>>>>> Second, and more concretely: round-trip fidelity through Ossie is reduced. Several formats we convert from attach metrics to a specific entity — Omni has per-view measures, Honeydew has per-entity metric files, WisdomAI has per-table measures, and dbt MetricFlow binds metrics to a semantic model. Because Ossie has no dataset-scoped placement, the converters in this repository flatten all of those to the model level on import, and the entity association is not represented in the resulting document.
>>>>>>
>>>>>> On export, four converters independently reconstruct that association by scanning expressions for dataset.column qualifiers and falling back to the first dataset when none is found. The Honeydew converter emits a runtime warning when it does so. Two converters stash the owning entity in vendor-specific custom_extensions, which round-trips within a single vendor's toolchain but is not something a generic Ossie consumer can produce or interpret.
>>>>>>
>>>>>> Four converters reimplementing the same heuristic suggests the spec is missing a place to put information the source formats already carry.
>>>>>>
>>>>>> Third, export placement can be coarser than the target supports. The Snowflake converter maps every Ossie model-level metric to Snowflake's top-level metrics block, including metrics referencing only one table that would fit the narrower tables[].metrics position. The converter cannot distinguish the two cases because the Ossie document does not record the distinction.
>>>>>>
>>>>>> What's proposed
>>>>>>
>>>>>> datasets[].metrics, using the identical Metric structure. Scoping rules:
>>>>>>
>>>>>> -
>>>>>> A dataset-scoped metric's expression MUST only reference fields of its declaring dataset, and MUST NOT traverse relationships. Cross-dataset metrics remain model-scoped.
>>>>>>
>>>>>> -
>>>>>> Names MUST be unique within their dataset — two datasets MAY each declare item_count.
>>>>>>
>>>>>> -
>>>>>> A dataset-scoped name MUST NOT collide with any model-scoped metric name.
>>>>>>
>>>>>> -
>>>>>> Referenced from outside the dataset as dataset_name.metric_name.
>>>>>>
>>>>>>
>>>>>> Worth stating explicitly because it's easy to misread: rule 1 constrains the expression, not the query. A dataset-scoped metric can still be grouped by or filtered on dimensions from other datasets via relationships, since grouping dimensions come from the consumer at query time.
>>>>>>
>>>>>> Prior art
>>>>>>
>>>>>> Separating single-entity aggregations from cross-entity calculations is established practice. Four of the five systems I looked at do it — AtScale SML (metric bound to one dataset + column, with metric_calc as a separate type), Cube (measures within cubes), dbt MetricFlow (in-model vs top-level), and Snowflake semantic views (tables[].metrics vs top-level derived). Databricks UC metric views take a different approach, with one flat scope per view and joins declared inside it.
>>>>>>
>>>>>> On naming, the proposal extends Ossie's own existing convention rather than importing one: field names are already unique within a dataset rather than globally, and metric expressions already reference them as dataset.field. Applying the same scoped-uniqueness and qualified-reference pattern to metrics keeps the model internally consistent.
>>>>>>
>>>>>> On strictness it sits within the range the ecosystem spans — SML is more restrictive (one column, one aggregation method), Snowflake semantic views more permissive (table-level metrics may traverse relationships with a declared join path).
>>>>>>
>>>>>> Compatibility
>>>>>>
>>>>>> Additive and backward compatible. datasets[].metrics is optional and existing models remain valid unchanged. Because a dataset-scoped expression resolves within its own dataset, it is already valid as a model-scoped metric, so consumers wanting a single flat namespace can hoist without rewriting expressions. No converter changes are included; existing converters read semantic_model.metrics and that behaviour is untouched.
>>>>>>
>>>>>> Two open questions I'd particularly like input on
>>>>>>
>>>>>> -
>>>>>> Should a dataset-scoped metric eventually be allowed to traverse relationships when the join path is declared explicitly? The PR takes the conservative position, since relaxing later is backward compatible while tightening is not.
>>>>>>
>>>>>> -
>>>>>> Separately, I noticed the spec does not say whether one metric expression may reference another metric — there's no mechanism and no prohibition, while every system above has one. It's ambiguous rather than merely absent: SUM(orders.amount) / total_orders passes validation today because total_orders parses as an unqualified column. My inclination is to handle that separately since it needs aggregation-composition semantics settled, but the two features interact. The PR includes a sketch of what the reference-scoping rules would be, in case it's worth fixing those now for forward compatibility.
>>>>>>
>>>>>>
>>>>>> Validation
>>>>>>
>>>>>> Three of the rules are semantic constraints JSON Schema cannot express, so validate.py gains checks for duplicate names within a dataset, collisions with model-scoped names, and cross-dataset references (table qualifiers extracted via sqlglot). Scope checking skips rather than guesses for dialects sqlglot cannot parse. The TPC-DS example is updated to demonstrate both placements.
>>>>>>
>>>>>> AI disclosure
>>>>>>
>>>>>> This contribution was developed with AI assistance (Snowflake Cortex Code). I have reviewed all changes and am personally responsible for the content per the ASF Generative Tooling Guidance.
>>>>>>
>>>>>> Feedback and pushback welcome. Minimum discussion period is 7 days per the contribution guidelines.
>>>>>>
>>>>>> Best,
>>>>>> Josh Klahr
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>
Re: [DISCUSS] Dataset-scoped metrics
Posted by Klahr Joshua via dev <de...@ossie.apache.org>.
If I had to pick only one option (declared fields vs. physical columns) in the first version of this I think I would lean more towards physical column support for two reasons:
- agree with Chris that this makes the behavior consistent with fields
- from a somewhat biased perspective, aligns with what some existing implementations do
In the future I think we could add field support using data_set.field_name notation in metrics calculations as well.
On Friday, October 2, 2026 at 05:18:41 AM GMT+1, Jean-Baptiste Onofré <jb...@nanthrax.net> wrote:
Hi guys,
Thanks Kyoung Min for the summary, and it's good to know that most of
the validation is already in #343 (I did a couple of review already, I
will do a new pass).
Before we rework the validator, I think we need to close the point
Chris and I left open: do the unqualified references resolve to source
columns or to the dataset's declared fields? @Chris, I understand you
meant source columns, for consistency with field expressions. I would
still go with declared fields for this PR, for three reasons:
1. #486 makes model-scoped metrics see fields only. A dataset-scoped
metric over a raw column would then have no model-level equivalent, so
Josh's proposal doesn't work. With fields, we qualify each name with
the dataset.
2. The reference becomes checkable. The field list is in the document,
the source column's are not.
3. It answers Josh's collision question with the approach in #486: in
a metric, a bare name is the field. If we later allow undeclared
source columns as well, the field still wins, so that relax is
backward compatible.
The cost is real: a column used only in a metric has to be declared as
a field, and importers from formats with per-entity measures would
have to synthetise these fields. I think it's acceptable for a first
version.
@Kyoung Min, a commit against Josh's branch once the wording settles
sounds good to me, if Josh is fine with it. Happy to review :)
Regards
JB
On Fri, Oct 2, 2026 at 4:53 AM Kyoung Min Kim <ka...@gmail.com> wrote:
>
> Two things from the validate.py side, since JB's third point and Josh's
> question meet there.
>
> The current revision of #343 already carries most of what JB listed.
> validate_metric_scoping reports a duplicate metric name within a dataset and
> a dataset-scoped metric that collides with a field as errors, a model-scoped
> metric that shadows a dataset-scoped one as a warning, and a qualified
> reference that does not name a declared field as an error, with about 300
> lines of tests behind it. Keeping validation in the PR is therefore a matter
> of trimming those checks to whatever the narrowed rules end up being, not of
> writing them. The branch is a month behind main and conflicts in validate.py,
> spec.md, spec.yaml and both test files, so the rebase touches that code
> anyway.
>
> On fields versus source columns, the validator can only ever check one of the
> two. The document lists a dataset's fields, so a reference to a declared field
> can be checked for existence. It does not list the columns of the source, and
> #343 says as much: "not checked, because that requires catalog metadata the
> model does not carry". If the single-part references in Chris's third
> restriction resolve to source columns, the structural check shrinks to
> uniqueness plus "is single-part". If they resolve to the dataset's declared
> fields, as JB read it, the existence check is the same lookup #486 asks for in
> model-scoped metrics, where dataset.field must name a declared field and a
> source column is never visible to a metric expression. That reading also
> removes the collision Josh is asking about, because a bare name in a metric
> would mean the field and nothing else, the way the tie-break sentence in #486
> settles it for qualified names.
>
> Josh, if trimming the validator to the narrowed rules is useful to hand off, I
> can prepare it as a commit against your branch or as a follow-up PR the day
> the spec text settles. If you would rather keep it in one PR, I will review
> that part.
>
> Kyoung Min Kim
>
>
> >
> > Hi all,
> >
> > I completely agree with Chris's proposal to tighten the scope here.
> > Decoupling the placement (datasets[].metrics) from the larger,
> > in-flight semantic questions (expression composition, traversal paths,
> > grain locking) is the pragmatic way to get a clean spec vote (I love
> > pragmatic approaches :)).
> >
> > A few specific thoughts on the three restrictions and the rollout:
> > 1. Unqualified references and unified namespace are spot on.
> > Single-part references (SUM(amount) instead of orders.amount) neatly
> > eliminate the circularity issue in DDL/parser contexts that Justin
> > pointed out. Enforcing a single namespace across fields and metrics
> > within the dataset also elliminites the naming collision ambiguity.
> > 2. In the wording, we should be explicit about whether "source fields"
> > refers to the underlying physical table columns or the dataset's
> > declared fields (datasets[].fields). In Ossie, metrics should
> > aggregate the dataset's declared/modeled fields (or we need clear
> > precedence if both are permitted). Tightening this phrasing will avoid
> > confusion during the vote.
> > 3. While I support deferring complex AST evaluation or cross-dialect
> > query semantics to follow-up, I would prefer we do not strip the
> > validation entirely from this PR. The JSON schema update
> > (ossie-schema.json) and basic structural validation in validate.py
> > (uniqueness across fields + metrics, checking for unqualified
> > references, and null guards) should land together with the spec. In
> > Ossie, the schema and validator act as our executable spec: keeping
> > baseline structural checks ensures the vote lands on a solid,
> > verifiable foundation.
> >
> > Overall, this narrowed scope gives us a safe, backward-compatible
> > baseline that we can relax later one expression composition lands.
> >
> > Happy to review the updated PR and spec wording once ready.
> >
> > Regards
> > JB
> >
> > On Tue, Sep 29, 2026 at 11:50 PM Chris Eubank
> > <ch...@databricks.com> wrote:
> >>
> >> Hi Josh,
> >>
> >> Thank you for putting together this proposal for dataset-scoped metrics. It's a fundamental building block for several in-flight extensions: expression resolution / composability (thread), relational query semantics (thread) and dimensional query semantics (thread).
> >>
> >> Several open comments remain, mostly concerning those follow-up features (e.g. expression composition). I'm wondering if we can tighten this PR's scope to land the minimal spec extension needed for a spec vote, and defer the rest. The validation logic could even move to a follow-up PR.
> >>
> >> Concretely, I'd propose these three restrictions:
> >>
> >> 1. Datasets may declare metrics. Within a dataset, names must be unique across fields and metrics.
> >> 2. A dataset's metric expression may reference only that dataset's source fields. We can lift this restriction once the expression-composition rules land.
> >> 3. Those references must be single-part (unqualified) within the expression and must resolve to the dataset's source fields.
> >>
> >> Would this narrower scope work for you as a path to a spec vote? If so, I'd be glad to help however is most useful - happy to pair on the scoped wording or take a first pass for us to iterate on.
> >>
> >> Kind regards,
> >> Chris
> >>
> >> On Thu, Aug 27, 2026 at 2:20 PM Khushboo Bhatia <kb...@apache.org> wrote:
> >>>
> >>> Hey Josh,
> >>> Thanks for starting this discussion. This issue has been raised in multiple discussions on the repo.
> >>>
> >>> I am generally supportive of adding these. Leaving some comments on the PR for the subtle differences between semantic model scoped metrics vs dataset scoped.
> >>>
> >>> Khushboo
> >>>
> >>> On 2026/08/27 16:57:13 Klahr Joshua via dev wrote:
> >>>>
> >>>>
> >>>>
> >>>> Hi all,
> >>>>
> >>>> I'd like to propose allowing metrics to be declared on an individual dataset (datasets[].metrics) in addition to the semantic model (semantic_model.metrics), using the same metric structure in both placements.
> >>>>
> >>>> PR: https://github.com/apache/ossie/pull/343
> >>>>
> >>>> Motivation
> >>>>
> >>>> Today every metric must be declared at the semantic model level, even when it aggregates a single dataset's own fields. Three costs follow from that.
> >>>>
> >>>> First, a dataset is not independently interpretable — you cannot tell from a dataset what metrics exist over it without scanning the whole model, and simple aggregations end up separated from the fields they depend on.
> >>>>
> >>>> Second, and more concretely: round-trip fidelity through Ossie is reduced. Several formats we convert from attach metrics to a specific entity — Omni has per-view measures, Honeydew has per-entity metric files, WisdomAI has per-table measures, and dbt MetricFlow binds metrics to a semantic model. Because Ossie has no dataset-scoped placement, the converters in this repository flatten all of those to the model level on import, and the entity association is not represented in the resulting document.
> >>>>
> >>>> On export, four converters independently reconstruct that association by scanning expressions for dataset.column qualifiers and falling back to the first dataset when none is found. The Honeydew converter emits a runtime warning when it does so. Two converters stash the owning entity in vendor-specific custom_extensions, which round-trips within a single vendor's toolchain but is not something a generic Ossie consumer can produce or interpret.
> >>>>
> >>>> Four converters reimplementing the same heuristic suggests the spec is missing a place to put information the source formats already carry.
> >>>>
> >>>> Third, export placement can be coarser than the target supports. The Snowflake converter maps every Ossie model-level metric to Snowflake's top-level metrics block, including metrics referencing only one table that would fit the narrower tables[].metrics position. The converter cannot distinguish the two cases because the Ossie document does not record the distinction.
> >>>>
> >>>> What's proposed
> >>>>
> >>>> datasets[].metrics, using the identical Metric structure. Scoping rules:
> >>>>
> >>>> -
> >>>> A dataset-scoped metric's expression MUST only reference fields of its declaring dataset, and MUST NOT traverse relationships. Cross-dataset metrics remain model-scoped.
> >>>>
> >>>> -
> >>>> Names MUST be unique within their dataset — two datasets MAY each declare item_count.
> >>>>
> >>>> -
> >>>> A dataset-scoped name MUST NOT collide with any model-scoped metric name.
> >>>>
> >>>> -
> >>>> Referenced from outside the dataset as dataset_name.metric_name.
> >>>>
> >>>>
> >>>> Worth stating explicitly because it's easy to misread: rule 1 constrains the expression, not the query. A dataset-scoped metric can still be grouped by or filtered on dimensions from other datasets via relationships, since grouping dimensions come from the consumer at query time.
> >>>>
> >>>> Prior art
> >>>>
> >>>> Separating single-entity aggregations from cross-entity calculations is established practice. Four of the five systems I looked at do it — AtScale SML (metric bound to one dataset + column, with metric_calc as a separate type), Cube (measures within cubes), dbt MetricFlow (in-model vs top-level), and Snowflake semantic views (tables[].metrics vs top-level derived). Databricks UC metric views take a different approach, with one flat scope per view and joins declared inside it.
> >>>>
> >>>> On naming, the proposal extends Ossie's own existing convention rather than importing one: field names are already unique within a dataset rather than globally, and metric expressions already reference them as dataset.field. Applying the same scoped-uniqueness and qualified-reference pattern to metrics keeps the model internally consistent.
> >>>>
> >>>> On strictness it sits within the range the ecosystem spans — SML is more restrictive (one column, one aggregation method), Snowflake semantic views more permissive (table-level metrics may traverse relationships with a declared join path).
> >>>>
> >>>> Compatibility
> >>>>
> >>>> Additive and backward compatible. datasets[].metrics is optional and existing models remain valid unchanged. Because a dataset-scoped expression resolves within its own dataset, it is already valid as a model-scoped metric, so consumers wanting a single flat namespace can hoist without rewriting expressions. No converter changes are included; existing converters read semantic_model.metrics and that behaviour is untouched.
> >>>>
> >>>> Two open questions I'd particularly like input on
> >>>>
> >>>> -
> >>>> Should a dataset-scoped metric eventually be allowed to traverse relationships when the join path is declared explicitly? The PR takes the conservative position, since relaxing later is backward compatible while tightening is not.
> >>>>
> >>>> -
> >>>> Separately, I noticed the spec does not say whether one metric expression may reference another metric — there's no mechanism and no prohibition, while every system above has one. It's ambiguous rather than merely absent: SUM(orders.amount) / total_orders passes validation today because total_orders parses as an unqualified column. My inclination is to handle that separately since it needs aggregation-composition semantics settled, but the two features interact. The PR includes a sketch of what the reference-scoping rules would be, in case it's worth fixing those now for forward compatibility.
> >>>>
> >>>>
> >>>> Validation
> >>>>
> >>>> Three of the rules are semantic constraints JSON Schema cannot express, so validate.py gains checks for duplicate names within a dataset, collisions with model-scoped names, and cross-dataset references (table qualifiers extracted via sqlglot). Scope checking skips rather than guesses for dialects sqlglot cannot parse. The TPC-DS example is updated to demonstrate both placements.
> >>>>
> >>>> AI disclosure
> >>>>
> >>>> This contribution was developed with AI assistance (Snowflake Cortex Code). I have reviewed all changes and am personally responsible for the content per the ASF Generative Tooling Guidance.
> >>>>
> >>>> Feedback and pushback welcome. Minimum discussion period is 7 days per the contribution guidelines.
> >>>>
> >>>> Best,
> >>>> Josh Klahr
> >>>>
> >>>>
> >>>>
> >>>>
> >>>>
>
Re: [DISCUSS] Dataset-scoped metrics
Posted by Justin Talbot via dev <de...@ossie.apache.org>.
One more nuance I think we need to consider:
Should a dataset field be able to reference another dataset field? And
should dataset fields and dataset metrics have the same name precedence
rules?
If yes to both, then we have a problem because adding field references to
dataset fields would be a breaking change. Currently, dataset fields
reference source columns without any qualification.
Justin
On Fri, Oct 2, 2026 at 9:50 AM Chris Eubank <ch...@databricks.com>
wrote:
> Thanks for the clarification; that logic makes sense to me. It sounds like
> we agree that dataset metrics references are unqualified and can initially
> only point to dataset fields.
>
> JB & Kyoung Min, does this now look good to move forward with updating
> the PR?
>
> On Fri, Oct 2, 2026 at 9:43 AM Justin Talbot <ju...@databricks.com>
> wrote:
>
>> Thanks all.
>>
>> I agree with Chris that we'll eventually need to support
>> both---dataset-scoped metrics should be able to reference both fields from
>> that same dataset and the dataset's source columns. We may also eventually
>> want dataset-scoped metrics to reference other metrics from that same
>> dataset.
>>
>> I agree that we should ship references to dataset fields first (JB's
>> proposal). But my reasoning is slightly different and requires a change to
>> resolution precedence rules in #343.
>>
>> Data field names can conflict with source column names. So when a dataset
>> metric references an ambiguous name, we need to have a precedence rule or
>> must require that users always qualify conflicting names. Changing the
>> precedence order or requiring qualified names, when we didn't before,
>> constitutes a breaking change. So we should try to get this right in v1.
>>
>> I propose:
>>
>> In a dataset-scoped metric definition:
>> - dataset field names take precedence over source column names and are
>> always referenced unqualified,
>> - when a user wants to reference a source column name that is shadowed by
>> a dataset field, they must qualify it.
>>
>> These two rules imply that we can ship only dataset field references in
>> the first version and then safely extend this to include source column
>> references in the future without requiring a breaking change.
>>
>> The above is a change from the current #343; it has source column names
>> taking precendence over dataset field names.
>>
>> Justin
>>
>> On Thu, Oct 1, 2026 at 9:56 PM Chris Eubank <
>> christian.eubank@databricks.com> wrote:
>>
>>> Hi JB,
>>>
>>> It might be worth getting Justin's interpretation from #486, but I think
>>> that having dataset-level metrics initially point to source fields is more
>>> consistent with that PR, which states:
>>>
>>> 1. "In a **field expression**, identifiers refer to columns of the
>>> dataset's `source`. A field expression cannot refer to other fields."
>>> 2. "In a **metric expression**, identifiers must be qualified as
>>> `dataset.field` and refer to a field declared in that dataset. They cannot
>>> refer to columns of the dataset's `source` or to other metrics."
>>>
>>> This lines up with the rules that:
>>> 1. Expressions that declare dataset concepts (currently, only fields)
>>> must only use inputs from the dataset source
>>> 2. Expressions that declare model concepts (currently, only metrics)
>>> must only use inputs from the datasets (and not sources directly)
>>>
>>> From a visibility perspective, having dataset metrics refer to source
>>> fields allows you to declare a metric without exposing the metric's inputs
>>> as dataset fields. If you want a model-level metric to reference a source
>>> field, you can achieve that goal by adding a passthrough field on the
>>> dataset. I definitely hear the pain of not being able to validate the
>>> metric inputs but that still remains a problem even if the metrics point to
>>> dataset fields (since field inputs cannot be validated).
>>>
>>> That said, I'm happy to disagree and commit if the community leans
>>> toward defining dataset metrics over dataset fields, especially since we
>>> will eventually need to support metrics built on source & dataset fields.
>>>
>>> @Kyoung Min, +1 to JB's offer. I'm happy to review or pair on your
>>> extensions. I'm excited for dataset metrics to land!
>>>
>>> Kind regards,
>>> Chris
>>>
>>>
>>> On Thu, Oct 1, 2026 at 9:17 PM Jean-Baptiste Onofré <jb...@nanthrax.net>
>>> wrote:
>>>
>>>> Hi guys,
>>>>
>>>> Thanks Kyoung Min for the summary, and it's good to know that most of
>>>> the validation is already in #343 (I did a couple of review already, I
>>>> will do a new pass).
>>>>
>>>> Before we rework the validator, I think we need to close the point
>>>> Chris and I left open: do the unqualified references resolve to source
>>>> columns or to the dataset's declared fields? @Chris, I understand you
>>>> meant source columns, for consistency with field expressions. I would
>>>> still go with declared fields for this PR, for three reasons:
>>>> 1. #486 makes model-scoped metrics see fields only. A dataset-scoped
>>>> metric over a raw column would then have no model-level equivalent, so
>>>> Josh's proposal doesn't work. With fields, we qualify each name with
>>>> the dataset.
>>>> 2. The reference becomes checkable. The field list is in the document,
>>>> the source column's are not.
>>>> 3. It answers Josh's collision question with the approach in #486: in
>>>> a metric, a bare name is the field. If we later allow undeclared
>>>> source columns as well, the field still wins, so that relax is
>>>> backward compatible.
>>>>
>>>> The cost is real: a column used only in a metric has to be declared as
>>>> a field, and importers from formats with per-entity measures would
>>>> have to synthetise these fields. I think it's acceptable for a first
>>>> version.
>>>>
>>>> @Kyoung Min, a commit against Josh's branch once the wording settles
>>>> sounds good to me, if Josh is fine with it. Happy to review :)
>>>>
>>>> Regards
>>>> JB
>>>>
>>>> On Fri, Oct 2, 2026 at 4:53 AM Kyoung Min Kim <ka...@gmail.com>
>>>> wrote:
>>>> >
>>>> > Two things from the validate.py side, since JB's third point and
>>>> Josh's
>>>> > question meet there.
>>>> >
>>>> > The current revision of #343 already carries most of what JB listed.
>>>> > validate_metric_scoping reports a duplicate metric name within a
>>>> dataset and
>>>> > a dataset-scoped metric that collides with a field as errors, a
>>>> model-scoped
>>>> > metric that shadows a dataset-scoped one as a warning, and a qualified
>>>> > reference that does not name a declared field as an error, with about
>>>> 300
>>>> > lines of tests behind it. Keeping validation in the PR is therefore a
>>>> matter
>>>> > of trimming those checks to whatever the narrowed rules end up being,
>>>> not of
>>>> > writing them. The branch is a month behind main and conflicts in
>>>> validate.py,
>>>> > spec.md, spec.yaml and both test files, so the rebase touches that
>>>> code
>>>> > anyway.
>>>> >
>>>> > On fields versus source columns, the validator can only ever check
>>>> one of the
>>>> > two. The document lists a dataset's fields, so a reference to a
>>>> declared field
>>>> > can be checked for existence. It does not list the columns of the
>>>> source, and
>>>> > #343 says as much: "not checked, because that requires catalog
>>>> metadata the
>>>> > model does not carry". If the single-part references in Chris's third
>>>> > restriction resolve to source columns, the structural check shrinks to
>>>> > uniqueness plus "is single-part". If they resolve to the dataset's
>>>> declared
>>>> > fields, as JB read it, the existence check is the same lookup #486
>>>> asks for in
>>>> > model-scoped metrics, where dataset.field must name a declared field
>>>> and a
>>>> > source column is never visible to a metric expression. That reading
>>>> also
>>>> > removes the collision Josh is asking about, because a bare name in a
>>>> metric
>>>> > would mean the field and nothing else, the way the tie-break sentence
>>>> in #486
>>>> > settles it for qualified names.
>>>> >
>>>> > Josh, if trimming the validator to the narrowed rules is useful to
>>>> hand off, I
>>>> > can prepare it as a commit against your branch or as a follow-up PR
>>>> the day
>>>> > the spec text settles. If you would rather keep it in one PR, I will
>>>> review
>>>> > that part.
>>>> >
>>>> > Kyoung Min Kim
>>>> >
>>>> >
>>>> > >
>>>> > > Hi all,
>>>> > >
>>>> > > I completely agree with Chris's proposal to tighten the scope here.
>>>> > > Decoupling the placement (datasets[].metrics) from the larger,
>>>> > > in-flight semantic questions (expression composition, traversal
>>>> paths,
>>>> > > grain locking) is the pragmatic way to get a clean spec vote (I love
>>>> > > pragmatic approaches :)).
>>>> > >
>>>> > > A few specific thoughts on the three restrictions and the rollout:
>>>> > > 1. Unqualified references and unified namespace are spot on.
>>>> > > Single-part references (SUM(amount) instead of orders.amount) neatly
>>>> > > eliminate the circularity issue in DDL/parser contexts that Justin
>>>> > > pointed out. Enforcing a single namespace across fields and metrics
>>>> > > within the dataset also elliminites the naming collision ambiguity.
>>>> > > 2. In the wording, we should be explicit about whether "source
>>>> fields"
>>>> > > refers to the underlying physical table columns or the dataset's
>>>> > > declared fields (datasets[].fields). In Ossie, metrics should
>>>> > > aggregate the dataset's declared/modeled fields (or we need clear
>>>> > > precedence if both are permitted). Tightening this phrasing will
>>>> avoid
>>>> > > confusion during the vote.
>>>> > > 3. While I support deferring complex AST evaluation or cross-dialect
>>>> > > query semantics to follow-up, I would prefer we do not strip the
>>>> > > validation entirely from this PR. The JSON schema update
>>>> > > (ossie-schema.json) and basic structural validation in validate.py
>>>> > > (uniqueness across fields + metrics, checking for unqualified
>>>> > > references, and null guards) should land together with the spec. In
>>>> > > Ossie, the schema and validator act as our executable spec: keeping
>>>> > > baseline structural checks ensures the vote lands on a solid,
>>>> > > verifiable foundation.
>>>> > >
>>>> > > Overall, this narrowed scope gives us a safe, backward-compatible
>>>> > > baseline that we can relax later one expression composition lands.
>>>> > >
>>>> > > Happy to review the updated PR and spec wording once ready.
>>>> > >
>>>> > > Regards
>>>> > > JB
>>>> > >
>>>> > > On Tue, Sep 29, 2026 at 11:50 PM Chris Eubank
>>>> > > <ch...@databricks.com> wrote:
>>>> > >>
>>>> > >> Hi Josh,
>>>> > >>
>>>> > >> Thank you for putting together this proposal for dataset-scoped
>>>> metrics. It's a fundamental building block for several in-flight
>>>> extensions: expression resolution / composability (thread), relational
>>>> query semantics (thread) and dimensional query semantics (thread).
>>>> > >>
>>>> > >> Several open comments remain, mostly concerning those follow-up
>>>> features (e.g. expression composition). I'm wondering if we can tighten
>>>> this PR's scope to land the minimal spec extension needed for a spec vote,
>>>> and defer the rest. The validation logic could even move to a follow-up PR.
>>>> > >>
>>>> > >> Concretely, I'd propose these three restrictions:
>>>> > >>
>>>> > >> 1. Datasets may declare metrics. Within a dataset, names must be
>>>> unique across fields and metrics.
>>>> > >> 2. A dataset's metric expression may reference only that dataset's
>>>> source fields. We can lift this restriction once the expression-composition
>>>> rules land.
>>>> > >> 3. Those references must be single-part (unqualified) within the
>>>> expression and must resolve to the dataset's source fields.
>>>> > >>
>>>> > >> Would this narrower scope work for you as a path to a spec vote?
>>>> If so, I'd be glad to help however is most useful - happy to pair on the
>>>> scoped wording or take a first pass for us to iterate on.
>>>> > >>
>>>> > >> Kind regards,
>>>> > >> Chris
>>>> > >>
>>>> > >> On Thu, Aug 27, 2026 at 2:20 PM Khushboo Bhatia <
>>>> kbhatia@apache.org> wrote:
>>>> > >>>
>>>> > >>> Hey Josh,
>>>> > >>> Thanks for starting this discussion. This issue has been raised
>>>> in multiple discussions on the repo.
>>>> > >>>
>>>> > >>> I am generally supportive of adding these. Leaving some comments
>>>> on the PR for the subtle differences between semantic model scoped metrics
>>>> vs dataset scoped.
>>>> > >>>
>>>> > >>> Khushboo
>>>> > >>>
>>>> > >>> On 2026/08/27 16:57:13 Klahr Joshua via dev wrote:
>>>> > >>>>
>>>> > >>>>
>>>> > >>>>
>>>> > >>>> Hi all,
>>>> > >>>>
>>>> > >>>> I'd like to propose allowing metrics to be declared on an
>>>> individual dataset (datasets[].metrics) in addition to the semantic model
>>>> (semantic_model.metrics), using the same metric structure in both
>>>> placements.
>>>> > >>>>
>>>> > >>>> PR: https://github.com/apache/ossie/pull/343
>>>> > >>>>
>>>> > >>>> Motivation
>>>> > >>>>
>>>> > >>>> Today every metric must be declared at the semantic model level,
>>>> even when it aggregates a single dataset's own fields. Three costs follow
>>>> from that.
>>>> > >>>>
>>>> > >>>> First, a dataset is not independently interpretable — you cannot
>>>> tell from a dataset what metrics exist over it without scanning the whole
>>>> model, and simple aggregations end up separated from the fields they depend
>>>> on.
>>>> > >>>>
>>>> > >>>> Second, and more concretely: round-trip fidelity through Ossie
>>>> is reduced. Several formats we convert from attach metrics to a specific
>>>> entity — Omni has per-view measures, Honeydew has per-entity metric files,
>>>> WisdomAI has per-table measures, and dbt MetricFlow binds metrics to a
>>>> semantic model. Because Ossie has no dataset-scoped placement, the
>>>> converters in this repository flatten all of those to the model level on
>>>> import, and the entity association is not represented in the resulting
>>>> document.
>>>> > >>>>
>>>> > >>>> On export, four converters independently reconstruct that
>>>> association by scanning expressions for dataset.column qualifiers and
>>>> falling back to the first dataset when none is found. The Honeydew
>>>> converter emits a runtime warning when it does so. Two converters stash the
>>>> owning entity in vendor-specific custom_extensions, which round-trips
>>>> within a single vendor's toolchain but is not something a generic Ossie
>>>> consumer can produce or interpret.
>>>> > >>>>
>>>> > >>>> Four converters reimplementing the same heuristic suggests the
>>>> spec is missing a place to put information the source formats already carry.
>>>> > >>>>
>>>> > >>>> Third, export placement can be coarser than the target supports.
>>>> The Snowflake converter maps every Ossie model-level metric to Snowflake's
>>>> top-level metrics block, including metrics referencing only one table that
>>>> would fit the narrower tables[].metrics position. The converter cannot
>>>> distinguish the two cases because the Ossie document does not record the
>>>> distinction.
>>>> > >>>>
>>>> > >>>> What's proposed
>>>> > >>>>
>>>> > >>>> datasets[].metrics, using the identical Metric structure.
>>>> Scoping rules:
>>>> > >>>>
>>>> > >>>> -
>>>> > >>>> A dataset-scoped metric's expression MUST only reference fields
>>>> of its declaring dataset, and MUST NOT traverse relationships.
>>>> Cross-dataset metrics remain model-scoped.
>>>> > >>>>
>>>> > >>>> -
>>>> > >>>> Names MUST be unique within their dataset — two datasets MAY
>>>> each declare item_count.
>>>> > >>>>
>>>> > >>>> -
>>>> > >>>> A dataset-scoped name MUST NOT collide with any model-scoped
>>>> metric name.
>>>> > >>>>
>>>> > >>>> -
>>>> > >>>> Referenced from outside the dataset as dataset_name.metric_name.
>>>> > >>>>
>>>> > >>>>
>>>> > >>>> Worth stating explicitly because it's easy to misread: rule 1
>>>> constrains the expression, not the query. A dataset-scoped metric can still
>>>> be grouped by or filtered on dimensions from other datasets via
>>>> relationships, since grouping dimensions come from the consumer at query
>>>> time.
>>>> > >>>>
>>>> > >>>> Prior art
>>>> > >>>>
>>>> > >>>> Separating single-entity aggregations from cross-entity
>>>> calculations is established practice. Four of the five systems I looked at
>>>> do it — AtScale SML (metric bound to one dataset + column, with metric_calc
>>>> as a separate type), Cube (measures within cubes), dbt MetricFlow (in-model
>>>> vs top-level), and Snowflake semantic views (tables[].metrics vs top-level
>>>> derived). Databricks UC metric views take a different approach, with one
>>>> flat scope per view and joins declared inside it.
>>>> > >>>>
>>>> > >>>> On naming, the proposal extends Ossie's own existing convention
>>>> rather than importing one: field names are already unique within a dataset
>>>> rather than globally, and metric expressions already reference them as
>>>> dataset.field. Applying the same scoped-uniqueness and qualified-reference
>>>> pattern to metrics keeps the model internally consistent.
>>>> > >>>>
>>>> > >>>> On strictness it sits within the range the ecosystem spans — SML
>>>> is more restrictive (one column, one aggregation method), Snowflake
>>>> semantic views more permissive (table-level metrics may traverse
>>>> relationships with a declared join path).
>>>> > >>>>
>>>> > >>>> Compatibility
>>>> > >>>>
>>>> > >>>> Additive and backward compatible. datasets[].metrics is optional
>>>> and existing models remain valid unchanged. Because a dataset-scoped
>>>> expression resolves within its own dataset, it is already valid as a
>>>> model-scoped metric, so consumers wanting a single flat namespace can hoist
>>>> without rewriting expressions. No converter changes are included; existing
>>>> converters read semantic_model.metrics and that behaviour is untouched.
>>>> > >>>>
>>>> > >>>> Two open questions I'd particularly like input on
>>>> > >>>>
>>>> > >>>> -
>>>> > >>>> Should a dataset-scoped metric eventually be allowed to traverse
>>>> relationships when the join path is declared explicitly? The PR takes the
>>>> conservative position, since relaxing later is backward compatible while
>>>> tightening is not.
>>>> > >>>>
>>>> > >>>> -
>>>> > >>>> Separately, I noticed the spec does not say whether one metric
>>>> expression may reference another metric — there's no mechanism and no
>>>> prohibition, while every system above has one. It's ambiguous rather than
>>>> merely absent: SUM(orders.amount) / total_orders passes validation today
>>>> because total_orders parses as an unqualified column. My inclination is to
>>>> handle that separately since it needs aggregation-composition semantics
>>>> settled, but the two features interact. The PR includes a sketch of what
>>>> the reference-scoping rules would be, in case it's worth fixing those now
>>>> for forward compatibility.
>>>> > >>>>
>>>> > >>>>
>>>> > >>>> Validation
>>>> > >>>>
>>>> > >>>> Three of the rules are semantic constraints JSON Schema cannot
>>>> express, so validate.py gains checks for duplicate names within a dataset,
>>>> collisions with model-scoped names, and cross-dataset references (table
>>>> qualifiers extracted via sqlglot). Scope checking skips rather than guesses
>>>> for dialects sqlglot cannot parse. The TPC-DS example is updated to
>>>> demonstrate both placements.
>>>> > >>>>
>>>> > >>>> AI disclosure
>>>> > >>>>
>>>> > >>>> This contribution was developed with AI assistance (Snowflake
>>>> Cortex Code). I have reviewed all changes and am personally responsible for
>>>> the content per the ASF Generative Tooling Guidance.
>>>> > >>>>
>>>> > >>>> Feedback and pushback welcome. Minimum discussion period is 7
>>>> days per the contribution guidelines.
>>>> > >>>>
>>>> > >>>> Best,
>>>> > >>>> Josh Klahr
>>>> > >>>>
>>>> > >>>>
>>>> > >>>>
>>>> > >>>>
>>>> > >>>>
>>>> >
>>>>
>>>
Re: [DISCUSS] Dataset-scoped metrics
Posted by Chris Eubank via dev <de...@ossie.apache.org>.
Thanks for the clarification; that logic makes sense to me. It sounds like
we agree that dataset metrics references are unqualified and can initially
only point to dataset fields.
JB & Kyoung Min, does this now look good to move forward with updating the
PR?
On Fri, Oct 2, 2026 at 9:43 AM Justin Talbot <ju...@databricks.com>
wrote:
> Thanks all.
>
> I agree with Chris that we'll eventually need to support
> both---dataset-scoped metrics should be able to reference both fields from
> that same dataset and the dataset's source columns. We may also eventually
> want dataset-scoped metrics to reference other metrics from that same
> dataset.
>
> I agree that we should ship references to dataset fields first (JB's
> proposal). But my reasoning is slightly different and requires a change to
> resolution precedence rules in #343.
>
> Data field names can conflict with source column names. So when a dataset
> metric references an ambiguous name, we need to have a precedence rule or
> must require that users always qualify conflicting names. Changing the
> precedence order or requiring qualified names, when we didn't before,
> constitutes a breaking change. So we should try to get this right in v1.
>
> I propose:
>
> In a dataset-scoped metric definition:
> - dataset field names take precedence over source column names and are
> always referenced unqualified,
> - when a user wants to reference a source column name that is shadowed by
> a dataset field, they must qualify it.
>
> These two rules imply that we can ship only dataset field references in
> the first version and then safely extend this to include source column
> references in the future without requiring a breaking change.
>
> The above is a change from the current #343; it has source column names
> taking precendence over dataset field names.
>
> Justin
>
> On Thu, Oct 1, 2026 at 9:56 PM Chris Eubank <
> christian.eubank@databricks.com> wrote:
>
>> Hi JB,
>>
>> It might be worth getting Justin's interpretation from #486, but I think
>> that having dataset-level metrics initially point to source fields is more
>> consistent with that PR, which states:
>>
>> 1. "In a **field expression**, identifiers refer to columns of the
>> dataset's `source`. A field expression cannot refer to other fields."
>> 2. "In a **metric expression**, identifiers must be qualified as
>> `dataset.field` and refer to a field declared in that dataset. They cannot
>> refer to columns of the dataset's `source` or to other metrics."
>>
>> This lines up with the rules that:
>> 1. Expressions that declare dataset concepts (currently, only fields)
>> must only use inputs from the dataset source
>> 2. Expressions that declare model concepts (currently, only metrics) must
>> only use inputs from the datasets (and not sources directly)
>>
>> From a visibility perspective, having dataset metrics refer to source
>> fields allows you to declare a metric without exposing the metric's inputs
>> as dataset fields. If you want a model-level metric to reference a source
>> field, you can achieve that goal by adding a passthrough field on the
>> dataset. I definitely hear the pain of not being able to validate the
>> metric inputs but that still remains a problem even if the metrics point to
>> dataset fields (since field inputs cannot be validated).
>>
>> That said, I'm happy to disagree and commit if the community leans toward
>> defining dataset metrics over dataset fields, especially since we will
>> eventually need to support metrics built on source & dataset fields.
>>
>> @Kyoung Min, +1 to JB's offer. I'm happy to review or pair on your
>> extensions. I'm excited for dataset metrics to land!
>>
>> Kind regards,
>> Chris
>>
>>
>> On Thu, Oct 1, 2026 at 9:17 PM Jean-Baptiste Onofré <jb...@nanthrax.net>
>> wrote:
>>
>>> Hi guys,
>>>
>>> Thanks Kyoung Min for the summary, and it's good to know that most of
>>> the validation is already in #343 (I did a couple of review already, I
>>> will do a new pass).
>>>
>>> Before we rework the validator, I think we need to close the point
>>> Chris and I left open: do the unqualified references resolve to source
>>> columns or to the dataset's declared fields? @Chris, I understand you
>>> meant source columns, for consistency with field expressions. I would
>>> still go with declared fields for this PR, for three reasons:
>>> 1. #486 makes model-scoped metrics see fields only. A dataset-scoped
>>> metric over a raw column would then have no model-level equivalent, so
>>> Josh's proposal doesn't work. With fields, we qualify each name with
>>> the dataset.
>>> 2. The reference becomes checkable. The field list is in the document,
>>> the source column's are not.
>>> 3. It answers Josh's collision question with the approach in #486: in
>>> a metric, a bare name is the field. If we later allow undeclared
>>> source columns as well, the field still wins, so that relax is
>>> backward compatible.
>>>
>>> The cost is real: a column used only in a metric has to be declared as
>>> a field, and importers from formats with per-entity measures would
>>> have to synthetise these fields. I think it's acceptable for a first
>>> version.
>>>
>>> @Kyoung Min, a commit against Josh's branch once the wording settles
>>> sounds good to me, if Josh is fine with it. Happy to review :)
>>>
>>> Regards
>>> JB
>>>
>>> On Fri, Oct 2, 2026 at 4:53 AM Kyoung Min Kim <ka...@gmail.com>
>>> wrote:
>>> >
>>> > Two things from the validate.py side, since JB's third point and Josh's
>>> > question meet there.
>>> >
>>> > The current revision of #343 already carries most of what JB listed.
>>> > validate_metric_scoping reports a duplicate metric name within a
>>> dataset and
>>> > a dataset-scoped metric that collides with a field as errors, a
>>> model-scoped
>>> > metric that shadows a dataset-scoped one as a warning, and a qualified
>>> > reference that does not name a declared field as an error, with about
>>> 300
>>> > lines of tests behind it. Keeping validation in the PR is therefore a
>>> matter
>>> > of trimming those checks to whatever the narrowed rules end up being,
>>> not of
>>> > writing them. The branch is a month behind main and conflicts in
>>> validate.py,
>>> > spec.md, spec.yaml and both test files, so the rebase touches that code
>>> > anyway.
>>> >
>>> > On fields versus source columns, the validator can only ever check one
>>> of the
>>> > two. The document lists a dataset's fields, so a reference to a
>>> declared field
>>> > can be checked for existence. It does not list the columns of the
>>> source, and
>>> > #343 says as much: "not checked, because that requires catalog
>>> metadata the
>>> > model does not carry". If the single-part references in Chris's third
>>> > restriction resolve to source columns, the structural check shrinks to
>>> > uniqueness plus "is single-part". If they resolve to the dataset's
>>> declared
>>> > fields, as JB read it, the existence check is the same lookup #486
>>> asks for in
>>> > model-scoped metrics, where dataset.field must name a declared field
>>> and a
>>> > source column is never visible to a metric expression. That reading
>>> also
>>> > removes the collision Josh is asking about, because a bare name in a
>>> metric
>>> > would mean the field and nothing else, the way the tie-break sentence
>>> in #486
>>> > settles it for qualified names.
>>> >
>>> > Josh, if trimming the validator to the narrowed rules is useful to
>>> hand off, I
>>> > can prepare it as a commit against your branch or as a follow-up PR
>>> the day
>>> > the spec text settles. If you would rather keep it in one PR, I will
>>> review
>>> > that part.
>>> >
>>> > Kyoung Min Kim
>>> >
>>> >
>>> > >
>>> > > Hi all,
>>> > >
>>> > > I completely agree with Chris's proposal to tighten the scope here.
>>> > > Decoupling the placement (datasets[].metrics) from the larger,
>>> > > in-flight semantic questions (expression composition, traversal
>>> paths,
>>> > > grain locking) is the pragmatic way to get a clean spec vote (I love
>>> > > pragmatic approaches :)).
>>> > >
>>> > > A few specific thoughts on the three restrictions and the rollout:
>>> > > 1. Unqualified references and unified namespace are spot on.
>>> > > Single-part references (SUM(amount) instead of orders.amount) neatly
>>> > > eliminate the circularity issue in DDL/parser contexts that Justin
>>> > > pointed out. Enforcing a single namespace across fields and metrics
>>> > > within the dataset also elliminites the naming collision ambiguity.
>>> > > 2. In the wording, we should be explicit about whether "source
>>> fields"
>>> > > refers to the underlying physical table columns or the dataset's
>>> > > declared fields (datasets[].fields). In Ossie, metrics should
>>> > > aggregate the dataset's declared/modeled fields (or we need clear
>>> > > precedence if both are permitted). Tightening this phrasing will
>>> avoid
>>> > > confusion during the vote.
>>> > > 3. While I support deferring complex AST evaluation or cross-dialect
>>> > > query semantics to follow-up, I would prefer we do not strip the
>>> > > validation entirely from this PR. The JSON schema update
>>> > > (ossie-schema.json) and basic structural validation in validate.py
>>> > > (uniqueness across fields + metrics, checking for unqualified
>>> > > references, and null guards) should land together with the spec. In
>>> > > Ossie, the schema and validator act as our executable spec: keeping
>>> > > baseline structural checks ensures the vote lands on a solid,
>>> > > verifiable foundation.
>>> > >
>>> > > Overall, this narrowed scope gives us a safe, backward-compatible
>>> > > baseline that we can relax later one expression composition lands.
>>> > >
>>> > > Happy to review the updated PR and spec wording once ready.
>>> > >
>>> > > Regards
>>> > > JB
>>> > >
>>> > > On Tue, Sep 29, 2026 at 11:50 PM Chris Eubank
>>> > > <ch...@databricks.com> wrote:
>>> > >>
>>> > >> Hi Josh,
>>> > >>
>>> > >> Thank you for putting together this proposal for dataset-scoped
>>> metrics. It's a fundamental building block for several in-flight
>>> extensions: expression resolution / composability (thread), relational
>>> query semantics (thread) and dimensional query semantics (thread).
>>> > >>
>>> > >> Several open comments remain, mostly concerning those follow-up
>>> features (e.g. expression composition). I'm wondering if we can tighten
>>> this PR's scope to land the minimal spec extension needed for a spec vote,
>>> and defer the rest. The validation logic could even move to a follow-up PR.
>>> > >>
>>> > >> Concretely, I'd propose these three restrictions:
>>> > >>
>>> > >> 1. Datasets may declare metrics. Within a dataset, names must be
>>> unique across fields and metrics.
>>> > >> 2. A dataset's metric expression may reference only that dataset's
>>> source fields. We can lift this restriction once the expression-composition
>>> rules land.
>>> > >> 3. Those references must be single-part (unqualified) within the
>>> expression and must resolve to the dataset's source fields.
>>> > >>
>>> > >> Would this narrower scope work for you as a path to a spec vote? If
>>> so, I'd be glad to help however is most useful - happy to pair on the
>>> scoped wording or take a first pass for us to iterate on.
>>> > >>
>>> > >> Kind regards,
>>> > >> Chris
>>> > >>
>>> > >> On Thu, Aug 27, 2026 at 2:20 PM Khushboo Bhatia <kb...@apache.org>
>>> wrote:
>>> > >>>
>>> > >>> Hey Josh,
>>> > >>> Thanks for starting this discussion. This issue has been raised in
>>> multiple discussions on the repo.
>>> > >>>
>>> > >>> I am generally supportive of adding these. Leaving some comments
>>> on the PR for the subtle differences between semantic model scoped metrics
>>> vs dataset scoped.
>>> > >>>
>>> > >>> Khushboo
>>> > >>>
>>> > >>> On 2026/08/27 16:57:13 Klahr Joshua via dev wrote:
>>> > >>>>
>>> > >>>>
>>> > >>>>
>>> > >>>> Hi all,
>>> > >>>>
>>> > >>>> I'd like to propose allowing metrics to be declared on an
>>> individual dataset (datasets[].metrics) in addition to the semantic model
>>> (semantic_model.metrics), using the same metric structure in both
>>> placements.
>>> > >>>>
>>> > >>>> PR: https://github.com/apache/ossie/pull/343
>>> > >>>>
>>> > >>>> Motivation
>>> > >>>>
>>> > >>>> Today every metric must be declared at the semantic model level,
>>> even when it aggregates a single dataset's own fields. Three costs follow
>>> from that.
>>> > >>>>
>>> > >>>> First, a dataset is not independently interpretable — you cannot
>>> tell from a dataset what metrics exist over it without scanning the whole
>>> model, and simple aggregations end up separated from the fields they depend
>>> on.
>>> > >>>>
>>> > >>>> Second, and more concretely: round-trip fidelity through Ossie is
>>> reduced. Several formats we convert from attach metrics to a specific
>>> entity — Omni has per-view measures, Honeydew has per-entity metric files,
>>> WisdomAI has per-table measures, and dbt MetricFlow binds metrics to a
>>> semantic model. Because Ossie has no dataset-scoped placement, the
>>> converters in this repository flatten all of those to the model level on
>>> import, and the entity association is not represented in the resulting
>>> document.
>>> > >>>>
>>> > >>>> On export, four converters independently reconstruct that
>>> association by scanning expressions for dataset.column qualifiers and
>>> falling back to the first dataset when none is found. The Honeydew
>>> converter emits a runtime warning when it does so. Two converters stash the
>>> owning entity in vendor-specific custom_extensions, which round-trips
>>> within a single vendor's toolchain but is not something a generic Ossie
>>> consumer can produce or interpret.
>>> > >>>>
>>> > >>>> Four converters reimplementing the same heuristic suggests the
>>> spec is missing a place to put information the source formats already carry.
>>> > >>>>
>>> > >>>> Third, export placement can be coarser than the target supports.
>>> The Snowflake converter maps every Ossie model-level metric to Snowflake's
>>> top-level metrics block, including metrics referencing only one table that
>>> would fit the narrower tables[].metrics position. The converter cannot
>>> distinguish the two cases because the Ossie document does not record the
>>> distinction.
>>> > >>>>
>>> > >>>> What's proposed
>>> > >>>>
>>> > >>>> datasets[].metrics, using the identical Metric structure. Scoping
>>> rules:
>>> > >>>>
>>> > >>>> -
>>> > >>>> A dataset-scoped metric's expression MUST only reference fields
>>> of its declaring dataset, and MUST NOT traverse relationships.
>>> Cross-dataset metrics remain model-scoped.
>>> > >>>>
>>> > >>>> -
>>> > >>>> Names MUST be unique within their dataset — two datasets MAY each
>>> declare item_count.
>>> > >>>>
>>> > >>>> -
>>> > >>>> A dataset-scoped name MUST NOT collide with any model-scoped
>>> metric name.
>>> > >>>>
>>> > >>>> -
>>> > >>>> Referenced from outside the dataset as dataset_name.metric_name.
>>> > >>>>
>>> > >>>>
>>> > >>>> Worth stating explicitly because it's easy to misread: rule 1
>>> constrains the expression, not the query. A dataset-scoped metric can still
>>> be grouped by or filtered on dimensions from other datasets via
>>> relationships, since grouping dimensions come from the consumer at query
>>> time.
>>> > >>>>
>>> > >>>> Prior art
>>> > >>>>
>>> > >>>> Separating single-entity aggregations from cross-entity
>>> calculations is established practice. Four of the five systems I looked at
>>> do it — AtScale SML (metric bound to one dataset + column, with metric_calc
>>> as a separate type), Cube (measures within cubes), dbt MetricFlow (in-model
>>> vs top-level), and Snowflake semantic views (tables[].metrics vs top-level
>>> derived). Databricks UC metric views take a different approach, with one
>>> flat scope per view and joins declared inside it.
>>> > >>>>
>>> > >>>> On naming, the proposal extends Ossie's own existing convention
>>> rather than importing one: field names are already unique within a dataset
>>> rather than globally, and metric expressions already reference them as
>>> dataset.field. Applying the same scoped-uniqueness and qualified-reference
>>> pattern to metrics keeps the model internally consistent.
>>> > >>>>
>>> > >>>> On strictness it sits within the range the ecosystem spans — SML
>>> is more restrictive (one column, one aggregation method), Snowflake
>>> semantic views more permissive (table-level metrics may traverse
>>> relationships with a declared join path).
>>> > >>>>
>>> > >>>> Compatibility
>>> > >>>>
>>> > >>>> Additive and backward compatible. datasets[].metrics is optional
>>> and existing models remain valid unchanged. Because a dataset-scoped
>>> expression resolves within its own dataset, it is already valid as a
>>> model-scoped metric, so consumers wanting a single flat namespace can hoist
>>> without rewriting expressions. No converter changes are included; existing
>>> converters read semantic_model.metrics and that behaviour is untouched.
>>> > >>>>
>>> > >>>> Two open questions I'd particularly like input on
>>> > >>>>
>>> > >>>> -
>>> > >>>> Should a dataset-scoped metric eventually be allowed to traverse
>>> relationships when the join path is declared explicitly? The PR takes the
>>> conservative position, since relaxing later is backward compatible while
>>> tightening is not.
>>> > >>>>
>>> > >>>> -
>>> > >>>> Separately, I noticed the spec does not say whether one metric
>>> expression may reference another metric — there's no mechanism and no
>>> prohibition, while every system above has one. It's ambiguous rather than
>>> merely absent: SUM(orders.amount) / total_orders passes validation today
>>> because total_orders parses as an unqualified column. My inclination is to
>>> handle that separately since it needs aggregation-composition semantics
>>> settled, but the two features interact. The PR includes a sketch of what
>>> the reference-scoping rules would be, in case it's worth fixing those now
>>> for forward compatibility.
>>> > >>>>
>>> > >>>>
>>> > >>>> Validation
>>> > >>>>
>>> > >>>> Three of the rules are semantic constraints JSON Schema cannot
>>> express, so validate.py gains checks for duplicate names within a dataset,
>>> collisions with model-scoped names, and cross-dataset references (table
>>> qualifiers extracted via sqlglot). Scope checking skips rather than guesses
>>> for dialects sqlglot cannot parse. The TPC-DS example is updated to
>>> demonstrate both placements.
>>> > >>>>
>>> > >>>> AI disclosure
>>> > >>>>
>>> > >>>> This contribution was developed with AI assistance (Snowflake
>>> Cortex Code). I have reviewed all changes and am personally responsible for
>>> the content per the ASF Generative Tooling Guidance.
>>> > >>>>
>>> > >>>> Feedback and pushback welcome. Minimum discussion period is 7
>>> days per the contribution guidelines.
>>> > >>>>
>>> > >>>> Best,
>>> > >>>> Josh Klahr
>>> > >>>>
>>> > >>>>
>>> > >>>>
>>> > >>>>
>>> > >>>>
>>> >
>>>
>>
Re: [DISCUSS] Dataset-scoped metrics
Posted by Justin Talbot via dev <de...@ossie.apache.org>.
Thanks all.
I agree with Chris that we'll eventually need to support
both---dataset-scoped metrics should be able to reference both fields from
that same dataset and the dataset's source columns. We may also eventually
want dataset-scoped metrics to reference other metrics from that same
dataset.
I agree that we should ship references to dataset fields first (JB's
proposal). But my reasoning is slightly different and requires a change to
resolution precedence rules in #343.
Data field names can conflict with source column names. So when a dataset
metric references an ambiguous name, we need to have a precedence rule or
must require that users always qualify conflicting names. Changing the
precedence order or requiring qualified names, when we didn't before,
constitutes a breaking change. So we should try to get this right in v1.
I propose:
In a dataset-scoped metric definition:
- dataset field names take precedence over source column names and are
always referenced unqualified,
- when a user wants to reference a source column name that is shadowed by a
dataset field, they must qualify it.
These two rules imply that we can ship only dataset field references in the
first version and then safely extend this to include source column
references in the future without requiring a breaking change.
The above is a change from the current #343; it has source column names
taking precendence over dataset field names.
Justin
On Thu, Oct 1, 2026 at 9:56 PM Chris Eubank <ch...@databricks.com>
wrote:
> Hi JB,
>
> It might be worth getting Justin's interpretation from #486, but I think
> that having dataset-level metrics initially point to source fields is more
> consistent with that PR, which states:
>
> 1. "In a **field expression**, identifiers refer to columns of the
> dataset's `source`. A field expression cannot refer to other fields."
> 2. "In a **metric expression**, identifiers must be qualified as
> `dataset.field` and refer to a field declared in that dataset. They cannot
> refer to columns of the dataset's `source` or to other metrics."
>
> This lines up with the rules that:
> 1. Expressions that declare dataset concepts (currently, only fields) must
> only use inputs from the dataset source
> 2. Expressions that declare model concepts (currently, only metrics) must
> only use inputs from the datasets (and not sources directly)
>
> From a visibility perspective, having dataset metrics refer to source
> fields allows you to declare a metric without exposing the metric's inputs
> as dataset fields. If you want a model-level metric to reference a source
> field, you can achieve that goal by adding a passthrough field on the
> dataset. I definitely hear the pain of not being able to validate the
> metric inputs but that still remains a problem even if the metrics point to
> dataset fields (since field inputs cannot be validated).
>
> That said, I'm happy to disagree and commit if the community leans toward
> defining dataset metrics over dataset fields, especially since we will
> eventually need to support metrics built on source & dataset fields.
>
> @Kyoung Min, +1 to JB's offer. I'm happy to review or pair on your
> extensions. I'm excited for dataset metrics to land!
>
> Kind regards,
> Chris
>
>
> On Thu, Oct 1, 2026 at 9:17 PM Jean-Baptiste Onofré <jb...@nanthrax.net>
> wrote:
>
>> Hi guys,
>>
>> Thanks Kyoung Min for the summary, and it's good to know that most of
>> the validation is already in #343 (I did a couple of review already, I
>> will do a new pass).
>>
>> Before we rework the validator, I think we need to close the point
>> Chris and I left open: do the unqualified references resolve to source
>> columns or to the dataset's declared fields? @Chris, I understand you
>> meant source columns, for consistency with field expressions. I would
>> still go with declared fields for this PR, for three reasons:
>> 1. #486 makes model-scoped metrics see fields only. A dataset-scoped
>> metric over a raw column would then have no model-level equivalent, so
>> Josh's proposal doesn't work. With fields, we qualify each name with
>> the dataset.
>> 2. The reference becomes checkable. The field list is in the document,
>> the source column's are not.
>> 3. It answers Josh's collision question with the approach in #486: in
>> a metric, a bare name is the field. If we later allow undeclared
>> source columns as well, the field still wins, so that relax is
>> backward compatible.
>>
>> The cost is real: a column used only in a metric has to be declared as
>> a field, and importers from formats with per-entity measures would
>> have to synthetise these fields. I think it's acceptable for a first
>> version.
>>
>> @Kyoung Min, a commit against Josh's branch once the wording settles
>> sounds good to me, if Josh is fine with it. Happy to review :)
>>
>> Regards
>> JB
>>
>> On Fri, Oct 2, 2026 at 4:53 AM Kyoung Min Kim <ka...@gmail.com> wrote:
>> >
>> > Two things from the validate.py side, since JB's third point and Josh's
>> > question meet there.
>> >
>> > The current revision of #343 already carries most of what JB listed.
>> > validate_metric_scoping reports a duplicate metric name within a
>> dataset and
>> > a dataset-scoped metric that collides with a field as errors, a
>> model-scoped
>> > metric that shadows a dataset-scoped one as a warning, and a qualified
>> > reference that does not name a declared field as an error, with about
>> 300
>> > lines of tests behind it. Keeping validation in the PR is therefore a
>> matter
>> > of trimming those checks to whatever the narrowed rules end up being,
>> not of
>> > writing them. The branch is a month behind main and conflicts in
>> validate.py,
>> > spec.md, spec.yaml and both test files, so the rebase touches that code
>> > anyway.
>> >
>> > On fields versus source columns, the validator can only ever check one
>> of the
>> > two. The document lists a dataset's fields, so a reference to a
>> declared field
>> > can be checked for existence. It does not list the columns of the
>> source, and
>> > #343 says as much: "not checked, because that requires catalog metadata
>> the
>> > model does not carry". If the single-part references in Chris's third
>> > restriction resolve to source columns, the structural check shrinks to
>> > uniqueness plus "is single-part". If they resolve to the dataset's
>> declared
>> > fields, as JB read it, the existence check is the same lookup #486 asks
>> for in
>> > model-scoped metrics, where dataset.field must name a declared field
>> and a
>> > source column is never visible to a metric expression. That reading also
>> > removes the collision Josh is asking about, because a bare name in a
>> metric
>> > would mean the field and nothing else, the way the tie-break sentence
>> in #486
>> > settles it for qualified names.
>> >
>> > Josh, if trimming the validator to the narrowed rules is useful to hand
>> off, I
>> > can prepare it as a commit against your branch or as a follow-up PR the
>> day
>> > the spec text settles. If you would rather keep it in one PR, I will
>> review
>> > that part.
>> >
>> > Kyoung Min Kim
>> >
>> >
>> > >
>> > > Hi all,
>> > >
>> > > I completely agree with Chris's proposal to tighten the scope here.
>> > > Decoupling the placement (datasets[].metrics) from the larger,
>> > > in-flight semantic questions (expression composition, traversal paths,
>> > > grain locking) is the pragmatic way to get a clean spec vote (I love
>> > > pragmatic approaches :)).
>> > >
>> > > A few specific thoughts on the three restrictions and the rollout:
>> > > 1. Unqualified references and unified namespace are spot on.
>> > > Single-part references (SUM(amount) instead of orders.amount) neatly
>> > > eliminate the circularity issue in DDL/parser contexts that Justin
>> > > pointed out. Enforcing a single namespace across fields and metrics
>> > > within the dataset also elliminites the naming collision ambiguity.
>> > > 2. In the wording, we should be explicit about whether "source fields"
>> > > refers to the underlying physical table columns or the dataset's
>> > > declared fields (datasets[].fields). In Ossie, metrics should
>> > > aggregate the dataset's declared/modeled fields (or we need clear
>> > > precedence if both are permitted). Tightening this phrasing will avoid
>> > > confusion during the vote.
>> > > 3. While I support deferring complex AST evaluation or cross-dialect
>> > > query semantics to follow-up, I would prefer we do not strip the
>> > > validation entirely from this PR. The JSON schema update
>> > > (ossie-schema.json) and basic structural validation in validate.py
>> > > (uniqueness across fields + metrics, checking for unqualified
>> > > references, and null guards) should land together with the spec. In
>> > > Ossie, the schema and validator act as our executable spec: keeping
>> > > baseline structural checks ensures the vote lands on a solid,
>> > > verifiable foundation.
>> > >
>> > > Overall, this narrowed scope gives us a safe, backward-compatible
>> > > baseline that we can relax later one expression composition lands.
>> > >
>> > > Happy to review the updated PR and spec wording once ready.
>> > >
>> > > Regards
>> > > JB
>> > >
>> > > On Tue, Sep 29, 2026 at 11:50 PM Chris Eubank
>> > > <ch...@databricks.com> wrote:
>> > >>
>> > >> Hi Josh,
>> > >>
>> > >> Thank you for putting together this proposal for dataset-scoped
>> metrics. It's a fundamental building block for several in-flight
>> extensions: expression resolution / composability (thread), relational
>> query semantics (thread) and dimensional query semantics (thread).
>> > >>
>> > >> Several open comments remain, mostly concerning those follow-up
>> features (e.g. expression composition). I'm wondering if we can tighten
>> this PR's scope to land the minimal spec extension needed for a spec vote,
>> and defer the rest. The validation logic could even move to a follow-up PR.
>> > >>
>> > >> Concretely, I'd propose these three restrictions:
>> > >>
>> > >> 1. Datasets may declare metrics. Within a dataset, names must be
>> unique across fields and metrics.
>> > >> 2. A dataset's metric expression may reference only that dataset's
>> source fields. We can lift this restriction once the expression-composition
>> rules land.
>> > >> 3. Those references must be single-part (unqualified) within the
>> expression and must resolve to the dataset's source fields.
>> > >>
>> > >> Would this narrower scope work for you as a path to a spec vote? If
>> so, I'd be glad to help however is most useful - happy to pair on the
>> scoped wording or take a first pass for us to iterate on.
>> > >>
>> > >> Kind regards,
>> > >> Chris
>> > >>
>> > >> On Thu, Aug 27, 2026 at 2:20 PM Khushboo Bhatia <kb...@apache.org>
>> wrote:
>> > >>>
>> > >>> Hey Josh,
>> > >>> Thanks for starting this discussion. This issue has been raised in
>> multiple discussions on the repo.
>> > >>>
>> > >>> I am generally supportive of adding these. Leaving some comments on
>> the PR for the subtle differences between semantic model scoped metrics vs
>> dataset scoped.
>> > >>>
>> > >>> Khushboo
>> > >>>
>> > >>> On 2026/08/27 16:57:13 Klahr Joshua via dev wrote:
>> > >>>>
>> > >>>>
>> > >>>>
>> > >>>> Hi all,
>> > >>>>
>> > >>>> I'd like to propose allowing metrics to be declared on an
>> individual dataset (datasets[].metrics) in addition to the semantic model
>> (semantic_model.metrics), using the same metric structure in both
>> placements.
>> > >>>>
>> > >>>> PR: https://github.com/apache/ossie/pull/343
>> > >>>>
>> > >>>> Motivation
>> > >>>>
>> > >>>> Today every metric must be declared at the semantic model level,
>> even when it aggregates a single dataset's own fields. Three costs follow
>> from that.
>> > >>>>
>> > >>>> First, a dataset is not independently interpretable — you cannot
>> tell from a dataset what metrics exist over it without scanning the whole
>> model, and simple aggregations end up separated from the fields they depend
>> on.
>> > >>>>
>> > >>>> Second, and more concretely: round-trip fidelity through Ossie is
>> reduced. Several formats we convert from attach metrics to a specific
>> entity — Omni has per-view measures, Honeydew has per-entity metric files,
>> WisdomAI has per-table measures, and dbt MetricFlow binds metrics to a
>> semantic model. Because Ossie has no dataset-scoped placement, the
>> converters in this repository flatten all of those to the model level on
>> import, and the entity association is not represented in the resulting
>> document.
>> > >>>>
>> > >>>> On export, four converters independently reconstruct that
>> association by scanning expressions for dataset.column qualifiers and
>> falling back to the first dataset when none is found. The Honeydew
>> converter emits a runtime warning when it does so. Two converters stash the
>> owning entity in vendor-specific custom_extensions, which round-trips
>> within a single vendor's toolchain but is not something a generic Ossie
>> consumer can produce or interpret.
>> > >>>>
>> > >>>> Four converters reimplementing the same heuristic suggests the
>> spec is missing a place to put information the source formats already carry.
>> > >>>>
>> > >>>> Third, export placement can be coarser than the target supports.
>> The Snowflake converter maps every Ossie model-level metric to Snowflake's
>> top-level metrics block, including metrics referencing only one table that
>> would fit the narrower tables[].metrics position. The converter cannot
>> distinguish the two cases because the Ossie document does not record the
>> distinction.
>> > >>>>
>> > >>>> What's proposed
>> > >>>>
>> > >>>> datasets[].metrics, using the identical Metric structure. Scoping
>> rules:
>> > >>>>
>> > >>>> -
>> > >>>> A dataset-scoped metric's expression MUST only reference fields of
>> its declaring dataset, and MUST NOT traverse relationships. Cross-dataset
>> metrics remain model-scoped.
>> > >>>>
>> > >>>> -
>> > >>>> Names MUST be unique within their dataset — two datasets MAY each
>> declare item_count.
>> > >>>>
>> > >>>> -
>> > >>>> A dataset-scoped name MUST NOT collide with any model-scoped
>> metric name.
>> > >>>>
>> > >>>> -
>> > >>>> Referenced from outside the dataset as dataset_name.metric_name.
>> > >>>>
>> > >>>>
>> > >>>> Worth stating explicitly because it's easy to misread: rule 1
>> constrains the expression, not the query. A dataset-scoped metric can still
>> be grouped by or filtered on dimensions from other datasets via
>> relationships, since grouping dimensions come from the consumer at query
>> time.
>> > >>>>
>> > >>>> Prior art
>> > >>>>
>> > >>>> Separating single-entity aggregations from cross-entity
>> calculations is established practice. Four of the five systems I looked at
>> do it — AtScale SML (metric bound to one dataset + column, with metric_calc
>> as a separate type), Cube (measures within cubes), dbt MetricFlow (in-model
>> vs top-level), and Snowflake semantic views (tables[].metrics vs top-level
>> derived). Databricks UC metric views take a different approach, with one
>> flat scope per view and joins declared inside it.
>> > >>>>
>> > >>>> On naming, the proposal extends Ossie's own existing convention
>> rather than importing one: field names are already unique within a dataset
>> rather than globally, and metric expressions already reference them as
>> dataset.field. Applying the same scoped-uniqueness and qualified-reference
>> pattern to metrics keeps the model internally consistent.
>> > >>>>
>> > >>>> On strictness it sits within the range the ecosystem spans — SML
>> is more restrictive (one column, one aggregation method), Snowflake
>> semantic views more permissive (table-level metrics may traverse
>> relationships with a declared join path).
>> > >>>>
>> > >>>> Compatibility
>> > >>>>
>> > >>>> Additive and backward compatible. datasets[].metrics is optional
>> and existing models remain valid unchanged. Because a dataset-scoped
>> expression resolves within its own dataset, it is already valid as a
>> model-scoped metric, so consumers wanting a single flat namespace can hoist
>> without rewriting expressions. No converter changes are included; existing
>> converters read semantic_model.metrics and that behaviour is untouched.
>> > >>>>
>> > >>>> Two open questions I'd particularly like input on
>> > >>>>
>> > >>>> -
>> > >>>> Should a dataset-scoped metric eventually be allowed to traverse
>> relationships when the join path is declared explicitly? The PR takes the
>> conservative position, since relaxing later is backward compatible while
>> tightening is not.
>> > >>>>
>> > >>>> -
>> > >>>> Separately, I noticed the spec does not say whether one metric
>> expression may reference another metric — there's no mechanism and no
>> prohibition, while every system above has one. It's ambiguous rather than
>> merely absent: SUM(orders.amount) / total_orders passes validation today
>> because total_orders parses as an unqualified column. My inclination is to
>> handle that separately since it needs aggregation-composition semantics
>> settled, but the two features interact. The PR includes a sketch of what
>> the reference-scoping rules would be, in case it's worth fixing those now
>> for forward compatibility.
>> > >>>>
>> > >>>>
>> > >>>> Validation
>> > >>>>
>> > >>>> Three of the rules are semantic constraints JSON Schema cannot
>> express, so validate.py gains checks for duplicate names within a dataset,
>> collisions with model-scoped names, and cross-dataset references (table
>> qualifiers extracted via sqlglot). Scope checking skips rather than guesses
>> for dialects sqlglot cannot parse. The TPC-DS example is updated to
>> demonstrate both placements.
>> > >>>>
>> > >>>> AI disclosure
>> > >>>>
>> > >>>> This contribution was developed with AI assistance (Snowflake
>> Cortex Code). I have reviewed all changes and am personally responsible for
>> the content per the ASF Generative Tooling Guidance.
>> > >>>>
>> > >>>> Feedback and pushback welcome. Minimum discussion period is 7 days
>> per the contribution guidelines.
>> > >>>>
>> > >>>> Best,
>> > >>>> Josh Klahr
>> > >>>>
>> > >>>>
>> > >>>>
>> > >>>>
>> > >>>>
>> >
>>
>
Re: [DISCUSS] Dataset-scoped metrics
Posted by Chris Eubank via dev <de...@ossie.apache.org>.
Hi JB,
It might be worth getting Justin's interpretation from #486, but I think
that having dataset-level metrics initially point to source fields is more
consistent with that PR, which states:
1. "In a **field expression**, identifiers refer to columns of the
dataset's `source`. A field expression cannot refer to other fields."
2. "In a **metric expression**, identifiers must be qualified as
`dataset.field` and refer to a field declared in that dataset. They cannot
refer to columns of the dataset's `source` or to other metrics."
This lines up with the rules that:
1. Expressions that declare dataset concepts (currently, only fields) must
only use inputs from the dataset source
2. Expressions that declare model concepts (currently, only metrics) must
only use inputs from the datasets (and not sources directly)
From a visibility perspective, having dataset metrics refer to source
fields allows you to declare a metric without exposing the metric's inputs
as dataset fields. If you want a model-level metric to reference a source
field, you can achieve that goal by adding a passthrough field on the
dataset. I definitely hear the pain of not being able to validate the
metric inputs but that still remains a problem even if the metrics point to
dataset fields (since field inputs cannot be validated).
That said, I'm happy to disagree and commit if the community leans toward
defining dataset metrics over dataset fields, especially since we will
eventually need to support metrics built on source & dataset fields.
@Kyoung Min, +1 to JB's offer. I'm happy to review or pair on your
extensions. I'm excited for dataset metrics to land!
Kind regards,
Chris
On Thu, Oct 1, 2026 at 9:17 PM Jean-Baptiste Onofré <jb...@nanthrax.net> wrote:
> Hi guys,
>
> Thanks Kyoung Min for the summary, and it's good to know that most of
> the validation is already in #343 (I did a couple of review already, I
> will do a new pass).
>
> Before we rework the validator, I think we need to close the point
> Chris and I left open: do the unqualified references resolve to source
> columns or to the dataset's declared fields? @Chris, I understand you
> meant source columns, for consistency with field expressions. I would
> still go with declared fields for this PR, for three reasons:
> 1. #486 makes model-scoped metrics see fields only. A dataset-scoped
> metric over a raw column would then have no model-level equivalent, so
> Josh's proposal doesn't work. With fields, we qualify each name with
> the dataset.
> 2. The reference becomes checkable. The field list is in the document,
> the source column's are not.
> 3. It answers Josh's collision question with the approach in #486: in
> a metric, a bare name is the field. If we later allow undeclared
> source columns as well, the field still wins, so that relax is
> backward compatible.
>
> The cost is real: a column used only in a metric has to be declared as
> a field, and importers from formats with per-entity measures would
> have to synthetise these fields. I think it's acceptable for a first
> version.
>
> @Kyoung Min, a commit against Josh's branch once the wording settles
> sounds good to me, if Josh is fine with it. Happy to review :)
>
> Regards
> JB
>
> On Fri, Oct 2, 2026 at 4:53 AM Kyoung Min Kim <ka...@gmail.com> wrote:
> >
> > Two things from the validate.py side, since JB's third point and Josh's
> > question meet there.
> >
> > The current revision of #343 already carries most of what JB listed.
> > validate_metric_scoping reports a duplicate metric name within a dataset
> and
> > a dataset-scoped metric that collides with a field as errors, a
> model-scoped
> > metric that shadows a dataset-scoped one as a warning, and a qualified
> > reference that does not name a declared field as an error, with about 300
> > lines of tests behind it. Keeping validation in the PR is therefore a
> matter
> > of trimming those checks to whatever the narrowed rules end up being,
> not of
> > writing them. The branch is a month behind main and conflicts in
> validate.py,
> > spec.md, spec.yaml and both test files, so the rebase touches that code
> > anyway.
> >
> > On fields versus source columns, the validator can only ever check one
> of the
> > two. The document lists a dataset's fields, so a reference to a declared
> field
> > can be checked for existence. It does not list the columns of the
> source, and
> > #343 says as much: "not checked, because that requires catalog metadata
> the
> > model does not carry". If the single-part references in Chris's third
> > restriction resolve to source columns, the structural check shrinks to
> > uniqueness plus "is single-part". If they resolve to the dataset's
> declared
> > fields, as JB read it, the existence check is the same lookup #486 asks
> for in
> > model-scoped metrics, where dataset.field must name a declared field and
> a
> > source column is never visible to a metric expression. That reading also
> > removes the collision Josh is asking about, because a bare name in a
> metric
> > would mean the field and nothing else, the way the tie-break sentence in
> #486
> > settles it for qualified names.
> >
> > Josh, if trimming the validator to the narrowed rules is useful to hand
> off, I
> > can prepare it as a commit against your branch or as a follow-up PR the
> day
> > the spec text settles. If you would rather keep it in one PR, I will
> review
> > that part.
> >
> > Kyoung Min Kim
> >
> >
> > >
> > > Hi all,
> > >
> > > I completely agree with Chris's proposal to tighten the scope here.
> > > Decoupling the placement (datasets[].metrics) from the larger,
> > > in-flight semantic questions (expression composition, traversal paths,
> > > grain locking) is the pragmatic way to get a clean spec vote (I love
> > > pragmatic approaches :)).
> > >
> > > A few specific thoughts on the three restrictions and the rollout:
> > > 1. Unqualified references and unified namespace are spot on.
> > > Single-part references (SUM(amount) instead of orders.amount) neatly
> > > eliminate the circularity issue in DDL/parser contexts that Justin
> > > pointed out. Enforcing a single namespace across fields and metrics
> > > within the dataset also elliminites the naming collision ambiguity.
> > > 2. In the wording, we should be explicit about whether "source fields"
> > > refers to the underlying physical table columns or the dataset's
> > > declared fields (datasets[].fields). In Ossie, metrics should
> > > aggregate the dataset's declared/modeled fields (or we need clear
> > > precedence if both are permitted). Tightening this phrasing will avoid
> > > confusion during the vote.
> > > 3. While I support deferring complex AST evaluation or cross-dialect
> > > query semantics to follow-up, I would prefer we do not strip the
> > > validation entirely from this PR. The JSON schema update
> > > (ossie-schema.json) and basic structural validation in validate.py
> > > (uniqueness across fields + metrics, checking for unqualified
> > > references, and null guards) should land together with the spec. In
> > > Ossie, the schema and validator act as our executable spec: keeping
> > > baseline structural checks ensures the vote lands on a solid,
> > > verifiable foundation.
> > >
> > > Overall, this narrowed scope gives us a safe, backward-compatible
> > > baseline that we can relax later one expression composition lands.
> > >
> > > Happy to review the updated PR and spec wording once ready.
> > >
> > > Regards
> > > JB
> > >
> > > On Tue, Sep 29, 2026 at 11:50 PM Chris Eubank
> > > <ch...@databricks.com> wrote:
> > >>
> > >> Hi Josh,
> > >>
> > >> Thank you for putting together this proposal for dataset-scoped
> metrics. It's a fundamental building block for several in-flight
> extensions: expression resolution / composability (thread), relational
> query semantics (thread) and dimensional query semantics (thread).
> > >>
> > >> Several open comments remain, mostly concerning those follow-up
> features (e.g. expression composition). I'm wondering if we can tighten
> this PR's scope to land the minimal spec extension needed for a spec vote,
> and defer the rest. The validation logic could even move to a follow-up PR.
> > >>
> > >> Concretely, I'd propose these three restrictions:
> > >>
> > >> 1. Datasets may declare metrics. Within a dataset, names must be
> unique across fields and metrics.
> > >> 2. A dataset's metric expression may reference only that dataset's
> source fields. We can lift this restriction once the expression-composition
> rules land.
> > >> 3. Those references must be single-part (unqualified) within the
> expression and must resolve to the dataset's source fields.
> > >>
> > >> Would this narrower scope work for you as a path to a spec vote? If
> so, I'd be glad to help however is most useful - happy to pair on the
> scoped wording or take a first pass for us to iterate on.
> > >>
> > >> Kind regards,
> > >> Chris
> > >>
> > >> On Thu, Aug 27, 2026 at 2:20 PM Khushboo Bhatia <kb...@apache.org>
> wrote:
> > >>>
> > >>> Hey Josh,
> > >>> Thanks for starting this discussion. This issue has been raised in
> multiple discussions on the repo.
> > >>>
> > >>> I am generally supportive of adding these. Leaving some comments on
> the PR for the subtle differences between semantic model scoped metrics vs
> dataset scoped.
> > >>>
> > >>> Khushboo
> > >>>
> > >>> On 2026/08/27 16:57:13 Klahr Joshua via dev wrote:
> > >>>>
> > >>>>
> > >>>>
> > >>>> Hi all,
> > >>>>
> > >>>> I'd like to propose allowing metrics to be declared on an
> individual dataset (datasets[].metrics) in addition to the semantic model
> (semantic_model.metrics), using the same metric structure in both
> placements.
> > >>>>
> > >>>> PR: https://github.com/apache/ossie/pull/343
> > >>>>
> > >>>> Motivation
> > >>>>
> > >>>> Today every metric must be declared at the semantic model level,
> even when it aggregates a single dataset's own fields. Three costs follow
> from that.
> > >>>>
> > >>>> First, a dataset is not independently interpretable — you cannot
> tell from a dataset what metrics exist over it without scanning the whole
> model, and simple aggregations end up separated from the fields they depend
> on.
> > >>>>
> > >>>> Second, and more concretely: round-trip fidelity through Ossie is
> reduced. Several formats we convert from attach metrics to a specific
> entity — Omni has per-view measures, Honeydew has per-entity metric files,
> WisdomAI has per-table measures, and dbt MetricFlow binds metrics to a
> semantic model. Because Ossie has no dataset-scoped placement, the
> converters in this repository flatten all of those to the model level on
> import, and the entity association is not represented in the resulting
> document.
> > >>>>
> > >>>> On export, four converters independently reconstruct that
> association by scanning expressions for dataset.column qualifiers and
> falling back to the first dataset when none is found. The Honeydew
> converter emits a runtime warning when it does so. Two converters stash the
> owning entity in vendor-specific custom_extensions, which round-trips
> within a single vendor's toolchain but is not something a generic Ossie
> consumer can produce or interpret.
> > >>>>
> > >>>> Four converters reimplementing the same heuristic suggests the spec
> is missing a place to put information the source formats already carry.
> > >>>>
> > >>>> Third, export placement can be coarser than the target supports.
> The Snowflake converter maps every Ossie model-level metric to Snowflake's
> top-level metrics block, including metrics referencing only one table that
> would fit the narrower tables[].metrics position. The converter cannot
> distinguish the two cases because the Ossie document does not record the
> distinction.
> > >>>>
> > >>>> What's proposed
> > >>>>
> > >>>> datasets[].metrics, using the identical Metric structure. Scoping
> rules:
> > >>>>
> > >>>> -
> > >>>> A dataset-scoped metric's expression MUST only reference fields of
> its declaring dataset, and MUST NOT traverse relationships. Cross-dataset
> metrics remain model-scoped.
> > >>>>
> > >>>> -
> > >>>> Names MUST be unique within their dataset — two datasets MAY each
> declare item_count.
> > >>>>
> > >>>> -
> > >>>> A dataset-scoped name MUST NOT collide with any model-scoped metric
> name.
> > >>>>
> > >>>> -
> > >>>> Referenced from outside the dataset as dataset_name.metric_name.
> > >>>>
> > >>>>
> > >>>> Worth stating explicitly because it's easy to misread: rule 1
> constrains the expression, not the query. A dataset-scoped metric can still
> be grouped by or filtered on dimensions from other datasets via
> relationships, since grouping dimensions come from the consumer at query
> time.
> > >>>>
> > >>>> Prior art
> > >>>>
> > >>>> Separating single-entity aggregations from cross-entity
> calculations is established practice. Four of the five systems I looked at
> do it — AtScale SML (metric bound to one dataset + column, with metric_calc
> as a separate type), Cube (measures within cubes), dbt MetricFlow (in-model
> vs top-level), and Snowflake semantic views (tables[].metrics vs top-level
> derived). Databricks UC metric views take a different approach, with one
> flat scope per view and joins declared inside it.
> > >>>>
> > >>>> On naming, the proposal extends Ossie's own existing convention
> rather than importing one: field names are already unique within a dataset
> rather than globally, and metric expressions already reference them as
> dataset.field. Applying the same scoped-uniqueness and qualified-reference
> pattern to metrics keeps the model internally consistent.
> > >>>>
> > >>>> On strictness it sits within the range the ecosystem spans — SML is
> more restrictive (one column, one aggregation method), Snowflake semantic
> views more permissive (table-level metrics may traverse relationships with
> a declared join path).
> > >>>>
> > >>>> Compatibility
> > >>>>
> > >>>> Additive and backward compatible. datasets[].metrics is optional
> and existing models remain valid unchanged. Because a dataset-scoped
> expression resolves within its own dataset, it is already valid as a
> model-scoped metric, so consumers wanting a single flat namespace can hoist
> without rewriting expressions. No converter changes are included; existing
> converters read semantic_model.metrics and that behaviour is untouched.
> > >>>>
> > >>>> Two open questions I'd particularly like input on
> > >>>>
> > >>>> -
> > >>>> Should a dataset-scoped metric eventually be allowed to traverse
> relationships when the join path is declared explicitly? The PR takes the
> conservative position, since relaxing later is backward compatible while
> tightening is not.
> > >>>>
> > >>>> -
> > >>>> Separately, I noticed the spec does not say whether one metric
> expression may reference another metric — there's no mechanism and no
> prohibition, while every system above has one. It's ambiguous rather than
> merely absent: SUM(orders.amount) / total_orders passes validation today
> because total_orders parses as an unqualified column. My inclination is to
> handle that separately since it needs aggregation-composition semantics
> settled, but the two features interact. The PR includes a sketch of what
> the reference-scoping rules would be, in case it's worth fixing those now
> for forward compatibility.
> > >>>>
> > >>>>
> > >>>> Validation
> > >>>>
> > >>>> Three of the rules are semantic constraints JSON Schema cannot
> express, so validate.py gains checks for duplicate names within a dataset,
> collisions with model-scoped names, and cross-dataset references (table
> qualifiers extracted via sqlglot). Scope checking skips rather than guesses
> for dialects sqlglot cannot parse. The TPC-DS example is updated to
> demonstrate both placements.
> > >>>>
> > >>>> AI disclosure
> > >>>>
> > >>>> This contribution was developed with AI assistance (Snowflake
> Cortex Code). I have reviewed all changes and am personally responsible for
> the content per the ASF Generative Tooling Guidance.
> > >>>>
> > >>>> Feedback and pushback welcome. Minimum discussion period is 7 days
> per the contribution guidelines.
> > >>>>
> > >>>> Best,
> > >>>> Josh Klahr
> > >>>>
> > >>>>
> > >>>>
> > >>>>
> > >>>>
> >
>
Re: [DISCUSS] Dataset-scoped metrics
Posted by Jean-Baptiste Onofré <jb...@nanthrax.net>.
Hi guys,
Thanks Kyoung Min for the summary, and it's good to know that most of
the validation is already in #343 (I did a couple of review already, I
will do a new pass).
Before we rework the validator, I think we need to close the point
Chris and I left open: do the unqualified references resolve to source
columns or to the dataset's declared fields? @Chris, I understand you
meant source columns, for consistency with field expressions. I would
still go with declared fields for this PR, for three reasons:
1. #486 makes model-scoped metrics see fields only. A dataset-scoped
metric over a raw column would then have no model-level equivalent, so
Josh's proposal doesn't work. With fields, we qualify each name with
the dataset.
2. The reference becomes checkable. The field list is in the document,
the source column's are not.
3. It answers Josh's collision question with the approach in #486: in
a metric, a bare name is the field. If we later allow undeclared
source columns as well, the field still wins, so that relax is
backward compatible.
The cost is real: a column used only in a metric has to be declared as
a field, and importers from formats with per-entity measures would
have to synthetise these fields. I think it's acceptable for a first
version.
@Kyoung Min, a commit against Josh's branch once the wording settles
sounds good to me, if Josh is fine with it. Happy to review :)
Regards
JB
On Fri, Oct 2, 2026 at 4:53 AM Kyoung Min Kim <ka...@gmail.com> wrote:
>
> Two things from the validate.py side, since JB's third point and Josh's
> question meet there.
>
> The current revision of #343 already carries most of what JB listed.
> validate_metric_scoping reports a duplicate metric name within a dataset and
> a dataset-scoped metric that collides with a field as errors, a model-scoped
> metric that shadows a dataset-scoped one as a warning, and a qualified
> reference that does not name a declared field as an error, with about 300
> lines of tests behind it. Keeping validation in the PR is therefore a matter
> of trimming those checks to whatever the narrowed rules end up being, not of
> writing them. The branch is a month behind main and conflicts in validate.py,
> spec.md, spec.yaml and both test files, so the rebase touches that code
> anyway.
>
> On fields versus source columns, the validator can only ever check one of the
> two. The document lists a dataset's fields, so a reference to a declared field
> can be checked for existence. It does not list the columns of the source, and
> #343 says as much: "not checked, because that requires catalog metadata the
> model does not carry". If the single-part references in Chris's third
> restriction resolve to source columns, the structural check shrinks to
> uniqueness plus "is single-part". If they resolve to the dataset's declared
> fields, as JB read it, the existence check is the same lookup #486 asks for in
> model-scoped metrics, where dataset.field must name a declared field and a
> source column is never visible to a metric expression. That reading also
> removes the collision Josh is asking about, because a bare name in a metric
> would mean the field and nothing else, the way the tie-break sentence in #486
> settles it for qualified names.
>
> Josh, if trimming the validator to the narrowed rules is useful to hand off, I
> can prepare it as a commit against your branch or as a follow-up PR the day
> the spec text settles. If you would rather keep it in one PR, I will review
> that part.
>
> Kyoung Min Kim
>
>
> >
> > Hi all,
> >
> > I completely agree with Chris's proposal to tighten the scope here.
> > Decoupling the placement (datasets[].metrics) from the larger,
> > in-flight semantic questions (expression composition, traversal paths,
> > grain locking) is the pragmatic way to get a clean spec vote (I love
> > pragmatic approaches :)).
> >
> > A few specific thoughts on the three restrictions and the rollout:
> > 1. Unqualified references and unified namespace are spot on.
> > Single-part references (SUM(amount) instead of orders.amount) neatly
> > eliminate the circularity issue in DDL/parser contexts that Justin
> > pointed out. Enforcing a single namespace across fields and metrics
> > within the dataset also elliminites the naming collision ambiguity.
> > 2. In the wording, we should be explicit about whether "source fields"
> > refers to the underlying physical table columns or the dataset's
> > declared fields (datasets[].fields). In Ossie, metrics should
> > aggregate the dataset's declared/modeled fields (or we need clear
> > precedence if both are permitted). Tightening this phrasing will avoid
> > confusion during the vote.
> > 3. While I support deferring complex AST evaluation or cross-dialect
> > query semantics to follow-up, I would prefer we do not strip the
> > validation entirely from this PR. The JSON schema update
> > (ossie-schema.json) and basic structural validation in validate.py
> > (uniqueness across fields + metrics, checking for unqualified
> > references, and null guards) should land together with the spec. In
> > Ossie, the schema and validator act as our executable spec: keeping
> > baseline structural checks ensures the vote lands on a solid,
> > verifiable foundation.
> >
> > Overall, this narrowed scope gives us a safe, backward-compatible
> > baseline that we can relax later one expression composition lands.
> >
> > Happy to review the updated PR and spec wording once ready.
> >
> > Regards
> > JB
> >
> > On Tue, Sep 29, 2026 at 11:50 PM Chris Eubank
> > <ch...@databricks.com> wrote:
> >>
> >> Hi Josh,
> >>
> >> Thank you for putting together this proposal for dataset-scoped metrics. It's a fundamental building block for several in-flight extensions: expression resolution / composability (thread), relational query semantics (thread) and dimensional query semantics (thread).
> >>
> >> Several open comments remain, mostly concerning those follow-up features (e.g. expression composition). I'm wondering if we can tighten this PR's scope to land the minimal spec extension needed for a spec vote, and defer the rest. The validation logic could even move to a follow-up PR.
> >>
> >> Concretely, I'd propose these three restrictions:
> >>
> >> 1. Datasets may declare metrics. Within a dataset, names must be unique across fields and metrics.
> >> 2. A dataset's metric expression may reference only that dataset's source fields. We can lift this restriction once the expression-composition rules land.
> >> 3. Those references must be single-part (unqualified) within the expression and must resolve to the dataset's source fields.
> >>
> >> Would this narrower scope work for you as a path to a spec vote? If so, I'd be glad to help however is most useful - happy to pair on the scoped wording or take a first pass for us to iterate on.
> >>
> >> Kind regards,
> >> Chris
> >>
> >> On Thu, Aug 27, 2026 at 2:20 PM Khushboo Bhatia <kb...@apache.org> wrote:
> >>>
> >>> Hey Josh,
> >>> Thanks for starting this discussion. This issue has been raised in multiple discussions on the repo.
> >>>
> >>> I am generally supportive of adding these. Leaving some comments on the PR for the subtle differences between semantic model scoped metrics vs dataset scoped.
> >>>
> >>> Khushboo
> >>>
> >>> On 2026/08/27 16:57:13 Klahr Joshua via dev wrote:
> >>>>
> >>>>
> >>>>
> >>>> Hi all,
> >>>>
> >>>> I'd like to propose allowing metrics to be declared on an individual dataset (datasets[].metrics) in addition to the semantic model (semantic_model.metrics), using the same metric structure in both placements.
> >>>>
> >>>> PR: https://github.com/apache/ossie/pull/343
> >>>>
> >>>> Motivation
> >>>>
> >>>> Today every metric must be declared at the semantic model level, even when it aggregates a single dataset's own fields. Three costs follow from that.
> >>>>
> >>>> First, a dataset is not independently interpretable — you cannot tell from a dataset what metrics exist over it without scanning the whole model, and simple aggregations end up separated from the fields they depend on.
> >>>>
> >>>> Second, and more concretely: round-trip fidelity through Ossie is reduced. Several formats we convert from attach metrics to a specific entity — Omni has per-view measures, Honeydew has per-entity metric files, WisdomAI has per-table measures, and dbt MetricFlow binds metrics to a semantic model. Because Ossie has no dataset-scoped placement, the converters in this repository flatten all of those to the model level on import, and the entity association is not represented in the resulting document.
> >>>>
> >>>> On export, four converters independently reconstruct that association by scanning expressions for dataset.column qualifiers and falling back to the first dataset when none is found. The Honeydew converter emits a runtime warning when it does so. Two converters stash the owning entity in vendor-specific custom_extensions, which round-trips within a single vendor's toolchain but is not something a generic Ossie consumer can produce or interpret.
> >>>>
> >>>> Four converters reimplementing the same heuristic suggests the spec is missing a place to put information the source formats already carry.
> >>>>
> >>>> Third, export placement can be coarser than the target supports. The Snowflake converter maps every Ossie model-level metric to Snowflake's top-level metrics block, including metrics referencing only one table that would fit the narrower tables[].metrics position. The converter cannot distinguish the two cases because the Ossie document does not record the distinction.
> >>>>
> >>>> What's proposed
> >>>>
> >>>> datasets[].metrics, using the identical Metric structure. Scoping rules:
> >>>>
> >>>> -
> >>>> A dataset-scoped metric's expression MUST only reference fields of its declaring dataset, and MUST NOT traverse relationships. Cross-dataset metrics remain model-scoped.
> >>>>
> >>>> -
> >>>> Names MUST be unique within their dataset — two datasets MAY each declare item_count.
> >>>>
> >>>> -
> >>>> A dataset-scoped name MUST NOT collide with any model-scoped metric name.
> >>>>
> >>>> -
> >>>> Referenced from outside the dataset as dataset_name.metric_name.
> >>>>
> >>>>
> >>>> Worth stating explicitly because it's easy to misread: rule 1 constrains the expression, not the query. A dataset-scoped metric can still be grouped by or filtered on dimensions from other datasets via relationships, since grouping dimensions come from the consumer at query time.
> >>>>
> >>>> Prior art
> >>>>
> >>>> Separating single-entity aggregations from cross-entity calculations is established practice. Four of the five systems I looked at do it — AtScale SML (metric bound to one dataset + column, with metric_calc as a separate type), Cube (measures within cubes), dbt MetricFlow (in-model vs top-level), and Snowflake semantic views (tables[].metrics vs top-level derived). Databricks UC metric views take a different approach, with one flat scope per view and joins declared inside it.
> >>>>
> >>>> On naming, the proposal extends Ossie's own existing convention rather than importing one: field names are already unique within a dataset rather than globally, and metric expressions already reference them as dataset.field. Applying the same scoped-uniqueness and qualified-reference pattern to metrics keeps the model internally consistent.
> >>>>
> >>>> On strictness it sits within the range the ecosystem spans — SML is more restrictive (one column, one aggregation method), Snowflake semantic views more permissive (table-level metrics may traverse relationships with a declared join path).
> >>>>
> >>>> Compatibility
> >>>>
> >>>> Additive and backward compatible. datasets[].metrics is optional and existing models remain valid unchanged. Because a dataset-scoped expression resolves within its own dataset, it is already valid as a model-scoped metric, so consumers wanting a single flat namespace can hoist without rewriting expressions. No converter changes are included; existing converters read semantic_model.metrics and that behaviour is untouched.
> >>>>
> >>>> Two open questions I'd particularly like input on
> >>>>
> >>>> -
> >>>> Should a dataset-scoped metric eventually be allowed to traverse relationships when the join path is declared explicitly? The PR takes the conservative position, since relaxing later is backward compatible while tightening is not.
> >>>>
> >>>> -
> >>>> Separately, I noticed the spec does not say whether one metric expression may reference another metric — there's no mechanism and no prohibition, while every system above has one. It's ambiguous rather than merely absent: SUM(orders.amount) / total_orders passes validation today because total_orders parses as an unqualified column. My inclination is to handle that separately since it needs aggregation-composition semantics settled, but the two features interact. The PR includes a sketch of what the reference-scoping rules would be, in case it's worth fixing those now for forward compatibility.
> >>>>
> >>>>
> >>>> Validation
> >>>>
> >>>> Three of the rules are semantic constraints JSON Schema cannot express, so validate.py gains checks for duplicate names within a dataset, collisions with model-scoped names, and cross-dataset references (table qualifiers extracted via sqlglot). Scope checking skips rather than guesses for dialects sqlglot cannot parse. The TPC-DS example is updated to demonstrate both placements.
> >>>>
> >>>> AI disclosure
> >>>>
> >>>> This contribution was developed with AI assistance (Snowflake Cortex Code). I have reviewed all changes and am personally responsible for the content per the ASF Generative Tooling Guidance.
> >>>>
> >>>> Feedback and pushback welcome. Minimum discussion period is 7 days per the contribution guidelines.
> >>>>
> >>>> Best,
> >>>> Josh Klahr
> >>>>
> >>>>
> >>>>
> >>>>
> >>>>
>
Re: [DISCUSS] Dataset-scoped metrics
Posted by Kyoung Min Kim <ka...@gmail.com>.
Two things from the validate.py side, since JB's third point and Josh's
question meet there.
The current revision of #343 already carries most of what JB listed.
validate_metric_scoping reports a duplicate metric name within a dataset and
a dataset-scoped metric that collides with a field as errors, a model-scoped
metric that shadows a dataset-scoped one as a warning, and a qualified
reference that does not name a declared field as an error, with about 300
lines of tests behind it. Keeping validation in the PR is therefore a matter
of trimming those checks to whatever the narrowed rules end up being, not of
writing them. The branch is a month behind main and conflicts in validate.py,
spec.md, spec.yaml and both test files, so the rebase touches that code
anyway.
On fields versus source columns, the validator can only ever check one of the
two. The document lists a dataset's fields, so a reference to a declared field
can be checked for existence. It does not list the columns of the source, and
#343 says as much: "not checked, because that requires catalog metadata the
model does not carry". If the single-part references in Chris's third
restriction resolve to source columns, the structural check shrinks to
uniqueness plus "is single-part". If they resolve to the dataset's declared
fields, as JB read it, the existence check is the same lookup #486 asks for in
model-scoped metrics, where dataset.field must name a declared field and a
source column is never visible to a metric expression. That reading also
removes the collision Josh is asking about, because a bare name in a metric
would mean the field and nothing else, the way the tie-break sentence in #486
settles it for qualified names.
Josh, if trimming the validator to the narrowed rules is useful to hand off, I
can prepare it as a commit against your branch or as a follow-up PR the day
the spec text settles. If you would rather keep it in one PR, I will review
that part.
Kyoung Min Kim
>
> Hi all,
>
> I completely agree with Chris's proposal to tighten the scope here.
> Decoupling the placement (datasets[].metrics) from the larger,
> in-flight semantic questions (expression composition, traversal paths,
> grain locking) is the pragmatic way to get a clean spec vote (I love
> pragmatic approaches :)).
>
> A few specific thoughts on the three restrictions and the rollout:
> 1. Unqualified references and unified namespace are spot on.
> Single-part references (SUM(amount) instead of orders.amount) neatly
> eliminate the circularity issue in DDL/parser contexts that Justin
> pointed out. Enforcing a single namespace across fields and metrics
> within the dataset also elliminites the naming collision ambiguity.
> 2. In the wording, we should be explicit about whether "source fields"
> refers to the underlying physical table columns or the dataset's
> declared fields (datasets[].fields). In Ossie, metrics should
> aggregate the dataset's declared/modeled fields (or we need clear
> precedence if both are permitted). Tightening this phrasing will avoid
> confusion during the vote.
> 3. While I support deferring complex AST evaluation or cross-dialect
> query semantics to follow-up, I would prefer we do not strip the
> validation entirely from this PR. The JSON schema update
> (ossie-schema.json) and basic structural validation in validate.py
> (uniqueness across fields + metrics, checking for unqualified
> references, and null guards) should land together with the spec. In
> Ossie, the schema and validator act as our executable spec: keeping
> baseline structural checks ensures the vote lands on a solid,
> verifiable foundation.
>
> Overall, this narrowed scope gives us a safe, backward-compatible
> baseline that we can relax later one expression composition lands.
>
> Happy to review the updated PR and spec wording once ready.
>
> Regards
> JB
>
> On Tue, Sep 29, 2026 at 11:50 PM Chris Eubank
> <ch...@databricks.com> wrote:
>>
>> Hi Josh,
>>
>> Thank you for putting together this proposal for dataset-scoped metrics. It's a fundamental building block for several in-flight extensions: expression resolution / composability (thread), relational query semantics (thread) and dimensional query semantics (thread).
>>
>> Several open comments remain, mostly concerning those follow-up features (e.g. expression composition). I'm wondering if we can tighten this PR's scope to land the minimal spec extension needed for a spec vote, and defer the rest. The validation logic could even move to a follow-up PR.
>>
>> Concretely, I'd propose these three restrictions:
>>
>> 1. Datasets may declare metrics. Within a dataset, names must be unique across fields and metrics.
>> 2. A dataset's metric expression may reference only that dataset's source fields. We can lift this restriction once the expression-composition rules land.
>> 3. Those references must be single-part (unqualified) within the expression and must resolve to the dataset's source fields.
>>
>> Would this narrower scope work for you as a path to a spec vote? If so, I'd be glad to help however is most useful - happy to pair on the scoped wording or take a first pass for us to iterate on.
>>
>> Kind regards,
>> Chris
>>
>> On Thu, Aug 27, 2026 at 2:20 PM Khushboo Bhatia <kb...@apache.org> wrote:
>>>
>>> Hey Josh,
>>> Thanks for starting this discussion. This issue has been raised in multiple discussions on the repo.
>>>
>>> I am generally supportive of adding these. Leaving some comments on the PR for the subtle differences between semantic model scoped metrics vs dataset scoped.
>>>
>>> Khushboo
>>>
>>> On 2026/08/27 16:57:13 Klahr Joshua via dev wrote:
>>>>
>>>>
>>>>
>>>> Hi all,
>>>>
>>>> I'd like to propose allowing metrics to be declared on an individual dataset (datasets[].metrics) in addition to the semantic model (semantic_model.metrics), using the same metric structure in both placements.
>>>>
>>>> PR: https://github.com/apache/ossie/pull/343
>>>>
>>>> Motivation
>>>>
>>>> Today every metric must be declared at the semantic model level, even when it aggregates a single dataset's own fields. Three costs follow from that.
>>>>
>>>> First, a dataset is not independently interpretable — you cannot tell from a dataset what metrics exist over it without scanning the whole model, and simple aggregations end up separated from the fields they depend on.
>>>>
>>>> Second, and more concretely: round-trip fidelity through Ossie is reduced. Several formats we convert from attach metrics to a specific entity — Omni has per-view measures, Honeydew has per-entity metric files, WisdomAI has per-table measures, and dbt MetricFlow binds metrics to a semantic model. Because Ossie has no dataset-scoped placement, the converters in this repository flatten all of those to the model level on import, and the entity association is not represented in the resulting document.
>>>>
>>>> On export, four converters independently reconstruct that association by scanning expressions for dataset.column qualifiers and falling back to the first dataset when none is found. The Honeydew converter emits a runtime warning when it does so. Two converters stash the owning entity in vendor-specific custom_extensions, which round-trips within a single vendor's toolchain but is not something a generic Ossie consumer can produce or interpret.
>>>>
>>>> Four converters reimplementing the same heuristic suggests the spec is missing a place to put information the source formats already carry.
>>>>
>>>> Third, export placement can be coarser than the target supports. The Snowflake converter maps every Ossie model-level metric to Snowflake's top-level metrics block, including metrics referencing only one table that would fit the narrower tables[].metrics position. The converter cannot distinguish the two cases because the Ossie document does not record the distinction.
>>>>
>>>> What's proposed
>>>>
>>>> datasets[].metrics, using the identical Metric structure. Scoping rules:
>>>>
>>>> -
>>>> A dataset-scoped metric's expression MUST only reference fields of its declaring dataset, and MUST NOT traverse relationships. Cross-dataset metrics remain model-scoped.
>>>>
>>>> -
>>>> Names MUST be unique within their dataset — two datasets MAY each declare item_count.
>>>>
>>>> -
>>>> A dataset-scoped name MUST NOT collide with any model-scoped metric name.
>>>>
>>>> -
>>>> Referenced from outside the dataset as dataset_name.metric_name.
>>>>
>>>>
>>>> Worth stating explicitly because it's easy to misread: rule 1 constrains the expression, not the query. A dataset-scoped metric can still be grouped by or filtered on dimensions from other datasets via relationships, since grouping dimensions come from the consumer at query time.
>>>>
>>>> Prior art
>>>>
>>>> Separating single-entity aggregations from cross-entity calculations is established practice. Four of the five systems I looked at do it — AtScale SML (metric bound to one dataset + column, with metric_calc as a separate type), Cube (measures within cubes), dbt MetricFlow (in-model vs top-level), and Snowflake semantic views (tables[].metrics vs top-level derived). Databricks UC metric views take a different approach, with one flat scope per view and joins declared inside it.
>>>>
>>>> On naming, the proposal extends Ossie's own existing convention rather than importing one: field names are already unique within a dataset rather than globally, and metric expressions already reference them as dataset.field. Applying the same scoped-uniqueness and qualified-reference pattern to metrics keeps the model internally consistent.
>>>>
>>>> On strictness it sits within the range the ecosystem spans — SML is more restrictive (one column, one aggregation method), Snowflake semantic views more permissive (table-level metrics may traverse relationships with a declared join path).
>>>>
>>>> Compatibility
>>>>
>>>> Additive and backward compatible. datasets[].metrics is optional and existing models remain valid unchanged. Because a dataset-scoped expression resolves within its own dataset, it is already valid as a model-scoped metric, so consumers wanting a single flat namespace can hoist without rewriting expressions. No converter changes are included; existing converters read semantic_model.metrics and that behaviour is untouched.
>>>>
>>>> Two open questions I'd particularly like input on
>>>>
>>>> -
>>>> Should a dataset-scoped metric eventually be allowed to traverse relationships when the join path is declared explicitly? The PR takes the conservative position, since relaxing later is backward compatible while tightening is not.
>>>>
>>>> -
>>>> Separately, I noticed the spec does not say whether one metric expression may reference another metric — there's no mechanism and no prohibition, while every system above has one. It's ambiguous rather than merely absent: SUM(orders.amount) / total_orders passes validation today because total_orders parses as an unqualified column. My inclination is to handle that separately since it needs aggregation-composition semantics settled, but the two features interact. The PR includes a sketch of what the reference-scoping rules would be, in case it's worth fixing those now for forward compatibility.
>>>>
>>>>
>>>> Validation
>>>>
>>>> Three of the rules are semantic constraints JSON Schema cannot express, so validate.py gains checks for duplicate names within a dataset, collisions with model-scoped names, and cross-dataset references (table qualifiers extracted via sqlglot). Scope checking skips rather than guesses for dialects sqlglot cannot parse. The TPC-DS example is updated to demonstrate both placements.
>>>>
>>>> AI disclosure
>>>>
>>>> This contribution was developed with AI assistance (Snowflake Cortex Code). I have reviewed all changes and am personally responsible for the content per the ASF Generative Tooling Guidance.
>>>>
>>>> Feedback and pushback welcome. Minimum discussion period is 7 days per the contribution guidelines.
>>>>
>>>> Best,
>>>> Josh Klahr
>>>>
>>>>
>>>>
>>>>
>>>>
Re: [DISCUSS] Dataset-scoped metrics
Posted by Jean-Baptiste Onofré <jb...@nanthrax.net>.
Hi all,
I completely agree with Chris's proposal to tighten the scope here.
Decoupling the placement (datasets[].metrics) from the larger,
in-flight semantic questions (expression composition, traversal paths,
grain locking) is the pragmatic way to get a clean spec vote (I love
pragmatic approaches :)).
A few specific thoughts on the three restrictions and the rollout:
1. Unqualified references and unified namespace are spot on.
Single-part references (SUM(amount) instead of orders.amount) neatly
eliminate the circularity issue in DDL/parser contexts that Justin
pointed out. Enforcing a single namespace across fields and metrics
within the dataset also elliminites the naming collision ambiguity.
2. In the wording, we should be explicit about whether "source fields"
refers to the underlying physical table columns or the dataset's
declared fields (datasets[].fields). In Ossie, metrics should
aggregate the dataset's declared/modeled fields (or we need clear
precedence if both are permitted). Tightening this phrasing will avoid
confusion during the vote.
3. While I support deferring complex AST evaluation or cross-dialect
query semantics to follow-up, I would prefer we do not strip the
validation entirely from this PR. The JSON schema update
(ossie-schema.json) and basic structural validation in validate.py
(uniqueness across fields + metrics, checking for unqualified
references, and null guards) should land together with the spec. In
Ossie, the schema and validator act as our executable spec: keeping
baseline structural checks ensures the vote lands on a solid,
verifiable foundation.
Overall, this narrowed scope gives us a safe, backward-compatible
baseline that we can relax later one expression composition lands.
Happy to review the updated PR and spec wording once ready.
Regards
JB
On Tue, Sep 29, 2026 at 11:50 PM Chris Eubank
<ch...@databricks.com> wrote:
>
> Hi Josh,
>
> Thank you for putting together this proposal for dataset-scoped metrics. It's a fundamental building block for several in-flight extensions: expression resolution / composability (thread), relational query semantics (thread) and dimensional query semantics (thread).
>
> Several open comments remain, mostly concerning those follow-up features (e.g. expression composition). I'm wondering if we can tighten this PR's scope to land the minimal spec extension needed for a spec vote, and defer the rest. The validation logic could even move to a follow-up PR.
>
> Concretely, I'd propose these three restrictions:
>
> 1. Datasets may declare metrics. Within a dataset, names must be unique across fields and metrics.
> 2. A dataset's metric expression may reference only that dataset's source fields. We can lift this restriction once the expression-composition rules land.
> 3. Those references must be single-part (unqualified) within the expression and must resolve to the dataset's source fields.
>
> Would this narrower scope work for you as a path to a spec vote? If so, I'd be glad to help however is most useful - happy to pair on the scoped wording or take a first pass for us to iterate on.
>
> Kind regards,
> Chris
>
> On Thu, Aug 27, 2026 at 2:20 PM Khushboo Bhatia <kb...@apache.org> wrote:
>>
>> Hey Josh,
>> Thanks for starting this discussion. This issue has been raised in multiple discussions on the repo.
>>
>> I am generally supportive of adding these. Leaving some comments on the PR for the subtle differences between semantic model scoped metrics vs dataset scoped.
>>
>> Khushboo
>>
>> On 2026/08/27 16:57:13 Klahr Joshua via dev wrote:
>> >
>> >
>> >
>> > Hi all,
>> >
>> > I'd like to propose allowing metrics to be declared on an individual dataset (datasets[].metrics) in addition to the semantic model (semantic_model.metrics), using the same metric structure in both placements.
>> >
>> > PR: https://github.com/apache/ossie/pull/343
>> >
>> > Motivation
>> >
>> > Today every metric must be declared at the semantic model level, even when it aggregates a single dataset's own fields. Three costs follow from that.
>> >
>> > First, a dataset is not independently interpretable — you cannot tell from a dataset what metrics exist over it without scanning the whole model, and simple aggregations end up separated from the fields they depend on.
>> >
>> > Second, and more concretely: round-trip fidelity through Ossie is reduced. Several formats we convert from attach metrics to a specific entity — Omni has per-view measures, Honeydew has per-entity metric files, WisdomAI has per-table measures, and dbt MetricFlow binds metrics to a semantic model. Because Ossie has no dataset-scoped placement, the converters in this repository flatten all of those to the model level on import, and the entity association is not represented in the resulting document.
>> >
>> > On export, four converters independently reconstruct that association by scanning expressions for dataset.column qualifiers and falling back to the first dataset when none is found. The Honeydew converter emits a runtime warning when it does so. Two converters stash the owning entity in vendor-specific custom_extensions, which round-trips within a single vendor's toolchain but is not something a generic Ossie consumer can produce or interpret.
>> >
>> > Four converters reimplementing the same heuristic suggests the spec is missing a place to put information the source formats already carry.
>> >
>> > Third, export placement can be coarser than the target supports. The Snowflake converter maps every Ossie model-level metric to Snowflake's top-level metrics block, including metrics referencing only one table that would fit the narrower tables[].metrics position. The converter cannot distinguish the two cases because the Ossie document does not record the distinction.
>> >
>> > What's proposed
>> >
>> > datasets[].metrics, using the identical Metric structure. Scoping rules:
>> >
>> > -
>> > A dataset-scoped metric's expression MUST only reference fields of its declaring dataset, and MUST NOT traverse relationships. Cross-dataset metrics remain model-scoped.
>> >
>> > -
>> > Names MUST be unique within their dataset — two datasets MAY each declare item_count.
>> >
>> > -
>> > A dataset-scoped name MUST NOT collide with any model-scoped metric name.
>> >
>> > -
>> > Referenced from outside the dataset as dataset_name.metric_name.
>> >
>> >
>> > Worth stating explicitly because it's easy to misread: rule 1 constrains the expression, not the query. A dataset-scoped metric can still be grouped by or filtered on dimensions from other datasets via relationships, since grouping dimensions come from the consumer at query time.
>> >
>> > Prior art
>> >
>> > Separating single-entity aggregations from cross-entity calculations is established practice. Four of the five systems I looked at do it — AtScale SML (metric bound to one dataset + column, with metric_calc as a separate type), Cube (measures within cubes), dbt MetricFlow (in-model vs top-level), and Snowflake semantic views (tables[].metrics vs top-level derived). Databricks UC metric views take a different approach, with one flat scope per view and joins declared inside it.
>> >
>> > On naming, the proposal extends Ossie's own existing convention rather than importing one: field names are already unique within a dataset rather than globally, and metric expressions already reference them as dataset.field. Applying the same scoped-uniqueness and qualified-reference pattern to metrics keeps the model internally consistent.
>> >
>> > On strictness it sits within the range the ecosystem spans — SML is more restrictive (one column, one aggregation method), Snowflake semantic views more permissive (table-level metrics may traverse relationships with a declared join path).
>> >
>> > Compatibility
>> >
>> > Additive and backward compatible. datasets[].metrics is optional and existing models remain valid unchanged. Because a dataset-scoped expression resolves within its own dataset, it is already valid as a model-scoped metric, so consumers wanting a single flat namespace can hoist without rewriting expressions. No converter changes are included; existing converters read semantic_model.metrics and that behaviour is untouched.
>> >
>> > Two open questions I'd particularly like input on
>> >
>> > -
>> > Should a dataset-scoped metric eventually be allowed to traverse relationships when the join path is declared explicitly? The PR takes the conservative position, since relaxing later is backward compatible while tightening is not.
>> >
>> > -
>> > Separately, I noticed the spec does not say whether one metric expression may reference another metric — there's no mechanism and no prohibition, while every system above has one. It's ambiguous rather than merely absent: SUM(orders.amount) / total_orders passes validation today because total_orders parses as an unqualified column. My inclination is to handle that separately since it needs aggregation-composition semantics settled, but the two features interact. The PR includes a sketch of what the reference-scoping rules would be, in case it's worth fixing those now for forward compatibility.
>> >
>> >
>> > Validation
>> >
>> > Three of the rules are semantic constraints JSON Schema cannot express, so validate.py gains checks for duplicate names within a dataset, collisions with model-scoped names, and cross-dataset references (table qualifiers extracted via sqlglot). Scope checking skips rather than guesses for dialects sqlglot cannot parse. The TPC-DS example is updated to demonstrate both placements.
>> >
>> > AI disclosure
>> >
>> > This contribution was developed with AI assistance (Snowflake Cortex Code). I have reviewed all changes and am personally responsible for the content per the ASF Generative Tooling Guidance.
>> >
>> > Feedback and pushback welcome. Minimum discussion period is 7 days per the contribution guidelines.
>> >
>> > Best,
>> > Josh Klahr
>> >
>> >
>> >
>> >
>> >
Re: [DISCUSS] Dataset-scoped metrics
Posted by Chris Eubank via dev <de...@ossie.apache.org>.
Hi Josh,
Thank you for putting together this proposal for dataset-scoped metrics.
It's a fundamental building block for several in-flight extensions: expression
resolution / composability (thread
<https://lists.apache.org/thread/bc9v9o0d2702gwm978km13mg6v16hzv1>),
relational query semantics (thread
<https://lists.apache.org/thread.html/wtghwy9ssd5m8k9b3psms4g2lcdj3z28?find_parent=true>)
and dimensional query semantics (thread
<https://lists.apache.org/thread/qo2fx0hl0cvlzn9q1ob9z7v7g81syj88>).
Several open comments remain, mostly concerning those follow-up features
(e.g. expression composition). I'm wondering if we can tighten this PR's
scope to land the minimal spec extension needed for a spec vote, and defer
the rest. The validation logic could even move to a follow-up PR.
Concretely, I'd propose these three restrictions:
1. *Datasets may declare metrics.* Within a dataset, names must be unique
across fields and metrics.
2. *A dataset's metric expression may reference only that dataset's source
fields.* We can lift this restriction once the expression-composition rules
land.
3. *Those references must be single-part *(unqualified) within the
expression and must resolve to the dataset's source fields.
Would this narrower scope work for you as a path to a spec vote? If so, I'd
be glad to help however is most useful - happy to pair on the scoped
wording or take a first pass for us to iterate on.
Kind regards,
Chris
On Thu, Aug 27, 2026 at 2:20 PM Khushboo Bhatia <kb...@apache.org> wrote:
> Hey Josh,
> Thanks for starting this discussion. This issue has been raised in
> multiple discussions on the repo.
>
> I am generally supportive of adding these. Leaving some comments on the PR
> for the subtle differences between semantic model scoped metrics vs dataset
> scoped.
>
> Khushboo
>
> On 2026/08/27 16:57:13 Klahr Joshua via dev wrote:
> >
> >
> >
> > Hi all,
> >
> > I'd like to propose allowing metrics to be declared on an individual
> dataset (datasets[].metrics) in addition to the semantic model
> (semantic_model.metrics), using the same metric structure in both
> placements.
> >
> > PR: https://github.com/apache/ossie/pull/343
> >
> > Motivation
> >
> > Today every metric must be declared at the semantic model level, even
> when it aggregates a single dataset's own fields. Three costs follow from
> that.
> >
> > First, a dataset is not independently interpretable — you cannot tell
> from a dataset what metrics exist over it without scanning the whole model,
> and simple aggregations end up separated from the fields they depend on.
> >
> > Second, and more concretely: round-trip fidelity through Ossie is
> reduced. Several formats we convert from attach metrics to a specific
> entity — Omni has per-view measures, Honeydew has per-entity metric files,
> WisdomAI has per-table measures, and dbt MetricFlow binds metrics to a
> semantic model. Because Ossie has no dataset-scoped placement, the
> converters in this repository flatten all of those to the model level on
> import, and the entity association is not represented in the resulting
> document.
> >
> > On export, four converters independently reconstruct that association by
> scanning expressions for dataset.column qualifiers and falling back to the
> first dataset when none is found. The Honeydew converter emits a runtime
> warning when it does so. Two converters stash the owning entity in
> vendor-specific custom_extensions, which round-trips within a single
> vendor's toolchain but is not something a generic Ossie consumer can
> produce or interpret.
> >
> > Four converters reimplementing the same heuristic suggests the spec is
> missing a place to put information the source formats already carry.
> >
> > Third, export placement can be coarser than the target supports. The
> Snowflake converter maps every Ossie model-level metric to Snowflake's
> top-level metrics block, including metrics referencing only one table that
> would fit the narrower tables[].metrics position. The converter cannot
> distinguish the two cases because the Ossie document does not record the
> distinction.
> >
> > What's proposed
> >
> > datasets[].metrics, using the identical Metric structure. Scoping rules:
> >
> > -
> > A dataset-scoped metric's expression MUST only reference fields of its
> declaring dataset, and MUST NOT traverse relationships. Cross-dataset
> metrics remain model-scoped.
> >
> > -
> > Names MUST be unique within their dataset — two datasets MAY each
> declare item_count.
> >
> > -
> > A dataset-scoped name MUST NOT collide with any model-scoped metric name.
> >
> > -
> > Referenced from outside the dataset as dataset_name.metric_name.
> >
> >
> > Worth stating explicitly because it's easy to misread: rule 1 constrains
> the expression, not the query. A dataset-scoped metric can still be grouped
> by or filtered on dimensions from other datasets via relationships, since
> grouping dimensions come from the consumer at query time.
> >
> > Prior art
> >
> > Separating single-entity aggregations from cross-entity calculations is
> established practice. Four of the five systems I looked at do it — AtScale
> SML (metric bound to one dataset + column, with metric_calc as a separate
> type), Cube (measures within cubes), dbt MetricFlow (in-model vs
> top-level), and Snowflake semantic views (tables[].metrics vs top-level
> derived). Databricks UC metric views take a different approach, with one
> flat scope per view and joins declared inside it.
> >
> > On naming, the proposal extends Ossie's own existing convention rather
> than importing one: field names are already unique within a dataset rather
> than globally, and metric expressions already reference them as
> dataset.field. Applying the same scoped-uniqueness and qualified-reference
> pattern to metrics keeps the model internally consistent.
> >
> > On strictness it sits within the range the ecosystem spans — SML is more
> restrictive (one column, one aggregation method), Snowflake semantic views
> more permissive (table-level metrics may traverse relationships with a
> declared join path).
> >
> > Compatibility
> >
> > Additive and backward compatible. datasets[].metrics is optional and
> existing models remain valid unchanged. Because a dataset-scoped expression
> resolves within its own dataset, it is already valid as a model-scoped
> metric, so consumers wanting a single flat namespace can hoist without
> rewriting expressions. No converter changes are included; existing
> converters read semantic_model.metrics and that behaviour is untouched.
> >
> > Two open questions I'd particularly like input on
> >
> > -
> > Should a dataset-scoped metric eventually be allowed to traverse
> relationships when the join path is declared explicitly? The PR takes the
> conservative position, since relaxing later is backward compatible while
> tightening is not.
> >
> > -
> > Separately, I noticed the spec does not say whether one metric
> expression may reference another metric — there's no mechanism and no
> prohibition, while every system above has one. It's ambiguous rather than
> merely absent: SUM(orders.amount) / total_orders passes validation today
> because total_orders parses as an unqualified column. My inclination is to
> handle that separately since it needs aggregation-composition semantics
> settled, but the two features interact. The PR includes a sketch of what
> the reference-scoping rules would be, in case it's worth fixing those now
> for forward compatibility.
> >
> >
> > Validation
> >
> > Three of the rules are semantic constraints JSON Schema cannot express,
> so validate.py gains checks for duplicate names within a dataset,
> collisions with model-scoped names, and cross-dataset references (table
> qualifiers extracted via sqlglot). Scope checking skips rather than guesses
> for dialects sqlglot cannot parse. The TPC-DS example is updated to
> demonstrate both placements.
> >
> > AI disclosure
> >
> > This contribution was developed with AI assistance (Snowflake Cortex
> Code). I have reviewed all changes and am personally responsible for the
> content per the ASF Generative Tooling Guidance.
> >
> > Feedback and pushback welcome. Minimum discussion period is 7 days per
> the contribution guidelines.
> >
> > Best,
> > Josh Klahr
> >
> >
> >
> >
> >
>
Re: [DISCUSS] Dataset-scoped metrics
Posted by Khushboo Bhatia <kb...@apache.org>.
Hey Josh,
Thanks for starting this discussion. This issue has been raised in multiple discussions on the repo.
I am generally supportive of adding these. Leaving some comments on the PR for the subtle differences between semantic model scoped metrics vs dataset scoped.
Khushboo
On 2026/08/27 16:57:13 Klahr Joshua via dev wrote:
>
>
>
> Hi all,
>
> I'd like to propose allowing metrics to be declared on an individual dataset (datasets[].metrics) in addition to the semantic model (semantic_model.metrics), using the same metric structure in both placements.
>
> PR: https://github.com/apache/ossie/pull/343
>
> Motivation
>
> Today every metric must be declared at the semantic model level, even when it aggregates a single dataset's own fields. Three costs follow from that.
>
> First, a dataset is not independently interpretable — you cannot tell from a dataset what metrics exist over it without scanning the whole model, and simple aggregations end up separated from the fields they depend on.
>
> Second, and more concretely: round-trip fidelity through Ossie is reduced. Several formats we convert from attach metrics to a specific entity — Omni has per-view measures, Honeydew has per-entity metric files, WisdomAI has per-table measures, and dbt MetricFlow binds metrics to a semantic model. Because Ossie has no dataset-scoped placement, the converters in this repository flatten all of those to the model level on import, and the entity association is not represented in the resulting document.
>
> On export, four converters independently reconstruct that association by scanning expressions for dataset.column qualifiers and falling back to the first dataset when none is found. The Honeydew converter emits a runtime warning when it does so. Two converters stash the owning entity in vendor-specific custom_extensions, which round-trips within a single vendor's toolchain but is not something a generic Ossie consumer can produce or interpret.
>
> Four converters reimplementing the same heuristic suggests the spec is missing a place to put information the source formats already carry.
>
> Third, export placement can be coarser than the target supports. The Snowflake converter maps every Ossie model-level metric to Snowflake's top-level metrics block, including metrics referencing only one table that would fit the narrower tables[].metrics position. The converter cannot distinguish the two cases because the Ossie document does not record the distinction.
>
> What's proposed
>
> datasets[].metrics, using the identical Metric structure. Scoping rules:
>
> -
> A dataset-scoped metric's expression MUST only reference fields of its declaring dataset, and MUST NOT traverse relationships. Cross-dataset metrics remain model-scoped.
>
> -
> Names MUST be unique within their dataset — two datasets MAY each declare item_count.
>
> -
> A dataset-scoped name MUST NOT collide with any model-scoped metric name.
>
> -
> Referenced from outside the dataset as dataset_name.metric_name.
>
>
> Worth stating explicitly because it's easy to misread: rule 1 constrains the expression, not the query. A dataset-scoped metric can still be grouped by or filtered on dimensions from other datasets via relationships, since grouping dimensions come from the consumer at query time.
>
> Prior art
>
> Separating single-entity aggregations from cross-entity calculations is established practice. Four of the five systems I looked at do it — AtScale SML (metric bound to one dataset + column, with metric_calc as a separate type), Cube (measures within cubes), dbt MetricFlow (in-model vs top-level), and Snowflake semantic views (tables[].metrics vs top-level derived). Databricks UC metric views take a different approach, with one flat scope per view and joins declared inside it.
>
> On naming, the proposal extends Ossie's own existing convention rather than importing one: field names are already unique within a dataset rather than globally, and metric expressions already reference them as dataset.field. Applying the same scoped-uniqueness and qualified-reference pattern to metrics keeps the model internally consistent.
>
> On strictness it sits within the range the ecosystem spans — SML is more restrictive (one column, one aggregation method), Snowflake semantic views more permissive (table-level metrics may traverse relationships with a declared join path).
>
> Compatibility
>
> Additive and backward compatible. datasets[].metrics is optional and existing models remain valid unchanged. Because a dataset-scoped expression resolves within its own dataset, it is already valid as a model-scoped metric, so consumers wanting a single flat namespace can hoist without rewriting expressions. No converter changes are included; existing converters read semantic_model.metrics and that behaviour is untouched.
>
> Two open questions I'd particularly like input on
>
> -
> Should a dataset-scoped metric eventually be allowed to traverse relationships when the join path is declared explicitly? The PR takes the conservative position, since relaxing later is backward compatible while tightening is not.
>
> -
> Separately, I noticed the spec does not say whether one metric expression may reference another metric — there's no mechanism and no prohibition, while every system above has one. It's ambiguous rather than merely absent: SUM(orders.amount) / total_orders passes validation today because total_orders parses as an unqualified column. My inclination is to handle that separately since it needs aggregation-composition semantics settled, but the two features interact. The PR includes a sketch of what the reference-scoping rules would be, in case it's worth fixing those now for forward compatibility.
>
>
> Validation
>
> Three of the rules are semantic constraints JSON Schema cannot express, so validate.py gains checks for duplicate names within a dataset, collisions with model-scoped names, and cross-dataset references (table qualifiers extracted via sqlglot). Scope checking skips rather than guesses for dialects sqlglot cannot parse. The TPC-DS example is updated to demonstrate both placements.
>
> AI disclosure
>
> This contribution was developed with AI assistance (Snowflake Cortex Code). I have reviewed all changes and am personally responsible for the content per the ASF Generative Tooling Guidance.
>
> Feedback and pushback welcome. Minimum discussion period is 7 days per the contribution guidelines.
>
> Best,
> Josh Klahr
>
>
>
>
>