Cómo diseñar esquemas de Data Connect

Con Firebase Data Connect, diseñas un esquema de GraphQL que representa el modelo de datos requerido para tu aplicación. Data Connect convierte este esquema en la instancia de Cloud SQL para PostgreSQL que respalda tu app. Luego, creas consultas y mutaciones para interactuar con el backend y agrupar estas operaciones en conectores para usar tus datos desde el código del cliente.

Data Connect ofrece herramientas de IA para ayudarte a diseñar e implementar tus esquemas. En esta guía, se presentan conceptos importantes del diseño de esquemas para complementar y respaldar tus flujos de trabajo estándar y asistidos por IA cuando comiences a desarrollar una app y más allá.

En la guía de inicio, se presentó un esquema de app de revisión de películas para PostgreSQL.

En esta guía, se desarrolla aún más ese esquema y se proporciona un listado de SQL equivalente al esquema final de la app de reseñas de películas.

El esquema de una app de opiniones sobre películas

Imagina que quieres crear un servicio que permita a los usuarios enviar y ver reseñas de películas.

Necesitas un esquema inicial para que la app admita consultas básicas. Extenderás este esquema más adelante para crear consultas relacionales complejas.

En Data Connect, definirás tipos de GraphQL para definir la forma de los datos que tus clientes pueden consultar y manipular. Cuando escribes tu esquema, tus tipos se traducen a tablas de Cloud SQL para PostgreSQL, la mayoría de las veces en una relación directa entre los tipos de GraphQL y las tablas de la base de datos, aunque son posibles otras asignaciones. En esta guía, se muestran algunos ejemplos, desde los más básicos hasta los más avanzados.

Cómo definir un tipo Movie básico

Puedes comenzar con un tipo de Movie.

El esquema de Movie contiene directivas principales, como las siguientes:

  • @table(name) y @col(name) para personalizar los nombres de las tablas y las columnas de SQL Data Connect genera nombres en formato snake_case si no se especifican.
  • @col(dataType) para personalizar los tipos de columnas de SQL
  • @default para configurar los valores predeterminados de las columnas de SQL durante la inserción.

Para obtener más detalles, consulta los documentos de referencia de @table, @col y @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
}

Almacena automáticamente datos importantes del usuario en un tipo User

Tu app hará un seguimiento de los usuarios, por lo que necesitas un tipo User.

La directiva @default es especialmente útil en este caso. El campo id aquí puede tomar automáticamente el ID del usuario de la autenticación: observa el uso de @default(expr: "auth.uid") en el siguiente ejemplo.

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

Escalares de claves y valores del servidor

Antes de analizar más la app de reseñas de películas, es importante presentar los escalares clave y los valores del servidor de Data Connect.

Los escalares clave son identificadores de objetos concisos que Data Connect ensambla automáticamente a partir de los campos clave de tus esquemas. Los escalares clave se relacionan con la eficiencia, ya que te permiten encontrar en una sola llamada información sobre la identidad y la estructura de tus datos. Son especialmente útiles cuando deseas realizar acciones secuenciales en registros nuevos y necesitas un identificador único para pasar a las próximas operaciones, y también cuando deseas acceder a claves relacionales para realizar operaciones adicionales más complejas.

Con los valores del servidor, puedes permitir que el servidor complete de forma dinámica los campos de tus tablas con valores almacenados o que se pueden calcular fácilmente según expresiones CEL particulares del servidor en el argumento expr. Por ejemplo, puedes definir un campo con una marca de tiempo aplicada cuando se accede al campo con la hora almacenada en una solicitud de operación, updatedAt: Timestamp! @default(expr: "request.time").

Cómo controlar relaciones de varios a varios en tipos Actor y MovieActor

Con los usuarios controlados, puedes volver a modelar los datos de películas.

Luego, querrás que los actores protagonicen tus películas.

La tabla Actor es bastante sencilla.

# 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)")
}

Si quieres que los actores participen en varias películas y que las películas tengan varios actores, necesitarás una "tabla de unión".

La tabla MovieActor controla la relación de varios a varios, y su clave primaria es una combinación de [movie, actor] (los campos de clave externa de movie y 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
}

Cuando defines una relación de SQL en la tabla con una restricción de clave externa, Data Connect genera automáticamente el campo correspondiente en el otro lado. No es necesario que definas el campo de asignación inversa (p.ej., de Actor a MovieActor).

Cómo controlar relaciones de uno a uno en un tipo MovieMetadata

Ahora, haz un seguimiento de los directores de películas y configura una relación uno a uno con Movie.

Puedes usar la directiva @ref para personalizar las restricciones de clave externa:

  • @ref(fields) especifica qué campos de clave externa se deben usar.
  • @ref(references) especifica los campos a los que se hace referencia en la tabla de destino (de forma predeterminada, la clave primaria, pero los campos @unique también funcionan). Esta es una opción más avanzada; Data Connect a menudo puede inferir esto por ti.

Para obtener más detalles, consulta los documentos de referencia de @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
}

Usa los campos generados a partir de tu esquema para crear operaciones

Tus operaciones de Data Connect extenderán un conjunto de campos Data Connect generados automáticamente según los tipos y las relaciones de tipos en tu esquema. Estos campos se generan con herramientas locales cada vez que editas tu esquema.

Supongamos que tu esquema contiene un tipo Movie y un tipo Actor asociado. Data Connect genera los campos movie, movies y actors_on_movies, entre otros.

Consulta con el campo
movie

El campo movie representa un solo registro en la tabla Movie.

Usa este campo para consultar una sola película por su clave.

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

Consulta con el campo
movies

El campo movies representa una lista de registros en la tabla Movie.

Usa este campo para consultar varias películas, por ejemplo, todas las películas de un año determinado.

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

Consulta con el campo
actors_on_movies

El campo actors_on_movies representa una lista de registros que conectan las tablas Actor y Movie. Usa este campo para consultar todos los actores asociados con una película determinada.

Usa este campo para consultar todos los actores asociados con una película determinada.

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

Teniendo esto en cuenta, puedes leer cómo implementar operaciones con estos campos en la guía para implementar consultas y la guía para implementar mutaciones.

Conceptos de esquema más avanzados

Campos de enumeración

Data Connect admite campos de enumeración que se asignan a tipos enumerados de PostgreSQL. Los enums te permiten definir rápidamente una lista de valores estáticos y predefinidos con un orden específico.

Para agregar una enumeración a tu esquema, declara la enumeración y sus valores predefinidos, y, luego, haz referencia a ella en tu tipo.

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
}

En el tipo Movie, agregamos un campo de enumeración originalAspectRatio para la relación de aspecto en la que se filmó la película y otro campo otherAspectRatios para una lista de otras relaciones de aspecto disponibles.

Administra los cambios en los campos de enumeración

Puedes agregar valores nuevos a tu enumeración, pero el orden de la lista de enumeración es muy significativo, por lo que debes insertar los valores nuevos con prudencia. El único cambio completamente retrocompatible en una enumeración es agregar un valor nuevo al final de la lista de valores. En particular, insertar un valor nuevo entre enumeraciones publicadas anteriormente o reordenar los valores existentes cambia el orden relativo cuando se usan operadores relativos, como "menor que", en las búsquedas. Quitar o cambiar el nombre de los valores siempre es un cambio incompatible con versiones anteriores.

Nunca debes reordenar los valores en la lista de valores de enumeración; el orden es importante, ya que cambia la forma en que se aplica el filtrado.

Los ajustes a los valores de enumeración deben realizarse con cuidado para no interrumpir versiones anteriores de tu operación o código de cliente. Cuando quites o cambies el nombre de un valor de enumeración, asegúrate de que no queden instancias en tu base de datos actual.

Cómo usar tus campos de enumeración en operaciones y en código cliente

Ahora que agregaste un campo de enumeración a tu esquema, puedes usarlo en consultas y código de cliente.

Obtén más información para escribir consultas con enumeraciones y sobre cómo escribir un cliente para permitir ajustes en tus enumeraciones en la guía de consultas.

Otros conceptos avanzados

Para ir más allá de los tipos y las relaciones básicos, pero útiles, consulta los ejemplos en la documentación de referencia.

Tipos de datos admitidos

Data Connect admite los siguientes tipos de datos escalares, con asignaciones a tipos de PostgreSQL a través de @col(dataType:).

Data Connect type Tipo integrado de GraphQL o tipo personalizado
Data Connect
Tipo de PostgreSQL predeterminado Tipos de PostgreSQL admitidos
(alias entre paréntesis)
String GraphQL texto text
bit(n), varbit(n)
char(n), varchar(n)
Int GraphQL int Int2 (smallint, smallserial),
int4 (integer, int, serial)
Número de punto flotante GraphQL float8 float4 (real)
float8 (doble precisión)
numeric (decimal)
Booleano GraphQL booleano booleano
UUID Personalizado uuid uuid
Int64 Personalizado bigint int8 (bigint, bigserial)
numeric (decimal)
Fecha Personalizado date fecha
Marca de tiempo Personalizado timestamptz

timestamptz

Nota: No se almacena información sobre la zona horaria local.
PostgreSQL convierte y almacena esas marcas de tiempo como UTC.

Enumeration Personalizado enum

enum

Vector Personalizado vector

vector

Consulta Cómo realizar una búsqueda de similitud de vectores con Vertex AI.

  • El List de GraphQL se asigna a un array unidimensional.
    • Por ejemplo, [Int] se asigna a int5[] y [Any] se asigna a jsonb[].
    • Data Connect no admite arrays anidados.

Esquema de SQL equivalente

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

Próximos pasos

También te puede interesar: