通过使用Supabase返回Postgres中的多维数据来加快应用程序
#javascript #网络开发人员 #postgres #supabase

让我们减少您每个页面正在运行的数据库查询量,并通过返回多维数据并正确键入IT来同时使您的代码清洁器。

目的是解释这一点。我们将使用团队和团队的**成员**之间存在的关系。每个团队都可以有一个或多个成员。可以在应用程序仪表板上的团队卡上看到这种关系,我们向每个团队展示的团队成员:

Illustration of teams and members

方法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函数开始:

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$
;

此功能的结果为您提供了一个看起来像这样的JSON数组:

[
  {
   "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。我们刚刚运行以下命令:

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
    }
...

这里的问题是,成员是JSON类型,并且不是可以为我们提供类型检查的强大类型。 我们不想调整这种类型,因为它是由supabase自动维护的。我们可以采取的措施避免这种方法是基于上述的以上的别名创建我们自己的类型的,看起来像这样:

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何时您有兴趣举办更好的有组织董事会会议的更新。