跳转至

LOD 编写参考

本页按编写模型时会遇到的问题组织规则,供写模型、排查报错时查阅。 第一次接触 LOD,请先读理解 LOD,那里讲了概念、四种用法的选择和过滤的基本区别。 示例中的字段名和指标名都来自电商示例。

本页常用的几个说法:

  • 基础指标:LOD 声明第一个参数引用的已有指标,比如 fixed(paid_total, orders.user_id) 里的 paid_total。
  • 固定字段:在字段上声明 fixed,结果是可以分组、筛选、再聚合的字段。
  • LOD 指标:在指标上声明 include、exclude 或 fixed,结果是查询直接返回的指标。
  • 上下文:查询的 context_filter 和 time_range,限定参与计算的原始记录。 跨表时只传播通过已声明关系或键对应能够读取的条件,见跨表计算。 与之相对的是普通筛选 where_sql,它挑选展示行,不缩小固定计算的输入。
  • 字段所在表:固定字段定义在哪个数据集上。它决定结果有哪些行,见跨表计算。

基础指标需要满足什么条件

基础指标必须是一个数据集上的一个聚合,或该聚合的筛选派生。 支持求和、计数、去重计数、平均、最小值和最大值,对应 SUM、COUNT、带 DISTINCT 的 COUNT、 AVG、MIN、MAX。多数据集模型中的裸 COUNT(*) 需要通过 D-DATASET 声明归属;计数已声明的非空标识字段也能明确归属。

LOD 指标的基础指标也可以由多个这样的聚合组成,即 compose、比值或表达式指标。 这时 LOD 把每个聚合都放到自己的粒度上算,再套用基础指标原来的式子(包括 fill)。所以 在「收入 − 退款」上做 exclude(net_revenue),得到的就是整条查询的收入减去退款,跨两张 事实表也一样;用它做分母就得到占比。 模型里的表达式要把基础指标的整条式子嵌在外层聚合里,比如 SUM(COALESCE(SUM(orders.amount), 0) - COALESCE(SUM(refunds.amount), 0))。 每个聚合按它所属成员的时间轴分桶,和 compose 本身一样;fixed 也在这些时间轴上应用查询的时间范围, 再在细节层次外面套一层 compose 时同样沿用。include 对每个数据集只汇总一次, 所以同一数据集上的聚合落在两条时间轴上时会被拒绝。

基础指标跨多个数据集时,include 先让每个数据集在自己的行上、按展示行和声明的维度算出 各自的聚合,再按这些分组合并,最后套用基础指标的式子。只要有一个数据集有某个分组,它就 是内层的一行。某个数据集在这一行没有数据,它的聚合就是 NULL,和跨事实 compose 在展示 粒度上缺成员时一样,除非基础指标的 fill 把它补上。外层聚合会跳过 NULL:不加 fill 时, AVG(include(net_revenue, orders.customer_id)) 会漏掉有订单、没有退款的客户;加上 fill: 0,这个客户按「收入 − 0」计入。每个声明的维度都必须是每个数据集自己的列:它属于 其中一个数据集,并通过 D-CONFORM 与其他每个数据集的 一列配对。跨多个数据集时,exclude() 和 fixed() 不能带分组字段,只能对整条查询计算; 需要带分组时,给每个成员各声明 LOD,再组合结果。

固定字段的基础指标始终只能是单个聚合。窗口结果、另一 LOD 指标,以及以它们为成员的 compose,都不能作为基础指标。 基础指标和分组字段也不能依赖 LOD 字段,包括通过普通计算字段间接依赖。 调整定义顺序不能绕过嵌套限制。include 引用的基础指标不能与其所在数据集的字段同名。

固定字段:指定分组后,把结果作为字段使用

固定字段使用 fixed:第一个参数是基础指标名,后续参数是固定分组字段,多个字段用逗号分隔。 字段必须属于当前固定字段所在的数据集;省略前缀时也在这里解析,建议显式写出数据集名。 多个字段组成一个联合分组,不能理解成分别按各字段计算。

电商示例中的 fixed(paid_total, orders.user_id) 表示按用户汇总实付。 同表计算可以省略所有分组字段,此时得到整个计算范围的一个值;跨表计算不能省略。 固定字段不能使用 include/exclude,声明本身也不能再包一层聚合。

查询可以读取、分组、筛选或排序该字段,普通计算字段也可以读取它。 当普通指标再次聚合固定字段时,每个展示组内,同一个固定分组只参与一次。 理解 LOD 里广告渠道的用户均值因此按两位买家求平均,不按他们的五笔订单重复加权。 同一对象出现在多个展示组时,在每个组内各参与一次,各组人数不一定可以直接相加。

再次聚合时读取什么 每一份按什么计算
原始列或原始行计数 原始记录
固定字段 每个展示组内不同的固定分组
只依赖 LOD 值和常量的计算字段 这些 LOD 值的固定分组
同时读取原始列的计算字段 原始记录
条件读取 LOD 值的筛选指标 与基础指标相同;筛选后的行计数仍按原始记录

固定字段仅用来分组时,不改变原始行指标的计数方式。 多个不同固定分组的字段可以共同展示或筛选,但一次查询只能对同一组固定分组字段的值再次聚合, 也不能同时请求按原始记录计算的指标。两个指标写出来是同一个表达式时也一样:对行计数加筛选的筛选指标 按原始记录计数,直接写成指标的同一个 CASE WHEN 只读 LOD 值,按固定分组计数。

同表计算中,NULL 分组键有自己的分组,并匹配该组的固定值。 去重计数仍排除 NULL,不能把去重键数当作包含 NULL 组在内的总分组数。 联合分组匹配时保留所有字段。

LOD 指标:把结果直接作为指标返回

指标声明的第一个参数同样是基础指标名,后续参数是维度字段。 这些字段必须属于基础指标的数据集,且名称必须带数据集前缀。

类型 声明要求 分组与计算规则
include 至少一个维度,外面恰好包一层聚合 在展示分组上加入声明维度,先算基础指标,再执行外层聚合
exclude 可以不写维度,声明外面不能包聚合 从展示分组中去掉声明维度,重新计算基础指标;不写维度则去掉全部展示分组
指标 fixed 可以不写分组字段,声明外面不能包聚合 精确按指定字段计算;不写字段则得到整个计算范围的基准

include 的外层聚合支持 SUM、COUNT、AVG、MIN、MAX,外层不接受 DISTINCT。 电商示例的 AVG(include(order_count, orders.user_id)) 先计算各买家的订单数,再求平均。 没有 group_by 时,include 仍先按声明维度计算,再汇总为总体结果。 展示要求的时间粒度会进入它的内层计算。普通过滤和上下文都限定 include 的输入。

exclude 也遵守普通过滤和上下文。它在较粗分组上重新计算,不是相加已有的展示值, 因此去重计数也必须重新统计。带键 exclude 写到的每个字段都必须出现在查询 group_by 中, 时间粒度不限:排除一个查询没有分组的字段什么也排除不掉,结果只会是基础指标本身,所以查询以 lod_dimension_not_grouped 被拒。不带键的 exclude 不要求分组;没有 group_by 时, 就是所选记录上的基础聚合。

指标 fixed 遵守上下文,忽略普通展示过滤。每个固定分组字段必须原样出现在查询 group_by 中, 不能带时间粒度转换,否则查询以 lod_dimension_not_grouped 被拒;无字段的总体基准也可用于无分组查询。 指标目录把带键 exclude、fixed 写到的字段列为 required_dimensions,拒绝时缺的那些列在 missing_dimensions 里。 如果需要把固定分组的结果继续汇总,应改用固定字段加普通聚合,不能在 fixed 指标声明外再包聚合。

同一事实表上的普通指标、exclude、fixed 可以共查,并用 compose 计算比例或其他算术结果。 多个 include 指标共查时,基础数据集、声明维度列表及顺序必须一致;基础指标和外层函数可以不同。 include 不能与其基础指标所读数据集上的普通指标、exclude/fixed 指标或查询实际读取的 LOD 字段共查。

声明写在模型的哪里

字段或指标保留普通的 expression.dialects,使用 ANSI_SQL 表达式。 指标 SQL 引用的是已声明的语义字段,名称要带数据集前缀,不能直接引用未声明的物理列。

LOD 扩展放在所属字段或指标的 custom_extensions 中,vendor_name 为 DATUS。 data 是 JSON 编码的字符串。 其中 v 是扩展版本字符串;1.11 支持本页的全部规则。 可选的 requires 列出能力键:固定字段用 lod,派生指标用 derive。

定义内容 JSON 对象中的位置
固定字段 lod.expr 写 fixed 声明
LOD 指标 derive.type 为 lod,derive.expr 写 include、exclude 或 fixed 声明
带筛选的基础指标 derive.type 为 filter,derive.base 写基础指标名,derive.where 写条件
指标间算术组合 derive.type 为 compose,derive.expr 写使用指标名称的算术表达式

下面是电商示例中“买家累计消费”字段的定义,放在 orders 的 fields 列表内。 完整模型还需要定义它引用的 paid_total 指标。

- name: buyer_lifetime_paid
  expression:
    dialects:
      - dialect: ANSI_SQL
        expression: SUM(orders.paid_amount)
  datatype: Decimal
  custom_extensions:
    - vendor_name: DATUS
      data: '{"v":"1.11","is_dimension":false,"lod":{"expr":"fixed(paid_total, orders.user_id)"}}'

LOD 指标放在模型的 metrics 列表内。下面是“渠道内平均每位买家的订单数”, 它引用基础指标 order_count(COUNT(DISTINCT orders.order_id)):

- name: mean_orders
  expression:
    dialects:
      - dialect: ANSI_SQL
        expression: AVG(COUNT(DISTINCT orders.order_id))
  custom_extensions:
    - vendor_name: DATUS
      data: '{"v":"1.11","derive":{"type":"lod","expr":"AVG(include(order_count, orders.user_id))"}}'

为什么同时需要 SQL 和 LOD 声明

expression 描述计算内容,DATUS 声明说明要按什么粒度分步计算,两者必须一致。 核心 SQL 必须完整展开,不能只写指标名、占位常量或 fixed/include/exclude 声明。

定义方式 核心 SQL 应写什么
固定字段 基础指标的完整聚合 SQL
include 指标 用声明中的外层函数包住基础指标的聚合 SQL
exclude 或 fixed 指标 用 SUM 包住基础指标的聚合 SQL,即使基础指标不是求和
对固定字段再次聚合的普通指标 直接对固定字段引用使用普通聚合函数
compose 指标 把表达式中每个指标名替换成该指标的完整核心 SQL

上述累计消费字段的核心 SQL 是 SUM(orders.paid_amount),对它求用户均值的普通指标是 AVG(orders.buyer_lifetime_paid);上一节的 include 指标写成 AVG(COUNT(DISTINCT orders.order_id))。

exclude 和 fixed 指标的写法最容易让人困惑。下面是电商示例的“当天全站销售额”:

- name: site_amount
  expression:
    dialects:
      - dialect: ANSI_SQL
        expression: SUM(SUM(orders.paid_amount))
  custom_extensions:
    - vendor_name: DATUS
      data: '{"v":"1.11","derive":{"type":"lod","expr":"fixed(paid_total, orders.paid_date)"}}'

内层 SUM(orders.paid_amount) 是基础指标 paid_total 的 SQL,外层 SUM 是固定的包装, 表示“对基础指标在另一粒度上的结果再取一次”。即使基础指标是 COUNT 或 MAX,外层也写 SUM, 比如以 order_count 为基础的 fixed 指标写成 SUM(COUNT(DISTINCT orders.order_id))。 这个外层 SUM 只用于让 Dosi 核对 SQL 和声明是否一致,不会把基准值累加一遍。 嵌套聚合不能作为普通 SQL 直接执行,Dosi 按扩展声明将它拆成合法的计算步骤。 两份表达不一致时返回 derive_expression_mismatch。

筛选派生的核心 SQL 要展开筛选条件,并代入参数默认值,不保留参数标记。 筛选行数的 COUNT 使用条件表达式:匹配时返回 1,否则返回 NULL,然后计数; 不能仅因语义等价就把这里的 1 换成标识字段。 对值求和或去重计数时,则在匹配时返回被聚合的值,否则返回 NULL,再执行对应聚合。 固定字段重复该筛选聚合 SQL,lod.expr 按名称引用筛选指标。

指标名可以出现在 derive.expr,不能代替核心 SQL。 类型转换和零分母处理,也要在 compose 声明与展开后的核心表达式中保持一致。

计算期间不同:用参数定义独立窗口

当固定字段需要自己的计算期间,先定义带筛选的基础指标,把期间写成参数, 再让固定字段引用它。params 与 derive 并列放在基础指标的 DATUS JSON 中,不放在固定字段上。

下面是电商示例里“参考期实付”的两段定义。先是带参数的基础指标,放在 metrics 列表内:

- name: reference_paid_amount
  expression:
    dialects:
      - dialect: ANSI_SQL
        expression: SUM(CASE WHEN orders.paid_date_key >= 0 AND orders.paid_date_key < 99991231 THEN orders.paid_amount END)
  custom_extensions:
    - vendor_name: DATUS
      data: '{"v":"1.11",
        "derive":{"type":"filter","base":"paid_total",
          "where":"orders.paid_date_key >= :reference_start AND orders.paid_date_key < :reference_end"},
        "params":[{"name":"reference_start","type":"int","default":0,"min":0,"max":99991231},
                  {"name":"reference_end","type":"int","default":99991231,"min":0,"max":99991231}]}'

再在 users 数据集上定义引用它的固定字段,每个用户一个值:

- name: reference_paid
  expression:
    dialects:
      - dialect: ANSI_SQL
        expression: SUM(CASE WHEN orders.paid_date_key >= 0 AND orders.paid_date_key < 99991231 THEN orders.paid_amount END)
  datatype: Decimal
  custom_extensions:
    - vendor_name: DATUS
      data: '{"v":"1.11","is_dimension":false,"lod":{"expr":"fixed(reference_paid_amount, users.user_id)"}}'

核心 SQL 里的 0 和 99991231 是参数默认值代入后的结果,规则见上一节。 查询时用 --param reference_start=20300401 --param reference_end=20300501 指定 4 月。

每个参数声明 name、type、default。int、float 可设 min/max, string 可设 allowed;不限定字符串取值时允许开放输入。allowed 与数值上下界不能同时使用。 没有取值列表或数值边界时会产生 datus_param_unbounded 提示,但不会因此使 validate --strict 失败。 已知业务范围时应明确声明。整数范围本身不校验日期编码是否合法,D-PARAM 也没有日期类型。

筛选条件用冒号加参数名引用参数,查询通过 params 或 CLI --param 传值,不自行拼接 SQL。 没有传值时使用默认值。

一个参数值会传给本次查询里所有声明了同名参数的指标,包括查询直接选择的指标, 以及查询读到的固定字段背后的基础指标。所以两段需要分别控制的期间,必须用不同的参数名, 比如电商示例的参考期 reference_start、观察期 observation_start; 共用一个名字,就等于强制两段期间相同。同名参数的值还必须同时满足每个声明的类型和取值范围。 固定字段的一次计算只接受单值,不能绑定列表。

查询无论通过分组、过滤、明细投影,还是通过依赖它的计算字段读取固定字段,参数都会传到它的基础指标。 如果本次查询里没有任何指标声明这个参数名,返回 unknown_metric_param,通常是参数名拼错了; 值的类型错误、超出取值范围,或者给固定字段的参数传了列表,返回 param_out_of_domain。

同表中,展示记录与固定字段观察不同期间时,把展示日期条件放在普通 where_sql, 把字段期间放在参数化基础指标里。查询 time_range 会进入固定计算,导致两段期间相交。 字段自己的条件只能追加约束,不能覆盖 source、时间范围或上下文限制。

需要查询时间范围自动作用于日期字段时,声明原生时间维度或用 D-FORMAT 说明存储格式。普通整数日期码也可以直接与整数参数比较,无须把它声明为时间轴。

跨表计算:先确定结果属于哪张表

固定字段可以汇总另一张表的记录。字段定义在哪个数据集,那个数据集就是字段所在表。 它决定哪些记录有资格进入结果;只在被汇总表中出现的对象不会新增结果行。 固定分组字段仍属于字段所在表,每个字段都必须有明确的跨表对应关系。

已声明的对应方式 被汇总表如何找到分组字段
两表间直接多对一关系,两个方向均可 使用配对键列
从被汇总表沿多对一路径到达字段所在表 沿路径读取对应字段
D-CONFORM 配对 使用另一事实表中的配对字段

列名相同不代表自动对应。缺少对应、对应冲突或跨表 fixed 没有分组字段,都会被拒绝。 普通关系声明 from、to、from_columns、to_columns,默认多对一。 D-CONFORM 使用相同的列对应列表,并声明 DATUS cardinality: "many_to_many"; 它用于结果对应,不把两张事实明细表直接连接。参见 D-CONFORM。

没有匹配记录时,跨表 COUNT 为 0,其他聚合为 NULL。 跨表 NULL 键不匹配任何键,与同表 NULL 分组不同。 SUM 在无记录或全部值为 NULL 时都返回 NULL;业务明确要求零时,通过普通计算字段使用 COALESCE。

查询的时间范围落在被汇总表自己的时间字段上,不管两张表的日期列叫什么、有没有配对; 没有时间字段的表读全部数据,并给出 time_axis_missing 警告。context_filter 按子条件 传播到被汇总表能读取的位置,包括多对一路径或配对列。读不到的子条件跳过,并给出 context_skipped 警告;查询里任何部分都读不到的条件直接拒绝(context_unreadable)。 只能经由存储格式不同的配对列读到的条件返回 not_implemented,这时把期间放进时间范围。 普通 where_sql 仍不会缩小固定计算的输入,所以某次查询要这个字段读全部历史时, 把查询自己的日期写在那里(S-FILTER-2)。

两张事实表各自分类后对齐

两张记录业务活动的事实表,可以各自定义固定字段,再用 D-CONFORM 声明共同分类维度。 各自计算字段和指标后,再对齐分组结果。查询可以使用配对维度的任一名称, 只请求另一侧指标时也一样。结果保留两侧分组,缺失 COUNT 默认补 0,SUM 保持 NULL, 除非另外声明填充规则。两边人数相加不表示跨表去重,因为两边可能包含同一对象。

这与“一个固定字段直接汇总另一张表”是不同用法:同时查询两表指标并按后者分组, 会返回 fan_out_risk。普通关系的连接列也不能读取 LOD 值;D-CONFORM 对应不属于这种明细连接。

过滤的详细规则

context_filter 不能读取 LOD 值,因为它先限定用于计算 LOD 的记录。 固定字段条件只在固定值产生后生效,不会反过来限定任何固定计算的输入。 已选出的 LOD 指标可以用于结果筛选,执行在计算完成后、排序及 limit 之前;它不能成为分组维度。 未选出的指标用于结果筛选会返回 result_filter_metric_not_selected, 一个结果条件同时引用指标和未分组字段会返回 result_filter_unprojected。

AND 的各子条件分别确定位置。混合原始字段与固定字段的 OR、NOT 或比较表达式, 在固定值可用后整体执行;不能把 OR 拆成多个筛选。 只为展示维度或普通过滤引入的关联影响展示记录,不缩小固定计算的输入,INNER 关联也一样。 需要让匹配记录限定计算时,应把相应条件放进上下文。

时间范围左闭右开。固定分组中的时间键在内部计算时保留原值与存储编码, 再次按固定结果分组时才应用展示时间粒度;include 则在内层细分时应用该粒度。 LOD 字段及依赖它的字段不能声明 dimension.is_time: true。 可以把它们的 datatype 声明为日期,但这不会使它们成为可接收查询时间范围的时间轴。

如何查询已经定义的 LOD

指标查询使用 dosi query,选择指标并指定需要的分组。 group_by、where_sql、context_filter、time_range、order_by、limit、params 分别对应 CLI 的 --group-by、--where、--context-filter、--start-time/--end-time、 --order、--limit、--param;指标名称通过 --metrics 提供。 无需在查询里再写一套 LOD 表达式。

明细查询使用 dosi select,通过 --from 指定根数据集,通过 --fields 指定输出字段。 可以读取、筛选、排序根数据集上的固定字段,其基础指标可以来自其他表。 当前不能读取关联数据集上的 LOD 字段或依赖它的计算字段;如果需要的是那张表的记录,应以它为根。

明细查询保留根数据集的记录行数。--distinct 仅去掉重复输出行,不去掉参与 LOD 计算的原始记录。 可以读取沿多对一关系可达的普通维度字段;向多侧遍历会被拒绝,避免记录膨胀。 明细字段的基础指标使用相同的 --param 绑定规则。

能力边界:哪些计算需要拆开

限制针对某次查询实际读取的字段和指标,不是禁止它们共存于模型中。 拆分查询时仍要保持相同的业务范围和分母。

下面这些组合可以放在同一次查询里:

同一次查询中的组合 说明
原始行指标,固定字段仅用于分组或筛选 例如电商示例第 1 题,按一次购买/多次购买分组统计买家数和金额
对同一组固定分组字段的值再次聚合 每个展示组内,每个固定分组只算一次
多组固定字段只用于展示或筛选 其中只有一组可以提供再聚合的值
同一事实表的普通指标、exclude/fixed 指标及 compose 例如电商示例第 4 题
多个 include 指标 基础指标读的数据集相同,声明维度的列表和顺序也相同
LOD 指标与别的事实表的指标 每张事实表各算一个分支,LOD 在自己那张表的行上、按那张表的筛选和日期计算,结果按分组字段对齐

下面这些组合需要换一种写法,或者拆成几次查询。拆分时,每次查询都要用相同的分组、 上下文和时间范围,结果再按分组字段对齐,这样各部分的业务范围和分母才一致。

同一次查询中的组合 怎么办
再聚合固定值,同时计算原始行指标 拆成两次查询:一次只放再聚合固定值的指标,一次放原始行指标
再聚合两组不同固定分组字段的值 每组固定分组各查一次
include 与其基础指标所读事实表的普通指标、exclude/fixed 指标或 LOD 字段混用 include 指标单独查询,其余指标另查一次。电商示例第 3 题就是这样处理的
exclude/fixed 指标与查询读取、分组或筛选的 LOD 字段混用 拆成两次查询
带键 exclude 的字段或 fixed 指标的分组字段没有出现在查询分组中(lod_dimension_not_grouped) 把这些字段加进 group_by:exclude 的粒度不限,fixed 的要原样加、不带时间粒度。如果要把 fixed 的结果再汇总到更粗的分组,改用固定字段加普通聚合
LOD 与窗口指标或 time_ranges 分开执行;多个期间的对比,每个期间查一次
指标只是关联到某张表,却按那张表的固定字段分组或筛选 改用以那张表为主的指标,或者把固定字段定义在指标自己的数据集上
基础指标或分组字段间接依赖 LOD,或窗口基础指标使用 LOD 当前没有替代写法,LOD 不能嵌套
普通关系的连接列或时间轴使用 LOD 值 当前没有替代写法
对 LOD 指标、条件或维度做归因分析 当前不支持。发现接口会把 LOD 指标标为不可归因,LOD 维度可能被跳过并给出提示

声明位置错误、基础指标不存在、指标维度没有数据集前缀、对应歧义或非法嵌套,属于建模错误。 不支持的查询组合可能返回 not_implemented、fan_out_risk 或其他具体错误,不能统称为同一错误。 展示维度与另一固定键的输出名称冲突时,返回 duplicate_output_name。

空输入没有分组时返回一行,COUNT 为 0,其他聚合为 NULL;有分组时不产生结果组。 外层 COUNT 统计非 NULL 的中间值,不一定等于原始记录数。LOD 不自动补齐日历或缺失对象。

校验、版本与兼容性

先在 datus 模式校验模型,再编译、执行实际查询。 重点核对每个对象的记录数不同时是否仍正确、缺失匹配、过滤位置、日期边界和零分母。 用独立原始 SQL 对照,并改变数据和参数验证计算规则。

dosi validate --osi-datus 检查结构和编译;--strict 还会因字段声明等建模规范警告而失败, 但不把所有编译提示都当成错误。dosi info 可以查看当前运行引擎的扩展能力。

字段应声明真实主键、关系列及维度用途。时间路由要求 dimension.is_time: true。 不推荐作为分析维度的技术字段,在该字段的 DATUS JSON 中设置 is_dimension: false; true 表示明确推荐。这个设置不禁止查询显式按该字段分组。 仅填写核心 dimension 对象不保证消除所有字段角色提示;标识字段未声明为键或关系列时仍可能出现 underdeclared_field。应明确其实际用途,不能虚构唯一性。固定字段及其派生字段不能声明为时间轴。 通用关系检查也可能对明确声明的事实表对应关系提示目标不唯一,无须为消除提示虚构主键。

Dosi 接受的模型文件结构

Dosi 要求根节点包含 version: "0.2.0.dev0",一个文档只放一个模型,有两种写法: 放在只有一个条目的 semantic_model 列表里,条目内是模型的 name、datasets、 relationships、metrics;或者把这些字段直接放在根节点。该引擎不接受 version: "0.1.1"。

上游 0.2.0.dev0 是仍在变化的开发版规范,较新快照把模型移到了根节点,版本字符串却没变。 在上游正式发布这一改动之前,优先用列表写法,0.2.0.dev0 的 schema 校验的是这种写法。参见 一个文档一个模型。

basic 模式不会执行相同的 LOD 计算

basic 模式忽略 DATUS 扩展并提示。没有生效 LOD 元数据的聚合字段会被丢弃, 返回 aggregate_field_dropped,后续引用失败。LOD 指标中的嵌套聚合及其组合也会明确失败, 不能依赖 basic 模式得到等价结果。

共同接口见语义契约、扩展格式、CLI 参考、 明细查询和错误说明。