Schema 对象参考
最后更新:2026 年 7 月 16 日
本页讲解 PlayServ Backoffice 中用到的 Schema 核心概念。
当你需要某个 Schema 对象、字段种类或字段设置的明确定义,并想搞清楚它在整个数据模型中处于什么位置时,就来查这里。它要回答的是这类直白的问题:Entity 是什么,Relationship 和 Inclusion 差在哪,把一个对象标记为 Singleton 之后会怎样。
Schema 是一个 Project 数据结构的正式定义。它描述这个 Project 里有哪些对象、它们包含哪些字段、这些字段允许存放什么值,以及这些对象之间如何相互关联。
Entity、Entity Part 与 Enum
Schema 由三种核心对象类型构成 —— 它们在数据模型中各司其职。
独立对象
顶层的 Schema 对象,定义一个完整而独立的结构。Entity 是 Schema 的主要构成模块 —— 它们对应你游戏里的真实概念,比如 Player 档案、武器配置、商店道具或一局对战的记录。
每个 Entity 在 Data 视图中都有自己的 Record。它可以拥有自己的字段,可以被其他 Entity 通过 Relationship 字段引用,也可以通过 Inclusion 字段把 Entity Part 嵌进来。
可复用的片段
可复用的 Schema 对象,定义一段打算嵌进其他 Schema 对象里的结构。当同一组字段需要出现在多个地方,或者一个较大的对象更适合由若干个有名字的小块拼起来、而不是一口气定义完时,就用它。
Entity Part 在 Data 视图中没有自己的 Record —— 它们只作为包含它们的那些 Entity 的一部分而存在。属性块、配置分组,或是隶属于某个更大对象的背包结构,都是很好的例子。
受控的取值集合
定义一组固定可选值的类型。当某个字段应当从一份受控清单中取值、而不是自由填写时,就用它。Enum 让数据保持一致,避免取值随时间越走越偏。
比如一个 TankStatus enum,取值为 Active、Destroyed 和 Respawning,就能保证不会存进任何意料之外的状态值。字段通过 Enum reference 这个 kind 来引用 Enum。

字段的种类
每个 Entity 和 Entity Part 都由字段构成。添加字段时,你要选择它的 Kind —— 这决定了这个字段存什么,以及它如何与 Schema 的其余部分相连。
字段有四种 kind:Primitive、Enum reference、Relationship → entity 和 Inclusion ↩ part。
Primitive
Primitive 字段直接存放一个标量值。当这个字段只需装一个不引用任何其他 Schema 对象的值时,就用它。

| 类型 | 存放 |
|---|---|
| Text | 短字符串 |
| Long text | 多行字符串 |
| Integer | 整数 |
| Number | 浮点数 / 小数 |
| Decimal | 定精度数 |
| Boolean | true / false |
| Date & time | ISO 时间戳 |
| Date | YYYY-MM-DD |
| UUID | 唯一标识符 |
| JSON | 结构化对象 |
| 经校验的邮箱 | |
| URL | 链接 |
当字段直接装一个值时就选 Primitive —— 一个名字、一个速度数值、一个开关、一个时间戳。典型 Schema 中的大多数字段都是 Primitive 。
Enum reference
Enum reference 字段把取值限定为某个预定义 Enum 中的一个选项。

选定这个 kind 之后,再选择该字段要引用哪个 Enum。运行时,这个字段只能装那个 Enum 中定义过的值之一。
当某个字段只应接受一组受控取值时,就用 Enum reference —— 比如一个 status 字段,只能是 Draft、Active 或 Archived。这样既挡住了自由填写的值进入数据,也让合法选项在 Schema 里一目了然。
Relationship → entity
Relationship 字段建立一个指向另一个 Entity 的外键。当当前对象应当指向一条独立存在的 Record、而不是把对方的结构嵌进来时,就用它。

添加 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 存在时,就用它。

添加 Inclusion 字段时,选择目标 Entity Part 和基数:
- 1-to-1 —— 嵌入这个 part 的单个实例
- 1-to-many —— 嵌入这个 part 的一组实例
用 Inclusion 把可复用的结构块拼成更大的对象。比如一个 Tank entity,包含用于配置的 TankConf part 和用于战斗参数的 BattleConf part —— 这些结构本就属于这辆坦克,不需要单独存在。
与 Relationship 不同,Inclusion 不会建立指向另一条 Record 的链接,而是把 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 对象。

全局配置对象,或是整个 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_delete | cascade-delete、restrict、anonymise | Player 被删除时:一并删掉他的行;只要还有行就阻止删除;或者保留这些行但抹去归属链接。 |
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 的数据将要遵循的完整结构。
下一步
- Building a Game Schema —— 从定义走向动手搭建