资讯详情

资讯详情

Gel(EdgeDB)EdgeQL 类型操作符完全指南:is、类型联合、cast、typeof 与 introspect

数据库图数据库关系型数据库【免费下载链接】edgedbGel supercharges Postgres with a modern data model, graph queries, Auth AI solutions, and much more.项目地址https://gitcode.com/gh_mirrors/ed/edgedb点击查看免费下载本文围绕 GelEdgeDBEdgeQL 标准库中的类型操作符体系展开系统讲解is/is not类型检查、type | type类型联合、type expr类型转换、typeof静态类型推断与introspect类型内省五大操作符并结合__type__链接说明如何发现对象运行时类型。读完本文你将掌握在 Gel 中完成类型判断、类型过滤、类型转换与 schema 内省查询的完整实战方案可直接用于日常数据建模与 EdgeQL 查询开发。概览EdgeQL 的类型操作符全家桶Gel 的类型系统是 EdgeQL 语言的核心基础官方文档在 docs/reference/stdlib/type.rst 中集中定义了五个类型操作符它们分别解决判断、联合、转换、推断、内省五类问题操作符语法作用isanytype is type - bool类型检查判断左操作数是否为右侧类型的实例或其子类型is notanytype is not type - bool类型检查的否定形式typeortype \| type - type类型联合在类型检查等场景中引用多个类型的并集casttype anytype - anytype类型转换把值转换为指定类型的值typeoftypeof anytype - type静态类型推断把表达式转换为类型introspectintrospect type - schema::Type静态类型内省返回类型对应的内省对象其中is、typeof、introspect三个操作符的索引归属在文档中以§type §is§ type§、typeof§ type§、introspect§ type§等形式登记方便在 API 索引中检索。此外更基础的类型表达式str、int64、arrayx、tuplex, y等语法定义见 docs/reference/edgeql/types.rst。发现对象的类型__type__链接在 Gel 中任何对象都通过__type__链接携带其类型信息包括类型的name等属性。这是发现对象运行时类型最直接的方式select jsonPerson { __type__: { name } } limit 1;输出示例{Json({\__type__\: {\name\: \default::Villain\}})}如果希望把类型名直接拉到顶层可以给__type__内部的name属性命名select jsonPerson { type : .__type__.name } limit 1;输出示例{Json({\type\: \default::Villain\})}需要特别说明的是__type__链接没有任何魔法成分。它只是一个标准链接由于所有对象都继承自BaseObject该链接存在于每个对象上指向当前对象自身的类型。从内省视角看__type__的目标类型是schema::Type详见 docs/reference/datamodel/introspection/objects.rst 中对ObjectType的内省示例其中__type__链接的cardinality为One、target为schema::Type。与introspect typeof的区别静态 vs 动态这里有一个重要且易混淆的差异introspect typeof SomeType与SomeType.__type__虽然都涉及对象的类型但语义完全不同introspect typeof SomeType是静态求值的只考虑编译器推断出的类型不关心集合中实际存放了哪些对象SomeType.__type__是实际运行时的类型集合即从所有SomeType对象出发可达的全部类型。由于继承的存在静态推断类型与实际类型可能不一致但实际类型永远是静态推断类型的子类型。官方文档用如下例子演示文档原文见 docs/reference/stdlib/type.rst# 先清空 Foo 对象 delete Foo; # Foo 集合为空时静态类型仍是 default::Foo select (introspect typeof Foo).name; # {default::Foo} select Foo.__type__.name; # {} —— 集合为空没有实际对象也就没有实际类型 # 插入一个 Foo 对象 insert Foo; # Bar 也继承自 Foo因此 Bar 对象同样属于 Foo 类型 insert Bar; select (introspect typeof Foo).name; # {default::Foo} —— 静态类型不变 select Foo.__type__.name; # {default::Bar, default::Foo} —— 实际类型集合这个例子清楚地展示了当集合为空时Foo.__type__为空集合而introspect typeof Foo依然返回default::Foo当集合中同时存在Foo与Bar对象时__type__返回所有实际类型。is/is not类型检查操作符is操作符的完整签名是anytype is type - bool anytype is not type - bool它检查左操作数A是否为B的实例或者B的任意子类型的实例。右侧操作数可以是用逗号分隔的多个类型is not则给出否定判断。一个关键语义点is的右操作数B是特殊的类型表达式而不是普通的值表达式因此它不参与集合交互和最长公共前缀longest common prefix规则的计算。基础示例select 1 is int64; # {true} select User is not SystemUser filter User.name Alice; # {true}结合类型联合is可以一次检查多个类型select User is (Text | Named); # {true, ..., true} # 对每一个 user 实例返回一个结果在类型检查之外is还常与类型交叉type intersection语法[is Type]配合使用——后者用于把集合的元素按子类型进行过滤例如select Media[is Movie]只返回Media集合中的Movie元素详见 docs/reference/edgeql/types.rst 的 Type intersections 小节。注意不要混淆[is foo]与类型检查操作符is两种语法。type | type类型联合操作符类型联合操作符的签名是type | type - type它只在需要进行类型检查的上下文中有效最典型的用途就是与is/is not配合表示值是否属于这些类型中的任意一个select User is (Text | Named); # {true, ..., true}类型联合同样可用于指定链接目标类型。此时适用同样的逻辑对象要成为合法链接目标必须满足object is (A | B | C)。官方文档给出了一段完整的 SDL EdgeQL 示例。首先是 SDL 模式定义abstract type Named { required name: str; } abstract type Text { required body: str; } type Item extending Named; type Note extending Text; type User extending Named { multi stuff: Named | Text; }在上述 schema 下User.stuff链接的目标类型是Named | Text因此可以插入指向Item继承Named或Note继承Text对象的链接insert Item {name : cube}; # {Object { id: uuid... }} insert Note {body : some reminder}; # {Object { id: uuid... }} insert User { name : Alice, stuff : Note, # 所有 notes }; # {Object { id: uuid... }} insert User { name : Bob, stuff : Item, # 所有 items }; # {Object { id: uuid... }}查询时可以用[is Named]/[is Text]类型交叉分别提取联合类型中不同分支的属性select User { name, stuff: { [is Named].name, [is Text].body } };输出{ Object { name: Alice, stuff: {Object { name: {}, body: some reminder }} }, Object { name: Bob, stuff: {Object { name: cube, body: {} }} } }可以看到Alice的stuff是Note对象[is Named].name为空而[is Text].body有值Bob的stuff是Item对象情况恰好相反。这是多态查询polymorphic queries与嵌套形状nested shapes在联合类型上的典型应用。type expr类型转换cast操作符类型转换操作符的语法是 type expression其中type必须是合法的类型表达式表示非抽象标量或容器类型。类型转换是运行时操作只有当该类型对之间定义了类型转换操作且源值满足目标类型的要求时转换才会成功。Gel 允许对所有标量进行转换。三个核心规则需要牢记禁止在对象类型之间转换把某个Object转换为另一个Object是非法的。构造新Object的唯一途径是insert。不过类型交叉操作符[is Type]可以达到与对象转换类似的效果例如把父类型的对象集合按子类型过滤。type必须是具体类型不能转换为抽象类型。定义空集类型转换的一个重要用途是为空集{}声明类型用于类型消歧见下文。标量与容器转换示例# 把字符串字面量转换为整数 select int6442; # {42} # 把整数数组转换为字符串数组 select arraystr[1, 2, 3]; # {[1, 2, 3]} # 把 issue 编号转换为字符串 select strexample::Issue.number; # {142}类型转换还可以用于元组既可以把元组整体转换为另一类型也可以为元组元素声明便于访问的名字# 转换元组中各元素的类型 select tupleint64, str(1, 3); # {[1, 3]} # 为元组元素命名并访问 with stuff : (1, foo, 42) # 可能是某个计算的结果 select ( tuplea: int64, name: str, b: int64stuff ).name; # 访问 name 元素 # {foo}用 cast 定义空集类型类型消歧在计算属性或链接时如果表达式的类型无法自动确定例如if/else的一个分支是空集就必须用转换来显式声明类型。下面这个例子中name计算属性在Text is Issue为假时取str{}转换到str是必需的因为计算表达式的类型必须被确定with module example select Text { name : Text[is Issue].name if Text is Issue else str{}, # 这里的 str 转换是必要的 # 因为计算表达式的类型必须明确 body, };对空集进行转换也是唯一允许把值转换为Object类型的情形with module example select User { name, friends : User{} # 这个转换是唯一能表明计算链接 friends # 应当指向一组 User 对象的方式 };更完整的转换规则cast 层级与转换表Gel 的转换分为显式、赋值assignment与隐式implicit三个层级详见 docs/reference/reference/edgeql/casts.rst显式转换Explicit casts即type expr语法是本文档讨论的主体。type必须是表示非抽象标量或容器类型的合法类型表达式。例如select str10;得到{10}。此外从 Gel 3.0 起还支持把 UUID 转换为对象select Herouuid...如果该 UUID 不是目标类型的id会抛出CardinalityViolationError例如default::Hero with id ... does not exist。赋值转换Assignment casts发生在insert新对象时。数值类型通常会按目标属性的类型自动转换例如把默认的int64字面量42自动赋给int16属性。只要某对类型支持赋值转换就同时支持显式转换。隐式转换Implicit casts在表达式值类型与期望类型不匹配时自动发生主要用于不会丢失信息的数值转换通常是从精度低的类型到精度高的类型。注意int64 - float64是例外对超大整数可能发生有效数字截断。三种典型场景是函数参数类型不完全匹配如math::ceil只接受int64/float64/bigint/decimal传入float32会被隐式转换为float64、操作符操作数类型不匹配如1 2.3中int64的1被隐式转换为float64、以及集合中混用不同数值类型如select {1, 2.3, float324.5} is float64;全部为true。只要某对类型支持隐式转换就同时支持赋值转换和显式转换。完整的可转换性对照表casts table在 docs/reference/reference/edgeql/casts.rst 的 Casting Table 小节表项符号含义为表示可显式转换:表示支持赋值转换impl表示支持隐式转换。此外从浮点类型转换为整数类型时小数部分无法保留转换采用四舍五入、平局取偶round half to even规则例如 1.5 舍入为 22.5 也舍入为 2。非法转换的报错示例类型转换的边界由运行时强制检查示例见 docs/reference/edgeql/types.rstselect BlogPost10; # QueryError: cannot cast std::int64 to default::BlogPost select int64asdf; # InvalidValueError: invalid input syntax for type std::int64: asdf select int16100000000000000n; # NumericOutOfRangeError: std::int16 out of rangetypeof静态类型推断操作符typeof的签名是typeof anytype - type它把表达式转换为类型转换结果可以用于is、is not和introspect。需要特别注意的是目前typeof只支持标量类型和对象类型作为操作数不支持集合类型如array、tuple。考虑下面这种属性/链接名字无法体现目标类型的场景type Foo { bar: int16; baz: Bar; } type Bar extending Foo;用typeof可以判断某个表达式是否与属性bar具有相同类型。注意Foo.bar / 2的结果类型insert Foo { bar : 1 }; # {Object { id: uuid... }} select (Foo.bar / 2) is typeof Foo.bar; # {false} # int16 除以 2 的结果是 float64不再是 int16要确定表达式真实的推导类型可以配合introspectselect introspect (typeof Foo.bar).name; # {std::int16} select introspect (typeof (Foo.bar / 2)).name; # {std::float64}typeof还可以用来区分哪些 Foo 对象可以作为链接 baz 的目标。先插入一个Bar对象Bar继承Foo因此Bar对象也能出现在Foo集合中insert Bar { bar : 2 }; # {Object { id: uuid... }} select Foo { bar, can_be_baz : Foo is typeof Foo.baz };输出{ Object { bar: 1, can_be_baz: false }, Object { bar: 2, can_be_baz: true } }因为Foo.baz的静态类型是Bar而Foo.bar 1的那个对象是纯Foo实例、不是Bar所以can_be_baz为falsebar 2的对象实际类型是Bar因此为true。typeof的另一个实用场景是动态构造类型表达式例如select typeof 5100;等价于select int64100;。introspect静态类型内省操作符introspect的签名是introspect type - schema::Type它返回操作数所对应类型的内省对象introspection type与typeof配合效果很好。与typeof相同目前它只支持标量类型和对象类型不支持集合类型。select (introspect int16).name; # {std::int16} select (introspect Foo).name; # {default::Foo} select (introspect typeof Foo.bar).name; # {std::int16}一个值得注意的等价关系对任何对象类型SomeType表达式introspect SomeType与introspect typeof SomeType是等价的——因为对象类型名与表示该对象集合的表达式在语法上完全相同。从实现层面看Gel 的整个类型系统本身就是存在数据库里的所有类型都可以作为schema::Type的实例被内省查询introspect与typeof正是获得表达式内省类型的入口见 docs/reference/datamodel/introspection/index.rst。内省对象同样是继承自BaseObject的对象类型例如可以用如下查询列出所有内省类型select name : schema::ObjectType.name filter name like schema::%;introspect返回的schema::Type对象可以像普通对象一样展开查询其属性。对对象类型User而言可以查询其abstract、bases、ancestors、links、properties、constraints、indexes等对标量类型str则可以查询default、enum_values、abstract与祖先链如std::anyscalar对枚举类型如scalar type Color extending enumRed, Green, Blueenum_values会返回[Red, Green, Blue]。完整的对象类型与标量类型内省示例分别见 docs/reference/datamodel/introspection/objects.rst 与 docs/reference/datamodel/introspection/scalars.rst。组合实践用类型操作符构建多态查询综合以上操作符一个典型的组合用法是静态过滤 动态类型输出。例如在混合类型的集合上先判断再取不同类型的分支属性联合类型 [is X]交叉 __type__select User { name, type_name : .__type__.name, stuff: { [is Named].name, [is Text].body } };另一个高频组合是introspect (typeof expr).name它可以把任意表达式的推导类型以字符串形式输出非常适合调试类型推断、排查int16除法产生float64这类类型悄悄变宽的问题select introspect (typeof (Foo.bar / 2)).name; # {std::float64}小结需求推荐操作符关键限制判断值是否属于某类型is/is not右操作数为类型表达式不参与集合规则一次匹配多个类型type \| type联合 is仅用于类型检查上下文与链接目标声明转换标量/容器的值type expr非抽象标量或容器禁止对象间转换为表达式声明类型type{}cast 空集对象类型仅允许转换空集获取表达式的静态类型typeof expr不支持集合类型操作数获取类型的 schema 内省对象introspect Type不支持集合类型操作数获取对象的实际类型.__type__链接返回运行时实际类型集合含子类型掌握这组类型操作符你就能够在 Gel 中游刃有余地处理类型判断、多态查询、类型转换与 schema 内省。建议进一步阅读 docs/reference/edgeql/types.rst 了解类型表达式与类型交叉语法阅读 docs/reference/reference/edgeql/casts.rst 查询完整的 cast 转换表并通过 docs/reference/datamodel/introspection/index.rst 深入掌握内省查询能力。赞分享数据库图数据库关系型数据库【免费下载链接】edgedbGel supercharges Postgres with a modern data model, graph queries, Auth AI solutions, and much more.项目地址https://gitcode.com/gh_mirrors/ed/edgedb点击查看免费下载相关推荐EdgeQL 字面量Literals权威指南Gel/EdgeDB 中全部原始类型字面量的声明、类型推断与操作EdgeQL 字面量Literals权威指南Gel/EdgeDB 中全部原始类型字面量的声明、类型推断与操作 本文是 Gel原 EdgeDBEdgeQ数据库图数据库关系型数据库EdgeDBGel类型转换 Cast 自省完全指南掌握 schema::Cast、隐式转换与赋值转换EdgeDBGel类型转换 Cast 自省完全指南掌握 schema::Cast、隐式转换与赋值转换 本篇技术指南聚焦 EdgeDBGel数据模型中的数据库图数据库关系型数据库GeledgedbTypeScript Query Builder 完全指南用代码优先方式编写全类型安全 EdgeQL 查询GeledgedbTypeScript Query Builder 完全指南用代码优先方式编写全类型安全 EdgeQL 查询 Query Builder数据库图数据库关系型数据库创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →