Com o Firebase Data Connect, é possível criar conectores para suas instâncias do PostgreSQL gerenciadas com o Google Cloud SQL. Esses conectores são combinações de consultas e mutações para usar os dados do seu esquema.
O guia de início rápido apresentou um esquema de app de avaliação de filmes para PostgreSQL.
Esse guia também apresentou operações administrativas implantáveis e ad hoc, incluindo consultas.
- As consultas implantáveis são aquelas que você implementa para chamar de apps
clientes, com endpoints de API definidos por você. Você os agrupa em um conector
implantado no servidor. As ferramentas do Data Connect geram SDKs de cliente com base na sua API. As consultas implantadas não são protegidas pela política do IAM. Portanto, proteja-as usando a diretiva Data Connect
@auth
. - As consultas administrativas ad hoc são executadas em ambientes privilegiados para ler dados. É possível criar e executar essas consultas no console do Firebase ou em ambientes de desenvolvimento local usando a extensão Data Connect do VS Code.
Este guia analisa mais detalhadamente as consultas implantáveis.
Recursos das consultas Data Connect
Com o Data Connect, você pode fazer consultas básicas de todas as maneiras esperadas em um banco de dados PostgreSQL.
Mas com as extensões do Data Connect para GraphQL, é possível implementar consultas avançadas para apps mais rápidos e eficientes:
- Use escalares de chave retornados por muitas operações para simplificar operações repetidas em registros.
- Realize consultas no decorrer de operações de mutação de várias etapas para pesquisar dados, economizando linhas de código e viagens de ida e volta ao servidor.
Usar campos gerados para criar consultas
Suas operações Data Connect vão estender um conjunto de campos Data Connect gerados automaticamente com base nos tipos e nas relações de tipo no seu esquema. Esses campos são gerados por ferramentas locais sempre que você edita o esquema.
É possível usar campos gerados para implementar consultas cada vez mais complexas, desde a recuperação de registros individuais ou múltiplos de tabelas únicas até vários registros de tabelas relacionadas.Suponha que seu esquema contenha um tipo Movie
e um tipo Actor
associado.
O Data Connect gera campos movie
, movies
, actors_on_movies
e muito mais.
Consultar com o campo
movie
O campo |
Use este campo para consultar um único filme pela chave. query GetMovie($myKey: Movie_Key!) { movie(key: $myKey) { title } } |
Consultar com o campo
movies
O campo |
Use esse campo para consultar vários filmes, por exemplo, todos os filmes de um determinado ano. query GetMovies($myYear: Int!) { movies(where: { year: { eq: $myYear } }) { title } } |
Consultar com o campo
actors_on_movies
O campo |
Use este campo para consultar todos os atores associados a um determinado filme. query GetActorsOnMovie($myKey: Movie_Key!) { actors_on_movies(where: { movie: { key: { eq: $myKey } } }) { actor { name } } } |
Elementos essenciais de uma consulta
As consultas do Data Connect são consultas GraphQL com extensões do Data Connect. Assim como em uma consulta GraphQL normal, você pode definir um nome de operação e uma lista de variáveis GraphQL.
O Data Connect estende as consultas GraphQL com diretivas personalizadas, como
@auth
.
Portanto, a consulta a seguir tem:
- Uma definição de tipo
query
- Um nome de operação (consulta)
ListMoviesByGenre
- Um único argumento de consulta, aqui uma variável
$genre
do tipoString
- Uma única diretiva,
@auth
. - Um único campo,
movies
.
query ListMoviesByGenre($genre: String!) @auth(level: PUBLIC) {
movies(where: { genre: { eq: $genre } }) {
id
title
}
}
Todo argumento de consulta exige uma declaração de tipo, um tipo integrado como String
ou um tipo personalizado definido pelo esquema, como Movie
.
Este guia vai analisar a assinatura de consultas cada vez mais complexas. Você vai terminar apresentando expressões de relacionamento poderosas e concisas que podem ser usadas para criar consultas implantáveis.
Escalares de chave em consultas
Mas primeiro, uma observação sobre escalares importantes.
Data Connect define um escalar de chave especial para representar chaves primárias de cada tabela, identificadas por {TableType}_Key. É um objeto JSON de valores de chave primária.
Você recupera escalares de chave como uma resposta retornada pela maioria dos campos de leitura gerados automaticamente ou, é claro, de consultas em que você recuperou todos os campos necessários para criar a chave escalar.
Consultas automáticas singulares, como movie
no nosso exemplo em execução, oferecem suporte a um argumento de chave que aceita um escalar de chave.
Você pode transmitir um escalar de chave como um literal. No entanto, é possível definir variáveis para transmitir escalares de chave como entrada.
Consulta
query GetMovie($myKey: Movie_Key!) { movie(key: $myKey) { title } }
Resposta
{ "data": { "movie": { "title": "Example Movie Title" } } }
Eles podem ser fornecidos em JSON de solicitação assim (ou em outros formatos de serialização):
{
# …
"variables": {
"myKey": {"id": "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"}
}
}
Graças à análise escalar personalizada, um Movie_Key
também pode ser construído usando a sintaxe de objeto, que pode conter variáveis. Isso é útil principalmente quando você quer
dividir componentes individuais em variáveis diferentes por algum motivo.
Escrever consultas básicas
Você pode começar a escrever consultas para receber registros individuais do seu banco de dados ou listar registros de uma tabela com a opção de limitar e ordenar os resultados.
Recuperar registros individuais
A consulta mais simples recebe um único registro por ID. Sua consulta vai usar o campo movie
gerado automaticamente.
Consulta
query GetMovieById($id: UUID!) @auth(level: PUBLIC) { movie(id: $id) { id title imageUrl genre } }
Resposta
{ "data": { "movie": { "id": "some-uuid", "title": "Example Movie Title", "imageUrl": "https://example.com/movie.jpg", "genre": "Action" } } }
Recuperar todos os registros em uma tabela
Para recuperar um subconjunto de campos da lista completa de filmes da tabela Movies
, sua consulta vai usar o campo movies
gerado automaticamente, e sua implementação pode ser semelhante a esta:
Consulta
query ListMovies @auth(level: PUBLIC) { movies { id title imageUrl genre } }
Resposta
{ "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" } ] } }
Usar os operadores orderBy
, limit
e offset
É claro que listar todos os registros de uma tabela tem utilidade limitada.
É possível ordenar e paginar os resultados. Esses argumentos são aceitos, mas não retornados nos resultados.
Aqui, a consulta recebe os títulos dos 10 melhores filmes por classificação.
Consulta
query MoviesTop10 { movies(orderBy: [{ rating: DESC }], limit: 10) { # graphql: list the fields from the results to return title } }
Resposta
{ "data": { "movies": [ { "title": "Top Movie 1" }, { "title": "Top Movie 2" }, { "title": "Top Movie 3" } // ... other 7 movies ] } }
Você pode ter um caso de uso para buscar linhas de um deslocamento, como filmes de 11 a 20 ordenados por classificação.
Consulta
query Movies11to20 { movies(orderBy: [{ rating: DESC }], limit: 10, offset: 10) { # graphql: list the fields from the results to return title } }
Resposta
{ "data": { "movies": [ { "title": "Movie 11" }, { "title": "Movie 12" }, { "title": "Movie 13" } // ... other 7 movies ] } }
Usar aliases em consultas
O Data Connect oferece suporte a alias do GraphQL em consultas. Com os aliases, você renomeia os dados retornados nos resultados de uma consulta. Uma única consulta Data Connect pode aplicar vários filtros ou outras operações de consulta em uma solicitação eficiente ao servidor, emitindo várias "subconsultas" de uma só vez. Para evitar conflitos de nomes no conjunto de dados retornado, use aliases para distinguir as subconsultas.
Confira uma consulta em que uma expressão usa os aliases mostPopular
e leastPopular
.
Consulta
query ReviewPopularitySpread($genre: String) { mostPopular: review( first: { where: {genre: {eq: $genre}}, orderBy: {popularity: DESC} } ), leastPopular: review( last: { where: {genre: {eq: $genre}}, orderBy: {popularity: DESC} } ) }
Resposta
{ "data": { "mostPopular": [ { "popularity": 9 } ], "leastPopular": [ { "popularity": 1 } ] } }
Usar filtros de consulta
As consultas Data Connect são mapeadas para todos os filtros e operações de ordenação comuns do SQL.
Filtrar com operadores where
e orderBy
Retorna todas as linhas correspondentes da tabela (e associações aninhadas). Retorna uma matriz vazia se nenhum registro corresponder ao filtro.
Consulta
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 } }
Resposta
{ "data": { "mostPopular": [ { "id": "some-uuid", "title": "Example Movie Title", "genre": "Action", "description": "A great movie" } ] } }
Filtrar testando valores nulos
É possível testar valores null
usando o operador isNull
.
Consulta
query ListMoviesWithoutDescription { movies(where: { description: { isNull: true }}) { id title } }
Resposta
{ "data": { "movies": [ { "id": "some-uuid", "title": "Example Movie Title" }, { "id": "another-uuid", "title": "Another Movie Title" } ] } }
Para mais operadores, consulte o guia de referência de tipos de objetos de entrada.
Filtrar com comparações de valores
Você pode usar operadores como lt
(menor que) e ge
(maior ou igual a) para comparar valores nas consultas.
Consulta
query ListMoviesByRating($minRating: Int!, $maxRating: Int!) { movies(where: { rating: { ge: $minRating, lt: $maxRating }}) { id title } }
Resposta
{ "data": { "movies": [ { "id": "some-uuid", "title": "Example Movie Title" }, { "id": "another-uuid", "title": "Another Movie Title" } ] } }
Filtrar com operadores includes
e excludes
para campos de matriz
Você pode testar se um campo de matriz inclui um item especificado.
O exemplo a seguir ilustra o operador includes
.
O Data Connect é compatível com includesAll
, excludes
, excludesAll
e
muito mais. Confira todos esses operadores para números inteiros, strings, datas e outros tipos de dados nos cabeçalhos _ListFilter
da documentação de referência.
Consulta
query ListMoviesByTag($tag: String!) { movies(where: { tags: { includes: $tag }}) { # graphql: list the fields from the results to return id title } }
Resposta
{ "data": { "movies": [ { "id": "some-uuid", "title": "Example Movie Title" } ] } }
Filtrar com operações de string e expressões regulares
Suas consultas podem usar operações típicas de pesquisa e comparação de strings, incluindo expressões regulares. Para eficiência, você está agrupando várias operações aqui e desambiguando-as com aliases.
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}}) {...}
}
Filtrar com a lógica dos operadores _or
, _and
e _not
Use _or
para uma lógica mais complexa. O Data Connect também é compatível com os operadores _and
e _not
.
Consulta
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 } }
Resposta
{ "data": { "movies": [ { "title": "Movie Title 1" }, { "title": "Movie Title 2" } ] } }
Criar consultas relacionais
As consultas Data Connect podem acessar dados com base nas relações entre tabelas. Você pode usar as relações de objeto (um para um) ou matriz (um para muitos) definidas no seu esquema para fazer consultas aninhadas, ou seja, buscar dados de um tipo junto com dados de um tipo aninhado ou relacionado.
Essas consultas usam a sintaxe mágica Data Connect _on_
e _via
em
campos de leitura gerados.
Não se esqueça de analisar o esquema de exemplo.
Muitos para um
Agora vamos analisar uma consulta para ilustrar a sintaxe _on_
.
Consulta
query MyReviews @auth(level: USER) { user(key: {id_expr: "auth.uid"}) { reviews: reviews_on_user { movie { name } rating } } }
Resposta
{ "data": { "user": { "reviews": [ { "movie": { "name": "Movie Title" }, "rating": 5 } ] } } }
Um para um
É possível escrever uma consulta individual usando a sintaxe _on_
.
Consulta
query GetMovieMetadata($id: UUID!) @auth(level: PUBLIC) { movie(id: $id) { movieMetadatas_on_movie { director } } }
Resposta
{ "data": { "movie": { "movieMetadatas_on_movie": { "director": "Some Director" } } } }
Muitos para muitos
As consultas de muitos para muitos usam a sintaxe _via_
. Uma consulta de muitos para muitos pode
recuperar atores de um filme específico.
Consulta
query MoviesActors($id: UUID!) @auth(level: USER) { movie(id: $id) { actors: actors_via_MovieActors { name } } }
Resposta
{ "data": { "movie": { "actors": [ { "name": "Actor Name" } ] } } }
Mas podemos escrever uma consulta mais complexa, usando aliases, para filtrar com base em role
e recuperar atores e filmes associados nos resultados mainActors
e supportingActors
. Como é uma relação de muitos para muitos, a sintaxe _via_
é usada.
Consulta
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 } } }
Resposta
{ "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" } ] } } }
Consultas de agregação
O que são agregações e por que usá-las?
Com os campos de agregação, é possível fazer cálculos em uma lista de resultados. Com os campos de agregação, você pode fazer o seguinte:
- Encontrar a pontuação média de uma avaliação
- Encontrar o custo total dos itens em um carrinho de compras
- Encontrar o produto com a melhor ou pior avaliação
- Contar o número de produtos na sua loja
As agregações são realizadas no servidor, o que oferece vários benefícios em relação ao cálculo do lado do cliente:
- Performance mais rápida do app (já que você evita cálculos do lado do cliente)
- Redução dos custos de saída de dados, já que você envia apenas os resultados agregados em vez de todas as entradas.
- Segurança aprimorada (já que você pode dar aos clientes acesso a dados agregados em vez de todo o conjunto de dados)
Exemplo de esquema para agregações
Nesta seção, vamos mudar para um exemplo de esquema de vitrine, que é bom para explicar como usar agregações:
type Product @table {
name: String!
manufacturer: String!
quantityInStock: Int!
price: Float!
expirationDate: Date
}
Agregações simples
_count para todos os campos
O campo de agregação mais simples é _count
, que retorna quantas linhas correspondem à sua consulta. Para cada campo no seu tipo, o Data Connect gera campos agregados correspondentes, dependendo do tipo de campo.
Consulta
query CountProducts {
products {
_count
}
}
Resposta one
one
Por exemplo, se você tiver cinco produtos no banco de dados, o resultado será:
{
"products": [
{
"_count": 5
}
]
}
Todos os campos têm um campo <field>_count
, que conta quantas linhas têm um valor não nulo nesse campo.
Consulta
query CountProductsWithExpirationDate {
products {
expirationDate_count
}
}
Respostafield_count
field_count
Por exemplo, se você tiver três produtos com uma data de validade, o resultado será:
{
"products": [
{
"expirationDate_count": 3
}
]
}
_min, _max, _sum e _avg para campos numéricos
Os campos numéricos (int, float, int64) também têm <field>_min
, <field>_max
, <field>_sum
e <field>_avg
.
Consulta
query NumericAggregates {
products {
quantityInStock_max
price_min
price_avg
quantityInStock_sum
}
}
Resposta_min _max _sum _avg
_min _max _sum _avg
Por exemplo, se você tiver os seguintes produtos:
- Produto A:
quantityInStock: 10
,price: 2.99
- Produto B:
quantityInStock: 5
,price: 5.99
- Produto C:
quantityInStock: 20
,price: 1.99
O resultado seria:
{
"products": [
{
"quantityInStock_max": 20,
"price_min": 1.99,
"price_avg": 3.6566666666666666,
"quantityInStock_sum": 35
}
]
}
_min e _max para datas e carimbos de data/hora
Os campos de data e carimbo de data/hora têm <field>_min
e <field>_max
.
Consulta
query DateAndTimeAggregates {
products {
expirationDate_max
expirationDate_min
}
}
Resposta_min _maxdatetime
_min _maxdatetime
Por exemplo, se você tiver as seguintes datas de expiração:
- Produto A:
2024-01-01
- Produto B:
2024-03-01
- Produto C:
2024-02-01
O resultado seria:
{
"products": [
{
"expirationDate_max": "2024-03-01",
"expirationDate_min": "2024-01-01"
}
]
}
Distinto
O argumento distinct
permite extrair todos os valores exclusivos de um campo (ou combinação de campos). Exemplo:
Consulta
query ListDistinctManufacturers {
products(distinct: true) {
manufacturer
}
}
Respostadistinct
distinct
Por exemplo, se você tiver os seguintes fabricantes:
- Produto A:
manufacturer: "Acme"
- Produto B:
manufacturer: "Beta"
- Produto C:
manufacturer: "Acme"
O resultado seria:
{
"products": [
{ "manufacturer": "Acme" },
{ "manufacturer": "Beta" }
]
}
Também é possível usar o argumento distinct
em campos agregados para agregar os valores distintos. Exemplo:
Consulta
query CountDistinctManufacturers {
products {
manufacturer_count(distinct: true)
}
}
Respostadistinctonaggregate
distinctonaggregate
Por exemplo, se você tiver os seguintes fabricantes:
- Produto A:
manufacturer: "Acme"
- Produto B:
manufacturer: "Beta"
- Produto C:
manufacturer: "Acme"
O resultado seria:
{
"products": [
{
"manufacturer_count": 2
}
]
}
Agregações agrupadas
Para fazer uma agregação agrupada, selecione uma combinação de campos agregados e não agregados em um tipo. Isso agrupa todas as linhas correspondentes que têm o mesmo valor para os campos não agregados e calcula os campos agregados para esse grupo. Exemplo:
Consulta
query MostExpensiveProductByManufacturer {
products {
manufacturer
price_max
}
}
Respostagroupedaggregates
groupedaggregates
Por exemplo, se você tiver os seguintes produtos:
- Produto A:
manufacturer: "Acme"
,price: 2.99
- Produto B:
manufacturer: "Beta"
,price: 5.99
- Produto C:
manufacturer: "Acme"
,price: 1.99
O resultado seria:
{
"products": [
{ "manufacturer": "Acme", "price_max": 2.99 },
{ "manufacturer": "Beta", "price_max": 5.99 }
]
}
having
e where
com agregados agrupados
Também é possível usar o argumento having
e where
para retornar apenas grupos que atendam a um critério fornecido.
having
permite filtrar grupos pelos campos agregados.Com
where
, é possível filtrar as linhas com base em campos não agregados.
Consulta
query FilteredMostExpensiveProductByManufacturer {
products(having: {price_max: {ge: 2.99}}) {
manufacturer
price_max
}
}
Respostahavingwhere
havingwhere
Por exemplo, se você tiver os seguintes produtos:
- Produto A:
manufacturer: "Acme"
,price: 2.99
- Produto B:
manufacturer: "Beta"
,price: 5.99
- Produto C:
manufacturer: "Acme"
,price: 1.99
O resultado seria:
{
"products": [
{ "manufacturer": "Acme", "price_max": 2.99 },
{ "manufacturer": "Beta", "price_max": 5.99 }
]
}
Agrega em várias tabelas
Os campos de agregação podem ser usados em conjunto com os campos de relacionamento um-para-muitos gerados para responder a perguntas complexas sobre seus dados. Confira um esquema modificado, com uma tabela separada, Manufacturer
, que podemos usar nos exemplos:
type Product @table {
name: String!
manufacturer: Manufacturer!
quantityInStock: Int!
price: Float!
expirationDate: Date
}
type Manufacturer @table {
name: String!
headquartersCountry: String!
}
Agora podemos usar campos agregados para fazer coisas como descobrir quantos produtos um fabricante produz:
Consulta
query GetProductCount($id: UUID) {
manufacturers {
name
products_on_manufacturer {
_count
}
}
}
Resposta aggregatesacrosstables
aggregatesacrosstables
Por exemplo, se você tiver os seguintes fabricantes:
- Fabricante A:
name: "Acme"
,products_on_manufacturer: 2
- Fabricante B:
name: "Beta"
,products_on_manufacturer: 1
O resultado seria:
{
"manufacturers": [
{ "name": "Acme", "products_on_manufacturer": { "_count": 2 } },
{ "name": "Beta", "products_on_manufacturer": { "_count": 1 } }
]
}
Escrever consultas avançadas: usar campos query
para ler dados em operações de várias etapas
Há muitas situações em que você pode querer ler seu banco de dados durante a execução de uma mutação para pesquisar e verificar dados existentes antes de realizar, por exemplo, inserções ou atualizações. Essas opções economizam operações de ida e volta e, portanto, custos.
O Data Connect é compatível com essa funcionalidade. Consulte operações de várias etapas.
Próximas etapas
Talvez você se interesse por:
- Gerar consultas para seus apps usando ferramentas de assistência de IA
- Autorizar suas consultas de acordo com o guia de autorização
- Chamando consultas do código do cliente para Web, iOS, Android e Flutter.