Gel(EdgeDB)集合类型内省:schema::Array 与 schema::Tuple 的 schema 模块自省指南

原创2026-09-21 15:42:441,750 阅读
文章标签:数据库图数据库关系型数据库

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 的字段定义与命名规则。
登录后查看全文
edgedb