Firebase Data Connect позволяет создавать коннекторы для ваших экземпляров PostgreSQL, управляемых с помощью Google Cloud SQL. Эти коннекторы представляют собой комбинации запросов и мутаций для использования данных из вашей схемы.
В руководстве по началу работы была представлена схема приложения для написания отзывов о фильмах для PostgreSQL.
В этом руководстве также были описаны как развертываемые, так и нерегламентированные административные операции, включая запросы.
- Развертываемые запросы — это запросы, которые вы реализуете для вызова из клиентских приложений с помощью определяемых вами конечных точек API. Вы объединяете их в коннектор , развертываемый на сервере. Инструменты Data Connect генерируют клиентские SDK на основе вашего API. Развернутые запросы не защищены политикой IAM, поэтому обязательно защитите их с помощью директивы Data Connect
@auth. - Для чтения данных выполняются произвольные административные запросы из привилегированных сред. Вы можете создавать и выполнять их в консоли Firebase или в локальных средах разработки с помощью нашего расширения Data Connect для VS Code.
В этом руководстве более подробно рассматриваются развертываемые запросы .
Особенности запросов Data Connect
Data Connect позволяет выполнять базовые запросы всеми способами, которые вы ожидаете от базы данных PostgreSQL.
Однако благодаря расширениям Data Connect для GraphQL вы можете реализовывать сложные запросы для более быстрой и эффективной работы приложений:
- Используйте ключевые скалярные значения, возвращаемые многими операциями, чтобы упростить повторяющиеся операции над записями.
- Выполняйте запросы в ходе многоэтапных операций изменения данных для поиска информации, экономя строки кода и количество обращений к серверу.
Используйте сгенерированные поля для построения запросов.
Ваши операции Data Connect будут расширять набор полей, автоматически генерируемых Data Connect на основе типов и связей между типами в вашей схеме. Эти поля генерируются локальными инструментами при каждом редактировании вашей схемы.
С помощью сгенерированных полей можно реализовывать запросы все большей сложности, от извлечения отдельных записей или нескольких записей из одной таблицы до нескольких записей из связанных таблиц. Предположим, ваша схема содержит тип Movie и связанный с ним тип Actor . Data Connect генерирует поля movie , movies , actors_on_movies и другие.
Запрос с использованием
movie
Поле | Используйте это поле для поиска отдельного фильма по его ключу. query GetMovie($myKey: Movie_Key!) { movie(key: $myKey) { title } } |
Запрос с использованием
movies
Поле | Используйте это поле для запроса нескольких фильмов, например, всех фильмов с заданным годом. query GetMovies($myYear: Int!) { movies(where: { year: { eq: $myYear } }) { title } } |
Запрос с использованием
поле actors_on_movies
Поле | Используйте это поле для запроса всех актеров, связанных с данным фильмом. query GetActorsOnMovie($myKey: Movie_Key!) { actors_on_movies(where: { movie: { key: { eq: $myKey } } }) { actor { name } } } |
Основные элементы запроса
Запросы Data Connect — это запросы GraphQL с расширениями Data Connect . Как и в случае с обычным запросом GraphQL, вы можете определить имя операции и список переменных GraphQL.
Data Connect расширяет возможности запросов GraphQL с помощью настраиваемых директив , таких как @auth .
So the following query has:
- Определение типа
query - Имя операции (запроса)
ListMoviesByGenre - Единственный аргумент запроса, в данном случае переменная
$genreStringтипа. - Одна директива,
@auth. - Одна область —
movies.
query ListMoviesByGenre($genre: String!) @auth(level: PUBLIC) {
movies(where: { genre: { eq: $genre } }) {
id
title
}
}
Для каждого аргумента запроса требуется объявление типа: встроенного, например, String , или пользовательского, определенного в схеме типа, например, Movie .
В этом руководстве мы рассмотрим сигнатуры все более сложных запросов. В заключение вы познакомитесь с мощными и лаконичными выражениями связей, которые можно использовать для создания развертываемых запросов.
Ключевые скалярные значения в запросах
Но сначала несколько слов о ключевых скалярах.
Data Connect определяет специальный скалярный ключ для представления первичных ключей каждой таблицы, обозначаемый параметром {TableType}_Key. Это JSON-объект со значениями первичных ключей.
Скалярные ключи извлекаются в ответе, возвращаемом большинством автоматически генерируемых полей чтения, или, конечно же, из запросов, в которых вы получили все поля, необходимые для формирования скалярного ключа.
Автоматические запросы, например, запрос на просмотр movie в нашем примере, поддерживают аргумент key, который принимает скалярное значение ключа.
Вы можете передать скалярный ключ в качестве литерала. Но вы также можете определить переменные для передачи скалярных ключей в качестве входных данных.
Запрос
query GetMovie($myKey: Movie_Key!) { movie(key: $myKey) { title } }
Ответ
{ "data": { "movie": { "title": "Example Movie Title" } } }
Эти данные можно предоставить в JSON-запросе следующим образом (или в других форматах сериализации):
{
# …
"variables": {
"myKey": {"id": "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"}
}
}
Благодаря пользовательскому синтаксическому анализу скалярных значений, объект Movie_Key также может быть создан с использованием объектного синтаксиса, который может содержать переменные. Это особенно полезно, когда по какой-либо причине необходимо разбить отдельные компоненты на разные переменные.
Пишите простые запросы
Вы можете начать писать запросы для получения отдельных записей из вашей базы данных или для получения списка записей из таблицы с возможностью ограничения и упорядочивания результатов.
Получение отдельных записей
Простейший запрос получает одну запись по ID. Ваш запрос будет использовать автоматически сгенерированное поле movie .
Запрос
query GetMovieById($id: UUID!) @auth(level: PUBLIC) { movie(id: $id) { id title imageUrl genre } }
Ответ
{ "data": { "movie": { "id": "some-uuid", "title": "Example Movie Title", "imageUrl": "https://example.com/movie.jpg", "genre": "Action" } } }
Извлечь все записи из таблицы
Для получения подмножества полей из полного списка фильмов из таблицы Movies ваш запрос будет использовать автоматически сгенерированное поле movies , и ваша реализация может выглядеть следующим образом.
Запрос
query ListMovies @auth(level: PUBLIC) { movies { id title imageUrl genre } }
Ответ
{ "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" } ] } }
Используйте операторы orderBy , limit и offset
Естественно, перечисление всех записей из таблицы имеет ограниченную полезность.
Вы можете упорядочивать результаты и выполнять пагинацию. Эти аргументы принимаются, но не возвращаются в результатах.
В данном случае запрос получает названия 10 фильмов, занимающих первые места в рейтинге.
Запрос
query MoviesTop10 { movies(orderBy: [{ rating: DESC }], limit: 10) { # graphql: list the fields from the results to return title } }
Ответ
{ "data": { "movies": [ { "title": "Top Movie 1" }, { "title": "Top Movie 2" }, { "title": "Top Movie 3" } // ... other 7 movies ] } }
Возможно, вам потребуется получить строки, начиная с определенного смещения, например, фильмы с 11 по 20, отсортированные по рейтингу.
Запрос
query Movies11to20 { movies(orderBy: [{ rating: DESC }], limit: 10, offset: 10) { # graphql: list the fields from the results to return title } }
Ответ
{ "data": { "movies": [ { "title": "Movie 11" }, { "title": "Movie 12" }, { "title": "Movie 13" } // ... other 7 movies ] } }
Используйте псевдонимы в запросах.
Data Connect поддерживает псевдонимы GraphQL в запросах. С помощью псевдонимов вы переименовываете данные, возвращаемые в результатах запроса. Один запрос Data Connect может применять несколько фильтров или других операций запроса в одном эффективном запросе к серверу, фактически выполняя несколько «подзапросов» одновременно. Чтобы избежать конфликтов имен в возвращаемом наборе данных, вы используете псевдонимы для различения подзапросов.
Вот запрос, в котором выражение использует псевдонимы mostPopular и leastPopular .
Запрос
query ReviewPopularitySpread($genre: String) { mostPopular: review( first: { where: {genre: {eq: $genre}}, orderBy: {popularity: DESC} } ), leastPopular: review( last: { where: {genre: {eq: $genre}}, orderBy: {popularity: DESC} } ) }
Ответ
{ "data": { "mostPopular": [ { "popularity": 9 } ], "leastPopular": [ { "popularity": 1 } ] } }
Используйте фильтры запроса
Запросы Data Connect соответствуют всем распространенным фильтрам SQL и операциям сортировки.
Фильтрация по принципу where с использованием операторов orderBy
Возвращает все соответствующие строки из таблицы (и вложенные ассоциации). Возвращает пустой массив, если ни одна запись не соответствует фильтру.
Запрос
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 } }
Ответ
{ "data": { "mostPopular": [ { "id": "some-uuid", "title": "Example Movie Title", "genre": "Action", "description": "A great movie" } ] } }
Фильтрация по проверке на наличие нулевых значений
Проверить наличие null значений можно с помощью оператора isNull .
Запрос
query ListMoviesWithoutDescription { movies(where: { description: { isNull: true }}) { id title } }
Ответ
{ "data": { "movies": [ { "id": "some-uuid", "title": "Example Movie Title" }, { "id": "another-uuid", "title": "Another Movie Title" } ] } }
Дополнительные операторы см. в справочнике по типам входных объектов .
Фильтрация по сравнению значений
You can use operators like lt (less than) and ge (greater than or equal) to compare values in your queries.
Запрос
query ListMoviesByRating($minRating: Int!, $maxRating: Int!) { movies(where: { rating: { ge: $minRating, lt: $maxRating }}) { id title } }
Ответ
{ "data": { "movies": [ { "id": "some-uuid", "title": "Example Movie Title" }, { "id": "another-uuid", "title": "Another Movie Title" } ] } }
Filter with includes and excludes operators for array fields
Вы можете проверить, содержит ли поле массива указанный элемент.
Следующий пример иллюстрирует оператор includes .
Data Connect поддерживает операторы includesAll , excludes , excludesAll и другие. Ознакомьтесь со всеми такими операторами для целых чисел, строк, дат и других типов данных в разделе _ListFilter справочной документации .
Запрос
query ListMoviesByTag($tag: String!) { movies(where: { tags: { includes: $tag }}) { # graphql: list the fields from the results to return id title } }
Ответ
{ "data": { "movies": [ { "id": "some-uuid", "title": "Example Movie Title" } ] } }
Фильтрация с использованием строковых операций и регулярных выражений.
В ваших запросах могут использоваться типичные операции поиска и сравнения строк, включая регулярные выражения. Обратите внимание, что для повышения эффективности вы объединяете здесь несколько операций и уточняете их с помощью псевдонимов.
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}}) {...}
}
Фильтрация с использованием операторов _or , _and , _not
Для более сложной логики используйте _or . Data Connect также поддерживает операторы _and и _not .
Запрос
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 } }
Ответ
{ "data": { "movies": [ { "title": "Movie Title 1" }, { "title": "Movie Title 2" } ] } }
Пишите реляционные запросы
Запросы Data Connect могут получать доступ к данным на основе связей между таблицами. Вы можете использовать объектные (один к одному) или массивные (один ко многим) связи, определенные в вашей схеме, для создания вложенных запросов, то есть для получения данных для одного типа вместе с данными из вложенного или связанного типа.
В подобных запросах используется специальный синтаксис Data Connect _on_ и _via в генерируемых полях для чтения.
Не забудьте ознакомиться с примером схемы .
Многие к одному
Теперь рассмотрим запрос, чтобы _on_ синтаксис.
Запрос
query MyReviews @auth(level: USER) { user(key: {id_expr: "auth.uid"}) { reviews: reviews_on_user { movie { name } rating } } }
Ответ
{ "data": { "user": { "reviews": [ { "movie": { "name": "Movie Title" }, "rating": 5 } ] } } }
Один на один
Запрос типа «один к одному» можно написать, используя синтаксис _on_ .
Запрос
query GetMovieMetadata($id: UUID!) @auth(level: PUBLIC) { movie(id: $id) { movieMetadatas_on_movie { director } } }
Ответ
{ "data": { "movie": { "movieMetadatas_on_movie": { "director": "Some Director" } } } }
Многие ко многим
Запросы типа «многие ко многим» используют синтаксис _via_ . Запрос типа «многие ко многим» может, например, извлечь информацию об актерах для указанного фильма.
Запрос
query MoviesActors($id: UUID!) @auth(level: USER) { movie(id: $id) { actors: actors_via_MovieActors { name } } }
Ответ
{ "data": { "movie": { "actors": [ { "name": "Actor Name" } ] } } }
Но мы можем написать более сложный запрос, используя псевдонимы, для фильтрации по role , чтобы получить актеров и связанные с ними фильмы в результатах mainActors и supportingActors . Поскольку это связь «многие ко многим», используется синтаксис _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 } } }
Ответ
{ "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" } ] } } }
Агрегационные запросы
Что такое заполнители и зачем их использовать?
Агрегатные поля позволяют выполнять вычисления над списком результатов. С помощью агрегатных полей можно делать, например:
- Find the average score of a review
- Узнайте общую стоимость товаров в корзине покупок.
- Найдите товар с самым высоким или самым низким рейтингом.
- Count the number of products in your store
Агрегирование выполняется на сервере, что дает ряд преимуществ по сравнению с вычислением на стороне клиента:
- Faster app performance (since you avoid client side calculations)
- Reduced data egress costs (since you send just the aggregated results instead of all of the inputs)
- Повышенная безопасность (поскольку вы можете предоставлять клиентам доступ к агрегированным данным, а не ко всему набору данных).
Пример схемы для агрегатов
In this section, we'll switch to a storefront example schema, which is a good for explaining how to use aggregates:
type Product @table {
name: String!
manufacturer: String!
quantityInStock: Int!
price: Float!
expirationDate: Date
}
Простые агрегаты
_count for all fields
Простейшее агрегирующее поле — _count : оно возвращает количество строк, соответствующих вашему запросу. Для каждого поля вашего типа Data Connect генерирует соответствующие агрегирующие поля в зависимости от типа поля.
Запрос
query CountProducts {
products {
_count
}
}
Ответ one
one
For example, if you have 5 products in your database, the result would be:
{
"products": [
{
"_count": 5
}
]
}
All fields have a <field>_count field, which counts how many rows have a non-null value in that field.
Запрос
query CountProductsWithExpirationDate {
products {
expirationDate_count
}
}
Ответ field_count
field_count
For example, if you have 3 products with an expiration date, the result would be:
{
"products": [
{
"expirationDate_count": 3
}
]
}
_min, _max, _sum, and _avg for numeric fields
Numeric fields (int, float, int64) also have <field>_min , <field>_max , <field>_sum , and <field>_avg .
Запрос
query NumericAggregates {
products {
quantityInStock_max
price_min
price_avg
quantityInStock_sum
}
}
Ответ _min _max _sum _avg
_min _max _sum _avg
Например, если у вас есть следующие товары:
- Товар A:
quantityInStock: 10,price: 2.99 - Product B:
quantityInStock: 5,price: 5.99 - Product C:
quantityInStock: 20,price: 1.99
The result would be:
{
"products": [
{
"quantityInStock_max": 20,
"price_min": 1.99,
"price_avg": 3.6566666666666666,
"quantityInStock_sum": 35
}
]
}
_min and _max for dates and timestamps
Date and timestamp fields have <field>_min and <field>_max .
Запрос
query DateAndTimeAggregates {
products {
expirationDate_max
expirationDate_min
}
}
Ответ _min _maxdatetime
_min _maxdatetime
For example, if you have the following expiration dates:
- Продукт A:
2024-01-01 - Продукт B:
2024-03-01 - Product C:
2024-02-01
The result would be:
{
"products": [
{
"expirationDate_max": "2024-03-01",
"expirationDate_min": "2024-01-01"
}
]
}
Отчетливый
The distinct argument lets you get all unique values for a field (or combination of fields). For example:
Запрос
query ListDistinctManufacturers {
products(distinct: true) {
manufacturer
}
}
Ответ distinct
distinct
For example, if you have the following manufacturers:
- Product A:
manufacturer: "Acme" - Product B:
manufacturer: "Beta" - Product C:
manufacturer: "Acme"
The result would be:
{
"products": [
{ "manufacturer": "Acme" },
{ "manufacturer": "Beta" }
]
}
You can also use the distinct argument on aggregate fields to instead aggregate the distinct values. For example:
Запрос
query CountDistinctManufacturers {
products {
manufacturer_count(distinct: true)
}
}
Ответ distinctonaggregate
distinctonaggregate
For example, if you have the following manufacturers:
- Product A:
manufacturer: "Acme" - Product B:
manufacturer: "Beta" - Product C:
manufacturer: "Acme"
The result would be:
{
"products": [
{
"manufacturer_count": 2
}
]
}
Grouped aggregates
Выполнение групповой агрегации осуществляется путем выбора комбинации агрегированных и неагрегированных полей определенного типа. Это группирует все совпадающие строки, имеющие одинаковое значение для неагрегированных полей, и вычисляет агрегированные поля для этой группы. Например:
Запрос
query MostExpensiveProductByManufacturer {
products {
manufacturer
price_max
}
}
Ответ groupedaggregates
groupedaggregates
For example, if you have the following products:
- Product A:
manufacturer: "Acme",price: 2.99 - Product B:
manufacturer: "Beta",price: 5.99 - Product C:
manufacturer: "Acme",price: 1.99
В результате получится следующее:
{
"products": [
{ "manufacturer": "Acme", "price_max": 2.99 },
{ "manufacturer": "Beta", "price_max": 5.99 }
]
}
having и where сгруппированные агрегаты
You can also use the having and where argument to only return groups that meet a provided criteria.
-
havinglets you filter groups by their aggregate fields wherelets you filter the rows based on non-aggregate fields.
Запрос
query FilteredMostExpensiveProductByManufacturer {
products(having: {price_max: {ge: 2.99}}) {
manufacturer
price_max
}
}
Ответ havingwhere
havingwhere
For example, if you have the following products:
- Product A:
manufacturer: "Acme",price: 2.99 - Product B:
manufacturer: "Beta",price: 5.99 - Product C:
manufacturer: "Acme",price: 1.99
The result would be:
{
"products": [
{ "manufacturer": "Acme", "price_max": 2.99 },
{ "manufacturer": "Beta", "price_max": 5.99 }
]
}
Aggregates across tables
Агрегированные поля можно использовать совместно с полями, создаваемыми в рамках отношений «один ко многим», для ответа на сложные вопросы о ваших данных. Вот модифицированная схема с отдельной таблицей Manufacturer , которую мы можем использовать в примерах:
type Product @table {
name: String!
manufacturer: Manufacturer!
quantityInStock: Int!
price: Float!
expirationDate: Date
}
type Manufacturer @table {
name: String!
headquartersCountry: String!
}
Now we can use aggregate fields to do things like find how many products a manufacturer makes:
Запрос
query GetProductCount($id: UUID) {
manufacturers {
name
products_on_manufacturer {
_count
}
}
}
Ответ aggregatesacrosstables
aggregatesacrosstables
For example, if you have the following manufacturers:
- Manufacturer A:
name: "Acme",products_on_manufacturer: 2 - Manufacturer B:
name: "Beta",products_on_manufacturer: 1
The result would be:
{
"manufacturers": [
{ "name": "Acme", "products_on_manufacturer": { "_count": 2 } },
{ "name": "Beta", "products_on_manufacturer": { "_count": 1 } }
]
}
Query enumeration fields
You can query enumeration fields in your schema .
Предположим, что наша процедура перечисления реализована следующим образом:
enum AspectRatio {
ACADEMY
WIDESCREEN
ANAMORPHIC
IMAX
"No information available on aspect ratio"
UNAVAILABLE
}
For example, you can list all movies formatted in your preferred aspect ratio.
query ListMoviesByAspectRatio($aspectRatio: AspectRatio!) @auth(level: PUBLIC) {
movies(where:
{originalAspectRatio: {eq: $aspectRatio}}
) {
id
title
imageUrl
releaseYear
genre
rating
tags
description
otherAspectRatios
}
}
Фильтрация полей перечисления
Для перечислений доступны стандартные действия фильтрации: eq , ne , ge , gt , lt , le , in nin , а также includes , excludes , includesAll и excludesAll для списков.
order «больше» и «меньше» используют порядок значений перечисления для корректного выполнения. Значения в начале перечисления меньше значений в конце определения перечисления.
For example, the following where clause would return all movies with ratio less than the input aspect ratio.
- В первом примере, если
$aspectRatioравно IMAX, то будут выбраны фильмы сratio, совпадающим с ACADEMY, WIDESCREEN и ANAMORPHIC. - In the second example, only movies available in both ACADEMY and WIDESCREEN would be returned.
movies(where: {originalAspectRatio: {lt: $aspectRatio}})
movies(where: {otherAspectRatios: {includesAll: [ACADEMY, WIDESCREEN]}})
Perform aggregation queries on enumeration fields
Для перечислений поддерживается только _count .
The following query counts the total number of movies of each aspect ratio.
query MovieCountByOriginalAspectRatio @auth(level: PUBLIC) {
movies {
_count
originalAspectRatio
}
}
Вызов запросов перечисления из клиентского кода
Learn how to use enumeration queries from clients, and how to design client calls to maximize your flexibility in managing enumerations, in the SDK guides for iOS , Android , web and Flutter .
Write advanced queries: use query fields to read data in multi-step operations
Существует множество ситуаций, когда может потребоваться чтение базы данных во время выполнения операции изменения, чтобы проверить и подтвердить существующие данные перед выполнением, например, вставки или обновления. Такие варианты позволяют избежать повторных обращений к базе данных и, следовательно, снизить затраты.
Data Connect поддерживает эту функциональность. См. раздел "Многоэтапные операции" .
Следующие шаги
Возможно, вас заинтересует:
- Генерация запросов для ваших приложений с помощью инструментов искусственного интеллекта.
- Авторизуйте свои запросы в соответствии с руководством по авторизации.
- Вызов запросов из клиентского кода для веб-приложений , iOS , Android и Flutter .