Firebase Data Connect vous permet de créer des connecteurs pour vos instances PostgreSQL gérées avec Google Cloud SQL. Ces connecteurs sont des combinaisons de requêtes et de mutations permettant d'utiliser les données de votre schéma.
Le guide de démarrage a présenté un schéma d'application d'avis sur des films pour PostgreSQL.
Ce guide présentait également les opérations administratives déployables et ad hoc, y compris les requêtes.
- Les requêtes déployables sont celles que vous implémentez pour appeler à partir d'applications clientes, avec les points de terminaison d'API que vous définissez. Vous les regroupez dans un connecteur déployé sur le serveur. L'outil Data Connect génère des SDK client en fonction de votre API. Les requêtes déployées ne sont pas protégées par une stratégie IAM. Veillez donc à les sécuriser à l'aide de la directive Data Connect
@auth
. - Les requêtes administratives ad hoc sont exécutées à partir d'environnements privilégiés pour lire les données. Vous pouvez les créer et les exécuter dans la console Firebase ou dans des environnements de développement locaux à l'aide de notre extension VS Code Data Connect.
Ce guide examine plus en détail les requêtes déployables.
Caractéristiques des requêtes Data Connect
Data Connect vous permet d'effectuer des requêtes de base de toutes les manières possibles avec une base de données PostgreSQL.
Toutefois, grâce aux extensions de Data Connect pour GraphQL, vous pouvez implémenter des requêtes avancées pour des applications plus rapides et plus efficaces :
- Utilisez les scalaires de clé renvoyés par de nombreuses opérations pour simplifier les opérations répétées sur les enregistrements.
- Exécutez des requêtes au cours d'opérations de mutation en plusieurs étapes pour rechercher des données, ce qui vous permet d'économiser des lignes de code et des allers-retours vers le serveur.
Utiliser les champs générés pour créer des requêtes
Vos opérations Data Connect étendront un ensemble de champs Data Connect générés automatiquement en fonction des types et des relations de type dans votre schéma. Ces champs sont générés par des outils locaux chaque fois que vous modifiez votre schéma.
Vous pouvez utiliser des champs générés pour implémenter des requêtes de plus en plus complexes, qu'il s'agisse de récupérer des enregistrements individuels ou multiples à partir de tables uniques ou multiples à partir de tables associées.Supposons que votre schéma contienne un type Movie
et un type Actor
associé.
Data Connect génère les champs movie
, movies
, actors_on_movies
et plus encore.
Requête avec le champ
movie
Le champ |
Utilisez ce champ pour interroger un seul film à l'aide de sa clé. query GetMovie($myKey: Movie_Key!) { movie(key: $myKey) { title } } |
Requête avec le champ
movies
Le champ |
Utilisez ce champ pour interroger plusieurs films, par exemple tous les films d'une année donnée. query GetMovies($myYear: Int!) { movies(where: { year: { eq: $myYear } }) { title } } |
Requête avec le champ
actors_on_movies
Le champ |
Utilisez ce champ pour interroger tous les acteurs associés à un film donné. query GetActorsOnMovie($myKey: Movie_Key!) { actors_on_movies(where: { movie: { key: { eq: $myKey } } }) { actor { name } } } |
Éléments essentiels d'une requête
Les requêtes Data Connect sont des requêtes GraphQL avec des extensions Data Connect. Comme pour une requête GraphQL classique, vous pouvez définir un nom d'opération et une liste de variables GraphQL.
Data Connect étend les requêtes GraphQL avec des directives personnalisées telles que @auth
.
La requête suivante comporte :
- Définition du type
query
- Nom d'une opération
ListMoviesByGenre
(requête) - Un seul argument de requête, ici une variable
$genre
de typeString
- Une seule directive,
@auth
. - Un seul champ,
movies
.
query ListMoviesByGenre($genre: String!) @auth(level: PUBLIC) {
movies(where: { genre: { eq: $genre } }) {
id
title
}
}
Chaque argument de requête nécessite une déclaration de type, un type intégré tel que String
ou un type personnalisé défini par le schéma tel que Movie
.
Ce guide examine la signature des requêtes de plus en plus complexes. Vous terminerez par une présentation des expressions de relations puissantes et concises que vous pouvez utiliser pour créer vos requêtes déployables.
Scalaires clés dans les requêtes
Mais avant tout, parlons des scalaires clés.
Data Connect définit un scalaire clé spécial pour représenter les clés primaires de chaque table, identifiées par {TableType}_Key. Il s'agit d'un objet JSON de valeurs de clé primaire.
Vous récupérez les scalaires clés en tant que réponse renvoyée par la plupart des champs de lecture générés automatiquement, ou bien à partir de requêtes dans lesquelles vous avez récupéré tous les champs nécessaires à la création de la clé scalaire.
Les requêtes automatiques au singulier, comme movie
dans notre exemple en cours, acceptent un argument clé qui accepte un scalaire clé.
Vous pouvez transmettre un scalaire de clé en tant que littéral. Toutefois, vous pouvez définir des variables pour transmettre des scalaires clés en tant qu'entrées.
Requête
query GetMovie($myKey: Movie_Key!) { movie(key: $myKey) { title } }
Réponse
{ "data": { "movie": { "title": "Example Movie Title" } } }
Elles peuvent être fournies dans le JSON de la requête comme suit (ou dans d'autres formats de sérialisation) :
{
# …
"variables": {
"myKey": {"id": "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"}
}
}
Grâce à l'analyse des scalaires personnalisés, un Movie_Key
peut également être construit à l'aide de la syntaxe d'objet, qui peut contenir des variables. Cette méthode est surtout utile lorsque vous souhaitez, pour une raison ou une autre, répartir des composants individuels dans différentes variables.
Rédiger des requêtes de base
Vous pouvez commencer à écrire des requêtes pour obtenir des enregistrements individuels à partir de votre base de données, ou lister les enregistrements d'une table avec la possibilité de limiter et d'ordonner les résultats.
Récupérer des enregistrements individuels
La requête la plus simple permet d'obtenir un seul enregistrement par ID. Votre requête utilisera le champ movie
généré automatiquement.
Requête
query GetMovieById($id: UUID!) @auth(level: PUBLIC) { movie(id: $id) { id title imageUrl genre } }
Réponse
{ "data": { "movie": { "id": "some-uuid", "title": "Example Movie Title", "imageUrl": "https://example.com/movie.jpg", "genre": "Action" } } }
Récupérer tous les enregistrements d'une table
Pour récupérer un sous-ensemble de champs pour la liste complète des films de la table Movies
, votre requête utilisera le champ movies
généré automatiquement. Votre implémentation peut ressembler à ce qui suit.
Requête
query ListMovies @auth(level: PUBLIC) { movies { id title imageUrl genre } }
Réponse
{ "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" } ] } }
Utiliser les opérateurs orderBy
, limit
et offset
Bien sûr, lister tous les enregistrements d'une table n'est pas très utile.
Vous pouvez trier les résultats et les paginer. Ces arguments sont acceptés, mais ne sont pas renvoyés dans les résultats.
Ici, la requête récupère les titres des 10 films les mieux notés.
Requête
query MoviesTop10 { movies(orderBy: [{ rating: DESC }], limit: 10) { # graphql: list the fields from the results to return title } }
Réponse
{ "data": { "movies": [ { "title": "Top Movie 1" }, { "title": "Top Movie 2" }, { "title": "Top Movie 3" } // ... other 7 movies ] } }
Vous pouvez avoir un cas d'utilisation pour récupérer des lignes à partir d'un décalage, comme les films 11 à 20 classés par note.
Requête
query Movies11to20 { movies(orderBy: [{ rating: DESC }], limit: 10, offset: 10) { # graphql: list the fields from the results to return title } }
Réponse
{ "data": { "movies": [ { "title": "Movie 11" }, { "title": "Movie 12" }, { "title": "Movie 13" } // ... other 7 movies ] } }
Utiliser des alias dans les requêtes
Data Connect est compatible avec les alias GraphQL dans les requêtes. Les alias vous permettent de renommer les données renvoyées dans les résultats d'une requête. Une seule requête Data Connect peut appliquer plusieurs filtres ou d'autres opérations de requête dans une seule requête efficace au serveur, en émettant plusieurs "sous-requêtes" à la fois. Pour éviter les conflits de noms dans l'ensemble de données renvoyé, vous utilisez des alias pour distinguer les sous-requêtes.
Voici une requête dans laquelle une expression utilise les alias mostPopular
et leastPopular
.
Requête
query ReviewPopularitySpread($genre: String) { mostPopular: review( first: { where: {genre: {eq: $genre}}, orderBy: {popularity: DESC} } ), leastPopular: review( last: { where: {genre: {eq: $genre}}, orderBy: {popularity: DESC} } ) }
Réponse
{ "data": { "mostPopular": [ { "popularity": 9 } ], "leastPopular": [ { "popularity": 1 } ] } }
Utiliser des filtres de requête
Les requêtes Data Connect correspondent à tous les filtres SQL courants et aux opérations de tri.
Filtrer avec where
à l'aide des opérateurs orderBy
Renvoie toutes les lignes correspondantes de la table (et des associations imbriquées). Renvoie un tableau vide si aucun enregistrement ne correspond au filtre.
Requête
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 } }
Réponse
{ "data": { "mostPopular": [ { "id": "some-uuid", "title": "Example Movie Title", "genre": "Action", "description": "A great movie" } ] } }
Filtrer en recherchant les valeurs nulles
Vous pouvez tester les valeurs null
à l'aide de l'opérateur isNull
.
Requête
query ListMoviesWithoutDescription { movies(where: { description: { isNull: true }}) { id title } }
Réponse
{ "data": { "movies": [ { "id": "some-uuid", "title": "Example Movie Title" }, { "id": "another-uuid", "title": "Another Movie Title" } ] } }
Pour en savoir plus sur les opérateurs, consultez le guide de référence sur les types d'objets d'entrée.
Filtrer avec des comparaisons de valeurs
Vous pouvez utiliser des opérateurs tels que lt
(inférieur à) et ge
(supérieur ou égal à) pour comparer des valeurs dans vos requêtes.
Requête
query ListMoviesByRating($minRating: Int!, $maxRating: Int!) { movies(where: { rating: { ge: $minRating, lt: $maxRating }}) { id title } }
Réponse
{ "data": { "movies": [ { "id": "some-uuid", "title": "Example Movie Title" }, { "id": "another-uuid", "title": "Another Movie Title" } ] } }
Filtrer avec les opérateurs includes
et excludes
pour les champs de tableau
Vous pouvez tester si un champ de tableau inclut un élément spécifié.
L'exemple suivant illustre l'opérateur includes
.
Data Connect est compatible avec includesAll
, excludes
, excludesAll
et plus encore. Consultez tous ces opérateurs pour les nombres entiers, les chaînes, les dates et les autres types de données dans les titres _ListFilter
de la documentation de référence.
Requête
query ListMoviesByTag($tag: String!) { movies(where: { tags: { includes: $tag }}) { # graphql: list the fields from the results to return id title } }
Réponse
{ "data": { "movies": [ { "id": "some-uuid", "title": "Example Movie Title" } ] } }
Filtrer avec des opérations sur les chaînes et des expressions régulières
Vos requêtes peuvent utiliser des opérations de comparaison et de recherche de chaînes classiques, y compris des expressions régulières. Notez que, pour plus d'efficacité, vous regroupez plusieurs opérations ici et que vous les distinguez à l'aide d'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}}) {...}
}
Filtrer avec la logique des opérateurs _or
, _and
et _not
Utilisez _or
pour une logique plus complexe. Data Connect est également compatible avec les opérateurs _and
et _not
.
Requête
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 } }
Réponse
{ "data": { "movies": [ { "title": "Movie Title 1" }, { "title": "Movie Title 2" } ] } }
Écrire des requêtes relationnelles
Les requêtes Data Connect peuvent accéder aux données en fonction des relations entre les tables. Vous pouvez utiliser les relations d'objet (un-à-un) ou de tableau (un-à-plusieurs) définies dans votre schéma pour effectuer des requêtes imbriquées, c'est-à-dire extraire des données pour un type ainsi que des données d'un type imbriqué ou associé.
Ces requêtes utilisent la syntaxe magique Data Connect _on_
et _via
dans les champs de lecture générés.
N'oubliez pas de consulter l'exemple de schéma.
Plusieurs à un
Examinons maintenant une requête pour illustrer la syntaxe de _on_
.
Requête
query MyReviews @auth(level: USER) { user(key: {id_expr: "auth.uid"}) { reviews: reviews_on_user { movie { name } rating } } }
Réponse
{ "data": { "user": { "reviews": [ { "movie": { "name": "Movie Title" }, "rating": 5 } ] } } }
Un à un
Vous pouvez écrire une requête un-à-un à l'aide de la syntaxe _on_
.
Requête
query GetMovieMetadata($id: UUID!) @auth(level: PUBLIC) { movie(id: $id) { movieMetadatas_on_movie { director } } }
Réponse
{ "data": { "movie": { "movieMetadatas_on_movie": { "director": "Some Director" } } } }
Plusieurs à plusieurs
Les requêtes plusieurs-à-plusieurs utilisent la syntaxe _via_
. Une requête de type "plusieurs à plusieurs" peut récupérer les acteurs d'un film spécifique.
Requête
query MoviesActors($id: UUID!) @auth(level: USER) { movie(id: $id) { actors: actors_via_MovieActors { name } } }
Réponse
{ "data": { "movie": { "actors": [ { "name": "Actor Name" } ] } } }
Toutefois, nous pouvons écrire une requête plus complexe, en utilisant des alias, pour filtrer en fonction de role
afin de récupérer les acteurs et les films associés dans les résultats mainActors
et supportingActors
. Comme il s'agit d'une relation plusieurs-à-plusieurs, la syntaxe _via_
est utilisée.
Requête
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 } } }
Réponse
{ "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" } ] } } }
Requêtes d'agrégation
Que sont les agrégats et pourquoi les utiliser ?
Les champs d'agrégation vous permettent d'effectuer des calculs sur une liste de résultats. Avec les champs agrégés, vous pouvez, par exemple :
- Trouver la note moyenne d'un avis
- Trouver le coût total des articles dans un panier
- Trouver le produit le mieux ou le moins bien noté
- Compter le nombre de produits dans votre magasin
Les agrégats sont effectués sur le serveur, ce qui offre plusieurs avantages par rapport à leur calcul côté client :
- Des performances d'application plus rapides (puisque vous évitez les calculs côté client)
- Réduction des coûts de sortie des données (puisque vous n'envoyez que les résultats agrégés au lieu de toutes les entrées)
- Sécurité renforcée (vous pouvez donner aux clients l'accès à des données agrégées au lieu de l'ensemble des données)
Exemple de schéma pour les agrégats
Dans cette section, nous allons passer à un exemple de schéma de boutique, qui est un bon moyen d'expliquer comment utiliser les agrégats :
type Product @table {
name: String!
manufacturer: String!
quantityInStock: Int!
price: Float!
expirationDate: Date
}
Agrégats simples
_count pour tous les champs
Le champ agrégé le plus simple est _count
. Il renvoie le nombre de lignes correspondant à votre requête. Pour chaque champ de votre type, Data Connect génère les champs agrégés correspondants en fonction du type de champ.
Requête
query CountProducts {
products {
_count
}
}
Réponse one
one
Par exemple, si votre base de données contient cinq produits, le résultat sera le suivant :
{
"products": [
{
"_count": 5
}
]
}
Tous les champs comportent un champ <field>_count
, qui indique le nombre de lignes dont la valeur n'est pas nulle dans ce champ.
Requête
query CountProductsWithExpirationDate {
products {
expirationDate_count
}
}
Réponsefield_count
field_count
Par exemple, si vous avez trois produits avec une date d'expiration, le résultat sera le suivant :
{
"products": [
{
"expirationDate_count": 3
}
]
}
_min, _max, _sum et _avg pour les champs numériques
Les champs numériques (int, float, int64) comportent également <field>_min
, <field>_max
, <field>_sum
et <field>_avg
.
Requête
query NumericAggregates {
products {
quantityInStock_max
price_min
price_avg
quantityInStock_sum
}
}
Réponse_min _max _sum _avg
_min _max _sum _avg
Par exemple, si vous disposez des produits suivants :
- Produit A :
quantityInStock: 10
,price: 2.99
- Produit B :
quantityInStock: 5
,price: 5.99
- Produit C :
quantityInStock: 20
,price: 1.99
Le résultat serait le suivant :
{
"products": [
{
"quantityInStock_max": 20,
"price_min": 1.99,
"price_avg": 3.6566666666666666,
"quantityInStock_sum": 35
}
]
}
_min et _max pour les dates et les codes temporels
Les champs de date et d'horodatage comportent <field>_min
et <field>_max
.
Requête
query DateAndTimeAggregates {
products {
expirationDate_max
expirationDate_min
}
}
Réponse_min _maxdatetime
_min _maxdatetime
Par exemple, si vous disposez des dates d'expiration suivantes :
- Produit A :
2024-01-01
- Produit B :
2024-03-01
- Produit C :
2024-02-01
Le résultat serait le suivant :
{
"products": [
{
"expirationDate_max": "2024-03-01",
"expirationDate_min": "2024-01-01"
}
]
}
Distinct
L'argument distinct
vous permet d'obtenir toutes les valeurs uniques d'un champ (ou d'une combinaison de champs). Exemple :
Requête
query ListDistinctManufacturers {
products(distinct: true) {
manufacturer
}
}
Réponsedistinct
distinct
Par exemple, si vous avez les fabricants suivants :
- Produit A :
manufacturer: "Acme"
- Produit B :
manufacturer: "Beta"
- Produit C :
manufacturer: "Acme"
Le résultat serait le suivant :
{
"products": [
{ "manufacturer": "Acme" },
{ "manufacturer": "Beta" }
]
}
Vous pouvez également utiliser l'argument distinct
sur les champs agrégés pour agréger les valeurs distinctes. Exemple :
Requête
query CountDistinctManufacturers {
products {
manufacturer_count(distinct: true)
}
}
Réponsedistinctonaggregate
distinctonaggregate
Par exemple, si vous avez les fabricants suivants :
- Produit A :
manufacturer: "Acme"
- Produit B :
manufacturer: "Beta"
- Produit C :
manufacturer: "Acme"
Le résultat serait le suivant :
{
"products": [
{
"manufacturer_count": 2
}
]
}
Agrégats groupés
Pour effectuer une agrégation groupée, vous devez sélectionner un mélange de champs agrégés et non agrégés sur un type. Cela regroupe toutes les lignes correspondantes qui ont la même valeur pour les champs non agrégés et calcule les champs agrégés pour ce groupe. Exemple :
Requête
query MostExpensiveProductByManufacturer {
products {
manufacturer
price_max
}
}
Réponsegroupedaggregates
groupedaggregates
Par exemple, si vous disposez des produits suivants :
- Produit A :
manufacturer: "Acme"
,price: 2.99
- Produit B :
manufacturer: "Beta"
,price: 5.99
- Produit C :
manufacturer: "Acme"
,price: 1.99
Le résultat serait le suivant :
{
"products": [
{ "manufacturer": "Acme", "price_max": 2.99 },
{ "manufacturer": "Beta", "price_max": 5.99 }
]
}
having
et where
avec des agrégats groupés
Vous pouvez également utiliser les arguments having
et where
pour ne renvoyer que les groupes qui répondent à un critère donné.
having
vous permet de filtrer les groupes en fonction de leurs champs agrégés.where
vous permet de filtrer les lignes en fonction de champs non agrégés.
Requête
query FilteredMostExpensiveProductByManufacturer {
products(having: {price_max: {ge: 2.99}}) {
manufacturer
price_max
}
}
Réponsehavingwhere
havingwhere
Par exemple, si vous disposez des produits suivants :
- Produit A :
manufacturer: "Acme"
,price: 2.99
- Produit B :
manufacturer: "Beta"
,price: 5.99
- Produit C :
manufacturer: "Acme"
,price: 1.99
Le résultat serait le suivant :
{
"products": [
{ "manufacturer": "Acme", "price_max": 2.99 },
{ "manufacturer": "Beta", "price_max": 5.99 }
]
}
Agréger des données dans plusieurs tables
Les champs agrégés peuvent être utilisés conjointement avec les champs de relation un-à-plusieurs générés pour répondre à des questions complexes sur vos données. Voici un schéma modifié, avec une table distincte, Manufacturer
, que nous pouvons utiliser dans les exemples :
type Product @table {
name: String!
manufacturer: Manufacturer!
quantityInStock: Int!
price: Float!
expirationDate: Date
}
type Manufacturer @table {
name: String!
headquartersCountry: String!
}
Nous pouvons maintenant utiliser des champs agrégés pour, par exemple, déterminer le nombre de produits fabriqués par un fabricant :
Requête
query GetProductCount($id: UUID) {
manufacturers {
name
products_on_manufacturer {
_count
}
}
}
Réponse aggregatesacrosstables
aggregatesacrosstables
Par exemple, si vous avez les fabricants suivants :
- Fabricant A :
name: "Acme"
,products_on_manufacturer: 2
- Fabricant B :
name: "Beta"
,products_on_manufacturer: 1
Le résultat serait le suivant :
{
"manufacturers": [
{ "name": "Acme", "products_on_manufacturer": { "_count": 2 } },
{ "name": "Beta", "products_on_manufacturer": { "_count": 1 } }
]
}
Rédiger des requêtes avancées : utiliser les champs query
pour lire les données dans des opérations en plusieurs étapes
Dans de nombreuses situations, vous pouvez souhaiter lire votre base de données lors de l'exécution d'une mutation pour rechercher et valider les données existantes avant d'effectuer, par exemple, des insertions ou des mises à jour. Ces options permettent d'économiser des opérations aller-retour et donc des coûts.
Data Connect est compatible avec cette fonctionnalité. Consultez Opérations en plusieurs étapes.
Étapes suivantes
Vous pourriez être intéressé par :
- Générer des requêtes pour vos applications à l'aide des outils d'assistance par IA
- Autoriser vos requêtes conformément au guide d'autorisation
- Appeler des requêtes à partir de votre code client pour Web, iOS, Android et Flutter.