让我们减少您每个页面正在运行的数据库查询量,并通过返回多维数据并正确键入IT来同时使您的代码清洁器。
目的是解释这一点。我们将使用团队和团队的**成员**之间存在的关系。每个团队都可以有一个或多个成员。可以在应用程序仪表板上的团队卡上看到这种关系,我们向每个团队展示的团队成员:
方法1-多个查询ð
通常,在开发类似的内容时,我们可能会在我们的页面上进行查询:
SELECT * FROM teams;
,然后,对于每个团队,我们都会运行:
SELECT * FROM team_members tm INNER JOIN users u ON tm.user_id = u.id WHERE tm.id = {TEAM_ID}
ð±ok,因此,虽然该SQL确实很容易理解它会产生 n+1 问题。这基本上意味着对于每个团队,您都必须运行其他查询,因为团队数量的增加,查询数量也会增加。
方法2-单个加入查询ð
另一种选择是运行类似的东西:
SELECT *
FROM teams t
INNER JOIN team_members tm ON t.id = tm.team_id
INNER JOIN users u ON tm.user_id = u.id
WHERE tm.id = {TEAM_ID}
这是非常直接的SQL,问题是您最终得到了这样的结果:
团队名称 | 名字 | 姓 | 职位 |
---|---|---|---|
董事会形状Inc | nick | 持有人 | cmo |
董事会形状Inc | rick | smoulders | CTO |
董事会形状Inc | 汤姆 | 持有人 | 首席执行官 |
董事会形状Inc | Mariana | 弗洛雷斯 | 主席 |
董事会形状Inc | David | 安德森 | 非行政 |
董事会形状Inc | mo | Haider | 非行政 |
董事会形状Inc | 西蒙 | 休斯 | 秘书 |
董事会形状Inc | sara | 哈特菲尔德 | 内容管理器 |
您有每个团队成员的一行,并且所有团队数据都将复制。这很烦人,因为您想迭代团队,然后迭代这些团队中的成员。虽然您可以在JS中执行此操作,但代码会有些混乱,并且您正在通过电线发送更多数据。
不采用这种方法的另一个重要原因是 supabase不能自动为您键入此查询。您将需要创建一个视图,并且该视图将是它自己的类型。
方法3-强烈键入多维结果ðð¥
这是您可能要考虑的另一种方法。每队返回一排,但团队成员嵌入为JSON,这就是方法。 **注。
从创建Postgres函数开始: 此功能的结果为您提供了一个看起来像这样的JSON数组: 现在,我们希望与我们的团队数据一起返回上述成员。因此,我们创建以下视图: 然后,您最终得到了这样的结果: 如果不是很明显,则每支团队将有一排。现在,您可以在这些团队中迭代团队并迭代成员。 在可能的情况下,我们想将打字留给supabase。我们刚刚运行以下命令: 它吐出了我们的表格和视图的所有类型。我们维护自己的数据库。
这种类型的问题是它看起来像这样: 这里的问题是,成员是JSON类型,并且不是可以为我们提供类型检查的强大类型。 我们不想调整这种类型,因为它是由supabase自动维护的。我们可以采取的措施避免这种方法是基于上述的以上的别名创建我们自己的类型的,看起来像这样:
CREATE OR REPLACE FUNCTION public.get_team_members(team_id uuid)
RETURNS jsonb
LANGUAGE plpgsql
STABLE SECURITY DEFINER
AS $function$
DECLARE
result jsonb;
BEGIN
SELECT json_agg(row_to_json(t)) INTO result
FROM (
SELECT
tm.team_id,
tm.user_id,
u.id as user_id,
u.first_name,
u.last_name
u.job_title
FROM
public.team_members AS tm
INNER JOIN
public.profiles AS u ON u.id = tm.user_id
WHERE
tm.team_id = get_team_members.team_id
) t;
RETURN result;
END;
$function$
;
[
{
"team_id": "aad4f3b2-2bfa-451a-a1e7-359184c17ca4",
"user_id": "5e36b5bf-a446-4d0a-b4f6-e2ed4e6ccb83",
"last_name": "Holder",
"first_name": "Nick",
"job_title": "CMO"
},
{
"team_id": "aad4f3b2-2bfa-451a-a1e7-359184c17ca4",
"user_id": "0e23efeb-a011-4d7c-8dbc-d32404f28f5c",
"last_name": "Flores",
"first_name": "Mariana",
"job_title": "Chairperson"
},
...Abbreviated
]
CREATE OR REPLACE VIEW public.teams_with_team_members
AS SELECT
t.name,
get_team_members(t.id) AS members
FROM teams t
名称
成员
董事会形状Inc
[JSON数组如上]
supabase键入的魔力
supabase gen types typescript --local --schema public > src/database.types.ts
export type ViewTeamsWithTeamMembers =
Database['public']['Views']['teams_with_team_members']['Row']
teams_with_team_members: {
Row: {
name: string | null
members: Json | null
}
...
export interface TeamsWithTeamMemberProfiles
extends Omit<ViewTeamsWithTeamMembers, 'members'> {
members: TeamMembers[] | null
}
export type TeamMembers = Database['public']['Tables']['users']['Row']
实际上,上面存在错误,因为我们的 get_team_members 数据库功能实际上并没有从用户返回记录,这是一个连接的supabase doens的混合记录,因此实际上还有另一种自定义类型,您需要在此处放置另一种自定义类型,但我保留了这种简单的解释。重要的是,不要调整您的supabase类型,将其用作您自己类型的起点。
这种方法还可以帮助RLS规则,因为我们在视图上的功能是使用安全性定义器设置的。有关此阅读的更多信息,请阅读我们的帖子How to implement RLS for a team invite system with Supabase。
与Supabase和Postgres合作非常愉快,我们希望这为您提供一些调整传统方法的方法的想法。
请follow us on twitter,以获取有关我们发布新工程内容和give BoardShape a try何时您有兴趣举办更好的有组织董事会会议的更新。