Firebase SQL Connect 可讓您為透過 Cloud SQL 管理的 PostgreSQL 執行個體建立連接器。這些連接器結合了查詢和變異,可使用結構定義中的資料。
入門指南介紹了 PostgreSQL 的電影評論應用程式結構定義。
該指南也介紹了可部署和臨時的管理作業,包括查詢。
- 「可部署查詢」是指您實作的查詢,可透過您定義的 API 端點從用戶端應用程式呼叫。您會將這些項目組合成連接器,並部署至伺服器。SQL Connect 工具會根據您的 API 產生用戶端 SDK。已部署的查詢不受 IAM 政策保護,因此請務必使用 SQL Connect
@auth指令保護查詢。 - 臨時管理查詢是從具備權限的環境執行,用於讀取資料。您可以在 Firebase 控制台建立及執行這些查詢,也可以使用 SQL Connect VS Code 擴充功能,在本機開發環境中執行。
本指南將深入探討可部署的查詢。
SQL Connect 查詢的功能
SQL Connect 可讓您以 PostgreSQL 資料庫預期的所有方式執行基本查詢。
不過,透過 SQL Connect 的 GraphQL 擴充功能,您可以實作進階查詢,打造更快速、更有效率的應用程式:
- 使用許多作業傳回的索引鍵純量,簡化記錄的重複作業
- 在多步驟修改作業中執行查詢來查詢資料,節省程式碼行數和伺服器來回次數。
使用生成的欄位建立查詢
SQL Connect 作業會根據結構定義中的類型和類型關係,擴充 SQL Connect 自動產生的一組欄位。每當您編輯結構定義時,本機工具就會產生這些欄位。
您可以使用產生的欄位,實作越來越複雜的查詢,從單一資料表擷取個別記錄或多筆記錄,到從相關資料表擷取多筆記錄。假設結構定義包含 Movie 類型和相關聯的 Actor 類型。SQL 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 } } } |
查詢的基本元素
SQL Connect 查詢是具有 SQL Connect 擴充功能的 GraphQL 查詢。就像一般 GraphQL 查詢一樣,您可以定義作業名稱和 GraphQL 變數清單。
SQL Connect 使用自訂指令 (例如 @auth) 擴充 GraphQL 查詢。
因此,下列查詢包含:
query類型定義ListMoviesByGenre作業 (查詢) 名稱- 單一查詢引數,此處為
String類型的$genre變數 - 單一指令:
@auth。 - 單一欄位
movies。
query ListMoviesByGenre($genre: String!) @auth(level: PUBLIC) {
movies(where: { genre: { eq: $genre } }) {
id
title
}
}
每個查詢引數都需要類型宣告、內建類型 (例如 String),或自訂的結構定義定義類型 (例如 Movie)。
本指南將探討複雜度逐漸提高的查詢簽章。最後,您將介紹強大、簡潔的關係運算式,可用於建構可部署的查詢。
查詢中的重要純量
但首先,請注意鍵純量。
SQL 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-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 ] } }
在查詢中使用別名
SQL Connect 支援在查詢中使用 GraphQL 別名。別名可讓您重新命名查詢結果中傳回的資料。單一 SQL 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 } ] } }
使用查詢篩選器
SQL 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" } ] } }
測試空值來篩選
您可以使用 isNull 運算子測試 null 值。
查詢
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 運算子。
SQL 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。SQL 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" } ] } }
編寫關聯式查詢
SQL Connect 查詢可根據資料表之間的關係存取資料。您可以使用結構定義中定義的物件 (一對一) 或陣列 (一對多) 關係,進行巢狀查詢,也就是擷取某個類型的資料,以及巢狀或相關類型的資料。
這類查詢會在產生的讀取欄位中使用魔術 SQL 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:它會傳回符合查詢的資料列數量。對於類型中的每個欄位,SQL Connect 會根據欄位類型產生對應的匯總欄位。
查詢
query CountProducts {
products {
_count
}
}
回應one
one
舉例來說,如果資料庫中有 5 項產品,結果會是:
{
"products": [
{
"_count": 5
}
]
}
所有欄位都有 <field>_count 欄位,用於計算該欄位中非空值的資料列數量。
查詢
query CountProductsWithExpirationDate {
products {
expirationDate_count
}
}
回應field_count
field_count
舉例來說,如果您有 3 項產品設有到期日,結果會是:
{
"products": [
{
"expirationDate_count": 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
舉例來說,假設到期日如下:
- 產品 A:
2024-01-01 - 產品 B:
2024-03-01 - 產品 C:
2024-02-01
結果如下:
{
"products": [
{
"expirationDate_max": "2024-03-01",
"expirationDate_min": "2024-01-01"
}
]
}
不重複
distinct 引數可讓您取得欄位 (或欄位組合) 的所有不重複值。例如:
查詢
query ListDistinctManufacturers {
products(distinct: true) {
manufacturer
}
}
回應distinct
distinct
舉例來說,假設您有下列製造商:
- 產品 A:
manufacturer: "Acme" - 產品 B:
manufacturer: "Beta" - 產品 C:
manufacturer: "Acme"
結果如下:
{
"products": [
{ "manufacturer": "Acme" },
{ "manufacturer": "Beta" }
]
}
您也可以在匯總欄位上使用 distinct 引數,改為匯總不重複的值。例如:
查詢
query CountDistinctManufacturers {
products {
manufacturer_count(distinct: true)
}
}
回應distinctonaggregate
distinctonaggregate
舉例來說,假設您有下列製造商:
- 產品 A:
manufacturer: "Acme" - 產品 B:
manufacturer: "Beta" - 產品 C:
manufacturer: "Acme"
結果如下:
{
"products": [
{
"manufacturer_count": 2
}
]
}
分組匯總
如要執行分組匯總,請選取類型的匯總和非匯總欄位組合。這會將非匯總欄位值相同的所有相符資料列分組,並計算該群組的匯總欄位。例如:
查詢
query MostExpensiveProductByManufacturer {
products {
manufacturer
price_max
}
}
回應groupedaggregates
groupedaggregates
舉例來說,假設您有下列產品:
- 產品 A:
manufacturer: "Acme",price: 2.99 - 產品 B:
manufacturer: "Beta",price: 5.99 - 產品 C:
manufacturer: "Acme"、price: 1.99
結果如下:
{
"products": [
{ "manufacturer": "Acme", "price_max": 2.99 },
{ "manufacturer": "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
舉例來說,假設您有下列產品:
- 產品 A:
manufacturer: "Acme",price: 2.99 - 產品 B:
manufacturer: "Beta",price: 5.99 - 產品 C:
manufacturer: "Acme"、price: 1.99
結果如下:
{
"products": [
{ "manufacturer": "Acme", "price_max": 2.99 },
{ "manufacturer": "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 } }
]
}
查詢列舉欄位
您可以查詢結構定義中的列舉欄位。
假設列舉的實作方式如下:
enum AspectRatio {
ACADEMY
WIDESCREEN
ANAMORPHIC
IMAX
"No information available on aspect ratio"
UNAVAILABLE
}
舉例來說,你可以列出所有符合偏好顯示比例的電影。
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 中,大於和小於運算子會使用列舉值的順序,以便有意義地執行。列舉開頭的值小於列舉定義結尾的值。
舉例來說,下列 where 子句會傳回 ratio 小於輸入顯示比例的所有電影。
- 在第一個範例中,如果
$aspectRatio為 IMAX,則系統會選取ratio與 ACADEMY、WIDESCREEN 和 ANAMORPHIC 相符的電影。 - 在第二個範例中,系統只會傳回同時提供 ACADEMY 和 WIDESCREEN 版本的電影。
movies(where: {originalAspectRatio: {lt: $aspectRatio}})
movies(where: {otherAspectRatios: {includesAll: [ACADEMY, WIDESCREEN]}})
對列舉欄位執行匯總查詢
列舉僅支援 _count。
以下查詢會計算每種顯示比例的電影總數。
query MovieCountByOriginalAspectRatio @auth(level: PUBLIC) {
movies {
_count
originalAspectRatio
}
}
從用戶端程式碼呼叫列舉查詢
如要瞭解如何使用用戶端的列舉查詢,以及如何設計用戶端呼叫,以盡可能彈性地管理列舉,請參閱 iOS、Android、網頁和 Flutter 的 SDK 指南。
撰寫進階查詢:使用 query 欄位,在多步驟作業中讀取資料
在許多情況下,您可能需要在執行異動期間讀取資料庫,以便在執行插入或更新等作業前,查詢及驗證現有資料。這些選項可省去來回作業,進而節省成本。
SQL Connect 支援這項功能。請參閱多步驟作業。
後續步驟
您可能感興趣的內容: