ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Gel/EdgeDB 链接属性(Link Properties)完全指南:声明、约束、索引与增删改查实战

Gel/EdgeDB 链接属性(Link Properties)完全指南:声明、约束、索引与增删改查实战 Gel/EdgeDB 链接属性Link Properties完全指南声明、约束、索引与增删改查实战【免费下载链接】edgedbGel supercharges Postgres with a modern data model, graph queries, Auth AI solutions, and much more.项目地址: https://gitcode.com/gh_mirrors/ed/edgedb本文基于 Gel/EdgeDB 官方文档《Link properties》整理扩充系统讲解如何在multi链接上声明携带元数据的链接属性Link Properties即property覆盖 SDL 声明语法、前缀的读写查询、表达式约束、抽象链接索引、for循环联合查询限制与计算反向链接等全部核心知识点并结合仓库源码与测试用例说明底层实现原理。读完本文你将能独立设计、插入、更新、查询带元数据的多对多关系模型。什么是链接属性在 Gel/EdgeDB 中链接Link定义了两个对象类型之间的关系而链接属性Link Properties则像对象属性一样挂在链接本身之上用来存储关于这条链接的元数据。例如两个用户之间的友情强度、好友的昵称、一张卡牌在某副牌组中的数量、一条观影记录的时间戳等。type Person { name: str; multi family_members: Person { relationship: str; # 链接属性描述这条亲属链接的关系 } }这里relationship并不是Person对象的属性而是Person与Person之间那条family_members链接的属性。在查询语法中链接属性统一使用前缀访问例如relationship、strength、count。链接属性在底层通过链接表关联表持久化因此有一个额外的硬性约束链接属性永远是single的见 linkprops.rst。换言之你不能把某个链接属性声明为multi——一条链接只能携带一个标量值。高级特性提醒由于读写链接属性需要用到非平凡的语法官方将其定位为进阶功能。大多数场景下应优先使用普通对象属性正如原文档引用的那句话链接属性就像降落伞——你不常需要它但当你真的需要时它可能是决定性的。链接属性 vs 对象属性一个形象的比喻理解二者区别的最佳方式是普通属性用于构造一个对象而链接属性用于构造对象之间的链接。# 用 name / email 构造一个 Person 对象 insert Person { name : Jane, email : janejane.com } # 用 strength 构造 Bob 与 Jane 之间的朋友链接 insert Person { name : Bob, email : bobbob.com, friends : ( insert Person { name : Jane, email : janejane.com, strength : 3.14 } ) }第二段查询中strength : 3.14并不是 Jane 的属性而是用来量化 Bob → Jane 这条friends链接的强度。适用场景多对多关系优先官方文档明确建议链接属性最适合用于多对多many-to-many关系——即没有排他约束的multi链接。此时链接本身就是一个独立概念天然需要自己的数据。对于一对一、一对多、多对一关系同样的数据应该直接存成对象属性而不是链接属性。更完整的背景可参见 链接文档 中Link properties一节其中补充说明了链接属性只能是原始数据标量、枚举、数组或元组不能是指向其他对象的链接。由于链接天然是去重的同一对对象之间只能存在一条链接如果需要记录同一部电影看了多次这类场景可以把时间戳存成arraydatetime或引入一个专门的对象类型如WatchHistory来建模详见 links.rst。自Gel 7.0 起versionchanged链接属性现在可以被声明为required必填。声明链接属性声明语法与对象属性几乎一致在链接的{}块内直接写属性声明即可。type Person { required name: str { constraint exclusive }; multi friends: Person { strength: float64; # 链接属性这段友谊的强度 } }从源码角度看链接在 schema 层是Link类它同时继承Source与Pointer见 edb/schema/links.py也就是说链接本身也是一个源source可以携带指向自身的指针即链接属性。链接属性在编译期被统一建模为Property类型见 edb/schema/properties.py并通过std::source/std::target这两个特殊指针建立链接属性 → 链接的归属关系见 properties.py。声明 required 链接属性Gel 7.0abstract link friendship { required strength: float64; # 7.0 起允许 required }为链接属性添加约束链接属性可以像对象属性一样声明约束。下面的例子保证strength永远非负使用了链接属性在约束表达式中的特殊写法__subject__strength——__subject__在这里指代这条链接本身type Person { required name: str { constraint exclusive }; multi friends: Person { strength: float64; constraint expression on ( __subject__strength 0 ); } }如果违反约束例如插入strength : -1Gel 会抛出约束冲突异常拒绝该写入。为链接属性创建索引链接属性不能直接在链接上单独索引官方给出的做法是把带属性的链接抽取为一个abstract link在抽象链接上声明索引然后让具体链接extending它。abstract link friendship { required strength: float64; index on (__subject__strength); } type Person { required name: str { constraint exclusive }; multi friends: Person { extending friendship; }; }这样strength的索引就通过继承机制被Person.friends复用。抽象链接的index声明只能引用链接属性参见 links.rst 中链接语法部分对index-declarations的说明。同样的模式在 links.rst 的Abstract links一节也有镜像示例abstract link link_with_strengthindex on (__subject__strength)。插入带链接属性的数据插入链接属性时有多种写法按场景选择方式一链接到数据库中已存在的对象select detached形状如果目标对象已经在库中需要把strength写进select子查询的**形状shape**里并用detached运算符把Person引用从insert查询的作用域中解绑insert Person { name : Bob, friends : ( select detached Person { strength : 3.14 } filter .name Alice ) }detached是必须的它确保子查询中的Person不再被外层insert Person的隐式作用域遮蔽。方式二嵌套插入做嵌套插入时链接属性可以直接写在内层insert的形状里insert Person { name : Bob, friends : ( insert Person { name : Jane, strength : 3.14 } ) }方式三with捕获表达式先用with捕获一个返回对象类型的表达式这里配合unless conflict ... else实现了存在即复用的 upsert 语义再在链接时附上属性with alice : ( insert Person { name : Alice } unless conflict on .name else ( select Person filter .name Alice limit 1 ) ) insert Person { name : Bob, friends : alice { strength : 3.14 } };更新链接属性更新已存在链接的属性使用update ... set并在链接目标的形状里重写strengthupdate Person filter .name Bob set { friends ( select .friends { strength : 3.7 } filter .name Alice ) };这段查询把 Bob → Alice 这条friends链接的strength更新为 3.7。要点说明在multi链接的上下文中表现得像insert/update 运算符若链接尚不存在则创建若已存在则更新其属性。上面的写法通过.friends选中当前已链接的对象并就地更新如果想要插入/更新一个更大的对象集合则应改用detached选择例如select detached Person ...。查询链接属性在select的**形状shape**内使用属性名语法即可读取链接属性。注意strength不是对象上的属性而是链接friends上的属性gel select Person { .... name, .... friends: { .... name, .... strength .... } .... }; { default::Person {name: Alice, friends: {}}, default::Person { name: Bob, friends: { default::Person {name: Alice, strength: 3.7} } }, }此外链接属性还可以出现在filter、order by等表达式中。仓库测试 tests/test_edgeql_linkprops.py 提供了大量实战样例例如按属性排序与过滤SELECT User { name, deck: { name, element, cost, count } ORDER BY count DESC THEN .name ASC } ORDER BY .name;以及通过反向链接路径读取属性User.avatartext、.deck[IS User]count对应测试见 test_edgeql_linkprops.py其使用的 schema 见 tests/schemas/cards.esdlUser.deck带count、User.friends带nickname、User.avatar带text。集合联合Union中的限制与 for 循环解法链接属性不能出现在集合联合set union中唯一的例外是for循环。以下查询会报错QueryError: invalid reference to link property in top level shape# 不可行 insert Movie { title : The Incredible Hulk, actors : {( select Person { character_name : The Hulk } filter .name Mark Ruffalo ), ( select Person { character_name : Iron Man } filter .name Robert Downey Jr. )} };正确做法是改用for ... union逐条绑定属性并用assert_distinct向编译器保证结果集去重# ✅ 可行 insert Movie { title : The Incredible Hulk, actors : assert_distinct(( with characters : { (The Hulk, Mark Ruffalo), (Iron Man, Robert Downey Jr.) } for character in characters union ( select Person { character_name : character.0 } filter .name character.1 ) )) };assert_distinct在这里是必须的——编译器需要它来确认actors集合没有重复元素从而允许该集合作为multi链接的值。与计算反向链接Computed Backlinks搭配使用链接属性也可以和计算反向链接配合即使followed是follows链接的属性你依然可以通过反向计算链接followers在形状中读取它。type Person { required name: str; multi follows: Person { followed: datetime { default : datetime_of_statement(); }; }; multi link followers : .follows[is Person]; }下面这个查询可以正常工作尽管followed属于follows而这里经由followers访问select Person { name, followers: { name, followed } };这印证了链接属性是链接上的数据只要最终落在同一条物理链接上无论从正向follows还是反向followers投影属性都可见。类似的交叉读取如Card.deck[IS User]count从被链接对象反向读取链接属性在 test_edgeql_linkprops.py 中有完整测试覆盖。底层实现从源码看链接属性的本质从 schema 层源码可以确认链接属性的设计本质edb/schema/links.py 中Link类同时继承s_sources.Source与pointers.Pointer——链接既是指针指向目标对象又是源能承载自身属性这正是链接属性在类型系统层面的根基。Link.is_link_property()返回Falselinks.py说明链接本身不是链接属性对应的Property.is_link_property()见 edb/schema/properties.py在指针继承体系中被用于区分对象属性与链接属性。Property.derive_refproperties.py在处理派生属性时会对std::source/std::target这两个特殊指针做重定向std::source指向链接的源对象类型std::target指向链接的目标类型。这解释了为什么约束表达式里能写__subject__strength也解释了链接属性必须single、只能存原始数据的约束来源。官方文档links.rst指出底层存储上multi链接保存在中间关联表中链接属性即该关联表的列single链接则存为对象类型上的列。这也呼应了链接属性用于多对多关系的最佳实践。小结操作核心语法要点声明在链接{}内写prop: type;Gel 7.0 可加required约束constraint expression on (__subject__prop ...)索引抽取abstract linkindex on (__subject__prop)再extending插入select detached形状 / 嵌套insert/with捕获后附加{ prop : ... }更新update ... set { link (select .link { prop : ... }) }查询形状内写prop可在filter/order by中引用联合限制不能在 set union 中引用改用for ... unionassert_distinct反向链接计算 backlink 的形状中同样可读取prop链接属性是 Gel 建模多对多关系的利器当关系本身携带数据强度、数量、时间、备注时用property把元数据钉在链接上比引入中间对象更简洁而一对一/一对多场景则应遵循官方建议回归普通对象属性。想进一步了解链接的基数、排他约束、删除策略与抽象链接可阅读 链接参考文档链接属性在 DDL 中的完整语法create property、create constraint等参见 links.rst 的 DDL commands 部分属性的 SDL/DDL 全量语法则见 属性参考文档。【免费下载链接】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),仅供参考
返回列表