Gel(EdgeDB)集合类型内省:schema::Array 与 schema::Tuple 的 schema 模块自省指南
Gel(EdgeDB)集合类型内省:schema::Array 与 schema::Tuple 的 schema 模块自省指南
本篇文章围绕 Gel(EdgeDB)官方文档 docs/reference/datamodel/introspection/colltypes.rst 展开,系统讲解如何通过 schema 模块对集合类型(Collection Types)——即 array、tuple、range——进行内省查询。你将掌握:如何查看 schema::Array / schema::Tuple 自身的结构、如何解析某个对象类型属性(如 favorites: array<str>)的目标类型及元素类型、以及如何用类型限定(type filter)内省函数返回值(如 sys::get_version)这类“命名元组 + 数组”的复杂组合类型。文中全部示例均基于当前仓库的 schema 定义与源码可验证。
Gel 将全部 schema 信息存放在 schema 模块(见 modules.rst),并通过内省查询(introspection queries)暴露给用户(见 introspection/index.rst)。所有内省类型本身也继承自 BaseObject,因此同样可以作为对象类型被自省。集合类型(Array / Tuple / Range)在 Gel 中是由 edb/schema/types.py 中分别定义的 Array、Tuple、Range 三个类表示的(见 types.py 与 types.py),它们是内省体系中的特殊成员:属于集合类型(collection types),但以 schema::Array、schema::Tuple、schema::Range 的形式在 schema 模块内暴露为对象类型,因而可以被 select ObjectType 查询。
前置:进入 schema 模块
所有内省示例都遵循同一个套路:
with module schema
select ObjectType {
name,
links: { name },
properties: { name }
}
filter .name = 'schema::Array';
with module schema把默认模块切换到schema,后续的ObjectType、Array、Tuple均指schema::下的内省类型;ObjectType即schema::ObjectType,是对应于每个对象类型的元数据对象;filter .name = '...'用于精确定位到目标类型。
内省 schema::Array
对 schema::Array 本身做内省,得到它的链接(links)与属性(properties):
with module schema
select ObjectType {
name,
links: { name },
properties: { name }
}
filter .name = 'schema::Array';
输出:
{
Object {
name: 'schema::Array',
links: {
Object { name: '__type__' },
Object { name: 'element_type' }
},
properties: {
Object { name: 'id' },
Object { name: 'name' }
}
}
}
关键结论:
schema::Array有一个链接element_type,指向该数组的元素类型;- 它没有自己的属性(除通用的
id、name外),元素信息全部通过element_type链接表达; element_type的底层实现对应 types.py 中Array.element_type这个 SchemaField(Range类同样定义了element_type字段,见 types.py)。
实际对象上的数组属性内省
假设 schema 中有如下类型(含一个数组属性 favorites):
type User {
required property name: str;
property favorites: array<str>;
}
内省 default::User 时,重点看 properties 的 target,并用类型限定 [is Array] 展开数组的元素类型:
with module schema
select ObjectType {
name,
properties: {
name,
target: {
name,
[is Array].element_type: { name },
},
},
}
filter .name = 'default::User';
输出:
{
Object {
name: 'default::User',
properties: {
Object {
name: 'favorites',
target: Object {
name: 'array',
element_type: Object { name: 'std::str' }
}
},
...
}
}
}
要点解析:
properties.target是属性所指向的类型对象;对数组属性来说,target是名为array的schema::Array实例;[is Array].element_type是**类型限定(type filter)**语法:只有当target是schema::Array时才展开其element_type链接。对非数组属性(如name: str),该子形状不会产生结果;element_type: { name }最终给出std::str——这就是array<str>中尖括号内的元素类型。
由于所有数组属性的 target 都指向同一个 schema::Array 实例(名称固定为 array),真正的类型差异体现在 element_type 上,因此内省数组属性时“展开 element_type”是必须的一步。
内省 schema::Tuple
对 schema::Tuple 本身做内省:
with module schema
select ObjectType {
name,
links: { name },
properties: { name }
}
filter .name = 'schema::Tuple';
输出:
{
Object {
name: 'schema::Tuple',
links: {
Object { name: '__type__' },
Object { name: 'element_types' }
},
properties: {
Object { name: 'id' },
Object { name: 'name' }
}
}
}
与 schema::Array 的关键差异:schema::Tuple 的链接是 element_types(复数),因为元组可以包含多个命名或未命名的元素;每个元素自身又是一个内省对象,通过它的 name 与 type 来描述“元素名 + 元素类型”。
内省元组:以 sys::get_version 为例
sys::get_version 是一个返回命名元组的函数,其定义位于 edb/lib/sys.edgeql:
CREATE FUNCTION
sys::get_version() -> tuple<major: std::int64,
minor: std::int64,
stage: sys::VersionStage,
...>
内省其返回类型(return_type),并用 [is Tuple] 展开命名元组元素:
with module schema
select `Function` {
return_type[is Tuple]: {
element_types: {
name,
type: { name }
} order by .num
}
}
filter .name = 'sys::get_version';
输出:
{
Object {
return_type: Object {
element_types: {
Object { name: 'major', type: Object { name: 'std::int64' } },
Object { name: 'minor', type: Object { name: 'std::int64' } },
Object { name: 'stage', type: Object { name: 'sys::VersionStage' } },
Object { name: 'stage_no', type: Object { name: 'std::int64' } },
Object { name: 'local', type: Object { name: 'array' } }
}
}
}
}
关键要点:
return_type[is Tuple]:类型限定确保只有元组返回类型才展开element_types;element_types的每个元素是schema::TupleElement一类的对象,含name(元素名)与type(元素类型);order by .num:元组元素是有序的,.num表示元素在元组中的位置序号,保证输出顺序与元组声明顺序一致;- 注意最后的
local元素:其类型是array(即schema::Array)——这是一个元组中嵌套数组的例子。如果需要继续下钻,可以对type再次应用[is Array].element_type,与数组内省部分的方法完全一致。
深入:集合类型在源码中的统一模型
从源码结构看,Gel 将三类集合类型统一建模在 edb/schema/types.py 的 Collection 基类之下:
Array(types.py):qlkind=ARRAY_TYPE,schema_name='array',唯一的类型参数是element_type;Tuple(types.py):qlkind=TUPLE_TYPE,schema_name='tuple',通过element_types表达多个命名/匿名元素;Range(types.py):qlkind=RANGE_TYPE,schema_name='range',同样持有element_type。
这解释了内省查询中 schema::Array / schema::Tuple 链接命名的由来:数组与 range 是“单元素”集合类型,所以用单数链接 element_type;元组是“多元素”集合类型,所以用复数链接 element_types。对 range 类型的内省方式与 array 完全同构——查找 schema::Range,其链接同样包含 element_type。
相关阅读与延伸
- objects.rst:对象类型内省,理解
ObjectType与links/properties的完整字段; - scalars.rst:标量类型内省,与集合类型内省配合可覆盖全部类型体系;
- functions.rst:函数内省,包含
return_type等更多用法; - primitives.rst 与 objects.rst:数组、元组、range 在数据模型层的定义,是理解其内省结构的基础;
- edb/schema/types.py:集合类型的源码实现,可验证
Array、Tuple、Range的字段定义与命名规则。