Как создавать схемы SQL Connect

С помощью Firebase SQL Connect вы разрабатываете схему GraphQL, которая представляет модель данных, необходимую для вашего приложения. SQL Connect преобразует эту схему в экземпляр Cloud SQL для PostgreSQL, который поддерживает ваше приложение. Затем вы создаете запросы и мутации для взаимодействия с серверной частью и объединяете эти операции в коннекторы для использования ваших данных из клиентского кода.

SQL Connect предлагает ИИ-инструменты, которые помогут вам разрабатывать и внедрять схемы. В этом руководстве рассказывается о важных концепциях проектирования схем, которые помогут вам в работе со стандартными и ИИ-инструментами как на начальном этапе разработки приложения, так и в дальнейшем.

В руководстве по началу работы мы представили схему приложения для отзывов о фильмах для PostgreSQL.

В этом руководстве мы продолжим развивать эту схему и предоставим список SQL, эквивалентный окончательной схеме приложения для рецензирования фильмов.

Схема для приложения с рецензиями на фильмы

Представьте, что вы хотите создать сервис, который позволяет пользователям отправлять и просматривать отзывы о фильмах.

Вам нужна начальная схема для такого приложения, чтобы поддерживать базовые запросы. Позже вы расширите эту схему, чтобы создавать сложные реляционные запросы.

В SQL Connect вы будете определять типы GraphQL, чтобы задавать форму данных, которые ваши клиенты могут запрашивать и которыми могут манипулировать. При написании схемы типы преобразуются в таблицы Cloud SQL для PostgreSQL. Чаще всего между типами GraphQL и таблицами базы данных существует прямая связь, хотя возможны и другие сопоставления. В этом руководстве приведены примеры от простых до более сложных.

Определите базовый тип Movie

Вы можете начать с типа Movie.

Схема Movie содержит основные директивы, такие как:

  • @table(name) и @col(name), чтобы настроить таблицу SQL и названия столбцов. Если не указано, SQL Connect генерирует имена в змеином регистре.
  • @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 может автоматически получать идентификатор пользователя из аутентификации. Обратите внимание на использование @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 автоматически собирает из ключевых полей в ваших схемах. Ключевые скаляры связаны с эффективностью, позволяя вам найти информацию об идентичности и структуре ваших данных за один вызов. Они особенно полезны, когда вам нужно выполнить последовательные действия с новыми записями и вам нужен уникальный идентификатор для передачи предстоящим операциям, а также когда вы хотите получить доступ к реляционным ключам для выполнения дополнительных, более сложных операций.

Используя значения сервера, вы можете разрешить серверу динамически заполнять поля в таблицах, используя сохраненные или легко вычисляемые значения в соответствии с определенными серверными выражениями CEL в аргументе expr. Например, вы можете определить поле с временной меткой, которая применяется при доступе к полю с использованием времени, сохраненного в запросе на операцию, 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 поддерживает следующие скалярные типы данных с присвоением типов PostgreSQL с помощью @col(dataType:).

Тип SQL Connect Встроенный тип GraphQL или
SQL Connect собственный тип
Тип PostgreSQL по умолчанию Поддерживаемые типы PostgreSQL
(псевдоним в скобках)
Строка GraphQL Текст text
bit(n), varbit(n)
char(n), varchar(n)
ПЕР GraphQL int 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

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

Дальнейшие действия

Вас может заинтересовать: