pgSelect 和 pgSelectSingle
虽然您通常不会手动创建 pgSelect()
或 pgSelectSingle()
步骤(而是使用相关资源上的 .get()
、.find()
或 .execute()
方法),但它们是您在 @dataplan/pg
中最常与之交互的步骤类。
pgSelect
pgSelect()
代表从数据库中选择的一行。您可能会在这个例子中获得一个 pgSelect 实例,例如 $pgSelect
// `pgSelect()` step representing all the messages in forum 1:
const $pgSelect = registry.pgResources.messages.find({ forum_id: constant(1) });
一旦您可以访问 pgSelect()
步骤,您就可以使用它的各种方法来添加条件、重新排序、添加分页限制等等。
您只能在创建 pgSelect()
的字段规划时进行这些更改。一旦该字段规划完成(通过 Grafast 的 deduplicate
生命周期事件),对条件、排序等的任何更改都可能导致您的模式违反 GraphQL 规范,因此这些操作是被禁止的。
如果您在突变计划解析器中使用 pgSelect,并且该 pgSelect 具有副作用(例如调用 VOLATILE
数据库函数),那么请确保代表该函数的 资源 正确配置了 isMutation: true
,并且您是通过 resource.execute()
创建该步骤的。(如果未执行此操作,则在某些情况下,该步骤可能会被树状摇动,因此副作用可能永远不会发生。)
(或者,如果直接创建 pgSelect
,则使用 mode="mutation"
创建,或设置 $pgSelect.hasSideEffects = true;
。)
下面我们记录了最常用的属性和方法,完整的文档请通过 TypeScript 探索方法及其类型。
请注意,通常在内部使用或通过辅助步骤(如 connection()
)使用的方法没有记录,因为您通常不会自己调用这些方法。
$pgSelect.alias
由于同一个表可能在最终查询中出现多次(在查询被内联/合并后),每个 $pgSelect 都被赋予自己的别名。别名是表示底层表的 SQL 片段,方便您引用表的列或类似内容。
const $users = usersResource.find();
const tbl = $users.alias;
$users.where(sql`${tbl}.username = 'Benjie'`);
$pgSelect.single()
如果此计划可能只返回一条记录,您可以使用 $pgSelect.single()
返回一个解析为该记录(在复合类型的情况下)或底层标量(在编解码器没有属性的资源的情况下)的计划。
注意:如果您调用此方法,而数据库实际上可能返回多条记录,那么您可能会遇到糟糕的情况。
// Instead of:
// const $user = usersResource.get({ id: constant(1) });
// you could do:
const $user = usersResource.find({ id: constant(1) }).single();
$pgSelect.singleAsRecord()
您很可能想要使用 .single()
;只有在您特别需要 PgSelectSingleStep
实例,即使您正在查询标量时,才使用此方法。
$pgSelect.row($row)
您可以使用此方法根据 $pgSelect
中的任意条目创建 pgSelectSingle
实例,例如,要从 $pgSelect
中获取第一行并将其设置为 pgSelectSingle
,您可以执行以下操作
const $row = $pgSelect.row(first($pgSelect));
$pgSelect.orderBy(spec)
在查询中添加排序子句。可以多次调用此方法以添加更多排序子句,这些子句始终附加到现有列表中。
参数可以是属性规范,一个具有以下属性的对象
attribute
(必需) - 要排序的属性的名称,必须是 $pgSelect 表示的编解码器上的属性之一callback
(可选) - 用于将属性包装在表达式中direction
(必需) -"ASC"
或"DESC"
nulls
(可选) -"FIRST"
或"LAST"
或null
或片段规范,一个具有以下属性的对象
fragment
(必需) - 要排序的 SQL 表达式codec
(必需) - 与fragment
中表达式类型匹配的编解码器direction
(必需) -"ASC"
或"DESC"
nulls
(可选) -"FIRST"
或"LAST"
或null
示例
// Get a pgSelect:
const $users = usersResource.find();
// Sort by username length
$users.orderBy({
attribute: "username",
callback(usernameExpression, codec) {
return [sql`length(${usernameExpression})`, TYPES.int];
},
direction: "ASC",
nulls: "LAST",
});
// Sort within that by user id
$users.orderBy({ attribute: "id", direction: "ASC" });
// Result is something like:
// `SELECT ... FROM users ORDER BY length(username) ASC NULLS LAST, id ASC`
$pgSelect.setOrderIsUnique()
如果您确定您指定的顺序足以确保排序没有歧义(即它是稳定的),请调用此方法。如果您不这样做,我们可能会(如果需要唯一顺序,例如用于游标分页)将主键或类似的唯一约束添加到排序中,以使其稳定。
$pgSelect.where(condition)
向查询添加 WHERE
子句,可以多次调用,条件将使用 AND
追加。
const $users = usersResource.find();
const tbl = $users.alias;
$users.where(sql`${tbl}.username = 'Benjie'`);
$pgSelect.placeholder($step, codec)
占位符接受任意步骤和表示其 SQL 类型应是什么的编解码器(如果步骤已包含编解码器的详细信息,则可选),并返回一个 SQL 表达式,允许在 SQL 查询中引用步骤的值。
const $users = usersResource.find();
const tbl = $users.alias;
const $username = fieldArgs.get("username");
const frag = $users.placeholder($username, TYPES.citext);
$users.where(sql`${tbl}.username = ${frag}`);
$pgSelect.singleRelation(relationName)
强制 $pgSelect
对给定关系进行左连接,并为您提供一个表示此连接的别名;这对于构建相关表上的条件很有用。
请注意,pgSelect
(集合)上的此方法与 pgSelectSingle
(行)上的同名方法不同,后者返回一个表示相关记录的步骤。
示例:“返回所有论坛未归档的帖子”
const $posts = postsResource.find();
const forumAlias = $posts.singleRelation("forum");
$posts.where(sql`${forumAlias}.is_archived = false`);
return $posts;
// Result is something like:
// `SELECT ... FROM posts LEFT JOIN forums ON (...) WHERE forums.is_archived = false`
如果关系不唯一,则会抛出错误。
$pgSelect.wherePlan()
这种高级方法不会直接添加条件,而是返回一个 PgConditionStep
(一个“修饰符步骤”),允许以不同的方式构建条件。如果您正在构建深度过滤参数,使用 applyPlan
计划解析器处理参数和输入字段,这将特别有用。
$pgSelect.setFirst($n)
限制返回的记录数量。
目前,此方法必须使用输入步骤调用(即,它必须来自 GraphQL 字段参数或是一个常量)。
$pgSelect.setLast($n)
限制返回的记录数量,但不是取列表中的第一个条目,而是取最后一个条目。
目前,此方法必须使用输入步骤调用(即,它必须来自 GraphQL 字段参数或是一个常量)。
$pgSelect.setOffset($n)
跳过给定数量的记录,不能与 setLast
结合使用。
目前,此方法必须使用输入步骤调用(即,它必须来自 GraphQL 字段参数或是一个常量)。
$pgSelect.groupBy(spec)
指示查询添加 GROUP BY
子句。目前,spec 必须是一个具有以下属性的对象
fragment
(必需) - 一个 SQL 片段,用于指示要GROUP BY
的内容
$pgSelect.having(condition)
类似于 $pgSelect.where(condition)
,但用于分组查询的 HAVING
子句,并且只支持 SQL 片段条件形式。
TODO: 此方法未经测试!
$pgSelect.havingPlan()
类似于 $pgSelect.wherePlan()
,但用于 HAVING
子句。
TODO: 此方法未经测试!
$pgSelect.setUnique()
仅在最多可能存在一行匹配行时调用此方法。如果您在不满足此条件的情况下将此设置为 true,则在内联期间可能会得到意外的结果;如有疑问,请将其保留为默认值。
$pgSelect.hasMore()
返回一个步骤,指示是否存在下一页(通过选择 1 行额外行并将其丢弃)。通常,这由 connection()
步骤的 pageInfo
逻辑使用,您不会自己调用它。
$pgSelect.setInliningForbidden()
如果您希望阻止此查询内联到其父级中,请调用此方法。您可能希望这样做来解决性能问题,或提高步骤的可缓存性。
$pgSelect.setTrusted()
默认情况下,所有 pgSelect
步骤都会应用其资源指定的授权检查,例如应用类似于行级安全性的条件。如果您希望绕过此行为,例如您知道条件会通过因为父级是可见的,则调用此方法。
const plans = {
Forum: {
posts($forum) {
const $posts = postsResource.find({ forum_id: $forum.get("id") });
// If we can see the forum, then we can see all the posts inside the
// forum, so don't bother adding the access conditions:
$posts.setTrusted();
return $posts;
},
},
};
pgSelectSingle
pgSelectSingle()
代表 pgSelect()
集合中的单个行。通常您不会直接构建此实例,而是从 resource.get()
、resource.execute()
(对于 isUnique 函数)、从 pgSelect
中列表的条目或通过 $pgSelect.single()
获取它。
以下是一些常用的方法
$pgSelectSingle.get(attr)
pgSelectSingle
上最常用的方法,它获取一个代表行中给定属性值的步骤。
您必须始终使用 $pgSelectSingle.get(attr)
而不是 access($pgSelectSingle, attr)
或类似方法,原因如下
pgSelectSingle
包含的数据是一个不可预测的元组(参见下面的 不透明步骤),.get(attr)
会告诉相关的pgSelect
步骤将attr
添加到要SELECT
的表达式列表中
$pgSelectSingle.select(fragment, codec)
返回一个代表给定 SQL 表达式值的 PgClassExpressionStep。
const $user = usersResource.find({ id: constant(1) });
const $usernameLength = $user.select(sql`length(username)`, TYPES.int);
$pgSelectSingle.placeholder($step, codec)
与底层 pgSelect
步骤上的 $pgSelect.placeholder($step, codec)
相同。
$pgSelectSingle.singleRelation(relationName)
返回一个代表通过 relationName
关系相关的单个记录的 pgSelectSingle
。
不要与 $pgSelect.singleRelation
混淆。
$pgSelectSingle.manyRelation(relationName)
返回一个代表通过 relationName
关系相关的记录的 pgSelect
。
$pgSelectSingle.record()
返回一个表示整个表的 PgClassExpressionStep,用于调试或与 pgSelectSingleFromRecord
一起使用。
$pgSelectSingle.cursor()
返回一个表示此行游标的步骤,通常用于连接边中的 cursor
字段。
$pgSelectSingle.getClassStep()
返回此 $pgSelectSingle
所来自的 PgSelectStep
。用于获取 alias
等信息。
pgSelectFromRecords(resource, $records)
构建一个 pgSelect
步骤,表示 $records
中的多行数据,就好像它们来自 resource
资源一样。
pgSelectFromRecord(resource, $record)
使用 $record
中的单行数据构建一个 pgSelect
步骤,就好像它来自 resource
资源一样。
pgSelectSingleFromRecord(resource, $record)
等效于 pgSelectFromRecord(resource, $record).single()
。
不透明步骤
pgSelect
和 pgSelectSingle
是我们所说的“不透明步骤” - 也就是说,您不打算直接使用它们的基础数据,而是使用它们的方法来提取您需要与其他步骤一起使用的数据。
目前,pgSelectSingle
不使用您可能期望的对象表示,而是使用一个元组,其中包含每个选定属性的条目。此元组的构成将根据您请求的属性以及顺序而有所不同,因此您不能依赖其结构。要获取属性,您应该使用 $pgSelectSingle.get(attr)
或类似方法。