Firebase Data Connect позволяет создавать коннекторы для экземпляров PostgreSQL, управляемых с помощью Google Cloud SQL. Эти коннекторы представляют собой комбинации запросов и мутаций для использования данных из вашей схемы.
В руководстве по началу работы представлена схема приложения для обзора фильмов для PostgreSQL.
В этом руководстве также были представлены как развертываемые, так и специальные административные операции, включая запросы.
- Развертываемые запросы — это запросы, которые вы реализуете для вызова из клиентских приложений с определяемыми вами конечными точками API. Вы объединяете их в коннектор , развёрнутый на сервере. Инструментарий Data Connect генерирует клиентские SDK на основе вашего API. Развёрнутые запросы не защищены политикой IAM, поэтому обязательно защитите их с помощью директивы Data Connect
@auth
. - Административные запросы ad hoc запускаются из привилегированных сред для чтения данных. Вы можете создавать и выполнять их в консоли 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
.
Итак, следующий запрос имеет:
- Определение типа
query
- Имя операции (запроса)
ListMoviesByGenre
- Один аргумент запроса, в данном случае переменная
$genre
String
типа - Единственная директива,
@auth
. - Одно поле –
movies
.
query ListMoviesByGenre($genre: String!) @auth(level: PUBLIC) {
movies(where: { genre: { eq: $genre } }) {
id
title
}
}
Каждый аргумент запроса требует объявления типа: встроенного, например String
, или пользовательского, определяемого схемой типа, например Movie
.
В этом руководстве мы рассмотрим сигнатуру всё более сложных запросов. В заключение мы познакомимся с эффективными и лаконичными выражениями отношений, которые можно использовать для создания готовых к развертыванию запросов.
Ключевые скаляры в запросах
Но сначала — замечание о ключевых скалярах.
Data Connect определяет специальный скалярный ключ для представления первичных ключей каждой таблицы, идентифицируемый {TableType}_Key. Это JSON-объект значений первичных ключей.
Вы извлекаете ключевые скаляры как ответ, возвращаемый большинством автоматически сгенерированных полей чтения, или, конечно, из запросов, в которых вы извлекаете все поля, необходимые для построения скалярного ключа.
Отдельные автоматические запросы, такие как movie
в нашем примере, поддерживают ключевой аргумент, который принимает ключевой скаляр.
Вы можете передать ключевой скаляр как литерал. Однако вы можете определить переменные для передачи ключевых скаляров в качестве входных данных.
Запрос
query GetMovie($myKey: Movie_Key!) { movie(key: $myKey) { title } }
Ответ
{ "data": { "movie": { "title": "Example Movie Title" } } }
Их можно предоставить в запросе JSON следующим образом (или в других форматах сериализации):
{
# …
"variables": {
"myKey": {"id": "xxxxxxxx-x
xxx-4xxx-yxxx-xxxxxxxxxxxx"}
}
}
Благодаря настраиваемому скалярному парсингу Movie_Key
также можно сконструировать с использованием синтаксиса объекта, который может содержать переменные. Это особенно полезно, когда по какой-то причине требуется разбить отдельные компоненты на отдельные переменные.
Напишите базовые запросы
Вы можете начать писать запросы для получения отдельных записей из вашей базы данных или перечислить записи из таблицы с возможностью ограничения и упорядочивания результатов.
Извлечь отдельные записи
Простейший запрос возвращает одну запись по идентификатору. Ваш запрос будет использовать автоматически сгенерированное поле 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" } ] } }
Дополнительные операторы см. в справочнике по типам входных объектов .
Фильтр со сравнением значений
Для сравнения значений в запросах можно использовать такие операторы, как lt
(меньше) и ge
(больше или равно).
Запрос
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" } ] } }
Фильтр с операторами includes
и excludes
для полей массива
Вы можете проверить, что поле массива содержит указанный элемент.
Следующий пример иллюстрирует оператор 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" } ] } } }
Запросы на агрегацию
Что такое агрегаты и зачем их использовать?
Агрегатные поля позволяют выполнять вычисления со списком результатов. С помощью агрегатных полей вы можете делать следующее:
- Найдите среднюю оценку обзора
- Узнать общую стоимость товаров в корзине
- Найдите продукт с самым высоким или самым низким рейтингом
- Подсчитайте количество товаров в вашем магазине
Агрегаты выполняются на сервере, что дает ряд преимуществ по сравнению с их вычислением на стороне клиента:
- Более высокая производительность приложения (поскольку вы избегаете вычислений на стороне клиента)
- Снижение затрат на передачу данных (поскольку вы отправляете только агрегированные результаты вместо всех входных данных)
- Повышенная безопасность (поскольку вы можете предоставить клиентам доступ к агрегированным данным, а не ко всему набору данных)
Пример схемы для агрегатов
В этом разделе мы перейдем к примеру схемы витрины, которая хорошо подходит для объяснения того, как использовать агрегаты:
type Product @table {
name: String!
manufacturer: String!
quantityInStock: Int!
price: Float!
expirationDate: Date
}
Простые агрегаты
_count для всех полей
Простейшее агрегатное поле — _count
: оно возвращает количество строк, соответствующих вашему запросу. Для каждого поля вашего типа Data Connect генерирует соответствующие агрегатные поля в зависимости от типа поля.
Запрос
query CountProducts {
products {
_count
}
}
Ответ one
one
Например, если в вашей базе данных 5 товаров, результат будет следующим:
{
"products": [
{
"_count&q
uot;: 5
}
]
}
Все поля имеют поле <field>_count
, которое подсчитывает количество строк, имеющих ненулевое значение в этом поле.
Запрос
query CountProductsWithExpirationDate {
products {
expirationDate_count
}
}
Ответ field_count
field_count
Например, если у вас есть 3 товара с истекающим сроком годности, результат будет следующим:
{
"products": [
{
"expirationDate_count&q
uot;: 3
}
]
}
_min, _max, _sum и _avg для числовых полей
Числовые поля (int, float, int64) также имеют <field>_min
, <field>_max
, <field>_sum
и <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
- Товар B:
quantityInStock: 5
,price: 5.99
- Товар C:
quantityInStock: 20
,price: 1.99
Результат будет следующим:
{
"products": [
{
"quantityInStock_max": 20,
"price_min": 1.99,
"price_avg": 3.6566666666666666,
"quantityInStock_sum": 35
}
]
}
_min и _max для дат и временных меток
Поля даты и времени имеют <field>_min
и <field>_max
.
Запрос
query DateAndTimeAggregates {
products {
expirationDate_max
expirationDate_min
}
}
Ответ _min _maxdatetime
_min _maxdatetime
Например, если у вас следующие сроки годности:
- Продукт А:
2024-01-01
- Продукт B:
2024-03-01
- Продукт C:
2024-02-01
Результат будет следующим:
{
"products": [
{
"expirationDate_max": "2024-03-01",
"expirationD
ate_min": "2024-01-01"
}
]
}
Отчетливый
Аргумент distinct
позволяет получить все уникальные значения для поля (или комбинации полей). Например:
Запрос
query ListDistinctManufacturers {
products(distinct: true) {
manufacturer
}
}
Ответ distinct
distinct
Например, если у вас есть следующие производители:
- Продукт А:
manufacturer: "Acme"
- Продукт B:
manufacturer: "Beta"
- Продукт C:
manufacturer: "Acme"
Результат будет следующим:
{
"products": [
{ "manufacturer": "Acme" },
{ &
quot;manufacturer": "Beta" }
]
}
Вы также можете использовать аргумент distinct
в полях агрегации, чтобы агрегировать отдельные значения. Например:
Запрос
query CountDistinctManufacturers {
products {
manufacturer_count(distinct: true)
}
}
Ответ distinctonaggregate
distinctonaggregate
Например, если у вас есть следующие производители:
- Продукт А:
manufacturer: "Acme"
- Продукт B:
manufacturer: "Beta"
- Продукт C:
manufacturer: "Acme"
Результат будет следующим:
{
"products": [
{
"manufacturer_count&q
uot;: 2
}
]
}
Сгруппированные агрегаты
Групповое агрегирование выполняется путем выбора сочетания агрегированных и неагрегированных полей для определенного типа. Это позволяет сгруппировать все соответствующие строки с одинаковыми значениями неагрегированных полей и вычислить агрегированные поля для этой группы. Например:
Запрос
query MostExpensiveProductByManufacturer {
products {
manufacturer
price_max
}
}
Ответ groupedaggregates
groupedaggregates
Например, если у вас есть следующие продукты:
- Продукт А:
manufacturer: "Acme"
,price: 2.99
- Продукт B:
manufacturer: "Beta"
,price: 5.99
- Продукт C:
manufacturer: "Acme"
,price: 1.99
Результат будет следующим:
{
"products": [
{ "manufacturer": "Acme", "price_max": 2.99 },
{ "manu
facturer": "Beta", "price_max": 5.99 }
]
}
having
и where
с сгруппированными агрегатами
Вы также можете использовать аргументы having
и where
чтобы возвращать только те группы, которые соответствуют заданным критериям.
-
having
позволяет вам фильтровать группы по их совокупным полям where
позволяет фильтровать строки на основе неагрегированных полей.
Запрос
query FilteredMostExpensiveProductByManufacturer {
products(having: {price_max: {ge: 2.99}}) {
manufacturer
price_max
}
}
Ответ havingwhere
havingwhere
Например, если у вас есть следующие продукты:
- Продукт А:
manufacturer: "Acme"
,price: 2.99
- Продукт B:
manufacturer: "Beta"
,price: 5.99
- Продукт C:
manufacturer: "Acme"
,price: 1.99
Результат будет следующим:
{
"products": [
{ "manufacturer": "Acme", "price_max": 2.99 },
{ "manu
facturer": "Beta", "price_max": 5.99 }
]
}
Агрегаты по таблицам
Агрегированные поля можно использовать совместно с полями, генерируемыми с отношением «один ко многим», для ответа на сложные вопросы о ваших данных. Вот модифицированная схема с отдельной таблицей Manufacturer
, которую можно использовать в примерах:
type Product @table {
name: String!
manufacturer: Manufacturer!
quantityInStock: Int!
price: Float!
expirationDate: Date
}
type Manufacturer @table {
name: String!
headquartersCountry: String!
}
Теперь мы можем использовать агрегированные поля, чтобы, например, узнать, сколько продукции выпускает производитель:
Запрос
query GetProductCount($id: UUID) {
manufacturers {
name
products_on_manufacturer {
_count
}
}
}
Ответ aggregatesacrosstables
aggregatesacrosstables
Например, если у вас есть следующие производители:
- Производитель A:
name: "Acme"
,products_on_manufacturer: 2
- Производитель B:
name: "Beta"
,products_on_manufacturer: 1
Результат будет следующим:
{
"manufacturers": [
{ "name": "Acme", "products_on_manufacturer": { "_count": 2 } },
{ "name":
"Beta", "products_on_manufacturer": { "_count": 1 } }
]
}
Написание расширенных запросов: использование полей query
для чтения данных в многошаговых операциях.
Существует множество ситуаций, в которых может потребоваться чтение базы данных во время выполнения мутации для поиска и проверки существующих данных перед выполнением, например, вставки или обновления. Эти возможности сокращают количество циклов обработки данных и, следовательно, затраты.
Data Connect поддерживает эту функцию. См. раздел «Многошаговые операции» .
Следующие шаги
Вас может заинтересовать:
- Создание запросов для ваших приложений с использованием инструментов помощи ИИ
- Авторизация ваших запросов в соответствии с руководством по авторизации
- Вызов запросов из клиентского кода для веб-приложений , iOS , Android и Flutter .
Firebase Data Connect позволяет создавать коннекторы для экземпляров PostgreSQL, управляемых с помощью Google Cloud SQL. Эти коннекторы представляют собой комбинации запросов и мутаций для использования данных из вашей схемы.
В руководстве по началу работы представлена схема приложения для обзора фильмов для PostgreSQL.
В этом руководстве также были представлены как развертываемые, так и специальные административные операции, включая запросы.
- Развертываемые запросы — это запросы, которые вы реализуете для вызова из клиентских приложений с определяемыми вами конечными точками API. Вы объединяете их в коннектор , развёрнутый на сервере. Инструментарий Data Connect генерирует клиентские SDK на основе вашего API. Развёрнутые запросы не защищены политикой IAM, поэтому обязательно защитите их с помощью директивы Data Connect
@auth
. - Административные запросы ad hoc запускаются из привилегированных сред для чтения данных. Вы можете создавать и выполнять их в консоли 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
.
Итак, следующий запрос имеет:
- Определение типа
query
- Имя операции (запроса)
ListMoviesByGenre
- Один аргумент запроса, в данном случае переменная
$genre
String
типа - Единственная директива,
@auth
. - Одно поле –
movies
.
query ListMoviesByGenre($genre: String!) @auth(level: PUBLIC) {
movies(where: { genre: { eq: $genre } }) {
id
title
}
}
Каждый аргумент запроса требует объявления типа: встроенного, например String
, или пользовательского, определяемого схемой типа, например Movie
.
В этом руководстве мы рассмотрим сигнатуру всё более сложных запросов. В заключение мы познакомимся с эффективными и лаконичными выражениями отношений, которые можно использовать для создания готовых к развертыванию запросов.
Ключевые скаляры в запросах
Но сначала — замечание о ключевых скалярах.
Data Connect определяет специальный скалярный ключ для представления первичных ключей каждой таблицы, идентифицируемый {TableType}_Key. Это JSON-объект значений первичных ключей.
Вы извлекаете ключевые скаляры как ответ, возвращаемый большинством автоматически сгенерированных полей чтения, или, конечно, из запросов, в которых вы извлекаете все поля, необходимые для построения скалярного ключа.
Отдельные автоматические запросы, такие как movie
в нашем примере, поддерживают ключевой аргумент, который принимает ключевой скаляр.
Вы можете передать ключевой скаляр как литерал. Однако вы можете определить переменные для передачи ключевых скаляров в качестве входных данных.
Запрос
query GetMovie($myKey: Movie_Key!) { movie(key: $myKey) { title } }
Ответ
{ "data": { "movie": { "title": "Example Movie Title" } } }
Их можно предоставить в запросе JSON следующим образом (или в других форматах сериализации):
{
# …
"variables": {
"myKey": {"id": "xxxxxxxx-x
xxx-4xxx-yxxx-xxxxxxxxxxxx"}
}
}
Благодаря настраиваемому скалярному парсингу Movie_Key
также можно сконструировать с использованием синтаксиса объекта, который может содержать переменные. Это особенно полезно, когда по какой-то причине требуется разбить отдельные компоненты на отдельные переменные.
Напишите базовые запросы
Вы можете начать писать запросы для получения отдельных записей из вашей базы данных или перечислить записи из таблицы с возможностью ограничения и упорядочивания результатов.
Извлечь отдельные записи
Простейший запрос возвращает одну запись по идентификатору. Ваш запрос будет использовать автоматически сгенерированное поле 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" } ] } }
Дополнительные операторы см. в справочнике по типам входных объектов .
Фильтр со сравнением значений
Для сравнения значений в запросах можно использовать такие операторы, как lt
(меньше) и ge
(больше или равно).
Запрос
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" } ] } }
Фильтр с операторами includes
и excludes
для полей массива
Вы можете проверить, что поле массива содержит указанный элемент.
Следующий пример иллюстрирует оператор 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" } ] } } }
Запросы на агрегацию
Что такое агрегаты и зачем их использовать?
Агрегатные поля позволяют выполнять вычисления со списком результатов. С помощью агрегатных полей вы можете делать следующее:
- Найдите среднюю оценку обзора
- Узнать общую стоимость товаров в корзине
- Найдите продукт с самым высоким или самым низким рейтингом
- Подсчитайте количество товаров в вашем магазине
Агрегаты выполняются на сервере, что дает ряд преимуществ по сравнению с их вычислением на стороне клиента:
- Более высокая производительность приложения (поскольку вы избегаете вычислений на стороне клиента)
- Снижение затрат на передачу данных (поскольку вы отправляете только агрегированные результаты вместо всех входных данных)
- Повышенная безопасность (поскольку вы можете предоставить клиентам доступ к агрегированным данным, а не ко всему набору данных)
Пример схемы для агрегатов
В этом разделе мы перейдем к примеру схемы витрины, которая хорошо подходит для объяснения того, как использовать агрегаты:
type Product @table {
name: String!
manufacturer: String!
quantityInStock: Int!
price: Float!
expirationDate: Date
}
Простые агрегаты
_count для всех полей
Простейшее агрегатное поле — _count
: оно возвращает количество строк, соответствующих вашему запросу. Для каждого поля вашего типа Data Connect генерирует соответствующие агрегатные поля в зависимости от типа поля.
Запрос
query CountProducts {
products {
_count
}
}
Ответ one
one
Например, если в вашей базе данных 5 товаров, результат будет следующим:
{
"products": [
{
"_count&q
uot;: 5
}
]
}
Все поля имеют поле <field>_count
, которое подсчитывает количество строк, имеющих ненулевое значение в этом поле.
Запрос
query CountProductsWithExpirationDate {
products {
expirationDate_count
}
}
Ответ field_count
field_count
Например, если у вас есть 3 товара с истекающим сроком годности, результат будет следующим:
{
"products": [
{
"expirationDate_count&q
uot;: 3
}
]
}
_min, _max, _sum и _avg для числовых полей
Числовые поля (int, float, int64) также имеют <field>_min
, <field>_max
, <field>_sum
и <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
- Товар B:
quantityInStock: 5
,price: 5.99
- Товар C:
quantityInStock: 20
,price: 1.99
Результат будет следующим:
{
"products": [
{
"quantityInStock_max": 20,
"price_min": 1.99,
"price_avg": 3.6566666666666666,
"quantityInStock_sum": 35
}
]
}
_min и _max для дат и временных меток
Поля даты и времени имеют <field>_min
и <field>_max
.
Запрос
query DateAndTimeAggregates {
products {
expirationDate_max
expirationDate_min
}
}
Ответ _min _maxdatetime
_min _maxdatetime
Например, если у вас следующие сроки годности:
- Продукт А:
2024-01-01
- Продукт B:
2024-03-01
- Продукт C:
2024-02-01
Результат будет следующим:
{
"products": [
{
"expirationDate_max": "2024-03-01",
"expirationD
ate_min": "2024-01-01"
}
]
}
Отчетливый
Аргумент distinct
позволяет получить все уникальные значения для поля (или комбинации полей). Например:
Запрос
query ListDistinctManufacturers {
products(distinct: true) {
manufacturer
}
}
Ответ distinct
distinct
Например, если у вас есть следующие производители:
- Продукт А:
manufacturer: "Acme"
- Продукт B:
manufacturer: "Beta"
- Продукт C:
manufacturer: "Acme"
Результат будет следующим:
{
"products": [
{ "manufacturer": "Acme" },
{ &
quot;manufacturer": "Beta" }
]
}
Вы также можете использовать аргумент distinct
в полях агрегации, чтобы агрегировать отдельные значения. Например:
Запрос
query CountDistinctManufacturers {
products {
manufacturer_count(distinct: true)
}
}
Ответ distinctonaggregate
distinctonaggregate
Например, если у вас есть следующие производители:
- Продукт А:
manufacturer: "Acme"
- Продукт B:
manufacturer: "Beta"
- Продукт C:
manufacturer: "Acme"
Результат будет следующим:
{
"products": [
{
"manufacturer_count&q
uot;: 2
}
]
}
Сгруппированные агрегаты
Групповое агрегирование выполняется путем выбора сочетания агрегированных и неагрегированных полей для определенного типа. Это позволяет сгруппировать все соответствующие строки с одинаковыми значениями неагрегированных полей и вычислить агрегированные поля для этой группы. Например:
Запрос
query MostExpensiveProductByManufacturer {
products {
manufacturer
price_max
}
}
Ответ groupedaggregates
groupedaggregates
Например, если у вас есть следующие продукты:
- Продукт А:
manufacturer: "Acme"
,price: 2.99
- Продукт B:
manufacturer: "Beta"
,price: 5.99
- Продукт C:
manufacturer: "Acme"
,price: 1.99
Результат будет следующим:
{
"products": [
{ "manufacturer": "Acme", "price_max": 2.99 },
{ "manu
facturer": "Beta", "price_max": 5.99 }
]
}
having
и where
с сгруппированными агрегатами
Вы также можете использовать аргументы having
и where
чтобы возвращать только те группы, которые соответствуют заданным критериям.
-
having
позволяет вам фильтровать группы по их совокупным полям where
позволяет фильтровать строки на основе неагрегированных полей.
Запрос
query FilteredMostExpensiveProductByManufacturer {
products(having: {price_max: {ge: 2.99}}) {
manufacturer
price_max
}
}
Ответ havingwhere
havingwhere
Например, если у вас есть следующие продукты:
- Продукт А:
manufacturer: "Acme"
,price: 2.99
- Продукт B:
manufacturer: "Beta"
,price: 5.99
- Продукт C:
manufacturer: "Acme"
,price: 1.99
Результат будет следующим:
{
"products": [
{ "manufacturer": "Acme", "price_max": 2.99 },
{ "manu
facturer": "Beta", "price_max": 5.99 }
]
}
Агрегаты по таблицам
Агрегированные поля можно использовать совместно с полями, генерируемыми с отношением «один ко многим», для ответа на сложные вопросы о ваших данных. Вот модифицированная схема с отдельной таблицей Manufacturer
, которую можно использовать в примерах:
type Product @table {
name: String!
manufacturer: Manufacturer!
quantityInStock: Int!
price: Float!
expirationDate: Date
}
type Manufacturer @table {
name: String!
headquartersCountry: String!
}
Теперь мы можем использовать агрегированные поля, чтобы, например, узнать, сколько продукции выпускает производитель:
Запрос
query GetProductCount($id: UUID) {
manufacturers {
name
products_on_manufacturer {
_count
}
}
}
Ответ aggregatesacrosstables
aggregatesacrosstables
Например, если у вас есть следующие производители:
- Производитель A:
name: "Acme"
,products_on_manufacturer: 2
- Производитель B:
name: "Beta"
,products_on_manufacturer: 1
Результат будет следующим:
{
"manufacturers": [
{ "name": "Acme", "products_on_manufacturer": { "_count": 2 } },
{ "name":
"Beta", "products_on_manufacturer": { "_count": 1 } }
]
}
Написание расширенных запросов: использование полей query
для чтения данных в многошаговых операциях.
Существует множество ситуаций, в которых может потребоваться чтение базы данных во время выполнения мутации для поиска и проверки существующих данных перед выполнением, например, вставки или обновления. Эти возможности сокращают количество циклов обработки данных и, следовательно, затраты.
Data Connect поддерживает эту функцию. См. раздел «Многошаговые операции» .
Следующие шаги
Вас может заинтересовать:
- Создание запросов для ваших приложений с использованием инструментов помощи ИИ
- Авторизация ваших запросов в соответствии с руководством по авторизации
- Вызов запросов из клиентского кода для веб-приложений , iOS , Android и Flutter .