Implementare le query di Data Connect

Firebase Data Connect ti consente di creare connettori per le tue istanze PostgreSQL gestite con Google Cloud SQL. Questi connettori sono combinazioni di query e mutazioni per utilizzare i dati dello schema.

La Guida introduttiva ha introdotto uno schema di app di recensioni di film per PostgreSQL.

Questa guida ha introdotto anche operazioni amministrative deployable e ad hoc, incluse le query.

  • Le query implementabili sono quelle che implementi per chiamare dalle app client, con gli endpoint API che definisci. Li raggruppi in un connettore di cui viene eseguito il deployment sul server. Gli strumenti Data Connect generano SDK client in base alla tua API. Le query di cui è stato eseguito il deployment non sono protette dalle norme IAM, quindi assicurati di proteggerle utilizzando la direttiva Data Connect @auth.
  • Le query amministrative ad hoc vengono eseguite da ambienti privilegiati per leggere i dati. Puoi crearli ed eseguirli nella console Firebase o in ambienti di sviluppo locali utilizzando la nostra estensione VS Code Data Connect.

Questa guida esamina più da vicino le query implementabili.

Funzionalità delle query Data Connect

Data Connect ti consente di eseguire query di base in tutti i modi che ti aspetteresti da un database PostgreSQL.

Ma con le estensioni di GraphQL di Data Connect, puoi implementare query avanzate per app più veloci ed efficienti:

  • Utilizza gli scalari chiave restituiti da molte operazioni per semplificare le operazioni ripetute sui record
  • Esegui query nel corso di operazioni di mutazione in più passaggi per cercare dati, risparmiando righe di codice e round trip al server.

Utilizzare i campi generati per creare query

Le operazioni Data Connect estenderanno un insieme di campi generati automaticamente 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.

Puoi utilizzare i campi generati per implementare query sempre più complesse, dal recupero di singoli record o più record da singole tabelle a più record da tabelle correlate.

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

Query con il campo
movie

Il campo movie rappresenta un singolo record nella tabella Movie.

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

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

Query con il campo
movies

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

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 }
}

Query con il campo
actors_on_movies

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 }
    }
  }

Elementi essenziali di una query

Le query Data Connect sono query GraphQL con estensioni Data Connect. Proprio come per una normale query GraphQL, puoi definire un nome dell'operazione e un elenco di variabili GraphQL.

Data Connect estende le query GraphQL con direttive personalizzate come @auth.

Pertanto, la seguente query ha:

  • Definizione del tipo query
  • Nome di un'operazione (query) ListMoviesByGenre
  • Un singolo argomento della query, in questo caso una variabile $genre di tipo String
  • Una singola direttiva, @auth.
  • Un singolo campo, movies.
query ListMoviesByGenre($genre: String!) @auth(level: PUBLIC) {
  movies(where: { genre: { eq: $genre } }) {
    id
    title
  }
}

Ogni argomento della query richiede una dichiarazione di tipo, un tipo integrato come String o un tipo personalizzato definito dallo schema come Movie.

Questa guida esamina la firma di query sempre più complesse. Concluderemo presentando espressioni di relazione potenti e concise che puoi utilizzare per creare le query implementabili.

Scalari chiave nelle query

Ma prima, una nota sugli scalari chiave.

Data Connect definisce uno scalare chiave speciale per rappresentare le chiavi primarie di ogni tabella, identificate da {TableType}_Key. È un oggetto JSON di valori della chiave primaria.

Recuperi 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 uno scalare chiave.

Potresti passare uno scalare chiave come valore letterale. Tuttavia, puoi definire variabili per passare gli scalari delle chiavi come input.

Query

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

Risposta

{
  "data": {
    "movie": {
      "title": "Example Movie Title"
    }
  }
}
    

Questi possono essere forniti in JSON di richiesta come questo (o altri formati di serializzazione):

{
  # 
  "variables": {
    "myKey": {"id": "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"}
  }
}

Grazie all'analisi scalare personalizzata, è possibile creare anche un Movie_Key utilizzando la sintassi dell'oggetto, che può contenere variabili. Questa opzione è utile soprattutto quando vuoi dividere i singoli componenti in variabili diverse per qualche motivo.

Scrivere query di base

Puoi iniziare a scrivere query per ottenere singoli record dal tuo database oppure elencare i record di una tabella con l'opzione per limitare e ordinare i risultati.

Recuperare singoli record

La query più semplice recupera un singolo record per ID. La query utilizzerà il campo movie generato automaticamente.

Query

query GetMovieById($id: UUID!) @auth(level: PUBLIC) {
  movie(id: $id) {
    id
    title
    imageUrl
    genre
  }
}
    

Risposta

{
  "data": {
    "movie": {
      "id": "some-uuid",
      "title": "Example Movie Title",
      "imageUrl": "https://example.com/movie.jpg",
      "genre": "Action"
    }
  }
}
    

Recuperare tutti i record in una tabella

Per recuperare un sottoinsieme di campi per l'elenco completo dei film dalla tabella Movies, la query utilizzerà il campo movies generato automaticamente e l'implementazione potrebbe essere simile alla seguente.

Query

query ListMovies @auth(level: PUBLIC) {
  movies {
    id
    title
    imageUrl
    genre
  }
}
    

Risposta

{
  "data": {
    "movies": [
      {
        "id": "some-uuid",
        "title": "Example Movie Title",
        "imageUrl": "https://example.com/movie.jpg",
        "genre": "Action"
      },
      {
        "id": "another-uuid",
        "title": "Another Movie Title",
        "imageUrl": "https://example.com/another-movie.jpg",
        "genre": "Comedy"
      }
    ]
  }
}
    

Utilizzare gli operatori orderBy, limit e offset

Naturalmente, elencare tutti i record di una tabella ha un'utilità limitata.

Puoi ordinare ed eseguire l'impaginazione dei risultati. Questi argomenti vengono accettati ma non restituiti nei risultati.

Qui, la query recupera i titoli dei 10 film con la valutazione più alta.

Query

query MoviesTop10 {
  movies(orderBy: [{ rating: DESC }], limit: 10) {
    # graphql: list the fields from the results to return
    title
  }
}
    

Risposta

{
  "data": {
    "movies": [
      { "title": "Top Movie 1" },
      { "title": "Top Movie 2" },
      { "title": "Top Movie 3" }
      // ... other 7 movies
    ]
  }
}
    

Potresti avere un caso d'uso per recuperare righe da un offset, ad esempio i film da 11 a 20 ordinati in base alla classificazione.

Query

query Movies11to20 {
  movies(orderBy: [{ rating: DESC }], limit: 10, offset: 10) {
    # graphql: list the fields from the results to return
    title
  }
}
    

Risposta

{
  "data": {
    "movies": [
      { "title": "Movie 11" },
      { "title": "Movie 12" },
      { "title": "Movie 13" }
      // ... other 7 movies
    ]
  }
}
    

Utilizzare gli alias nelle query

Data Connect supporta l'assegnazione di alias 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 un'unica richiesta efficiente al server, eseguendo di fatto più "query secondarie" 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 gli alias mostPopular e leastPopular.

Query

query ReviewPopularitySpread($genre: String) {
  mostPopular: review(
    first: {
      where: {genre: {eq: $genre}},
      orderBy: {popularity: DESC}
    }
  ),
  leastPopular: review(
    last: {
      where: {genre: {eq: $genre}},
      orderBy: {popularity: DESC}
    }
  )
}
    

Risposta

{
  "data": {
    "mostPopular": [
      { "popularity": 9 }
    ],
    "leastPopular": [
      { "popularity": 1 }
    ]
  }
}
    

Utilizzare i filtri delle query

Le query Data Connect vengono mappate a tutti i filtri e gli ordini SQL comuni.

Filtrare con where con operatori orderBy

Restituisce tutte le righe corrispondenti della tabella (e le associazioni nidificate). Restituisce un array vuoto se nessun record corrisponde al filtro.

Query

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
  }
}
    

Risposta

{
  "data": {
    "mostPopular": [
      {
        "id": "some-uuid",
        "title": "Example Movie Title",
        "genre": "Action",
        "description": "A great movie"
      }
    ]
  }
}
    

Filtrare in base al test per i valori nulli

Puoi testare i valori null utilizzando l'operatore isNull.

Query

query ListMoviesWithoutDescription {
  movies(where: { description: { isNull: true }}) {
    id
    title
  }
}
    

Risposta

{
  "data": {
    "movies": [
      {
        "id": "some-uuid",
        "title": "Example Movie Title"
      },
      {
        "id": "another-uuid",
        "title": "Another Movie Title"
      }
    ]
  }
}
    

Per altri operatori, consulta la guida di riferimento sui tipi di oggetti di input.

Filtrare con i confronti di valori

Puoi utilizzare operatori come lt (minore di) e ge (maggiore o uguale) per confrontare i valori nelle query.

Query

query ListMoviesByRating($minRating: Int!, $maxRating: Int!) {
  movies(where: { rating: { ge: $minRating, lt: $maxRating }}) {
    id
    title
  }
}
    

Risposta

{
  "data": {
    "movies": [
      {
        "id": "some-uuid",
        "title": "Example Movie Title"
      },
      {
        "id": "another-uuid",
        "title": "Another Movie Title"
      }
    ]
  }
}
    

Filtrare con gli operatori includes e excludes per i campi array

Puoi verificare che un campo array includa un elemento specificato.

L'esempio seguente illustra l'operatore includes.

Data Connect supporta includesAll, excludes, excludesAll e altro ancora. Esamina tutti questi operatori per numeri interi, stringhe, date e altri tipi di dati nelle intestazioni _ListFilter della documentazione di riferimento.

Query

query ListMoviesByTag($tag: String!) {
  movies(where: { tags: { includes: $tag }}) {
    # graphql: list the fields from the results to return
    id
    title
  }
}
    

Risposta

{
  "data": {
    "movies": [
      {
        "id": "some-uuid",
        "title": "Example Movie Title"
      }
    ]
  }
}
    

Filtrare con operazioni sulle stringhe ed espressioni regolari

Le query possono utilizzare le tipiche operazioni di ricerca e confronto di stringhe, incluse le espressioni regolari. Nota: per efficienza, qui stai raggruppando diverse operazioni e le stai disambiguando con 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}}) {...}
}

Filtra con la logica degli operatori _or, _and, _not

Utilizza _or per una logica più complessa. Data Connect supporta anche gli operatori _and e _not.

Query

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
  }
}
    

Risposta

{
  "data": {
    "movies": [
      { "title": "Movie Title 1" },
      { "title": "Movie Title 2" }
    ]
  }
}
    

Scrivere query relazionali

Le query Data Connect possono accedere ai dati in base alle relazioni tra le tabelle. Puoi utilizzare le relazioni oggetto (uno-a-uno) o array (uno-a-molti) definite nello schema per eseguire query nidificate, ovvero recuperare i dati per 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.

Ricordati di esaminare lo schema di esempio.

Many-to-one

Ora esaminiamo una query per illustrare la sintassi di _on_.

Query

query MyReviews @auth(level: USER) {
  user(key: {id_expr: "auth.uid"}) {
    reviews: reviews_on_user {
      movie { name }
      rating
    }
  }
}
    

Risposta

{
  "data": {
    "user": {
      "reviews": [
        {
          "movie": { "name": "Movie Title" },
          "rating": 5
        }
      ]
    }
  }
}
    

One to one

Puoi scrivere una query one-to-one utilizzando la sintassi _on_.

Query

query GetMovieMetadata($id: UUID!) @auth(level: PUBLIC) {
  movie(id: $id) {
    movieMetadatas_on_movie {
      director
    }
  }
}
    

Risposta

{
  "data": {
    "movie": {
      "movieMetadatas_on_movie": {
        "director": "Some Director"
      }
    }
  }
}
    

Many-to-many

Le query molti-a-molti utilizzano la sintassi _via_. Una query molti-a-molti potrebbe recuperare gli attori di un film specifico.

Query

query MoviesActors($id: UUID!) @auth(level: USER) {
  movie(id: $id) {
     actors: actors_via_MovieActors {
        name
     }
  }
}
    

Risposta

{
  "data": {
    "movie": {
      "actors": [
        {
          "name": "Actor Name"
        }
      ]
    }
  }
}
    

Ma possiamo scrivere una query più complessa, utilizzando gli alias, per filtrare in base a role per recuperare gli attori e i film associati in mainActors e supportingActors risultati. Poiché si tratta di una relazione molti-a-molti, viene utilizzata la sintassi _via_.

Query

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
    }
  }
}
    

Risposta

{
  "data": {
    "movie": {
      "mainActors": [
        {
          "name": "Main Actor Name"
        }
      ],
      "supportingActors": [
        {
          "name": "Supporting Actor Name"
        }
      ]
    },
    "actor": {
      "mainRoles": [
        {
          "title": "Main Role Movie Title"
        }
      ],
      "supportingRoles": [
        {
          "title": "Supporting Role Movie 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:

  • 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

Gli aggregati vengono eseguiti sul server, il che offre una serie di vantaggi rispetto al calcolo lato client:

  • Prestazioni più rapide dell'app (in quanto eviti i calcoli lato client)
  • Costi di uscita dei dati ridotti (in quanto 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 i dati aggregati

In questa sezione passeremo a uno schema di esempio di un negozio, che è un buon punto di partenza 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 che corrispondono alla tua 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

Ad esempio, se nel tuo database hai 5 prodotti, il risultato sarà:

{
  "products": [
    {
    "_count": 5
    }
  ]
}

Tutti i campi hanno un campo <field>_count, che conta il numero di righe con un valore non nullo in quel campo.

Query

query CountProductsWithExpirationDate {
  products {
    expirationDate_count
  }
}

Risposta
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

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 sarebbe:

{
  "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 ora hanno <field>_min e <field>_max.

Query

query DateAndTimeAggregates {
  products {
  expirationDate_max
  expirationDate_min
  }
}

Risposta
_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 sarebbe:

{
  "products": [
    {
    "expirationDate_max": "2024-03-01",
    "expirationDate_min": "2024-01-01"
    }
  ]
}

Distinct

L'argomento distinct ti consente di ottenere tutti i valori univoci per un campo (o una combinazione di campi). Ad esempio:

Query

query ListDistinctManufacturers {
  products(distinct: true) {
    manufacturer
  }
}

Risposta
distinct

Ad esempio, se hai i seguenti produttori:

  • Prodotto A: manufacturer: "Acme"
  • Prodotto B: manufacturer: "Beta"
  • Prodotto C: manufacturer: "Acme"

Il risultato sarebbe:

{
  "products": [
    { "manufacturer": "Acme" },
    { "manufacturer": "Beta" }
  ]
}

Puoi anche utilizzare l'argomento distinct nei campi aggregati per aggregare invece i valori distinti. Ad esempio:

Query

query CountDistinctManufacturers {
  products {
    manufacturer_count(distinct: true)
  }
}

Risposta
distinctonaggregate

Ad esempio, se hai i seguenti produttori:

  • Prodotto A: manufacturer: "Acme"
  • Prodotto B: manufacturer: "Beta"
  • Prodotto C: manufacturer: "Acme"

Il risultato sarebbe:

{
  "products": [
    {
    "manufacturer_count": 2
    }
  ]
}

Aggregati raggruppati

Esegui un'aggregazione raggruppata selezionando un mix di campi aggregati e non aggregati in un tipo. In questo modo vengono raggruppate tutte le righe corrispondenti che hanno lo stesso valore per i campi non aggregati e vengono calcolati i campi aggregati per quel gruppo. Ad esempio:

Query

query MostExpensiveProductByManufacturer {
  products {
  manufacturer
  price_max
  }
}

Risposta
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 sarebbe:

{
  "products": [
    { "manufacturer": "Acme", "price_max": 2.99 },
    { "manufacturer": "Beta", "price_max": 5.99 }
  ]
}
having e where con aggregazioni raggruppate

Puoi anche utilizzare gli argomenti having e where per restituire solo i gruppi che soddisfano un criterio fornito.

  • having ti consente di filtrare i gruppi in base ai campi aggregati
  • where consente di filtrare le righe in base a campi non aggregati.

Query

query FilteredMostExpensiveProductByManufacturer {
  products(having: {price_max: {ge: 2.99}}) {
  manufacturer
  price_max
  }
}

Risposta
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 sarebbe:

{
  "products": [
    { "manufacturer": "Acme", "price_max": 2.99 },
    { "manufacturer": "Beta", "price_max": 5.99 }
  ]
}

Aggregazioni tra tabelle

I campi aggregati possono essere utilizzati in combinazione con i campi della relazione uno-a-molti generata per rispondere a domande complesse sui tuoi 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

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 sarebbe:

{
  "manufacturers": [
    { "name": "Acme", "products_on_manufacturer": { "_count": 2 } },
    { "name": "Beta", "products_on_manufacturer": { "_count": 1 } }
  ]
}

Scrivere query avanzate: utilizza i campi query per leggere i dati in operazioni in più passaggi

Esistono molte situazioni in cui potresti voler leggere il database durante l'esecuzione di una mutazione per cercare e verificare i dati esistenti prima di eseguire, ad esempio, inserimenti o aggiornamenti. Queste opzioni consentono di risparmiare operazioni di andata e ritorno e quindi costi.

Data Connect supporta questa funzionalità. Consulta le operazioni in più passaggi.

Passaggi successivi

Potrebbe interessarti: