ObjectOS
构建数据

关系

用查找(lookup)与主从(master-detail)连接对象 —— 级联规则、过滤选择器、层级结构、连接对象与汇总字段。

关系

**关系就是一个字段。**把 lookupmasterDetail 字段指向另一个对象,ObjectOS 会把上层的一切都接好:外键完整性、Console 里的记录选择器、父记录页面上的相关列表,以及查询中的 expand

类型基数删除默认行为用于
lookup多对一set_null松散引用(工单 → 负责人)
masterDetail多对一、有归属cascade父子归属(订单 → 明细行)
tree自引用层级结构(分类、组织架构图)
user多对一set_null人员选择器 —— 特化为 sys_user 的查找
summary汇总到父记录聚合子记录(sum、count、avg)

查找(多对一)

主力字段。引用另一个对象中的一条记录:

import { ObjectSchema, Field } from '@objectstack/spec/data';

export const Contact = ObjectSchema.create({
  name: 'contact',
  fields: {
    account: Field.lookup('account', {
      label: 'Account',
      required: true,
    }),
  },
});

平台会校验被引用的记录确实存在 —— 每次写入都强制外键完整性。

**提示:**按所指向的对象给字段命名 —— 用 account,而不是 account_id。这样相关列表、expand 和 AI Builder 读起来都更顺。

父记录被删除时会发生什么

deleteBehavior 控制级联行为:

父记录删除时
set_null子记录的引用被清空(lookup 默认)
cascade子记录也一并删除
restrict只要还有子记录,就阻止删除

过滤查找

约束选择器提供哪些记录。静态条件用 lookupFilters;用 dependsOn 让候选记录随同一记录上另一个字段的取值收窄:

contact: Field.lookup('contact', {
  label: 'Contact',
  dependsOn: ['account'],          // 只显示所选 account 下的联系人
  lookupFilters: [
    { field: 'is_active', operator: 'eq', value: true },
  ],
})

警告:旧的 referenceFilters: string[] 属性(如 ['is_active = true'])虽然能通过 Schema 校验,但记录选择器 UI 并不读取它 —— 它什么都不过滤。请始终使用上面展示的结构化 lookupFilters + dependsOn

主从(有归属的子记录)

masterDetail 是带归属语义的查找:子记录属于父记录,默认随父级联删除,并且可以在父表单上以明细行的形式内联编辑。

export const OrderLine = ObjectSchema.create({
  name: 'order_line',
  fields: {
    order:    Field.masterDetail({ reference: 'order', label: 'Order' }),
    product:  Field.lookup('product', { label: 'Product' }),
    quantity: Field.number({ label: 'Quantity', min: 1 }),
  },
});
方面行为
删除运行时级联到子记录,除非 deleteBehavior: 'restrict'
归属子记录归父记录所有
编辑可选:在父表单上内联编辑明细行
汇总summary 字段只在主(master)侧有效

引用是可选或共享的,用 lookup;子记录离开父记录就没有意义的,用 masterDetail

自引用查找与树

查找可以指回自己的对象,用来构建层级结构:

parent_account: Field.lookup('account', {
  label: 'Parent Account',
  description: 'Parent company in hierarchy',
})

对于专门的层级结构(分类、组织架构图),使用 tree 字段类型 —— 一个存储和展开方式与 lookup 相同的自引用查找。注意引擎在写入时不做环路检查,所以一条绕回自身的链不会被自动拒绝。

**提示:**把 tree / 自引用查找字段与树形列表视图搭配使用,即可在 Console 中把层级渲染为嵌套行。

相关列表

这些是免费得到的。当查找指向父对象时,子记录会自动以相关列表的形式出现在父记录的详情页上:

  • Contact 有 account: Field.lookup('account')
  • → Account 详情页会显示一个 Contacts 相关列表

无需任何额外配置。

多对多:连接对象

ObjectOS 没有直接的多对多字段 —— 用连接对象来建模:一个带两个关系字段的对象,各指向一侧。

// Student ↔ Course,通过 Enrollment 连接
export const Enrollment = ObjectSchema.create({
  name: 'enrollment',
  fields: {
    student: Field.masterDetail({ reference: 'student', label: 'Student' }),
    course:  Field.masterDetail({ reference: 'course',  label: 'Course' }),
    grade:   Field.select({ label: 'Grade', options: [ /* ... */ ] }),
  },
  indexes: [
    { fields: ['student', 'course'], unique: true },  // 每对组合只允许一条报名记录
  ],
});

两侧都会把连接记录看作一个相关列表,而连接对象本身正是存放这段配对属性(成绩、角色、加入日期)的天然位置。

汇总(summary 字段)

summary 字段把子记录聚合到父记录上。只在主对象上有效 —— 即主从关系的父侧:

// 在 Order 对象上
total_lines: {
  type: 'summary',
  label: 'Line Count',
  summaryOperations: {
    object: 'order_line',    // 要聚合的子对象
    field: 'quantity',       // 要聚合的字段
    function: 'sum',         // count | sum | min | max | avg
  },
}

Summary 字段是只读的,由子记录计算得出 —— 你永远不会直接写它。

跨关系查询

expand 在一次调用里加载关联记录 —— 查询 API 会顺着查找字段把被引用的记录内联进来,客户端不必发出 N+1 次请求。

下一步

页面原因
数据模型这些关系所在的对象、字段与 Schema
公式用 CEL 在记录内计算值
验证规则跨字段规则与唯一约束
视图树形视图、Kanban(看板)与相关列表界面
字段类型lookupmaster_detailtreesummary 的完整参考
AI Builder在对话中描述关系,让平台自动接线

On this page