跳到主要内容

Schema 对象参考

最后更新:2026 年 7 月 16 日

本页讲解 PlayServ Backoffice 中用到的 Schema 核心概念。

当你需要某个 Schema 对象、字段种类或字段设置的明确定义,并想搞清楚它在整个数据模型中处于什么位置时,就来查这里。它要回答的是这类直白的问题:Entity 是什么,Relationship 和 Inclusion 差在哪,把一个对象标记为 Singleton 之后会怎样。

Schema 是一个 Project 数据结构的正式定义。它描述这个 Project 里有哪些对象、它们包含哪些字段、这些字段允许存放什么值,以及这些对象之间如何相互关联。


Entity、Entity Part 与 Enum​

Schema 由三种核心对象类型构成 —— 它们在数据模型中各司其职。

Entity

独立对象

顶层的 Schema 对象,定义一个完整而独立的结构。Entity 是 Schema 的主要构成模块 —— 它们对应你游戏里的真实概念,比如 Player 档案、武器配置、商店道具或一局对战的记录。

每个 Entity 在 Data 视图中都有自己的 Record。它可以拥有自己的字段,可以被其他 Entity 通过 Relationship 字段引用,也可以通过 Inclusion 字段把 Entity Part 嵌进来。

Entity Part

可复用的片段

可复用的 Schema 对象,定义一段打算嵌进其他 Schema 对象里的结构。当同一组字段需要出现在多个地方,或者一个较大的对象更适合由若干个有名字的小块拼起来、而不是一口气定义完时,就用它。

Entity Part 在 Data 视图中没有自己的 Record —— 它们只作为包含它们的那些 Entity 的一部分而存在。属性块、配置分组,或是隶属于某个更大对象的背包结构,都是很好的例子。

Enum

受控的取值集合

定义一组固定可选值的类型。当某个字段应当从一份受控清单中取值、而不是自由填写时,就用它。Enum 让数据保持一致,避免取值随时间越走越偏。

比如一个 TankStatus enum,取值为 Active、Destroyed 和 Respawning,就能保证不会存进任何意料之外的状态值。字段通过 Enum reference 这个 kind 来引用 Enum。

Schema 编辑器中的 Entity、Entity Part 与 Enum


字段的种类​

每个 Entity 和 Entity Part 都由字段构成。添加字段时,你要选择它的 Kind —— 这决定了这个字段存什么,以及它如何与 Schema 的其余部分相连。

字段有四种 kind:Primitive、Enum reference、Relationship → entity 和 Inclusion ↩ part。


Primitive​

Primitive 字段直接存放一个标量值。当这个字段只需装一个不引用任何其他 Schema 对象的值时,就用它。

带类型选择器的 Primitive 字段 kind

类型存放
Text短字符串
Long text多行字符串
Integer整数
Number浮点数 / 小数
Decimal定精度数
Booleantrue / false
Date & timeISO 时间戳
DateYYYY-MM-DD
UUID唯一标识符
JSON结构化对象
Email经校验的邮箱
URL链接

当字段直接装一个值时就选 Primitive —— 一个名字、一个速度数值、一个开关、一个时间戳。典型 Schema 中的大多数字段都是 Primitive。


Enum reference​

Enum reference 字段把取值限定为某个预定义 Enum 中的一个选项。

带 enum 选择器的 Enum reference 字段 kind

选定这个 kind 之后,再选择该字段要引用哪个 Enum。运行时,这个字段只能装那个 Enum 中定义过的值之一。

当某个字段只应接受一组受控取值时,就用 Enum reference —— 比如一个 status 字段,只能是 Draft、Active 或 Archived。这样既挡住了自由填写的值进入数据,也让合法选项在 Schema 里一目了然。


Relationship → entity​

Relationship 字段建立一个指向另一个 Entity 的外键。当当前对象应当指向一条独立存在的 Record、而不是把对方的结构嵌进来时,就用它。

带 entity 选择器和基数设置的 Relationship 字段 kind

添加 Relationship 字段时,选择目标 Entity 和基数:

  • 1-to-1 —— 当前对象恰好指向目标 Entity 的一条 Record
  • 1-to-many —— 当前对象指向目标 Entity 的多条 Record

当两个对象确实彼此独立、应当各自存在时,就用 Relationship。比如一个 Match entity 引用一组 Player entity —— Player 本就独立存在,还可能被多场对战引用。与 Inclusion 不同,Relationship 不会嵌入目标的结构,它只存一个指向对方的链接。

提示

当被关联的对象有自己的身份和生命周期时,用 Relationship。当那段结构本就属于父对象、不需要独立存在时,用 Inclusion。


Inclusion ↩ part​

Inclusion 字段把一个 Entity Part 直接嵌进当前对象,成为它结构的一部分。当嵌套的这段结构本就属于父对象、不应作为一条独立的 Record 存在时,就用它。

带 part 选择器和基数设置的 Inclusion 字段 kind

添加 Inclusion 字段时,选择目标 Entity Part 和基数:

  • 1-to-1 —— 嵌入这个 part 的单个实例
  • 1-to-many —— 嵌入这个 part 的一组实例

用 Inclusion 把可复用的结构块拼成更大的对象。比如一个 Tank entity,包含用于配置的 TankConf part 和用于战斗参数的 BattleConf part —— 这些结构本就属于这辆坦克,不需要单独存在。

与 Relationship 不同,Inclusion 不会建立指向另一条 Record 的链接,而是把 part 的字段直接插进父对象的结构里。

Relationship —— 指向另一条独立 RecordMatchPlayer外键Inclusion —— 内嵌结构TankTankConf (part)

用 Relationship 时,Match 和 Player 是两条独立的 Record,Match 只存一个链接。用 Inclusion 时,TankConf 没有自己的 Record,它的字段就住在 Tank 里面。


字段约束​

无论哪种 kind,每个字段都支持一组用于控制其行为的约束。

约束行为
Required字段必须有值。没有它,Record 无法保存。
Unique任意两条 Record 在该字段上不能取相同的值。
Indexed数据量大时,按该字段过滤或排序会更快。
Min / Max value限定数值范围。适用于 Integer、Number 和 Decimal 字段。
Min / Max length限定字符串长度。适用于 Text 和 Long text 字段。
Min / Max items限定数组字段中条目的数量。

Singleton​

Singleton 是一种只可能存在一条 Record 的 Schema 对象。

Schema 编辑器中的 Singleton entity

全局配置对象,或是整个 Project 范围内唯一、不该以 Record 列表形式存在的设置,就用 Singleton。典型例子是一个装着全服参数的 GameConfig entity —— 配置只有一份,而不是一堆。

区别很简单:

  • 普通 Entity 可以有很多条 Record
  • Singleton Entity 恰好只有一条

一个 Entity 被标记为 Singleton 后,Data 视图会随之改变 —— 显示的不再是带创建按钮的 Record 列表,而是单条 Record 的表单。

备注

Singleton entity 恰好只有一行,没有业务层面的主键。内部存储时会带一个隐藏的 id,好让外键照常工作。


Entity 设置​

除了字段之外,Entity 还带有若干设置,它们会改变其 Record 的行为方式。

Owned by player​

Entity 可以被标记为 owned by a player,这会把每条 Record 与创建它的那个 Player 绑定起来。数据面上按 Player 划分行作用域,靠的就是它 —— 客户端只看得到、也只写得了自己的行 —— 详见 Access Control。

对于 owned 的 Entity,你还要选择:当某个 Player 的账号被删除或合并时,他名下的行该怎么办。

设置取值含义
on_player_deletecascade-delete、restrict、anonymisePlayer 被删除时:一并删掉他的行;只要还有行就阻止删除;或者保留这些行但抹去归属链接。
on_player_merge(策略)两个 Player 账号合并为一个时,owned 的行如何处理。

这些策略是自动执行的 —— 删除或合并一个 Player,会在每一个 owned 的 Entity 上都应用一遍,因此不会留下孤立的 Record。

Raw fields​

默认情况下,一条 Record 只能包含 Schema 中声明过的字段,未声明的字段会被拒绝。Entity 也可以改为允许 raw fields(allow_raw_fields),让 Record 携带 Schema 未定义的任意额外键 —— 这是一个无 Schema 的逃生口,适合那些你不想事先建模其形状的数据。已声明的字段照常校验,raw fields 则不经检查地跟在旁边。

备注

要开就有意识地开。这是拿 Schema 的保障(校验、强类型查询)换取那个 Entity 上的灵活性 —— 对自由格式的 blob 很好用,但对任何你日后想查询或迁移的东西都会很别扭。


这些概念如何配合​

Schema 从核心对象开始:Entity 定义独立结构,Entity Part 定义可复用片段,Enum 定义受控取值集合。这些对象由字段构成。字段通过 Primitive 直接存值,通过 Enum reference 强制受控取值,通过 Relationship 关联到别的 Record,通过 Inclusion 嵌入可复用结构。Singleton 则标记出那些只该有一条 Record 的对象。

这些概念合在一起,定义出一个 Project 的数据将要遵循的完整结构。


下一步​