Progettare gli schemi di Data Connect

Con Firebase Data Connect, puoi progettare uno schema GraphQL che rappresenta il modello di dati richiesto per la tua applicazione. Data Connect converte questo schema nell'istanza Cloud SQL per PostgreSQL che supporta la tua app. Poi, puoi creare query e mutazioni per interagire con il backend e raggruppare queste operazioni in connettori per utilizzare i dati dal codice client.

Data Connect offre strumenti di AI per aiutarti a progettare e implementare i tuoi schemi. Questa guida introduce concetti importanti di progettazione dello schema per supportare e integrare i flussi di lavoro standard e assistiti dall'AI quando inizi a sviluppare un'appe non solo.

La guida introduttiva ha presentato uno schema di app per le recensioni di film per PostgreSQL.

Questa guida sviluppa ulteriormente questo schema e fornisce un elenco SQL equivalente allo schema finale dell'app per le recensioni di film.

Lo schema per un'app per le recensioni di film

Supponiamo che tu voglia creare un servizio che consenta agli utenti di inviare e visualizzare le recensioni di film.

Per un'app di questo tipo, hai bisogno di uno schema iniziale che supporti le query di base. In un secondo momento, estenderai questo schema per creare query relazionali complesse.

In Data Connect, definirai i tipi GraphQL per definire la forma dei dati che i tuoi client possono eseguire query e manipolare. Quando scrivi lo schema, i tipi vengono convertiti in tabelle Cloud SQL per PostgreSQL, il più delle volte in una relazione diretta tra i tipi GraphQL e le tabelle di database, anche se sono possibili altre mappature. Questa guida mostra alcuni esempi, da quelli di base a quelli più avanzati.

Definire un tipo Movie di base

Puoi iniziare con un tipo Movie.

Lo schema per Movie contiene direttive di base come:

  • @table(name) e @col(name) per personalizzare i nomi delle tabelle e delle colonne SQL. Data Connect genera nomi snake_case se non vengono specificati.
  • @col(dataType) per personalizzare i tipi di colonne SQL.
  • @default per configurare i valori predefiniti delle colonne SQL durante l'inserimento.

Per ulteriori dettagli, consulta la documentazione di riferimento per @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
}

Archiviare automaticamente i dati utente importanti in un tipo User

La tua app terrà traccia degli utenti, quindi hai bisogno di un tipo User.

La direttiva @default è particolarmente utile in questo caso. Il campo id qui può recuperare automaticamente l'ID utente dall'autenticazione: nota l' utilizzo di @default(expr: "auth.uid") nell'esempio seguente.

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

Scalari chiave e valori del server

Prima di esaminare più da vicino l'app per le recensioni di film, è importante introdurre Data Connect scalari chiave e valori del server.

Gli scalari chiave sono identificatori di oggetti concisi che Data Connect assembla automaticamente dai campi chiave degli schemi. Gli scalari chiave sono incentrati sull'efficienza e ti consentono di trovare in una singola chiamata informazioni sull'identità e la struttura dei tuoi dati. Sono particolarmente utili quando vuoi eseguire azioni sequenziali sui nuovi record e hai bisogno di un identificatore univoco da passare alle operazioni imminenti, nonché quando vuoi accedere alle chiavi relazionali per eseguire operazioni aggiuntive più complesse.

Utilizzando i valori del server, puoi consentire al server di compilare dinamicamente i campi nelle tabelle utilizzando valori archiviati o facilmente calcolabili in base a espressioni CEL lato server specifiche nell'argessa expr argomento. Ad esempio, puoi definire un campo con un timestamp applicato quando si accede al campo utilizzando l'ora memorizzata in una richiesta di operazione, updatedAt: Timestamp! @default(expr: "request.time").

Gestire le relazioni molti-a-molti nei tipi Actor e MovieActor

Ora che hai gestito gli utenti, puoi tornare alla modellazione dei dati dei film.

Poi, vuoi che gli attori recitino nei tuoi film.

La tabella Actor è piuttosto semplice.

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

Se vuoi che gli attori recitino in più film e che i film abbiano più attori, avrai bisogno di una "tabella di unione".

La tabella MovieActor gestisce la relazione molti-a-molti e la sua chiave primaria è una combinazione di [movie, actor] (i campi di chiave esterna di movie e 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
}

Quando definisci una relazione SQL nella tabella con un vincolo di chiave esterna, Data Connect genera automaticamente il campo corrispondente dall' altra parte. Non devi definire il campo di mappatura inversa (ad es. da Actor a MovieActor).

Gestire le relazioni uno-a-uno in un tipo MovieMetadata

Ora, tieni traccia dei registi dei film e configura una relazione uno-a-uno con Movie.

Puoi utilizzare la direttiva @ref per personalizzare i vincoli di chiave esterna:

  • @ref(fields) specifica i campi di chiave esterna da utilizzare.
  • @ref(references) specifica i campi a cui si fa riferimento nella tabella di destinazione (il valore predefinito è la chiave primaria, ma funzionano anche i campi @unique). Questa è un'opzione più avanzata; Data Connect spesso può dedurla per te.

Per ulteriori dettagli, consulta la documentazione di riferimento per @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
}

Utilizzare i campi generati dallo schema per creare operazioni

Le operazioni di Data Connect estenderanno un insieme di campi generati automaticamente da Data Connect in base ai tipi e alle relazioni tra i tipi nello schema. Questi campi vengono generati dagli strumenti locali ogni volta che modifichi lo schema.

Supponiamo che lo schema contenga un tipo Movie e un tipo Actor associato. Data Connect genera movie, movies, actors_on_movies campi e altri ancora.

Eseguire query con il
movie campo

Il campo movie rappresenta un singolo record nella Movie tabella.

Utilizza questo campo per eseguire query su un singolo film in base alla sua chiave.

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

Eseguire query con il campo
movies

Il campo movies rappresenta un elenco di record nella Movie tabella.

Utilizza questo campo per eseguire query su più film, ad esempio tutti i film di un determinato anno.

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

Eseguire query con il
actors_on_movies campo

Il campo actors_on_movies rappresenta un elenco di record che collegano le tabelle Actor e Movie. Utilizza questo campo per eseguire query su tutti gli attori associati a un determinato film.

Utilizza questo campo per eseguire query su tutti gli attori associati a un determinato film.

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

Tenendo presente questo, puoi leggere come implementare le operazioni utilizzando questi campi nella guida all'implementazione delle query e guida all'implementazione delle mutazioni.

Concetti di schema più avanzati

Campi di enumerazione

Data Connect supporta i campi di enumerazione che eseguono il mapping ai tipi enumerati di PostgreSQL. Gli enum ti consentono di definire rapidamente un elenco di valori statici e predefiniti con un ordine specifico.

Per aggiungere un enum allo schema, dichiara l'enum e i relativi valori predefiniti, quindi fai riferimento ad esso nel 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
}

Nel tipo Movie, abbiamo aggiunto un campo enum originalAspectRatio per le proporzioni in cui è stato girato il film e un altro campo otherAspectRatios per un elenco di altre proporzioni disponibili.

Gestire le modifiche ai campi di enumerazione

Puoi aggiungere nuovi valori all'enum, ma l'ordine dell'elenco enum è molto significativo, quindi inserisci i nuovi valori con attenzione. L'unica modifica completamente compatibile con le versioni precedenti di un enum è l'aggiunta di un nuovo valore alla fine dell'elenco di valori. In particolare, l'inserimento di un nuovo valore tra gli enum pubblicati in precedenza o il riordino dei valori esistenti modifica l'ordinamento relativo quando vengono utilizzati operatori relativi come "minore di" nelle query. La rimozione o la ridenominazione dei valori è sempre una modifica incompatibile con le versioni precedenti.

Non devi mai riordinare i valori nell'elenco dei valori enum; l'ordinamento è importante perché modifica la modalità di applicazione del filtro.

Le modifiche ai valori enum devono essere eseguite con attenzione per non interrompere le versioni precedenti del codice dell'operazione o del client. Quando rimuovi o ridenomini un valore enum, assicurati che non siano rimaste istanze nel database corrente.

Utilizzare i campi enum nelle operazioni e nel codice client

Ora che hai aggiunto un campo enum allo schema, puoi utilizzarlo nelle query e nel codice client.

Scopri di più sulla scrittura di query che utilizzano gli enum e su come scrivere un client per consentire le modifiche agli enum a partire dalla guida alle query.

Altri concetti avanzati

Per andare oltre i tipi e le relazioni di base ma utili, consulta gli esempi in la documentazione di riferimento.

Tipi di dati supportati

Data Connect supporta i seguenti tipi di dati scalari, con assegnazioni ai tipi PostgreSQL utilizzando @col(dataType:).

Data Connect tipo Tipo integrato GraphQL o
Data Connect tipo personalizzato
Tipo PostgreSQL predefinito Tipi PostgreSQL supportati
(alias tra parentesi)
Stringa GraphQL testo testo
bit(n), varbit(n)
char(n), varchar(n)
Int GraphQL int Int2 (smallint, smallserial),
int4 (integer, int, serial)
In virgola mobile GraphQL float8 float4 (real)
float8 (double precision)
numeric (decimal)
Booleano GraphQL booleano booleano
UUID Personalizzato uuid uuid
Int64 Personalizzato bigint int8 (bigint, bigserial)
numeric (decimal)
Data Personalizzato date date
Timestamp Personalizzato timestamptz

timestamptz

Nota: le informazioni sul fuso orario locale non vengono archiviate.
PostgreSQL converte e archivia questi timestamp come UTC.

Enumerazione Personalizzato enum

enum

Vettoriale Personalizzato vettore

vettore

Consulta Eseguire la ricerca di similarità vettoriale con Vertex AI.

  • GraphQL List esegue il mapping a un array unidimensionale.
    • Ad esempio, [Int] esegue il mapping a int5[], [Any] esegue il mapping a jsonb[].
    • Data Connect non supporta gli array nidificati.

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

Passaggi successivi

Potresti essere interessato a: