Проектирование схем Data Connect

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

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

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

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

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

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

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

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

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

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

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

  • Используйте @table(name) и @col(name) для настройки имен таблиц и столбцов SQL. Data 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 здесь может автоматически получить идентификатор пользователя из аутентификации: обратите внимание на использование @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)")
}

Ключевые скалярные значения и значения сервера

Прежде чем подробнее рассматривать приложение для написания отзывов о фильмах, важно ознакомиться с ключевыми скалярными значениями Data Connect и значениями сервера .

Ключевые скаляры — это краткие идентификаторы объектов, которые Data 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-связи для таблицы с ограничением внешнего ключа Data Connect автоматически генерирует соответствующее поле на другой стороне. Вам не нужно определять поле обратного сопоставления (например, от Actor к MovieActor ).

Обработка отношений «один к одному» в типе MovieMetadata

Теперь вы можете отслеживать кинорежиссеров, а также наладить личные отношения с Movie .

Для настройки ограничений внешнего ключа можно использовать директиву @ref :

  • @ref(fields) указывает, какие поля внешнего ключа следует использовать.
  • @ref(references) указывает поля, на которые ссылается целевая таблица (по умолчанию используется первичный ключ, но поля @unique также подойдут). Это более продвинутый вариант; Data 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
}

Используйте поля, сгенерированные на основе вашей схемы, для построения операций.

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

Предположим, ваша схема содержит тип Movie и связанный с ним тип Actor . Data 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 }
    }
  }

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

Более сложные концепции схем

Поля перечисления

Data 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 для списка других доступных соотношений сторон.

Управление изменениями в полях перечисления

Вы можете добавлять новые значения в перечисление, но порядок элементов в списке перечисления имеет большое значение, поэтому вставляйте новые значения с умом. Единственное полностью обратно совместимое изменение перечисления — это добавление нового значения в конец списка значений. Следует отметить, что вставка нового значения между ранее опубликованными перечислениями или изменение порядка существующих значений изменяет относительный порядок при использовании относительных операторов, таких как «меньше», в запросах. Удаление или переименование значений всегда является обратно несовместимым изменением.

Никогда не следует менять порядок значений в списке значений перечисления; порядок важен, поскольку он влияет на то, как применяется фильтрация.

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

Использование полей перечисления в операциях и клиентском коде

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

Узнайте больше о написании запросов с использованием перечислений, а также о том, как написать клиент для внесения изменений в ваши перечисления, начиная с руководства по запросам .

Другие продвинутые концепции

Чтобы выйти за рамки базовых, но полезных типов и взаимосвязей, обратитесь к примерам в справочной документации .

Поддерживаемые типы данных

Data Connect поддерживает следующие скалярные типы данных, присваивание значений типам PostgreSQL осуществляется с помощью @col(dataType:) .

Тип Data Connect Встроенный тип GraphQL или
Тип пользовательского Data Connect
Тип PostgreSQL по умолчанию Поддерживаемые типы PostgreSQL
(псевдоним в скобках)
Нить GraphQL текст текст
бит(n), варбит(n)
char(n), varchar(n)
Интерн. GraphQL инт Int2 (smallint, smallserial),
int4 (integer, int, serial)
Плавать GraphQL float8 float4 (real)
float8 (двойная точность)
числовой (десятичный)
Логический GraphQL булев булев
UUID Обычай uuid uuid
Int64 Обычай бигинт int8 (bigint, bigserial)
числовой (десятичный)
Дата Обычай дата дата
Отметка времени Обычай timestamptz

timestamptz

Примечание: информация о местном часовом поясе не сохраняется.
PostgreSQL преобразует и сохраняет такие метки времени в формате UTC.

Перечисление Обычай перечисление

перечисление

Вектор Обычай вектор

вектор

См. раздел «Выполнение поиска векторного сходства с помощью Vertex AI» .

  • GraphQL List преобразует данные в одномерный массив.
    • Например, [Int] соответствует int5[] , [Any] соответствует jsonb[] .
    • Data 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);

Следующие шаги

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