EdgeDB(Gel)DELETE 语句实战指南:从 Cheatsheet 出发掌握数据删除的两种范式
【免费下载链接】edgedbGel supercharges Postgres with a modern data model, graph queries, Auth & AI solutions, and much more.项目地址: https://gitcode.com/gh_mirrors/ed/edgedb
本文基于 EdgeDB 官方 Cheatsheet 中的 "Deleting data" 一页展开,系统讲解在 EdgeDB(Gel)中删除对象的两种核心写法:直接对类型集做filter删除,以及通过反向链接(backlink)定位并删除。读完本文,你将掌握delete语句的完整语法、filter/order by/offset/limit子句的语义、删除返回值的使用方式,以及链接删除策略(link deletion policy)与级联删除的底层机制,并能直接把这些模式应用到自己的 schema 中。
从 Cheatsheet 说起:两种删除范式
原 Cheatsheet(docs/resources/cheatsheets/delete.rst)以一组电影评论(User、Movie、Review)schema 为背景,给出了两种删除某个用户所有评论的写法。其中用到的对象类型定义在 docs/resources/cheatsheets/objects.rst,要点如下:
type User extending HasImage { required name: str { constraint exclusive; } } type Review { required body: str; required rating: int64 { constraint min_value(0); constraint max_value(5); } required flag: bool { default := False; } required author: User; required movie: Movie; required creation_time: datetime { default := datetime_current(); } }注意Review.author是required author: User,即每条评论都有一条指向作者的多对一链接;反之,从User出发可以通过反向链接.<author[is Review]找到该用户写过的全部评论。
范式一:直接按链接路径过滤删除
第一种写法直接指定要删除的类型,并通过链接路径过滤:
delete Review filter .author.name = 'trouble2020'要点在于filter子句里的路径表达式.author.name:EdgeQL 会自动沿Review -> author -> User.name解析,筛出所有作者名为trouble2020的Review对象并删除。这符合直觉,也是日常最常用的方式。
范式二:通过反向链接(backlink)删除
Cheatsheet 给出的"另一种方式"利用了反向链接遍历:
delete ( select User filter .name = 'troll2020' ).<author[is Review]逐步拆解:
select User filter .name = 'troll2020':先找出目标用户;.<author[is Review]:对该用户沿author链接的反方向做一次类型过滤遍历,得到"指向该用户的所有Review";- 整个括号表达式是一个对象集合,
delete直接作用于这个集合。
这里的[is Review]是类型过滤(type filter),保证反向遍历只保留Review类型,避免把其它也指向该User的对象误纳入删除范围。这种写法在"从一个对象出发删除其所有关联对象"的场景中非常自然,也常与update语句中的-=一起用于先解除链接再删除的模式(见下文链接删除策略)。
DELETE 语句的完整语法与语义
Cheatsheet 只是速查,完整的语句形式定义在 docs/reference/reference/edgeql/delete.rst:
[ with <with-item> [, ...] ] delete <expr> [ filter <filter-expr> ] [ order by <order-expr> [direction] [then ...] ] [ offset <offset-expr> ] [ limit <limit-expr> ] ;几个关键语义点:
with子句:既可以声明模块别名,也可以声明表达式别名,供delete语句内部引用(详见 docs/reference/edgeql/with.rst)。- 整个
delete <expr> ...语句是delete (select ...)的语法糖:<expr>、filter、order by、offset、limit共同决定"要删除的集合",其成型方式与显式select完全一致。这解释了为什么 Cheatsheet 范式二能直接delete (select User ...).<author[is Review]——括号里是一个合法的集合表达式。 - 语句执行成功后,返回被删除对象的集合。
在 docs/reference/edgeql/delete.rst 中可以看到带全部子句的完整示例:
delete Hero filter .name ilike 'the %' order by .name offset 10 limit 5;该示例先按名字模糊过滤,再排序、跳过前 10 条、最多删除 5 条——相当于一次"分批清理"。
删除与 select 的等价关系(源码佐证)
delete即delete (select ...)的语法糖这一点,在仓库测试 tests/test_edgeql_delete.py 的test_edgeql_delete_sugar_01(第 378 行起)中有直接验证:
DELETE DeleteTest FILTER .name[-1] != '2' ORDER BY .name OFFSET 2 LIMIT 2; # should delete 4 and 5测试先插入 6 条名为'sugar delete 1'到'sugar delete 6'的记录,然后执行带filter/order by/offset/limit的删除,最后断言只剩'sugar delete 1'、'sugar delete 2'、'sugar delete 3'、'sugar delete 6'——即OFFSET 2 LIMIT 2精准删掉了排序后的第 4、5 条。这说明排序与分页子句确实作用于"被删除集合"的成型过程,与select行为一致。
同文件中还有test_edgeql_delete_returning_01到_05、test_edgeql_delete_union、test_edgeql_delete_multi_simultaneous_01等用例,覆盖了删除返回值、并集删除、多对象同时删除等场景,是深入学习delete行为的第一手材料。
删除返回值:数据永久消失前的最后一刻
delete返回被删除对象的集合,你可以把它继续交给select,在数据被永久删除前取出其属性与链接:
with movie := (delete Movie filter .title = "Untitled") select movie {id, title};正如 docs/reference/edgeql/delete.rst 中所强调:这是该数据在永久删除前最后一次可用的时机。常见的实用模式包括:删除前把对象快照归档到审计表、统计本次删除了多少条、或把被删对象的 id 记录下来用于后续补偿逻辑。测试文件中的test_edgeql_delete_returning_*系列用例(如test_edgeql_delete_returning_01,tests/test_edgeql_delete.py 第 179 行)专门验证了这种"删除后立即 select"的返回行为。
链接删除策略:为什么删除会被拒绝
默认情况下,你不能删除一个仍被其它对象链接引用的对象。例如:
db> delete Hero filter .name = "Yelena Belova"; ConstraintViolationError: deletion of default::Hero (af7076e0-3e98-11ec-abb3-b3435bbe7c7e) is prohibited by link target policy {}这个报错发生的原因是 Yelena 仍出现在《黑寡妇》电影的characters链接列表中。必须先解除这条链接,再执行删除:
update Movie filter .title = "Black Widow" set { characters -= (select Hero filter .name = "Yelena Belova") }; delete Hero filter .name = "Yelena Belova";这与 Cheatsheet 范式二"先取出反向链接集合再删除"的思路互为补充:范式二是直接对反向链接集合执行删除,而这里先用-=把链接从源对象上移除,再删除目标对象。
如果业务上允许"删目标时自动断开链接",可以在 schema 中把链接的删除策略改为on target delete allow:
type Movie { required title: str { constraint exclusive }; required release_year: int64; multi characters: Person { on target delete allow; }; }删除策略的完整清单见 docs/reference/datamodel/links.rst(ref_datamodel_link_deletion一节)。
级联删除:delete source 与 delete target
链接的删除策略不仅限于"禁止/放行":
- 默认策略:禁止删除仍被引用的目标对象(上述报错即源于此);
on target delete allow:允许删除目标对象,链接随之消失;on target delete delete source:删除目标对象时,连带删除链接的源对象——这就是级联删除(cascading delete)的实现方式,例如删除作者时同时删掉他写的所有评论;on target delete restrict:更加严格的限制。
对应的还有on source delete相关策略。正如 docs/reference/edgeql/delete.rst 中的提醒:级联删除能力强大,使用时要格外谨慎,一个delete可能波及一整条对象链。建议在投入生产前,先在测试环境用delete的返回集合确认实际波及范围。
总结与推荐阅读
回到 Cheatsheet 的两行速查:
-- 范式一:按链接路径过滤 delete Review filter .author.name = 'trouble2020' -- 范式二:反向链接 + 类型过滤 delete ( select User filter .name = 'troll2020' ).<author[is Review]两者结果等价,但表达路径不同:范式一从"被删对象"出发正向过滤,范式二从"根对象"出发反向遍历。实际项目中,前者适合"按条件批量清理某类对象",后者适合"删除某对象的全部关联"。
如果希望进一步深入,推荐按以下顺序阅读仓库内材料:
- docs/resources/cheatsheets/objects.rst:本文示例所用的
User/Review/Movie完整 schema 定义; - docs/reference/edgeql/delete.rst:
delete的全面行为说明(链接删除策略、级联、返回值); - docs/reference/reference/edgeql/delete.rst:语句的正式语法与
delete (select ...)等价关系; - tests/test_edgeql_delete.py:覆盖语法糖、返回值、并集删除、多对象同时删除等场景的测试用例,是验证本文所有结论的最直接依据。
【免费下载链接】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),仅供参考