ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

PostGraphile 过滤(Filtering)完整指南:从 condition 基础过滤到高级自定义条件

PostGraphile 过滤(Filtering)完整指南:从 condition 基础过滤到高级自定义条件 后端API网关【免费下载链接】crystal Graphiles Crystal Monorepo; home to Grafast, PostGraphile, pg-introspection, pg-sql2 and much more!项目地址https://gitcode.com/gh_mirrors/cry/crystal点击查看免费下载导读本指南系统讲解 PostGraphile v5 的过滤能力从开箱即用的condition参数相等匹配过滤、围绕索引列的性能安全机制到通过addPgTableCondition、自定义查询、计算列、扩展 Schema 和社区插件实现高级过滤的完整路径。读完本文你将掌握如何用智能标签Smart Tags精确控制哪些列可被过滤、如何用addPgTableCondition编写可运行的过滤插件以及为什么 PostGraphile 官方强烈建议对通用过滤能力保持克制。一、开箱即用的过滤condition参数PostGraphile 在由其构建的连接Connection上默认支持基础过滤通过一个condition参数你可以用相等匹配的方式筛选记录。文档 connections.md 指出凡是来自表、视图和关系的连接绝大多数都支持用condition过滤返回结果。condition的使用方式非常直观——针对具体字段传入具体值即可{ # 找出用户名为 Alice 的用户 allUsers(condition: { username: Alice }) { nodes { id name } } }condition支持相等比较如username: Alice以及枚举值比较如category: ARTICLE。此外按照 add-pg-table-condition.md 的说明你可以把字段值指定为null从而只筛选出该列为IS NULL的记录。多个条件字段会同时生效最终在生成的 SQL 中通过AND组合成额外的WHERE子句。PostGraphile 会为每个表的集合字段如allForums自动构建对应的condition输入类型默认加入的是表的索引列。这一点在下一节详细展开。二、性能安全网默认只允许过滤已索引列过滤是数据库查询中性能敏感的操作。PostGraphile 对此设置了默认保护除非你正在使用 V4 preset否则默认不允许按未建立索引的列进行过滤。这样做的目的在于避免无索引列的过滤触发全表扫描从而在 API 层面就拦住性能隐患。2.1 源码中的实现依据这一默认行为由PgIndexBehaviorsPlugin实现源码位于 PgIndexBehaviorsPlugin.ts。该插件在 gather 阶段通过pgCodecs_attribute钩子检查每个属性列所属的表是否具备可用的索引只有relkind为r普通表、m物化视图、f外部表的实体才可能拥有索引canHaveIndexes视图、复合类型等无法建立索引的实体直接放行给它们疑罪从无的待遇对于可建索引的表若某列不在任何非部分索引!idx.indpred的键中则该属性会被标记为isIndexed: false。随后在 schema 阶段entityBehavior.pgCodecAttribute的inferred回调会为这些未索引列追加-filterBy与-orderBy行为片段——注意只有当extensions.isIndexed false且没有通过标签显式声明isIndexed时才会追加这为开发者通过智能标签强行打开过滤留下了口子。同理pgRelations_relation钩子会检查外键关系引用的远端表列是否被索引若未索引则移除关系的-select、-list、-connection、-single等行为。其中有一个重要特例以FAKE_开头虚拟约束的关系会被直接视为已索引这也是文档中虚拟约束被当作已索引处理Fake constraints are treated as if they are indexed的来源。2.2 用智能标签控制列的过滤可见性要突破默认限制让某个列出现在过滤选项中可以给该列打上behavior filterBy智能标签反过来behavior -filterBy可以强制把它从过滤选项中移除。智能标签可以通过多种方式附加详见 smart-tags.mdpostgraphile.tags.json5文件、数据库COMMENTSmart Comments、pgSmartTags实例或自定义插件。例如comment on column app_public.users.email is Ebehavior filterBy;或在postgraphile.tags.json5中{ version: 1, config: { attribute: { app_public.users.email: { tags: { behavior: filterBy, }, }, }, }, }从 PostGraphile 5.1.1 起还可以使用isIndexed智能标签把某列或某个外键约束视为已索引这直接影响默认的PgIndexBehaviorsPlugin判定参见 smart-tags.md 中关于isIndexed的说明。2.3 V4 preset 的差异condition:attribute:filterBy是控制能否在condition参数中按该属性过滤的行为作用域。如果你使用 V4 presetv4.ts 中的逻辑会对部分实体约第 188–218 行追加-condition:attribute:filterBy从而在condition中隐藏相应列。这与默认行为组合在一起形成了 V4 与 V5 之间过滤选项差异的一部分迁移到 V5 时建议用npx graphile behavior debug检查具体实体的行为归属见 behavior.md 的建议。三、高级过滤的四种官方路径当相等匹配不够用时PostGraphile 文档 filtering.md 给出了四种扩展过滤能力的路径自定义查询Custom Queries——把返回SETOF的函数暴露为根查询字段相关文档见 custom-queries.md计算列Computed Columns——把接收表行参数、返回标量或数组的函数暴露为表类型上的字段相关文档见 computed-columns.md扩展 SchemaextendSchema——直接扩展 GraphQL Schema见 extend-schema.mdaddPgTableCondition——为既有表集合字段的condition参数追加自定义条件字段下文详述。此外还可以编写自定义的 Graphile Engine 插件来增强既有连接相关机制见 extending-raw.md。3.1 通过智能标签让计算列参与过滤在过滤语境下与计算列紧密相关的智能标签是filterable自 v4.3.1 起现已被behavior filter filterBy取代见 smart-tags.md 的废弃说明作用于返回SETOF表类型表、视图、物化视图的函数时为该连接添加condition参数允许按其中的任意标量字段过滤作用于无必选参数、返回标量或数组的计算列函数时允许该函数出现在父表的condition参数中从而按该函数的返回值过滤父表。例如comment on function foo() is Efilterable; comment on function users_foo(users) is Efilterable;{ # 函数返回一组表行时连接上出现 condition 参数 foo(condition: { firstName: Alice }) { ... } # 函数返回标量时父表的 condition 中出现该字段 allUsers(condition: { foo: FOO_VALUE }) { ... } }如果计算列返回的是复合类型smart-tags.md 推荐用一个返回标量的包装计算列来实现排序/过滤若返回SETOF复合类型则建议用数组包装并结合 connection-filter 插件处理。四、addPgTableCondition为 condition 注入自定义 SQLaddPgTableCondition是官方提供的插件生成器用来给指定表的condition输入类型追加自定义条件字段。完整文档见 add-pg-table-condition.md实现源码位于 makeAddPgTableConditionPlugin.ts。4.1 函数签名function addPgTableCondition( match: { serviceName?: string; schemaName: string; tableName: string }, conditionFieldName: string, fieldSpecGenerator: (build: GraphileBuild.Build) GrafastInputFieldConfig, conditionGenerator?: ( value: unknown, helpers: { sql: typeof sql; sqlTableAlias: SQL; sqlValueWithCodec: typeof sqlValueWithCodec; build: ReturnTypetypeof pruneBuild; condition: PgCondition; }, ) SQL | null | undefined, ): GraphileConfig.Plugin;match定位目标表schemaNametableNameserviceName可选默认main对应多数据库服务场景conditionFieldName指定新增条件字段的名称fieldSpecGenerator返回 GraphQL 输入字段的配置其中应包含apply(condition, value)回调conditionGenerator是已废弃的旧式回调新代码应改用apply两者同时提供会抛出错误。4.2 示例一按主键列表过滤下面的插件为app_public.forums表新增idIn条件允许传入[Int!]数组只返回 ID 命中列表的记录import { addPgTableCondition } from postgraphile/utils; import { TYPES, listOfCodec } from postgraphile/dataplan/pg; export default addPgTableCondition( { schemaName: app_public, tableName: forums }, idIn, (build) { const { sqlValueWithCodec, listOfCodec, TYPES } build.dataplanPg; const { GraphQLList, GraphQLNonNull, GraphQLInt } build.graphql; return { description: Filters to records matching one of these ids, // 这是 graphql-js 的 [Int!]假定主键是整数 type: new GraphQLList(new GraphQLNonNull(GraphQLInt)), apply(condition, ids) { condition.where( (sql) sql${condition.alias}.id ANY(${sqlValueWithCodec( ids, listOfCodec(TYPES.int), )}), ); }, }; }, );关键点sqlValueWithCodec(ids, listOfCodec(TYPES.int))负责把运行时值安全地编码为带类型的 SQL 参数condition.alias指代app_public.forums表本身详见下文。最终 SQL 大致为WHERE app_public.forums.id ANY($1)。4.3 示例二关联子查询过滤下面的插件为app_public.forums新增containsPostsByUserId条件返回包含某用户发过帖的论坛帖子存于app_public.postsimport { addPgTableCondition } from postgraphile/utils; import { TYPES } from postgraphile/dataplan/pg; export default addPgTableCondition( { schemaName: app_public, tableName: forums }, containsPostsByUserId, (build) { const { sqlValueWithCodec, TYPES } build.dataplanPg; const { GraphQLInt } build.graphql; return { description: Filters the list of forums to only those which contain posts written by the specified user., type: GraphQLInt, apply(condition, userId) { condition.where((sql) { const sqlIdentifier sql.identifier(Symbol(postsByUser)); return sqlexists( select 1 from app_public.posts as ${sqlIdentifier} where ${sqlIdentifier}.forum_id ${condition.alias}.id and ${sqlIdentifier}.user_id ${sqlValueWithCodec( userId, TYPES.int, )} ); }); }, }; }, );对应 GraphQL 查询query ForumsContainingPostsByUser1 { allForums(condition: { containsPostsByUserId: 1 }) { nodes { id name } } }这里用sql.identifier(Symbol(...))生成一个唯一的 SQL 标识符作为子查询别名避免与外部查询的标识符冲突——这是编写关联子查询时的良好实践。4.4 实现层面的注意事项结合 makeAddPgTableConditionPlugin.ts 源码有以下值得注意的行为加载顺序生成的插件声明before: [PgConnectionArgOrderByPlugin]确保条件中附加的排序不会被默认排序插件覆盖未见生效警告finalize钩子会检查目标表上是否真的添加了条件字段若未命中比如表名写错会在控制台输出WARNING: failed to add condition ... to table ...匹配判定GraphQLInputObjectType_fields钩子中通过isPgCondition作用域、pgCodec以及table.extensions?.pg?.schemaName / name / serviceName判定当前输入类型是否为目标表的condition类型旧式conditionGenerator若字段规格中未提供apply插件会基于conditionGenerator自动构造apply——将返回的 SQL 表达式通过condition.where(expression)应用sqlTableAlias被映射为condition.alias。新代码应直接写apply。4.5 不要忘记condition.alias文档特别强调condition.alias表示match中那张表即schemaName.tableName表的 SQL 别名。如果你的apply没有使用condition.alias那么插件大概率是错的——过滤条件可能绑定到了错误的表上导致 WHERE 子句失效甚至产生错误的 SQL。五、PgCondition运行时原理apply收到的condition参数是dataplan/pg中的PgCondition类实例源码位于 pgCondition.ts。理解它的工作机制有助于编写正确的过滤插件。5.1 核心能力condition.alias当前查询上下文中目标表的 SQL 别名condition.where(spec)追加一个 WHERE 条件PgWhereConditionSpec可以是 SQL 片段或属性回调condition.having(spec)当isHaving为真时追加 HAVING 条件condition.andPlan() / orPlan() / notPlan()创建 AND / OR / NOT 逻辑组合条件实现多条件组合condition.existsPlan({ tableExpression, alias, equals })创建EXISTS子查询条件生成形如exists(select 1 from table as alias where condition)的 SQL并支持 true / false取反源码第 240–251 行——示例二中的关联子查询正是这种模式的手写版本condition.ignoreUnlessAmended()标记除非子条件添加了实际需求否则本条件不生效专为 connection-filter 这类插件设计避免生成空条件。5.2 条件如何进入最终 SQLPgCondition.apply()把收集到的条件分派给父级PASS_THRU模式下逐条转发给父级where其余模式则通过pgWhereConditionSpecListToSQL把条件列表用AND或ORNOT时包一层not (...)拼接成单个括号片段。因此多个 condition 字段天然通过 AND 组合这是 4.2 节示例中多个条件字段并存的运行基础。5.3 条件输入类型的构建过滤输入类型由 graphile-build-pg 的相关插件生成PgConditionCustomFieldsPlugin源码见 PgConditionCustomFieldsPlugin.ts负责把可过滤的 PostgreSQL 函数即filterable函数/计算列作为condition的附加字段暴露并受condition:proc:filterBy行为作用域控制。这些插件与PgIndexBehaviorsPlugin、PgAttributesPlugin等一起共同决定了condition输入类型中会出现哪些字段。六、行为系统视角下的过滤控制PostGraphile v5 的过滤能力与 behavior.md 描述的行为系统深度耦合。与过滤直接相关的行为作用域Scope包括filterBy——能否按某物列、表等过滤proc:filterBy——能否按某函数函数资源的结果过滤condition:proc:filterBy——能否在condition参数中按该函数结果过滤filter:proc:filterBy——能否在 connection-filter 插件的filter参数中按该函数结果过滤attribute:filterBy——能否按某属性列过滤condition:attribute:filterBy——能否在condition参数中按该属性过滤attribute:aggregate:filterBy、sum:attribute:aggregate:filterBy——能否按属性的聚合结果过滤resource:aggregates:filterBy、sum:resource:aggregates:filterBy——能否按另一资源的聚合结果过滤。行为字符串由若干片段组成支持/-修饰符与冒号分隔的作用域最终行为由插件默认、全局默认preset.schema.defaultBehavior、推断行为与实体标签smart tags按优先级拼接而成越靠后的片段优先级越高。在过滤场景中的实用技巧全局关闭连接过滤defaultBehavior: -connection:filter之类的配置结合 connections.md 中-connection list的用法类推按列开启/关闭过滤behavior filterBy/behavior -filterBy已在第二节演示调试行为归属npx graphile behavior debug可以快速确认哪些行为片段最终生效及其原因。注意避免使用已废弃的filterable、sortable与omit filter等 V4 时代的标签——它们只在使用 V4 preset 时才可用新项目应改用behavior体系。七、通用过滤插件能力与警告7.1 官方警告通用过滤可能是错误PostGraphile 文档在 filtering.md 中以醒目的警示框强调为 GraphQL API 添加强大的通用过滤能力是强烈不建议的。这不仅出自 PostGraphile 维护者 Benjie也包括 GraphQL 联合发明者 Lee Byron 以及 GraphQL 生态的多位专家。理由很直接通用过滤如任意字段的大于/小于/范围/模糊匹配极易导致客户端构造出性能灾难性的查询而且事后极难补救。官方建议是只添加非常具体的过滤条件且输入尽量简单例如上文addPgTableCondition的两种模式如果确实需要通用过滤务必想清楚受众与使用方式不要心血来潮就开启。7.2 connection-filter 插件社区中非常流行的通用过滤插件是 Matt Bretl 的postgraphile-plugin-connection-filtergraphile-contrib 组织维护。它为连接添加filter参数能力包括对关联表记录进行过滤使用大于greater than、小于less than与范围range过滤甚至按函数的输出进行过滤。如果你确实需要高级过滤且能配合**持久化查询persisted queries**来阻止恶意方提交复杂请求那么该插件值得一试——但请务必把上面的警告记在心里。持久化查询的配置方式见 production.md 中Simple query allowlist / persisted queries / persisted operations一节。7.3 其他社区插件还有更多与过滤相关的社区插件详见 community-plugins.md 的汇总页。在选择插件时建议优先考察其是否基于condition/filter行为作用域实现从而能与行为系统、智能标签协同工作。八、实践建议与小结把本文的内容落成一张决策表需求推荐方案参考按表字段精确匹配过滤默认condition参数要求列有索引connections.md让无索引列可被过滤behavior filterBy或isIndexed智能标签smart-tags.md按函数/计算列结果过滤自定义查询、计算列 filterable/behavior filterBycustom-queries.md、computed-columns.md按关联表、计算或任意 SQL 表达式过滤addPgTableCondition编写插件add-pg-table-condition.md通用、复杂过滤connection-filter 插件 持久化查询production.md精细控制某列的过滤可见性行为系统filterBy系列作用域behavior.md核心原则回顾PostGraphile 的过滤设计始终把性能安全放在首位——默认只允许过滤已索引列PgIndexBehaviorsPlugin在 PgIndexBehaviorsPlugin.ts 中落实在需要突破默认能力时优先选择addPgTableCondition这类小而具体的过滤插件实现见 makeAddPgTableConditionPlugin.ts并通过condition.alias与sqlValueWithCodec保证 SQL 的正确性与安全性最后除非有充分理由否则远离通用过滤插件——你的数据库和未来接手维护的同事都会感谢这个决定。赞分享后端API网关【免费下载链接】crystal Graphiles Crystal Monorepo; home to Grafast, PostGraphile, pg-introspection, pg-sql2 and much more!项目地址https://gitcode.com/gh_mirrors/cry/crystal点击查看免费下载相关推荐PostGraphile v4 数据过滤实战指南从 condition 基础过滤到高级过滤扩展PostGraphile v4 数据过滤实战指南从 condition 基础过滤到高级过滤扩展 本指南系统梳理 PostGraphile v4 中围绕数据过后端API网关PostGraphile 自定义 condition 过滤插件开发指南深入 makeAddPgTableConditionPluginPostGraphile 自定义 condition 过滤插件开发指南深入 makeAddPgTableConditionPlugin makeAddPgTa后端API网关使用 addPgTableCondition 为 PostGraphile 表集合扩展自定义 condition 过滤使用 addPgTableCondition 为 PostGraphile 表集合扩展自定义 condition 过滤 本篇指南讲解 PostGraphile后端API网关创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表