类型完全指南:构造、访问、类型语法与标准库实践)
Gel/EdgeDB 元组Tuple类型完全指南构造、访问、类型语法与标准库实践【免费下载链接】edgedbGel supercharges Postgres with a modern data model, graph queries, Auth AI solutions, and much more.项目地址: https://gitcode.com/gh_mirrors/ed/edgedb元组tuple是 Gel前身 EdgeDB核心类型系统中一种异构的有序序列类型与数组不同元组的每个元素可以拥有各自独立的类型并且既可以是匿名的也可以为元素命名形成命名元组。本文以标准库参考文档docs/reference/stdlib/tuple.rst为主体结合查询语言类型文档docs/reference/edgeql/types.rst、数据模型文档docs/reference/datamodel/primitives.rst及标准库/测试源码系统讲解元组的构造语法、元素访问、类型声明、比较语义与底层实现。读完后你将能在 EdgeQL 查询与 SDL schema 中熟练使用匿名/命名元组并理解其与数组、JSON、客户端数据结构的差异。元组Tuple是什么在 Gel 中元组类型是一种异构的其它类型序列一个元组可以同时容纳str、int64、bool、数组、其它元组甚至对象类型元素之间类型互不相同这与元素类型必须一致的数组arrayx形成鲜明对比。元组分为两种形态匿名元组unnamed tuple默认形态元素没有名字通过零基下标访问。命名元组named tuple每个元素都带有一个标识符name通过名字访问。官方参考文档对std::tuple的定义docs/reference/stdlib/tuple.rst明确指出任意类型都可以作为元组元素类型元组类型会在使用元组构造表达式时自动隐式创建。从实现角度Gel 在标准库中为元组定义了抽象的anytuple伪类型edb/lib/std/10-scalars.edgeql 第 22 行CREATE PSEUDO TYPE anytuple;所有具体元组类型都是它的实例标准库中的比较运算符与 JSON 转换也正是基于anytuple派生的。构造元组元组构造表达式由一对圆括号包裹、逗号分隔的表达式序列组成语法如下( expr [, ... ] )例如db select (foo, 42); {(foo, 42)}执行select (foo, 42)时元组构造表达式会自动生成一个对应的tuplestr, int64类型。命名元组命名元组的语法在构造表达式中为每个元素指定名字与值的绑定( identifier : expr [, ... ] )关键约束命名元组中所有元素都必须有名字不允许混用部分命名db select (number : 1, name : Gel); {(number : 1, name : Gel)}从编译器的 AST 层面看Gel 将元组构造分为两类节点edb/edgeql/ast.py 第 453–465 行匿名元组对应Tuple节点elements: list[Expr]命名元组对应NamedTuple节点其元素由TupleElement含name: Ptr与val: Expr组成——名字被单独建模为一个带源码位置引用的指针节点这正是命名元组类型校验能够精确报错包括重复字段名的实现基础。客户端返回值形态差异需要特别注意的是元组在客户端语言中的映射行为docs/reference/datamodel/primitives.rst 中的 Tuples 小节使用任一官方客户端库查询时匿名元组会被转换为列表/数组而命名元组会被转换为对象/字典/hashmap具体形态取决于语言。在设计查询返回结构时应据此选择匿名紧凑列表或命名键值映射形式。访问元组元素元组的元素通过点号.后跟元素索引或元素名字来引用expr.element-index其中expr是任意元组类型的表达式element-index既可以是零基下标匿名与命名元组均可用也可以是命名元组中的元素名字。官方示例db select (1, Gel).0; {1} db select (number : 1, name : Gel).name; {Gel} db select (number : 1, name : Gel).1; {Gel}可以看到命名元组既可以按名字.name访问也可以按下标.1访问两种方式等价。嵌套元组元组可以任意嵌套访问时逐层展开即可db select (nested_tuple : (1, 2)).nested_tuple.0; {1}上面的查询先通过.nested_tuple取出内层匿名元组(1, 2)再对其取.0得到1。访问不存在的元素引用不存在的元素下标越界或名字拼写错误会抛出错误。例如db select (1, 2).5; EdgeQLError: 5 is not a member of a tuple元组类型语法Type Syntax元组类型可以显式声明在表达式或 schema 定义中。匿名元组的类型语法为tuple element-type, [element-type, ...] 命名元组的类型语法为tuple element-name : element-type [, ... ] 任意类型都可以作为元组元素类型——包括标量、数组、其它元组甚至对象类型。EdgeQL 的类型表达式语法docs/reference/edgeql/types.rst中元组是少数拥有专属类型语法的类型之一与数组并列类型语法数组arrayx元组匿名tuplex, y, z元组命名tuplefoo: x, bar: y在 SDL schema 中使用元组类型元组类型在 schema 中可直接作为属性property类型。官方示例定义了一个带二维坐标的GameElement类型type GameElement { required name: str; required position: tuplex: int64, y: int64; }数据模型文档docs/reference/datamodel/primitives.rst进一步给出了更丰富的 schema 组合示例包括嵌套元组与数组中包含元组、元组中包含数组的交叉组合type Person { unnamed_tuple: tuplestr, bool, int64; nested_tuple: tupletuplestr, tuplebool, int64; tuple_of_arrays: tuplearraystr, arrayint64; } type BlogPost { metadata: tupletitle: str, published: bool, upvotes: int64; }需要注意要么全部元素都命名要么全部匿名不允许部分命名。此外命名元组与匿名元组在底层是同一数据结构schema 声明后仍可对元素名字进行增删改详见 docs/reference/datamodel/primitives.rst 的 Tuples 小节。在 EdgeQL 查询中使用元组类型类型语法可直接用于类型转换cast与类型检查db select tupleint64, str(1, 3); {(1, 3)} db select tuplex: int64, y: int64(1, 2); {(x : 1, y : 2)} db select (1, 3) is (tupleint64, str); {true} db select ([1, 2], a) is (tuplearrayint64, str); {true}前两条演示类型转换把一组值显式转换为指定元组类型转换过程中元素会按目标类型进行强制如1转为int64、3转为str。后两条演示类型检查is运算符判断表达式的类型是否与声明的元组类型一致数组元素、多元素混合均支持。关于类型转换的更一般规则可参考 docs/reference/edgeql/types.rst 的 Type casting 小节转换仅适用于原始primitive类型表达式对象类型之间不能随意互相转换。std::tuple 的语义相等与比较标准库对std::tuple的相等语义定义如下两个元组相等当且仅当它们的所有元素按相同顺序相等。注意命名元组的元素名字对比较结果没有影响。验证示例db select (1, 2, 3) (a : 1, b : 2, c : 3); {true}即(1, 2, 3)与(a : 1, b : 2, c : 3)比较为true元素值、顺序全部一致元素名被忽略。但反过来如果元素值或顺序不同则比较为false命名元组的名字虽然不参与比较却参与类型判定——命名元组类型不同名字集合/顺序不同时属于不同类型不能互相赋值见下文测试用例。从标准库实现看edb/lib/std/20-genericfuncs.edgeql 第 730–812 行Gel 为anytuple派生了一整套比较运算符全部以两个元组操作数返回std::bool相等与?可空版相等!不等与?!可空版不等、、、大小比较这些运算符可用于在FILTER、ORDER BY、条件表达式等场景中对整个元组进行比较排序。标准库中的元组应用场景元组在 Gel 标准库中广泛作为复合返回类型使用以下是几个有代表性的真实用例源码依据edb/lib/stdstd::enumerateedb/lib/std/20-genericfuncs.edgeql 第 449–460 行签名std::enumerate(vals: SET OF anytype) - SET OF tuplestd::int64, anytype返回形如(index, element)的元组集合为集合中的每个元素附加零基序号。它使用USING SQL EXPRESSION实现被标记为Immutable且保留可选性与上界基数。std::json_object_unpackedb/lib/std/30-jsonfuncs.edgeql 第 45–48 行返回SET OF tuplestd::str, std::json将 JSON 对象拆解为键值元组集合反向的std::json_object_pack则接收SET OF tuplestr, json重新组装 JSON 对象。std::json与元组的互转edb/lib/std/30-jsonfuncs.edgeql 第 300–306 行标准库声明了CREATE CAST FROM anytuple TO std::json与CREATE CAST FROM std::json TO anytuple两个转换使得元组与 JSON 之间可以互相转换。切片/区间运算符array、bytes、json的[]运算符如std::[]均接受tuplestd::int64, std::int64作为区间参数edb/lib/std/30-arrayfuncs.edgeql 第 351 行等这是元组作为复合参数的典型用法。源码与测试视角类型系统如何约束元组在源码层面元组构造与命名元组的区分体现在 AST 节点设计上edb/edgeql/ast.py 第 453–465 行匿名元组为Tuple元素是无名表达式列表命名元组为NamedTuple元素是TupleElement每个元素带独立的name: Ptr。这一结构使得编译期可以对命名元组做严格的字段名校验。测试用例 tests/test_edgeql_datatypes.py 第 1149–1181 行验证了三条关键规则可直接作为行为契约命名元组的字段顺序参与类型判定定义属性x - tuplea: int64, b: int64后用x : (b : 1, a : 2)字段顺序相反插入会报QueryError提示目标类型为tupleb: std::int64, a: std::int64而期望tuplea: std::int64, b: std::int64——命名元组类型由名字集合 顺序 元素类型共同决定。字段顺序不同的命名元组仍可 UNIONSELECT (b : 1, a : 2) UNION (a : 3, b : 4)正常返回两行结果[[1, 2], [3, 4]]说明 UNION 场景下编译器能统一两种字段排列。重复字段名是编译错误SELECT (a : 1, a : 2)以及 schema 中声明tuplea: int64, a: str都会抛出QueryError: named tuple has duplicate field a。这些行为与文档中所有元素必须命名名字参与类型、不参与值比较的描述完全一致是编写元组相关 schema 与查询时的第一手依据。常见错误与注意事项小结部分命名不允许命名元组的所有元素都必须有名字不能混用(a : 1, 2)这种写法。字段名重复是错误无论是查询表达式还是 schema 声明重复字段名都会在编译期报错。访问不存在的元素下标越界或名字不匹配会抛出EdgeQLError如5 is not a member of a tuple。命名元组类型对字段顺序敏感赋值/插入时元素名字顺序必须与目标类型一致否则报类型不匹配错误。比较时名字被忽略(1, 2, 3) (a : 1, b : 2, c : 3)为true判断相等只看元素值与顺序。客户端映射差异匿名元组返回为列表/数组命名元组返回为对象/字典/hashmap跨语言消费查询结果时需注意这一差异docs/reference/datamodel/primitives.rst。掌握上述语法与语义即可在 Gel 的 schema 建模如坐标、复合键、元数据字段、查询构造多值返回、键值展开以及与 JSON/客户端的互操作中充分运用元组这一灵活的数据结构。相关扩展阅读EdgeQL 类型表达式与类型转换、数据模型中的元组、标准库通用函数定义、元组行为测试。【免费下载链接】edgedbGel supercharges Postgres with a modern data model, graph queries, Auth AI solutions, and much more.项目地址: https://gitcode.com/gh_mirrors/ed/edgedb创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考