设计 SQL Connect 架构

借助 Firebase SQL Connect,您可以设计一个 GraphQL 架构,用于表示应用所需的数据模型。SQL Connect 会将此架构转换为支持应用的 Cloud SQL for PostgreSQL 实例。然后,您需要编写查询和变更来与后端交互,并将这些操作捆绑到连接器中,以便通过客户端代码使用数据。

SQL Connect 提供 AI 工具,可帮助您设计和实现架构。本指南介绍了架构设计的重要概念,以便在您开始开发应用及后续阶段,为您的标准工作流和 AI 辅助工作流提供支持和补充。

入门指南介绍了 PostgreSQL 的影评应用架构。

本指南进一步开发了该架构,并提供了与最终影评应用架构等效的 SQL 列表。

影评应用的架构

假设您要构建一项服务,让用户可以提交和查看影评。

您需要为此类应用提供初始架构,以支持基本查询。您稍后将扩展此架构,以创建复杂的相关查询。

在 SQL Connect 中,您将定义 GraphQL 类型,以定义客户端可以查询和操作的数据的形状。编写架构时,您的类型会转换为 Cloud SQL for PostgreSQL 表,通常是 GraphQL 类型与数据库表之间的直接关系,但也可能有其他映射。本指南展示了一些从基本到更高级的示例。

定义基本 Movie 类型

您可以从 Movie 类型开始。

Movie 的架构包含以下核心指令:

  • @table(name) 和 @col(name),用于自定义 SQL 表和列名称。 如果未指定,SQL Connect 会生成 snake_case 名称。
  • @col(dataType) 可用于自定义 SQL 列类型。
  • @default,用于在插入期间配置 SQL 列默认值。

如需了解详情,请参阅 @table、@col、@default 的参考文档。

# Movies
type Movie @table(name: "movie", key: "id") {
  id: UUID! @col(name: "movie_id") @default(expr: "uuidV4()")
  title: String!
  releaseYear: Int
  genre: String @col(dataType: "varchar(20)")
  rating: Int
  description: String
}

自动将重要用户数据存储在 User 类型中

您的应用将跟踪用户,因此您需要一个 User 类型。

在这种情况下,@default 指令特别有用。此处的 id 字段可以自动从身份验证中获取用户 ID:请注意以下示例中 @default(expr: "auth.uid") 的使用。

# Users
# Suppose a user can leave reviews for movies
type User @table {
  id: String! @default(expr: "auth.uid")
  username: String! @col(dataType: "varchar(50)")
}

键标量和服务器值

在进一步了解影评应用之前,有必要先介绍 SQL Connect 键标量和服务器值。

键标量是简洁的对象标识符,SQL Connect 会根据架构中的键字段自动组合这些标识符。关键标量与效率有关,可让您通过一次调用找到有关数据身份和结构的信息。当您想对新记录执行依序操作,并且需要一个唯一标识符来传递给后续操作时,以及当您想访问关系键以执行其他更复杂的操作时,它们尤其有用。

使用服务器值,您可以让服务器根据 expr 参数中特定的服务器端 CEL 表达式,使用存储的值或易于计算的值动态填充表中的字段。例如,您可以定义一个字段,并在使用操作请求 updatedAt: Timestamp! @default(expr: "request.time") 中存储的时间访问该字段时应用时间戳。

处理 Actor 和 MovieActor 类型中的多对多关系

处理完用户数据后,您可以继续对影片数据进行建模。

接下来,您需要演员来出演您的电影。

Actor 表非常简单。

# Actors
# Suppose an actor can participate in multiple movies and movies can have multiple actors
# Movie - Actors (or vice versa) is a many to many relationship
type Actor @table {
  id: UUID! @default(expr: "uuidV4()")
  name: String! @col(dataType: "varchar(30)")
}

如果您希望演员可以出演多部电影,并且电影可以有多位演员,则需要一个“联接表”。

MovieActor 表处理多对多关系,其主键是 [movie, actor](来自 movie 和 actor 的外键字段)的组合。

# Join table for many-to-many relationship for movies and actors
# The 'key' param signifies the primary keys of this table
# In this case, the keys are [movieId, actorId], the foreign key fields of the reference fields [movie, actor]
type MovieActor @table(key: ["movie", "actor"]) {
  movie: Movie!
  # movieId: UUID! <- implicitly added foreign key field
  actor: Actor!
  # actorId: UUID! <- implicitly added foreign key field
  role: String! # "main" or "supporting"
  # optional other fields
}

当您在具有外键约束条件的表上定义 SQL 关系时,SQL Connect 会自动在另一端生成相应的字段。您无需定义反向映射字段(例如,从 Actor 返回到 MovieActor)。

处理 MovieMetadata 类型中的一对一关系

现在,请跟踪电影导演,并与 Movie 建立一对一关系。

您可以使用 @ref 指令自定义外键限制条件:

  • @ref(fields) 指定要使用的外键字段。
  • @ref(references) 指定目标表中引用的字段(默认为主键,但 @unique 字段也有效)。这是一个更高级的选项;SQL Connect 通常可以为您推断出这一点。

如需了解详情,请参阅 @ref 的参考文档。

# Movie Metadata
# Movie - MovieMetadata is a one-to-one relationship
type MovieMetadata @table {
  # @unique ensures that each Movie only has one MovieMetadata.
  movie: Movie! @unique
  # Since it references to another table type, it adds a foreign key constraint.
  #  movie: Movie! @unique @ref(fields: "movieId", references: "id")
  #  movieId: UUID! <- implicitly added foreign key field
  director: String
}

使用根据架构生成的字段来构建操作

您的 SQL Connect 操作将扩展 SQL Connect 根据架构中的类型和类型关系自动生成的一组字段。每当您修改架构时,本地工具都会生成这些字段。

假设您的架构包含 Movie 类型和关联的 Actor 类型。SQL Connect 会生成 movie、movies、actors_on_movies 字段等。

使用
movie字段进行查询

movie 字段表示 Movie 表中的一条记录。

使用此字段可按键查询单个电影。

query GetMovie($myKey: Movie_Key!) {
  movie(key: $myKey) { title }
}

使用
movies字段进行查询

movies 字段表示 Movie 表中的记录列表。

使用此字段可查询多部电影,例如,指定年份的所有电影。

query GetMovies($myYear: Int!) {
  movies(where: { year: { eq: $myYear } }) { title }
}

使用
actors_on_movies字段进行查询

actors_on_movies 字段表示连接 Actor 和 Movie 表的记录列表。使用此字段可查询与给定影片关联的所有演员。

使用此字段可查询与给定影片关联的所有演员。

  query GetActorsOnMovie($myKey: Movie_Key!) {
    actors_on_movies(where: { movie: { key: { eq: $myKey } } }) {
      actor { name }
    }
  }

考虑到这一点,您可以参阅查询实现指南和变更实现指南,了解如何使用这些字段实现操作。

更多高级架构概念

枚举字段

SQL Connect 支持映射到 PostgreSQL 枚举类型的枚举字段。枚举可让您快速定义具有特定顺序的静态预定义值列表。

如需向架构添加枚举,请声明枚举及其预定义值,然后在类型中引用它。

enum AspectRatio {
   ACADEMY
   WIDESCREEN
   ANAMORPHIC
   IMAX
   "No information available on aspect ratio"
   UNAVAILABLE
}

type Movie
  @table {
  title: String! 
  genre: String
  description: String
  originalAspectRatio: AspectRatio! @default(value: WIDESCREEN)
  otherAspectRatios: [AspectRatio!]
  tags: [String]
  rating: Float
  imageUrl: String!
  releaseYear: Int
}

在 Movie 类型中,我们添加了一个枚举字段 originalAspectRatio,用于表示电影拍摄时的宽高比,以及另一个字段 otherAspectRatios,用于表示其他可用宽高比的列表。

管理对枚举字段的更改

您可以向枚举添加新值,但枚举列表的顺序非常重要,因此请谨慎插入新值。对枚举进行的唯一完全向后兼容的更改是在值列表末尾添加新值。值得注意的是,在先前发布的枚举之间插入新值或对现有值重新排序会更改在查询中使用“小于”等相对运算符时的相对顺序。移除或重命名值始终是向后不兼容的更改。

切勿对枚举值列表中的值重新排序;顺序很重要,因为它会改变过滤条件的运用方式。

应谨慎调整枚举值,以免破坏旧版操作或客户端代码。移除或重命名枚举值时,请确保当前数据库中没有剩余实例。

在操作和客户端代码中使用枚举字段

现在,您已向架构添加了枚举字段,可以在查询和客户端代码中使用此字段。

如需详细了解如何使用枚举编写查询,以及如何编写客户端以允许从查询指南开始调整枚举,请参阅相关文档。

其他高级概念

如需了解基本但有用的类型和关系之外的内容,请参阅参考文档中的示例。

支持的数据类型

SQL Connect 支持以下标量数据类型,并使用 @col(dataType:) 分配给 PostgreSQL 类型。

SQL Connect 类型 GraphQL 内置类型或
SQL Connect 自定义类型
默认 PostgreSQL 类型 支持的 PostgreSQL 类型
(括号中为别名)
字符串 GraphQL text text
bit(n)、varbit(n)
char(n)、varchar(n)
整数 GraphQL 整数 Int2(smallint、smallserial)、
int4(integer、int、serial)
浮点数 GraphQL float8 float4 (real)
float8 (double precision)
numeric (decimal)
布尔值 GraphQL 布尔值 布尔值
UUID 自定义 uuid uuid
Int64 自定义 bigint int8(bigint、bigserial)
numeric (decimal)
日期 自定义 date 日期
时间戳 自定义 timestamptz

timestamptz

注意:系统不会存储本地时区信息。
PostgreSQL 会将此类时间戳转换为世界协调时间 (UTC) 并存储。

枚举 自定义 enum

枚举

向量 自定义 vector

向量

请参阅使用 Vertex AI 执行向量相似度搜索。

  • GraphQL List 映射到一维数组。
    • 例如,[Int] 映射到 int5[],[Any] 映射到 jsonb[]。
    • SQL Connect 不支持嵌套数组。

等效 SQL 架构

-- Movies Table
CREATE TABLE Movies (
    movie_id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
    title VARCHAR(255) NOT NULL,
    release_year INT,
    genre VARCHAR(30),
    rating INT,
    description TEXT,
    tags TEXT[]
);
-- Movie Metadata Table
CREATE TABLE MovieMetadata (
    movie_id UUID REFERENCES Movies(movie_id) UNIQUE,
    director VARCHAR(255) NOT NULL,
    PRIMARY KEY (movie_id)
);
-- Actors Table
CREATE TABLE Actors (
    actor_id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
    name VARCHAR(30) NOT NULL
);
-- MovieActor Join Table for Many-to-Many Relationship
CREATE TABLE MovieActor (
    movie_id UUID REFERENCES Movies(movie_id),
    actor_id UUID REFERENCES Actors(actor_id),
    role VARCHAR(50) NOT NULL, # "main" or "supporting"
    PRIMARY KEY (movie_id, actor_id),
    FOREIGN KEY (movie_id) REFERENCES Movies(movie_id),
    FOREIGN KEY (actor_id) REFERENCES Actors(actor_id)
);
-- Users Table
CREATE TABLE Users (
    user_id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
    user_auth VARCHAR(255) NOT NULL
    username VARCHAR(30) NOT NULL
);
-- Reviews Table
CREATE TABLE Reviews (
    review_id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
    user_id UUID REFERENCES Users(user_id),
    movie_id UUID REFERENCES Movies(movie_id),
    rating INT,
    review_text TEXT,
    review_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    UNIQUE (movie_id, user_id)
    FOREIGN KEY (user_id) REFERENCES Users(user_id),
    FOREIGN KEY (movie_id) REFERENCES Movies(movie_id)
);
-- Self Join Example for Movie Sequel Relationship
ALTER TABLE Movies
ADD COLUMN sequel_to UUID REFERENCES Movies(movie_id);

后续步骤

您可能感兴趣的内容: