ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

PostGraphile 计算列(Computed Columns)实战指南:用 PostgreSQL 函数为 GraphQL 类型扩展字段

PostGraphile 计算列(Computed Columns)实战指南:用 PostgreSQL 函数为 GraphQL 类型扩展字段 后端API网关【免费下载链接】crystal Graphiles Crystal Monorepo; home to Grafast, PostGraphile, pg-introspection, pg-sql2 and much more!项目地址https://gitcode.com/gh_mirrors/cry/crystal点击查看免费下载计算列Computed Columns是 PostGraphile 的一项核心能力你只需要在 PostgreSQL 中定义符合约定的函数PostGraphile 就会自动把它暴露为对应表类型上的 GraphQL 字段——客户端看起来就像在查询一个普通列而实际值是函数计算的结果。本篇以官方快速参考示例为骨架结合仓库源码与测试证据带你掌握计算列的命名约定、参数设计、返回类型标量/集合/连接以及性能与调试要点读完即可在自己的 schema 中直接落地。一、什么是计算列计算列会为 GraphQL 表类型“附加”一个看似额外的字段。与真实列不同这个字段的值来自 PostgreSQL schema 中定义的一个函数。PostGraphile 会自动将这个函数暴露为类型上的字段它可以接收参数来影响计算结果并且可以返回标量、复合类型record、列表或集合。对于RETURNS SETOF ...的集合返回PostGraphile 会根据行为配置将其暴露为连接connection或列表。在 v4 快速参考文档 中官方用 examples 仓库的 schematopic表 topics_body_summary函数演示了完整效果v5 完整文档 则给出了完整的识别规则与实现细节。两者结合即可获得从入门到原理的完整认知。二、被识别为计算列的函数需要满足的规则要让 PostGraphile 把某个函数当作计算列处理函数必须同时满足以下条件来源computed-columns.md规则说明满足通用函数限制详见 function-restrictions.md不支持VARIADIC函数、不支持重载函数、不支持无类型信息的record返回命名以表名开头函数名必须以它所属的表名开头后跟下划线_首个参数为表类型第一个参数必须是该表对应的复合类型不能返回VOIDRETURNS VOID的函数不会被识别必须是STABLE或IMMUTABLE未声明时为VOLATILE默认会被视为变更操作而非查询与表同 schema函数必须定义在表所在的同一个 PostgreSQL schema 中例如假设存在person表下面的函数create function person_full_name(person person) returns text as $$ select person.given_name || || person.family_name $$ language sql stable;就会为person类型创建计算列可像普通字段一样查询{ personById(id: …) { # nodeId, id, ... fullName # 计算列但客户端完全无感知 } }三、快速参考示例bodySummary 字段v4 快速参考文档给出的第一个示例是在topic(id: 2)上同时查询真实列body与计算列bodySummary{ topic(id: 2) { body bodySummary } }对应的 SQL 函数定义如下注意函数名topics_body_summary以表名topics开头首参数为表类型t app_public.topics声明为language sql stable第二个参数max_length带默认值 30create function app_public.topics_body_summary( t app_public.topics, max_length int 30 ) returns text language sql stable as $$ select case when length(t.body) max_length then left(t.body, max_length - 3) || ... else t.body end; $$;查询结果为{ topic: { body: PostGraphile is a powerful, idomatic, and elegant tool., bodySummary: PostGraphile is a powerful,... } }值得说明的是示例中的字段名bodySummary而非默认的bodySummaryByTopic是使用了graphile-contrib/pg-simplify-inflector插件简化命名后的结果这一点在快速参考文档开头有明确提示——不同命名策略下字段名会不同但计算列机制本身不变。四、带参数的计算列动态控制函数行为函数除首个表类型参数外声明的其他参数都会暴露为 GraphQL 字段参数。上面的函数声明了max_length int 30因此可以在查询中传参覆盖默认值{ topic(id: 2) { body bodySummary(maxLength: 20) } }结果中摘要被截断到 20 个字符左右{ topic: { body: PostGraphile is a powerful, idomatic, and elegant tool., bodySummary: PostGraphile is a... } }这种“额外参数自动暴露为 GraphQL 参数”的能力让同一个计算列可以承担不同粒度的计算。更完整的参数化示例见 v5 文档-- 创建 User.greet(greeting: String) 字符串字段 create function my_schema.users_greet( u my_schema.users, --- 必填的表类型参数不暴露 greeting text --- 额外参数会暴露为 GraphQL 参数 ) returns text as $$ select greeting || , || u.first_name || || u.last_name || !; $$ language sql stable strict;对应查询{ userById(id: …) { greet(greeting: Greetings and salutations) } }注意strict声明SQL 函数在参数为 NULL 时返回 NULL。仓库测试 procedure-computed-fields.test.graphql 中还对“默认参数、可选参数、按名传参”的组合行为做了系统验证如headlineTrimmed(length: 15)、headlineTrimmed(length: 20, omission: [...])其生成的 SQL见 procedure-computed-fields.sql展示了带默认值与不带默认值的参数如何被分别编译为位置参数或命名参数c : $2::int4。五、标量、集合与连接一个完整的 users/friends 示例v5 文档给出了一个同时覆盖“标量字段”与“连接字段”的完整示例。先建两张表create table my_schema.users ( id serial not null primary key, first_name varchar not null, last_name varchar not null ); create table my_schema.friendships ( user_id integer not null, target_id integer not null, primary key (user_id, target_id) );然后创建两个计算列函数-- 创建 User.name 字符串字段标量 create function my_schema.users_name(u my_schema.users) returns varchar as $$ select u.first_name || || u.last_name; $$ language sql stable; -- 创建 User.friends 连接集合 create function my_schema.users_friends(u my_schema.users) returns setof my_schema.users as $$ select users.* from my_schema.users inner join my_schema.friendships on (friendships.target_id users.id) where friendships.user_id u.id; $$ language sql stable;其中||是 PostgreSQL 的字符串拼接运算符returns setof my_schema.users使得users_friends被暴露为User.friends连接而不是返回多行导致父查询行数膨胀。集合返回的底层处理v5 文档特别提示如果函数返回集合如person_favorite_posts(person)PostGraphile 会自动把该选择包装成子查询聚合防止父查询产生多余行。手工等价的 SQL 大致是select person.id, array( select posts.* from person_favorite_posts(person) posts ) as favorite_posts from person where id $1;这一行为在仓库测试中有直接证据simple-procedure-computed-fields.sql 中person_friends返回 setof被编译为array(select ... from c.person_friends(__person__) ...)的子查询聚合procedure-computed-fields.sql 则进一步覆盖了computedTextArray、computedIntervalArray、computedCompoundTypeArray数组元素为复合类型以及computedIntervalSetSETOF 暴露为带 cursor 的连接等复杂返回类型对应的预期结果见 procedure-computed-fields.json5。六、为什么叫“计算列”PostgreSQL 的字段选择语法PostgreSQL 官方文档指出col(table)与table.col两种写法可以互换——函数接受复合类型单参数时既可以用字段选择语法调用也可以用函数式风格书写。PostGraphile 正是利用了这一特性来模拟“计算字段”。所以在 PostGraphile 之外你也可以手工执行等价的 SQLselect person.id, person.person_full_name as full_name -- 或等价写法 -- person_full_name(person) as full_name from person where id $1;person.person_full_name让函数看起来就像person表上的一个列尽管该列并不存在——“computed column function” 这个命名即来源于此。七、性能说明函数调用被内联进原始 SELECTPostGraphile 会把计算列的函数调用内联到原始的SELECT语句中详见 computed-columns.md 中生成的 SQLa.post_headline_trimmed(__post__, $1::int4) as 2直接作为 SELECT 列表项出现因此不需要额外向数据库发起查询。但需要注意每个函数调用本身仍有一定开销在数千行上执行时开销会累积PostgreSQL 有时可以内联 SQL 函数以获得更好的性能但内联失败时性能可能下降如果遇到性能问题可以考虑改用extendSchema方案见 extend-schema.md 及 functions.md 中“Example computed column function”一节的说明。从测试证据看计算列还可用作排序依据orderby-computed测试orderby-computed.test.graphql 使用orderBy: [COMPUTED_ASC, ROW_ID_ASC]生成的 SQL 中排序键直接是order by c.edge_case_computed(__edge_case__) asc, __edge_case__.row_id asc见 orderby-computed.sql同样无需额外的查询往返。八、调试清单计算列缺失或行为异常时如果计算列没有出现或行为不符合预期请按 v5 调试清单 逐项排查命名函数名以表名 下划线开头例如person表对应person_full_name首参数第一个参数必须是表类型例如person_full_name(p person)稳定性函数标记为stable或immutablevolatile是默认值会被当作变更操作同 schema函数与表定义在同一个 schemacreate function same_schema_here.person_full_name(p same_schema_here.person)SQL 自检在纯 SQL 中用table.function或function(table)语法验证函数本身可正确执行select person_full_name(person) from person。九、延伸阅读计算列完整规范与规则computed-columns.md通用函数限制VARIADIC、重载、record 等function-restrictions.md连接connection形态说明connections.md用extendSchema替代计算列以优化性能extend-schema.md函数性能深入讨论functions.md自定义查询与建议含计算列设计建议custom-queries.md测试证据查询与 SQL 快照见tests/queries/v4 目录下的procedure-computed-fields.*、orderby-computed.*系列文件赞分享后端API网关【免费下载链接】crystal Graphiles Crystal Monorepo; home to Grafast, PostGraphile, pg-introspection, pg-sql2 and much more!项目地址https://gitcode.com/gh_mirrors/cry/crystal点击查看免费下载相关推荐PostGraphile 计算列Computed Columns实战指南用 PostgreSQL 函数为 GraphQL 类型动态扩展字段PostGraphile 计算列Computed Columns实战指南用 PostgreSQL 函数为 GraphQL 类型动态扩展字段 PostGra后端API网关PostGraphile 计算列Computed Columns完整指南用 PostgreSQL 函数为 GraphQL 类型动态添加字段PostGraphile 计算列Computed Columns完整指南用 PostgreSQL 函数为 GraphQL 类型动态添加字段 导读 计算列后端API网关PostGraphile 计算列Computed Columns完全指南用 PostgreSQL 函数为 GraphQL 表类型添加动态字段PostGraphile 计算列Computed Columns完全指南用 PostgreSQL 函数为 GraphQL 表类型添加动态字段 导读 计算列后端API网关创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进