實作數據連線查詢

Firebase Data Connect 可讓您為透過 Google Cloud SQL 管理的 PostgreSQL 執行個體建立連接器。這些連接器是查詢和突變的組合,可從結構定義使用資料。

入門指南介紹了 PostgreSQL 的電影評論應用程式結構定義。

該指南也介紹了可部署和隨選的管理作業,包括查詢。

  • 可部署的查詢是指您實作的查詢,可從用戶端應用程式呼叫,並搭配您定義的 API 端點。您會將這些檔案封裝成連接器,並部署至伺服器。Data Connect 工具會根據您的 API 產生用戶端 SDK。已部署的查詢不會受到 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 會產生 moviemoviesactors_on_movies 欄位等。

使用
movie 欄位查詢

movie 欄位代表 Movie 資料表中的單一記錄。

使用這個欄位,依鍵查詢單一電影。

query GetMovie($myKey: Movie_Key!) {
  movie(key: $myKey) { title }
}

使用
movies 欄位查詢

movies 欄位代表 Movie 資料表中的記錄清單。

使用這個欄位查詢多部電影,例如指定年份的所有電影。

query GetMovies($myYear: Int!) {
  movies(where: { year: { eq: $myYear } }) { title }
}

使用
actors_on_movies 欄位查詢

actors_on_movies 欄位代表連結 ActorMovie 資料表的記錄清單。使用這個欄位查詢與特定電影相關的所有演員。

使用這個欄位查詢與特定電影相關的所有演員。

  query GetActorsOnMovie($myKey: Movie_Key!) {
    actors_on_movies(where: { movie: { key: { eq: $myKey } } }) {
      actor { name }
    }
  }

查詢的基本要素

Data Connect 查詢是具有 Data Connect 擴充功能的 GraphQL 查詢。與一般 GraphQL 查詢相同,您可以定義作業名稱和 GraphQL 變數清單。

Data 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)。

本指南將探討越來越複雜的查詢簽章。最後,您將瞭解如何使用簡潔有力的關係運算式,建構可部署的查詢。

查詢中的主要純量

但首先,請注意重要純量。

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-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"
      }
    ]
  }
}
    

使用 orderBylimitoffset 運算子

當然,列出資料表中的所有記錄用處有限。

您可以排序結果並執行分頁。系統會接受這些引數,但不會在結果中傳回。

在此,查詢會依評分取得前 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查詢可透過一個有效率的要求,向伺服器套用多個篩選器或其他查詢作業,一次發出多個「子查詢」。為避免傳回的資料集發生名稱衝突,請使用別名區分子查詢。

以下查詢的運算式使用別名 mostPopularleastPopular

查詢

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 篩選和排序作業。

使用 orderBy 運算子篩選 where

傳回資料表 (和巢狀關聯) 中所有相符的資料列。如果沒有任何記錄符合篩選條件,則傳回空陣列。

查詢

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"
      }
    ]
  }
}
    

使用 includesexcludes 運算子篩選陣列欄位

您可以測試陣列欄位是否包含指定項目。

以下範例說明 includes 運算子。

Data Connect 支援 includesAllexcludesexcludesAll 等等。請參閱參考說明文件的_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 運算子邏輯篩選

如需更複雜的邏輯,請使用 _orData 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 查詢可根據資料表之間的關係存取資料。您可以使用結構定義中定義的物件 (一對一) 或陣列 (一對多) 關係,進行巢狀查詢,也就是擷取某種型別的資料,以及巢狀或相關型別的資料。

這類查詢會在產生的讀取欄位中使用 Magic 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 篩選,以擷取 mainActorssupportingActors 結果中的演員和相關電影。由於這是多對多關係,因此使用 _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

舉例來說,如果資料庫中有 5 項產品,結果會是:

{
  "products": [
    {
    "_count": 5
    }
  ]
}

所有欄位都有 <field>_count 欄位,可計算該欄位中有非空值的資料列數量。

查詢

query CountProductsWithExpirationDate {
  products {
    expirationDate_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

舉例來說,假設你有以下產品:

  • 產品 A:quantityInStock: 10price: 2.99
  • 產品 B:quantityInStock: 5price: 5.99
  • 產品 C:quantityInStock: 20price: 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

舉例來說,如果您的到期日如下:

  • 產品 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

舉例來說,假設您有下列製造商:

  • 產品 A:manufacturer: "Acme"
  • 產品 B:manufacturer: "Beta"
  • 產品 C:manufacturer: "Acme"

結果如下:

{
  "products": [
    { "manufacturer": "Acme" },
    { "manufacturer": "Beta" }
  ]
}

您也可以在匯總欄位中使用 distinct 引數,改為匯總不重複的值。例如:

查詢

query CountDistinctManufacturers {
  products {
    manufacturer_count(distinct: true)
  }
}

回應
distinctonaggregate

舉例來說,假設您有下列製造商:

  • 產品 A:manufacturer: "Acme"
  • 產品 B:manufacturer: "Beta"
  • 產品 C:manufacturer: "Acme"

結果如下:

{
  "products": [
    {
    "manufacturer_count": 2
    }
  ]
}

分組匯總

如要執行分組匯總,請在類型中選取匯總和非匯總欄位的組合。這會將非匯總欄位值相同的相符資料列分入同組,並計算該組的匯總欄位。例如:

查詢

query MostExpensiveProductByManufacturer {
  products {
  manufacturer
  price_max
  }
}

回應
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 }
  ]
}
havingwhere,並以分組匯總的形式呈現

您也可以使用 havingwhere 引數,只傳回符合所提供條件的群組。

  • having 可讓您依據群組的匯總欄位篩選群組
  • where 可讓您根據非匯總欄位篩選資料列。

查詢

query FilteredMostExpensiveProductByManufacturer {
  products(having: {price_max: {ge: 2.99}}) {
  manufacturer
  price_max
  }
}

回應
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

舉例來說,假設您有下列製造商:

  • 製造商 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 支援這項功能。 請參閱多步驟作業

後續步驟

你可能感興趣的內容: