Schema 与语义类型

Schema 定义 LogicForm 可见的业务字段和关系;Schema Mapping 定义这些字段位于哪个物理数据源。二者必须分开配置。

Schema 契约

interface SemanticSchemaDraft {
  id: string;
  name?: string;
  type?: string;
  properties: SemanticPropertyDraft[];
  temporalSemantics?: 'normal' | 'snapshot';
  parentProperty?: string;
  hierarchy?: HierarchyDefinition;
}

核心字段:

字段必填说明
idSchema 公开唯一标识,也是 logicform.schema 的值
propertiesProperty 列表
name业务展示名称
type调用方使用的 Schema 分类元数据
temporalSemanticsnormalsnapshot,默认 normal
parentProperty树结构中保存父节点 ID 的 Property 名称或 ID
hierarchy内置 hierarchy 插件的分段编码层级配置

Schema 不接受 _id_sidsiddbfromfromSQLprojections 作为 V2 语义契约。物理信息应写入 Schema Mapping。

Property 契约

interface SemanticPropertyDraft {
  id?: string;
  name: string;
  type: string;
  primal_type?: 'string' | 'number' | 'boolean' | 'date' | 'object';
  isArray?: boolean;
  ref?: string;
  granularity?: TimeGranularity;
  udf?: { sql: string };
  comparable?: {
    values: string[];
    direction: 1 | -1;
  };
  constraints?: Record<string, unknown>;
}
字段必填说明
name业务名称;LogicForm 推荐使用此字段引用 Property
type已注册的 Semantic Type 名称
id默认物理列标识;省略时取 name
primal_type通常不填写,由 Semantic Type 推导;显式值必须一致
isArray是否为数组,默认 falsetype 描述数组元素
refobject 必填object/entity 指向的目标 Schema id
granularitydate 可选日期粒度;日期字段省略时默认 day
udf.sql受信任的数据库表达式,用于动态 Property
comparablecategory 可选有序枚举的值和方向
constraintsSemantic Type 或上层建模使用的约束元数据

Resolved Property 一定包含 idprimal_typeisArray。Schema 与 Property 的 id 必须唯一,Property name 也必须唯一。

Primal Type

Primal Type 是数据库和 Query 共同理解的基础值类别:

Primal Type值类别
string字符串
number数值
boolean布尔值
date日期/时间,启用统一日期 Query 规范化
object指向另一个 Schema 的实体关系

Semantic Type 在 Primal Type 之上表达业务含义。调用方应优先选择准确的 Semantic Type,而不是只关注底层值类型。

全部内置 Semantic Type

当前内置 20 个类型:

Semantic TypePrimal Type用途与结果行为
IDstring实体稳定身份;大小写敏感,Schema 通常只配置一个
stringstring普通文本
namestring业务对象名称
humanNamestring人名
categorystring分类/枚举;可配置 comparable
imagestring图片地址或图片标识
filestring文件地址或文件标识
numbernumber一般数值
intnumber整数语义;运行时基础值仍为 number
currencynumber金额/货币值
percentagenumber比例值;例如 0.25 表示 25%
durationnumber时长数值;具体单位由建模元数据约定
ordernumber顺序或排序权重
booleanboolean布尔值;数据库返回数字 1 时结果转换为 true,其他数字转换为 false
timestampdate事件时间戳;snapshot Schema 的时间轴字段
datedate一般日期
start_datedate生命周期开始日期
end_datedate生命周期结束日期
entityobject实体关系;按 ref 批量填充目标实体
objectobject对象关系;按 ref 批量填充目标实体

entityobject 当前都表示 object 关系并使用相同的结果填充行为;保留两个名称用于业务建模和兼容表达。

数组不需要单独 Semantic Type。例如标签数组应写成:

{ id: 'tags', name: '标签', type: 'category', isArray: true }

Date 类型规则

timestampdatestart_dateend_date 共享:

  • 日期字符串标准化;
  • 绝对周期和 { granularity, offset } 相对周期;
  • DTDWTDMTDQTDYTD
  • 未来上界的 latest-data-date 截断;
  • granularity 元数据。

可用粒度:secondminutehourdayweekmonthquarteryear

Object / Entity 关系

const sales = {
  id: 'sales',
  properties: [
    { id: 'customer_id', name: '客户', type: 'entity', ref: 'customers' },
  ],
};

const customers = {
  id: 'customers',
  properties: [
    { id: 'id', name: '编号', type: 'ID' },
    { id: 'name', name: '名称', type: 'name' },
  ],
};

关系连接以源 object Property 的物理值对应目标 Schema 的唯一 ID Property。目标 Schema 必须有且只有一个 ID,两个 Schema 都必须有可用 Mapping。

直接输出 客户 时,结果为实体对象:

{
  "客户": {
    "_id": "customer-1",
    "编号": "customer-1",
    "名称": "甲客户"
  }
}

结果规则:

  • scalar object 最多递归填充 5 层;
  • array object 保留原顺序和重复值,只填充当前数组成员,不继续递归成员内部的 object;
  • 找不到实体时返回同形状占位对象:ID 和 _id 保留,字符串字段为缺失提示,其他标量为 null,数组为空数组;
  • 直接输出 object 不等于关系路径分组;前者返回实体,后者读取目标字段。

Snapshot 时间语义

{
  id: 'inventory',
  temporalSemantics: 'snapshot',
  properties: [
    { id: 'sku', name: 'SKU', type: 'ID' },
    { id: 'recorded_at', name: '记录时间', type: 'timestamp' },
    { id: 'quantity', name: '库存', type: 'number' },
  ],
}
  • snapshot Schema 必须恰好有一个 timestamp Property。
  • 有 ID 的 snapshot Schema 表示同一实体在不同时间的版本。
  • 无 ID 的 snapshot Schema 可作为根查询的数据观察集,但不能作为 object 关系的目标。
  • 关系查询 snapshot 目标时,会同时约束实体身份和有效时间周期。
  • 源时间粒度不能比目标 snapshot 粒度更粗。

旧字段 is_observationis_snapshot 不是 V2 Schema 契约。

动态 Property udf.sql

{
  id: 'gross_profit',
  name: '毛利',
  type: 'currency',
  udf: { sql: 'revenue - cost' },
}

udf.sql 是建模方提供的受信任物理 SQL 表达式。它不是终端用户输入,可能依赖数据库方言。动态 Property 在 LogicForm 中像普通 Property 一样被引用,但仍受 Mapping、权限和 Provider 能力约束。

可比较 Category

{
  id: 'priority',
  name: '优先级',
  type: 'category',
  comparable: {
    values: ['低', '中', '高'],
    direction: 1,
  },
}
  • values 必须是非空、无重复的字符串数组。
  • direction 必须为 1-1
  • Query 中的值必须存在于 values
  • $min$max 和范围比较按业务顺序计算。
  • comparable Property 不能是数组。

层级 Schema

内置 hierarchy 插件支持分段编码:

{
  id: 'regions',
  hierarchy: {
    property: '编号',
    storage: 'compact',
    levels: [
      { name: '国家', synonyms: ['国'], codeLength: 3 },
      { name: '省市', synonyms: ['省'], codeLength: 2 },
    ],
  },
  properties: [
    { id: 'code', name: '编号', type: 'ID' },
    { id: 'name', name: '名称', type: 'name' },
  ],
}

storage 可为:

  • compact:编码到当前层级结束;
  • zero-padded:低层级部分用 0 补齐到总长度。

property 可使用 Property 名称或 ID,解析后统一为名称。每个 level 的 name 唯一、codeLength 为正整数,synonyms 可用于 $hierarchyLevel.args.level

Schema Mapping

完整公开结构:

interface SchemaMappingDefinition {
  id?: string;
  schemaId: string;
  connectionId: string;
  source: string;
  sourceSql?: string;
  properties?: Record<string, string>;
  predMappings?: Array<{
    match: {
      schema?: string;
      operator: string;
      pred: string;
      query?: QueryType;
    };
    column: string;
  }>;
  priority?: number;
  freshness?:
    | { mode: 'ttl'; ttlSeconds: number }
    | { mode: 'daily'; expireAt: string; timeZone?: string }
    | { mode: 'manual' };
}

如果一个 Schema 有多个 Mapping,系统只会选择能够完整覆盖当前 Query、Groupby 和 Pred 的候选。相同优先级且同样匹配的候选会返回歧义错误,不依赖注册顺序猜测。任何调用方条件都不会因为 Mapping 不支持而被静默删除。