Firebase Data Connect ti consente di creare connettori per le tue istanze PostgreSQL gestite con Google Cloud SQL. Questi connettori sono combinazioni di schema, query e mutazioni per l'utilizzo dei dati.
La guida introduttiva ha introdotto uno schema dell'app di recensione di film per PostgreSQL e questa guida esamina più da vicino come progettare schemi Data Connect per PostgreSQL.
Questa guida abbina query e mutazioni Data Connect a esempi di schema. Perché parlare di query (e di mutazioni) in una guida sugli schemiData Connect? Come altre piattaforme basate su GraphQL, Firebase Data Connect è una piattaforma di sviluppo incentrata sulle query, pertanto, come sviluppatore, nella definizione del modello di dati dovrai tenere conto dei dati di cui i tuoi clienti hanno bisogno, che influenzeranno notevolmente lo schema di dati che sviluppi per il tuo progetto.
Questa guida inizia con un nuovo schema per le recensioni di film, poi illustra le query e le mutazioni derivate da questo schema e infine fornisce un elenco SQL equivalente allo schema Data Connect di base.
Lo schema di un'app di recensioni di film
Immagina di voler creare un servizio che consenta agli utenti di inviare e visualizzare recensioni di film.
Per un'app di questo tipo è necessario uno schema iniziale, che verrà esteso in un secondo momento per creare query relazionali complesse.
Tabella Film
Lo schema per i film contiene direttive di base come:
@table(name)
e@col(name)
per personalizzare i nomi delle tabelle e delle colonne SQL. Se non specificato, Data Connect genera nomi in snake_case.@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
}
Valori scalari chiave e del server
Prima di esaminare ulteriormente l'app di recensione di film, introduciamo Data Connect scalari chiave e valori del server.
Gli scalari chiave sono identificatori di oggetti concisi che Data Connect assembla automaticamente dai campi chiave negli schemi. Gli scalari principali riguardano l'efficienza, consentendoti di trovare in una singola chiamata informazioni sull'identità e sulla struttura dei tuoi dati. Sono particolarmente utili quando vuoi eseguire azioni sequenziali su nuovi record e hai bisogno di un identificatore univoco da passare alle operazioni successive, nonché quando vuoi accedere alle chiavi relazionali per eseguire ulteriori operazioni più complesse.
Utilizzando i valori del server, puoi consentire al server di compilare dinamicamente i campi delle tabelle utilizzando valori memorizzati o facilmente calcolabili in base a determinate espressioni CEL lato server nell'argomento expr
. Ad esempio, puoi
definire un campo con un timestamp applicato quando si accede al campo utilizzando il
tempo memorizzato in una richiesta di operazione, updatedAt: Timestamp! @default(expr: "request.time")
.
Tabella dei metadati dei film
Ora teniamo traccia dei registi dei film e stabiliamo una relazione uno a uno con Movie
.
Aggiungi il campo di riferimento per definire una relazione.
Puoi utilizzare la direttiva @ref
per personalizzare il vincolo della chiave esterna.
@ref(fields)
per specificare i campi della chiave esterna.@ref(references)
per specificare i campi a cui si fa riferimento nella tabella di destinazione. Per impostazione predefinita, questo riferimento è la chiave principale, ma sono supportati anche i campi con@unique
.
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
}
Actor e MovieActor
Poi vuoi che gli attori recitino nei tuoi film e, poiché hai una relazione molti-a-molti tra film e attori, crea una tabella di join.
# 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)")
}
# 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
}
Utente
Infine, gli utenti della tua app.
# Users
# Suppose a user can leave reviews for movies
type User @table {
id: String! @default(expr: "auth.uid")
username: String! @col(dataType: "varchar(50)")
}
Tipi di dati supportati
Data Connect supporta i seguenti tipi di dati scalari, con assegnazioni ai tipi PostgreSQL utilizzando @col(dataType:)
.
Tipo Data Connect | Tipo integrato di GraphQL o Data Connect tipo personalizzato |
Tipo PostgreSQL predefinito | Tipi PostgreSQL supportati (alias tra parentesi) |
---|---|---|---|
Stringa | GraphQL | testo | text 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 (precisione doppia) numeric (decimale) |
Booleano | GraphQL | booleano | booleano |
UUID | Personalizzato | uuid | UUID |
Int64 | Personalizzato | bigint | int8 (bigint, bigserial) numeric (decimale) |
Data | Personalizzato | date | data |
Timestamp | Personalizzato | timestamptz | timestamptz Nota:le informazioni sul fuso orario locale non vengono memorizzate. |
Vettoriale | Personalizzato | vector | vettoriale Consulta Eseguire ricerche di somiglianza vettoriale con Vertex AI. |
List
di GraphQL viene mappato a un array unidimensionale.- Ad esempio,
[Int]
corrisponde aint5[]
e[Any]
ajsonb[]
. - Data Connect non supporta gli array nidificati.
- Ad esempio,
Utilizzare i campi generati per creare query e mutazioni
Le query e le mutazioni Data Connect estenderanno un insieme di campi Data Connect generati automaticamente 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.
Come hai scoperto nella guida Per iniziare, la console Firebase e i nostri strumenti di sviluppo locale utilizzano questi campi generati automaticamente per fornirti query e mutazioni amministrative ad hoc che puoi utilizzare per eseguire il seeding dei dati e verificare i contenuti delle tabelle.
Durante il processo di sviluppo, implementerai query di deployment e mutazioni di deployment raggruppate nei connettori in base a questi campi generati automaticamente.
Nomi dei campi generati automaticamente
Data Connect deducono nomi adatti per i campi generati automaticamente in base alle dichiarazioni di tipo dello schema. Ad esempio, se utilizzi un'origine PostgreSQL e definisci una tabella denominata Movie
, il server genererà:
- Campi per la lettura dei dati nei casi d'uso con una singola tabella con i nomi facili da ricordare
movie
(singolare, per il recupero di singoli risultati passando argomenti comeeq
) emovies
(plurale, per il recupero di elenchi di risultati passando argomenti comegt
e operazioni comeorderby
). Data Connect genera anche campi per operazioni relazionali multi-tabella con nomi espliciti comeactors_on_movies
oactors_via_actormovie
. - Campi per la scrittura di dati con nomi familiari come
movie_insert
,movie_upsert
…
Il linguaggio di definizione dello schema ti consente inoltre di controllare esplicitamente il modo in cui vengono generati i nomi per i campi utilizzando gli argomenti delle istruzioni singular
e plural
.
Direttive per query e mutazioni
Oltre alle direttive utilizzate per definire tipi e tabelle,
Data Connect fornisce le direttive @auth
, @check
, @redact
e
@transaction
per migliorare il comportamento di query e mutazioni.
Direttiva | Applicabile a | Descrizione |
---|---|---|
@auth |
Query e mutazioni | Definisce il criterio di autenticazione per una query o una mutazione. Consulta la guida all'autorizzazione e all'attestazione. |
@check |
Query di ricerca dei dati di autorizzazione | Verifica che i campi specificati siano presenti nei risultati della query. Un'espressione Common Expression Language (CEL) viene utilizzata per testare i valori dei campi. Consulta la guida all'autorizzazione e all'attestazione. |
@redact |
Query | Oscura una parte della risposta del cliente. Consulta la guida all'autorizzazione e all'attestazione. |
@transaction |
Mutazioni | Impone che una mutazione venga sempre eseguita in una transazione di database. Consulta gli esempi di mutazioni delle app di film. |
Query per il database delle recensioni di film
Definisci una query Data Connect con una dichiarazione del tipo di operazione di query, il nome dell'operazione, zero o più argomenti dell'operazione e zero o più direttive con argomenti.
Nella guida rapida, la query di esempio listEmails
non ha richiesto parametri. Ovviamente, in molti casi, i dati passati ai campi di query saranno dinamici. Puoi utilizzare la sintassi $variableName
per lavorare con le variabili come uno dei componenti di una definizione di query.
Pertanto, la seguente query ha:
- Una definizione di tipo
query
- Un nome di operazione (query)
ListMoviesByGenre
- Un argomento dell'operazione
$genre
con una singola variabile - Una singola direttiva,
@auth
.
query ListMoviesByGenre($genre: String!) @auth(level: USER)
Ogni argomento della query richiede una dichiarazione di tipo, un valore predefinito come String
o un
tipo personalizzato definito dallo schema come Movie
.
Vediamo la firma di query sempre più complesse. Al termine, introdurrai espressioni di relazioni efficaci e concise che puoi utilizzare per creare le tue query di deployment.
Scalari chiave nelle query
Ma prima, una nota sugli scalari principali.
Data Connect definisce un tipo speciale per gli scalari chiave, identificati da
_Key
. Ad esempio, il tipo di una chiave scalare per la nostra tabella Movie
è
Movie_Key
.
Puoi recuperare gli scalari chiave come risposta restituita dalla maggior parte dei campi di lettura generati automaticamente o, naturalmente, dalle query in cui hai recuperato tutti i campi necessari per creare la chiave scalare.
Le query automatiche singolari, come movie
nel nostro esempio in esecuzione, supportano un argomento chiave che accetta una chiave scalare.
Puoi passare una chiave scalare come valore letterale. Tuttavia, puoi definire variabili per passare scalari chiave come input.
query GetMovie($myKey: Movie_Key!) {
movie(key: $myKey) { title }
}
Questi possono essere forniti nel JSON della richiesta come questo (o in altri formati di serializzazione):
{
# …
"variables": {
"myKey": {"foo": "some-string-value", "bar": 42}
}
}
Grazie alla sintassi scalare personalizzata, un Movie_Key
può essere costruito anche utilizzando la sintassi dell'oggetto, che può contenere variabili. Questa operazione è utile soprattutto se per qualche motivo vuoi suddividere i singoli componenti in variabili diverse.
Alias nelle query
Data Connect supporta l'aliasing GraphQL nelle query. Con gli alias, puoi rinominare i dati restituiti nei risultati di una query. Una singola query Data Connect può applicare più filtri o altre operazioni di query in una richiesta efficiente al server, emettendo effettivamente più "sottoquery" contemporaneamente. Per evitare conflitti di nomi nel set di dati restituito, utilizza gli alias per distinguere le sottoquery.
Ecco una query in cui un'espressione utilizza l'alias mostPopular
.
query ReviewTopPopularity($genre: String) {
mostPopular: review(first: {
where: {genre: {eq: $genre}},
orderBy: {popularity: DESC}
}) { … }
}
Query semplici con filtri
Le query Data Connect si mappano a tutti i filtri e le operazioni di ordinamento SQL comuni.
Operatori where
e orderBy
(query singolari e plurali)
Restituisce tutte le righe corrispondenti della tabella (e le associazioni nidificate). Restituisce un array vuoto se nessun record corrisponde al filtro.
query MovieByTopRating($genre: String) {
mostPopular: movies(
where: { genre: { eq: $genre } }, orderBy: { rating: DESC }
) {
# graphql: list the fields from the results to return
id
title
genre
description
}
}
query MoviesByReleaseYear($min: Int, $max: Int) {
movies(where: {releaseYear: {le: $max, ge: $min}}, orderBy: [{releaseYear: ASC}]) { … }
}
Operatori limit
e offset
(query singolari e plurali)
Puoi eseguire l'impaginazione dei risultati. Questi argomenti sono accettati, ma non vengono riportati nei risultati.
query MoviesTop10 {
movies(orderBy: [{ rating: DESC }], limit: 10) {
# graphql: list the fields from the results to return
title
}
}
include per i campi di array
Puoi verificare che un campo array includa un elemento specificato.
# Filter using arrays and embedded fields.
query ListMoviesByTag($tag: String!) {
movies(where: { tags: { includes: $tag }}) {
# graphql: list the fields from the results to return
id
title
}
}
Operazioni sulle stringhe ed espressioni regolari
Le query possono utilizzare le normali operazioni di ricerca e confronto di stringhe, incluse le espressioni regolari. Tieni presente che, per motivi di efficienza, stai raggruppando diverse operazioni qui e le stai disambiguando con gli alias.
query MoviesTitleSearch($prefix: String, $suffix: String, $contained: String, $regex: String) {
prefixed: movies(where: {title: {startsWith: $prefix}}) {...}
suffixed: movies(where: {title: {endsWith: $suffix}}) {...}
contained: movies(where: {title: {contains: $contained}}) {...}
matchRegex: movies(where: {title: {pattern: {regex: $regex}}}) {...}
}
or
e and
per i filtri composti
Utilizza or
e and
per una logica più complessa.
query ListMoviesByGenreAndGenre($minRating: Int!, $genre: String) {
movies(
where: { _or: [{ rating: { ge: $minRating } }, { genre: { eq: $genre } }] }
) {
# graphql: list the fields from the results to return
title
}
}
Query complesse
Le query Data Connect possono accedere ai dati in base alle relazioni tra le tabelle. Puoi utilizzare le relazioni di oggetti (one-to-one) o array (one-to-many) definite nello schema per eseguire query nidificate, ovvero recuperare i dati di un tipo insieme ai dati di un tipo nidificato o correlato.
Queste query utilizzano la sintassi magica Data Connect _on_
e _via
nei
campi di lettura generati.
Apportare modifiche allo schema dalla nostra versione iniziale.
Molti a uno
Aggiungiamo le recensioni alla nostra app, con una tabella Review
e modifiche a User
.
# User table is keyed by Firebase Auth UID.
type User @table {
# `@default(expr: "auth.uid")` sets it to Firebase Auth UID during insert and upsert.
id: String! @default(expr: "auth.uid")
username: String! @col(dataType: "varchar(50)")
# The `user: User!` field in the Review table generates the following one-to-many query field.
# reviews_on_user: [Review!]!
# The `Review` join table the following many-to-many query field.
# movies_via_Review: [Movie!]!
}
# Reviews is a join table tween User and Movie.
# It has a composite primary keys `userUid` and `movieId`.
# A user can leave reviews for many movies. A movie can have reviews from many users.
# User <-> Review is a one-to-many relationship
# Movie <-> Review is a one-to-many relationship
# Movie <-> User is a many-to-many relationship
type Review @table(name: "Reviews", key: ["movie", "user"]) {
user: User!
# The user field adds the following foreign key field. Feel free to uncomment and customize it.
# userUid: String!
movie: Movie!
# The movie field adds the following foreign key field. Feel free to uncomment and customize it.
# movieId: UUID!
rating: Int
reviewText: String
reviewDate: Date! @default(expr: "request.time")
}
Query many-to-one
Ora esaminiamo una query con aliasing per illustrare la sintassi di _via_
.
query UserMoviePreferences($username: String!) @auth(level: USER) {
users(where: { username: { eq: $username } }) {
likedMovies: movies_via_Review(where: { rating: { ge: 4 } }) {
title
genre
}
dislikedMovies: movies_via_Review(where: { rating: { le: 2 } }) {
title
genre
}
}
}
Uno a uno
Puoi vedere il pattern. Di seguito, lo schema è modificato per illustrazione.
# Movies
type Movie
@table(name: "Movies", singular: "movie", plural: "movies", key: ["id"]) {
id: UUID! @col(name: "movie_id") @default(expr: "uuidV4()")
title: String!
releaseYear: Int @col(name: "release_year")
genre: String
rating: Int @col(name: "rating")
description: String @col(name: "description")
tags: [String] @col(name: "tags")
}
# Movie Metadata
# Movie - MovieMetadata is a one-to-one relationship
type MovieMetadata
@table(
name: "MovieMetadata"
) {
# @ref creates a field in the current table (MovieMetadata) that holds the primary key of the referenced type
# In this case, @ref(fields: "id") is implied
movie: Movie! @ref
# movieId: UUID <- this is created by the above @ref
director: String @col(name: "director")
}
extend type MovieMetadata {
movieId: UUID! # matches primary key of referenced type
...
}
extend type Movie {
movieMetadata: MovieMetadata # can only be non-nullable on ref side
# conflict-free name, always generated
movieMetadatas_on_movie: MovieMetadata
}
Query per una sessione individuale
Puoi eseguire query utilizzando la sintassi _on_
.
# One to one
query GetMovieMetadata($id: UUID!) @auth(level: PUBLIC) {
movie(id: $id) {
movieMetadatas_on_movie {
director
}
}
}
Molti a molti
I film hanno bisogno di attori e gli attori hanno bisogno di film. Hanno un rapporto many-to-many
che puoi modellare con una tabella di join MovieActors
.
# MovieActors Join Table Definition
type MovieActors @table(
key: ["movie", "actor"] # join key triggers many-to-many generation
) {
movie: Movie!
actor: Actor!
}
# generated extensions for the MovieActors join table
extend type MovieActors {
movieId: UUID!
actorId: UUID!
}
# Extensions for Actor and Movie to handle many-to-many relationships
extend type Movie {
movieActors: [MovieActors!]! # standard many-to-one relation to join table
actors: [Actor!]! # many-to-many via join table
movieActors_on_actor: [MovieActors!]!
# since MovieActors joins distinct types, type name alone is sufficiently precise
actors_via_MovieActors: [Actor!]!
}
extend type Actor {
movieActors: [MovieActors!]! # standard many-to-one relation to join table
movies: [Movie!]! # many-to-many via join table
movieActors_on_movie: [MovieActors!]!
movies_via_MovieActors: [Movie!]!
}
Query per molti a molti
Diamo un'occhiata a una query con aliasing per illustrare la sintassi di _via_
.
query GetMovieCast($movieId: UUID!, $actorId: UUID!) @auth(level: PUBLIC) {
movie(id: $movieId) {
mainActors: actors_via_MovieActor(where: { role: { eq: "main" } }) {
name
}
supportingActors: actors_via_MovieActor(
where: { role: { eq: "supporting" } }
) {
name
}
}
actor(id: $actorId) {
mainRoles: movies_via_MovieActor(where: { role: { eq: "main" } }) {
title
}
supportingRoles: movies_via_MovieActor(
where: { role: { eq: "supporting" } }
) {
title
}
}
}
Query di aggregazione
Che cosa sono gli aggregati e perché utilizzarli?
I campi aggregati ti consentono di eseguire calcoli su un elenco di risultati. Con i campi aggregati puoi eseguire operazioni come:
- Trovare il punteggio medio di una recensione
- Trovare il costo totale degli articoli in un carrello degli acquisti
- Trovare il prodotto con la valutazione più alta o più bassa
- Contare il numero di prodotti nel tuo negozio
Le aggregazioni vengono eseguite sul server, il che offre una serie di vantaggi rispetto al calcolo lato client:
- Rendimento più rapido dell'app (poiché vengono evitati i calcoli lato client)
- Costi di esportazione dei dati ridotti (poiché invii solo i risultati aggregati anziché tutti gli input)
- Maggiore sicurezza (poiché puoi concedere ai clienti l'accesso ai dati aggregati anziché all'intero set di dati)
Schema di esempio per gli aggregati
In questa sezione passeremo a uno schema di esempio di vetrina, che è utile per spiegare come utilizzare gli aggregati:
type Product @table {
name: String!
manufacturer: String!
quantityInStock: Int!
price: Float!
expirationDate: Date
}
Aggregati semplici
_count per tutti i campi
Il campo aggregato più semplice è _count
: restituisce il numero di righe corrispondenti alla query. Per ogni campo del tipo, Data Connect
genera i campi aggregati corrispondenti a seconda del tipo di campo.
Query
query CountProducts {
products {
_count
}
}
Risposta one
one
Ad esempio, se il tuo database contiene 5 prodotti, il risultato sarà:
{
"products": [
{
"_count": 5
}
]
}
Tutti i campi hanno un campo <field>_count
, che conteggia il numero di righe con un valore non nullo in quel campo.
Query
query CountProductsWithExpirationDate {
products {
expirationDate_count
}
}
Rispostafield_count
field_count
Ad esempio, se hai 3 prodotti con una data di scadenza, il risultato sarà:
{
"products": [
{
"expirationDate_count": 3
}
]
}
_min, _max, _sum e _avg per i campi numerici
I campi numerici (int, float, int64) hanno anche <field>_min
, <field>_max
,
<field>_sum
e <field>_avg
.
Query
query NumericAggregates {
products {
quantityInStock_max
price_min
price_avg
quantityInStock_sum
}
}
Risposta_min _max _sum _avg
_min _max _sum _avg
Ad esempio, se hai i seguenti prodotti:
- Prodotto A:
quantityInStock: 10
,price: 2.99
- Prodotto B:
quantityInStock: 5
,price: 5.99
- Prodotto C:
quantityInStock: 20
,price: 1.99
Il risultato sarà:
{
"products": [
{
"quantityInStock_max": 20,
"price_min": 1.99,
"price_avg": 3.6566666666666666,
"quantityInStock_sum": 35
}
]
}
_min e _max per date e timestamp
I campi Data e Timestamp hanno <field>_min
e <field>_max
.
Query
query DateAndTimeAggregates {
products {
expirationDate_max
expirationDate_min
}
}
Risposta_min _maxdatetime
_min _maxdatetime
Ad esempio, se hai le seguenti date di scadenza:
- Prodotto A:
2024-01-01
- Prodotto B:
2024-03-01
- Prodotto C:
2024-02-01
Il risultato sarà:
{
"products": [
{
"expirationDate_max": "2024-03-01",
"expirationDate_min": "2024-01-01"
}
]
}
Distinct
L'argomento distinct
consente di ottenere tutti i valori univoci per un campo
(o una combinazione di campi). Ad esempio:
Query
query ListDistinctManufacturers {
products(distinct: true) {
manufacturer
}
}
Rispostadistinct
distinct
Ad esempio, se hai i seguenti produttori:
- Prodotto A:
manufacturer: "Acme"
- Prodotto B:
manufacturer: "Beta"
- Prodotto C:
manufacturer: "Acme"
Il risultato sarà:
{
"products": [
{ "manufacturer": "Acme" },
{ "manufacturer": "Beta" }
]
}
Puoi anche utilizzare l'argomento distinct
sui campi aggregati per aggregare i valori distinti. Ad esempio:
Query
query CountDistinctManufacturers {
products {
manufacturer_count(distinct: true)
}
}
Rispostadistinctonaggregate
distinctonaggregate
Ad esempio, se hai i seguenti produttori:
- Prodotto A:
manufacturer: "Acme"
- Prodotto B:
manufacturer: "Beta"
- Prodotto C:
manufacturer: "Acme"
Il risultato sarà:
{
"products": [
{
"manufacturer_count": 2
}
]
}
Dati aggregati raggruppati
Esegui un aggregato raggruppato selezionando una combinazione di campi aggregati e non aggregati per un tipo. Questo raggruppa tutte le righe corrispondenti che hanno lo stesso valore per i campi non aggregati e calcola i campi aggregati per quel gruppo. Ad esempio:
Query
query MostExpensiveProductByManufacturer {
products {
manufacturer
price_max
}
}
Rispostagroupedaggregates
groupedaggregates
Ad esempio, se hai i seguenti prodotti:
- Prodotto A:
manufacturer: "Acme"
,price: 2.99
- Prodotto B:
manufacturer: "Beta"
,price: 5.99
- Prodotto C:
manufacturer: "Acme"
,price: 1.99
Il risultato sarà:
{
"products": [
{ "manufacturer": "Acme", "price_max": 2.99 },
{ "manufacturer": "Beta", "price_max": 5.99 }
]
}
having
e where
con aggregati raggruppati
Puoi anche utilizzare l'argomento having
e where
per restituire solo i gruppi chesoddisfano i criteri specificati.
having
ti consente di filtrare i gruppi in base ai relativi campi aggregatiwhere
consente di filtrare le righe in base a campi non aggregati.
Query
query FilteredMostExpensiveProductByManufacturer {
products(having: {price_max: {ge: 2.99}}) {
manufacturer
price_max
}
}
Rispostahavingwhere
havingwhere
Ad esempio, se hai i seguenti prodotti:
- Prodotto A:
manufacturer: "Acme"
,price: 2.99
- Prodotto B:
manufacturer: "Beta"
,price: 5.99
- Prodotto C:
manufacturer: "Acme"
,price: 1.99
Il risultato sarà:
{
"products": [
{ "manufacturer": "Acme", "price_max": 2.99 },
{ "manufacturer": "Beta", "price_max": 5.99 }
]
}
Aggregazioni in più tabelle
I campi aggregati possono essere utilizzati insieme ai campi della relazione uno a molti generati per rispondere a domande complesse sui dati. Di seguito è riportato uno schema modificato, con una tabella separata, Manufacturer
, che possiamo utilizzare negli esempi:
type Product @table {
name: String!
manufacturer: Manufacturer!
quantityInStock: Int!
price: Float!
expirationDate: Date
}
type Manufacturer @table {
name: String!
headquartersCountry: String!
}
Ora possiamo utilizzare i campi aggregati per trovare, ad esempio, il numero di prodotti realizzati da un produttore:
Query
query GetProductCount($id: UUID) {
manufacturers {
name
products_on_manufacturer {
_count
}
}
}
Risposta aggregatesacrosstables
aggregatesacrosstables
Ad esempio, se hai i seguenti produttori:
- Produttore A:
name: "Acme"
,products_on_manufacturer: 2
- Produttore B:
name: "Beta"
,products_on_manufacturer: 1
Il risultato sarà:
{
"manufacturers": [
{ "name": "Acme", "products_on_manufacturer": { "_count": 2 } },
{ "name": "Beta", "products_on_manufacturer": { "_count": 1 } }
]
}
Mutazioni per il database delle recensioni di film
Come accennato, quando definisci una tabella nello schema, Data Connect genererà mutazioni implicite di base per ogni tabella.
type Movie @table { ... }
extend type Mutation {
# Insert a row into the movie table.
movie_insert(...): Movie_Key!
# Upsert a row into movie."
movie_upsert(...): Movie_Key!
# Update a row in Movie. Returns null if a row with the specified id/key does not exist
movie_update(...): Movie_Key
# Update rows based on a filter in Movie.
movie_updateMany(...): Int!
# Delete a single row in Movie. Returns null if a row with the specified id/key does not exist
movie_delete(...): Movie_Key
# Delete rows based on a filter in Movie.
movie_deleteMany(...): Int!
}
Con questi, puoi implementare casi CRUD di base sempre più complessi. Ripeti cinque volte velocemente.
Istruzione @transaction
Questa direttiva impone che una mutazione venga sempre eseguita in una transazione di database.
Le mutazioni con @transaction
hanno la garanzia di riuscire o meno completamente. Se uno dei campi all'interno della transazione non va a buon fine, viene eseguito il rollback dell'intera transazione. Dal punto di vista del client, qualsiasi errore si comporta come se l'intera richiesta non fosse riuscita a causa di un errore di richiesta e l'esecuzione non fosse iniziata.
Le mutazioni senza @transaction
eseguono ogni campo principale uno dopo l'altro
in sequenza. Mostra eventuali errori come errori di campo parziali, ma non gli impatti
delle esecuzioni successive.
Crea
Facciamo delle creazioni di base.
# Create a movie based on user input
mutation CreateMovie($title: String!, $releaseYear: Int!, $genre: String!, $rating: Int!) {
movie_insert(data: {
title: $title
releaseYear: $releaseYear
genre: $genre
rating: $rating
})
}
# Create a movie with default values
mutation CreateMovie2 {
movie_insert(data: {
title: "Sherlock Holmes"
releaseYear: 2009
genre: "Mystery"
rating: 5
})
}
O un upsert.
# Movie upsert using combination of variables and literals
mutation UpsertMovie($title: String!) {
movie_upsert(data: {
title: $title
releaseYear: 2009
genre: "Mystery"
rating: 5
genre: "Mystery/Thriller"
})
}
Eseguire gli aggiornamenti
Ecco gli aggiornamenti. Produttori e registi sperano certamente che queste valutazioni medie siano in linea con le tendenze.
Il campo movie_update
contiene un argomento id
previsto per identificare un record
e un campo data
che puoi utilizzare per impostare i valori in questo aggiornamento.
mutation UpdateMovie(
$id: UUID!,
$genre: String!,
$rating: Int!,
$description: String!
) {
movie_update(id: $id,
data: {
genre: $genre
rating: $rating
description: $description
})
}
Per eseguire più aggiornamenti, utilizza il campo movie_updateMany
.
# Multiple updates (increase all ratings of a genre)
mutation IncreaseRatingForGenre($genre: String!, $rating: Int!) {
movie_updateMany(
where: { genre: { eq: $genre } },
data:
{
rating: $rating
})
}
Utilizzare le operazioni di incremento, decremento, accodamento e anteposizione con _update
Sebbene nelle mutazioni _update
e _updateMany
sia possibile impostare esplicitamente i valori in
data:
, spesso è più opportuno applicare un operatore come incremento per aggiornare
i valori.
Per modificare l'esempio di aggiornamento precedente, supponiamo che tu voglia aumentare la valutazione
di un determinato film. Puoi utilizzare la sintassi rating_update
con l'operatore inc
.
mutation UpdateMovie(
$id: UUID!,
$ratingIncrement: Int!
) {
movie_update(id: $id, data: {
rating_update: {
inc: $ratingIncrement
}
})
}
Data Connect supporta i seguenti operatori per gli aggiornamenti dei campi:
inc
per incrementare i tipi di datiInt
,Int64
eFloat
dec
per decrementare i tipi di datiInt
,Int64
eFloat
append
per accodare ai tipi di elenco, ad eccezione degli elenchi di vettoriprepend
da anteporre ai tipi di elenchi, ad eccezione degli elenchi di vettori
Eseguire le eliminazioni
Ovviamente puoi eliminare i dati dei film. I conservatori di film vorranno sicuramente conservare le pellicole fisiche il più a lungo possibile.
# Delete by key
mutation DeleteMovie($id: UUID!) {
movie_delete(id: $id)
}
Qui puoi utilizzare _deleteMany
.
# Multiple deletes
mutation DeleteUnpopularMovies($minRating: Int!) {
movie_deleteMany(where: { rating: { le: $minRating } })
}
Scrivere mutazioni sulle relazioni
Scopri come utilizzare la mutazione _upsert
implicita in una relazione.
# Create or update a one to one relation
mutation MovieMetadataUpsert($movieId: UUID!, $director: String!) {
movieMetadata_upsert(
data: { movie: { id: $movieId }, director: $director }
)
}
Consenti a Data Connect di fornire valori utilizzando la sintassi field_expr
Come discusso in Valori scalari chiave e del server,
puoi progettare lo schema in modo che il server compili i valori per i campi comuni come id
e le date in risposta alle richieste del client.
Inoltre, puoi utilizzare i dati, ad esempio gli ID utente, inviati negli oggetti Data Connect request
dalle app client.
Quando implementi le mutazioni, utilizza la sintassi field_expr
per attivare gli aggiornamenti generati dal server o accedere ai dati delle richieste. Ad esempio, per passare
l'autorizzazione uid
memorizzata in una richiesta a un'operazione _upsert
, passa
"auth.uid"
nel campo userId_expr
.
# Add a movie to the user's favorites list
mutation AddFavoritedMovie($movieId: UUID!) @auth(level: USER) {
favorite_movie_upsert(data: { userId_expr: "auth.uid", movieId: $movieId })
}
# Remove a movie from the user's favorites list
mutation DeleteFavoritedMovie($movieId: UUID!) @auth(level: USER) {
favorite_movie_delete(key: { userId_expr: "auth.uid", movieId: $movieId })
}
In alternativa, in un'app di liste di cose da fare familiare, quando crei una nuova lista di cose da fare, puoi indicare id_expr
per indicare al server di generare automaticamente un UUID per l'elenco.
mutation CreateTodoListWithFirstItem(
$listName: String!
) @transaction {
# Step 1
todoList_insert(data: {
id_expr: "uuidV4()", # <-- auto-generated. Or a column-level @default on `type TodoList` will also work
name: $listName,
})
}
Per ulteriori informazioni, consulta i valori scalari _Expr
nel
riferimento ai valori scalari.
Query di ricerca dei dati di autorizzazione
Le mutazioni Data Connect possono essere autorizzate eseguendo prima una query sul database e verificando i risultati della query con le espressioni CEL. Questa operazione è utile, ad esempio, quando scrivi in una tabella e devi controllare i contenuti di una riga in un'altra tabella.
Questa funzionalità supporta:
- La direttiva
@check
, che consente di valutare i contenuti dei campi e, in base ai risultati di questa valutazione:- Procedi con le operazioni di creazione, aggiornamento ed eliminazione definite da una mutazione
- Procedi per restituire i risultati di una query
- Utilizza i valori restituiti per eseguire una logica diversa nel codice client
- L'istruzione
@redact
, che consente di omettere i risultati delle query dai risultati del protocollo di trasmissione.
Queste funzionalità sono utili per i flussi di autorizzazione.
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);
Quali sono i passaggi successivi?
- Scopri come rendere sicure le query e le mutazioni con autorizzazione e attestazione.
- Scopri come chiamare le query e le mutazioni da un SDK web, un SDK Android, un SDK iOS e un SDK Flutter generato automaticamente.