1. 事前準備
在本程式碼研究室中,您將整合 Firebase Data Connect 和 Cloud SQL 資料庫,以建構電影評論網頁應用程式。完成的應用程式會展示 Firebase Data Connect 如何簡化建構以 SQL 為動力的應用程式程序。其中包含以下功能:
- 驗證:為應用程式的查詢和突變導入自訂驗證機制,確保只有授權使用者能與資料互動。
- GraphQL 架構:根據電影評論網頁應用程式的需求,使用彈性的 GraphQL 架構建立及管理資料結構。
- SQL 查詢和變異式:使用 GraphQL 提供的查詢和變異式,擷取、更新及管理 Cloud SQL 中的資料。
- 進階搜尋 (可部分比對字串):使用篩選器和搜尋選項,根據標題、說明或標記等欄位搜尋電影。
- (選用) 向量搜尋整合:使用 Firebase Data Connect 的向量搜尋功能新增內容搜尋功能,根據輸入內容和偏好設定提供豐富的使用者體驗。
必要條件
您需要具備 JavaScript 的基本知識。
課程內容
- 使用本機模擬器設定 Firebase Data Connect。
- 使用 Data Connect 和 GraphQL 設計資料結構定義。
- 為電影評論應用程式編寫及測試各種查詢和變異。
- 瞭解 Firebase Data Connect 如何在應用程式中產生及使用 SDK。
- 部署結構定義,並有效率地管理資料庫。
事前準備
- Git
- Visual Studio Code
- 使用 nvm-windows (Windows) 或 nvm (macOS/Linux) 安裝 Node.js
- 如果您尚未建立 Firebase 專案,請前往 Firebase 控制台建立
- (選用) 針對向量搜尋,將專案升級至即付即用 Blaze 定價方案
2. 設定開發環境
在本程式碼研究室的這個階段,我們將逐步引導您設定環境,開始使用 Firebase Data Connect 建構電影評論應用程式。
- 複製專案存放區並安裝必要的依附元件:
git clone https://github.com/firebaseextended/codelab-dataconnect-web cd codelab-dataconnect-web cd ./app && npm i npm run dev
- 執行這些指令後,請在瀏覽器中開啟 http://localhost:5173,查看在本機上執行的網路應用程式。這個前端可用於建構電影評論應用程式,並與其功能互動。
- 使用 Visual Studio Code 開啟複製的
codelab-dataconnect-web
資料夾。您可以在這個畫面中定義結構定義、編寫查詢,以及測試應用程式功能。 - 如要使用 Data Connect 功能,請安裝 Firebase Data Connect Visual Studio 擴充功能。
您也可以從 Visual Studio Code Marketplace 安裝擴充功能,或在 VS Code 中搜尋。 - 在 Firebase 控制台中開啟或建立新的 Firebase 專案。
- 將 Firebase 專案連結至 Firebase Data Connect VSCode 擴充功能。在擴充功能中執行下列操作:
- 按一下「登入」按鈕。
- 按一下「連結 Firebase 專案」,然後選取 Firebase 專案。
- 使用 Firebase Data Connect VS Code 擴充功能啟動 Firebase 模擬器:
按一下「Start Emulators」,然後確認模擬器是否在終端機中執行。
3. 查看範例程式碼
在本節中,您將探索應用程式啟動程式碼集的幾個重點領域。雖然應用程式缺少部分功能,但有助於您瞭解整體結構。
資料夾和檔案結構
以下各子節將概略說明應用程式的資料夾和檔案結構。
dataconnect/
目錄
包含 Firebase Data Connect 設定、連接器 (用於定義查詢和變異) 和結構定義檔案。
schema/schema.gql
:定義 GraphQL 結構定義connector/queries.gql
:應用程式需要的查詢connector/mutations.gql
:應用程式需要的變異connector/connector.yaml
:SDK 產生作業的設定檔
app/src/
目錄
包含應用程式邏輯和與 Firebase Data Connect 的互動。
firebase.ts
:連結 Firebase 專案中 Firebase 應用程式的設定。lib/dataconnect-sdk/
:包含產生的 SDK。您可以在connector/connector.yaml
檔案中編輯 SDK 產生位置,系統會在您定義查詢或 mutation 時自動產生 SDK。
4. 定義電影評論的結構定義
在本節中,您將在結構定義中定義電影應用程式中關鍵實體之間的關係。Movie
、User
、Actor
和 Review
等實體會對應至資料庫表格,並使用 Firebase Data Connect 和 GraphQL 架構指令建立關係。完成後,應用程式就能處理所有相關作業,包括搜尋高評分電影、依類型篩選內容、讓使用者留下評論、標記喜愛的內容、探索類似電影,或透過向量搜尋功能,根據文字輸入內容尋找推薦電影。
核心實體和關聯
Movie
類型會保留標題、類型和標記等重要詳細資料,應用程式會用來搜尋和建立電影設定檔。User
類型會追蹤使用者互動,例如評論和收藏。Reviews
將使用者連結至電影,讓應用程式顯示使用者提供的評分和意見回饋。
電影、演員和使用者之間的關係,可讓應用程式更具動態性。MovieActor
彙整表格可協助顯示演員陣容詳細資料和演員的電影作品。FavoriteMovie
類型可讓使用者將電影設為喜愛項目,讓應用程式顯示個人化的喜愛項目清單,並突顯熱門選項。
設定 Movie
資料表
Movie
類型會定義電影實體的主要結構,包括 title
、genre
、releaseYear
和 rating
等欄位。
複製程式碼片段,然後貼到 dataconnect/schema/schema.gql
檔案中:
type Movie
@table {
id: UUID! @default(expr: "uuidV4()")
title: String!
imageUrl: String!
releaseYear: Int
genre: String
rating: Float
description: String
tags: [String]
}
重點整理:
- id:每部電影的專屬 UUID,使用
@default(expr: "uuidV4()")
產生。
設定 MovieMetadata
資料表
MovieMetadata
類型會與 Movie
類型建立一對一關係。其中包含電影導演等其他資料。
複製程式碼片段,然後貼到 dataconnect/schema/schema.gql
檔案中:
type MovieMetadata
@table {
# @ref creates a field in the current table (MovieMetadata)
# It is a reference that holds the primary key of the referenced type
# In this case, @ref(fields: "movieId", references: "id") is implied
movie: Movie! @ref
# movieId: UUID <- this is created by the above @ref
director: String
}
重點整理:
- 電影!@ref: 參照
Movie
類型,建立外鍵關係。
設定 Actor
資料表
複製程式碼片段,然後貼到 dataconnect/schema/schema.gql
檔案中:
type Actor @table {
id: UUID!
imageUrl: String!
name: String! @col(name: "name", dataType: "varchar(30)")
}
Actor
類型代表電影資料庫中的演員,每位演員都可能參與多部電影,形成多對多關係。
設定 MovieActor
資料表
複製程式碼片段,然後貼到 dataconnect/schema/schema.gql
檔案中:
type MovieActor @table(key: ["movie", "actor"]) {
# @ref creates a field in the current table (MovieActor) that holds the primary key of the referenced type
# In this case, @ref(fields: "id") is implied
movie: Movie!
# movieId: UUID! <- this is created by the implied @ref, see: implicit.gql
actor: Actor!
# actorId: UUID! <- this is created by the implied @ref, see: implicit.gql
role: String! # "main" or "supporting"
}
重點整理:
- movie:參照 Movie 類型,會隱含產生外鍵 movieId: UUID!。
- actor:參照 Actor 類型,會隱含產生外鍵 actorId: UUID!。
- role:定義演員在電影中的角色 (例如 「主要」或「輔助」)。
設定 User
資料表
User
類型定義了使用者實體,也就是透過留下評論或將電影設為喜愛項目,與電影互動的使用者。
複製程式碼片段,然後貼到 dataconnect/schema/schema.gql
檔案中:
type User
@table {
id: String! @col(name: "auth_uid")
username: String! @col(dataType: "varchar(50)")
# The following are generated from the @ref in the Review table
# reviews_on_user
# movies_via_Review
}
設定 FavoriteMovie
資料表
FavoriteMovie
類型是彙整資料表,可處理使用者與喜愛電影之間的多對多關係。每個資料表都會將 User
連結至 Movie
。
複製程式碼片段,然後貼到 dataconnect/schema/schema.gql
檔案中:
type FavoriteMovie
@table(name: "FavoriteMovies", singular: "favorite_movie", plural: "favorite_movies", key: ["user", "movie"]) {
# @ref is implicit
user: User!
movie: Movie!
}
重點整理:
- movie:參照 Movie 類型,會隱含產生外部關鍵字
movieId: UUID!
。 - user:參照使用者類型,會隱含產生外鍵
userId: UUID!
。
設定 Review
資料表
Review
類型代表評論實體,並將 User
和 Movie
類型連結為多對多關係 (一個使用者可以留下多則評論,每部電影也可能有許多評論)。
複製程式碼片段,然後貼到 dataconnect/schema/schema.gql
檔案中:
type Review @table(name: "Reviews", key: ["movie", "user"]) {
id: UUID! @default(expr: "uuidV4()")
user: User!
movie: Movie!
rating: Int
reviewText: String
reviewDate: Date! @default(expr: "request.time")
}
重點整理:
- user: 參照留下評論的使用者。
- movie: 參照正在評論的電影。
- reviewDate:自動設為使用
@default(expr: "request.time")
建立評論的時間。
自動產生的欄位和預設值
這個結構定義會使用 @default(expr: "uuidV4()")
等運算式,自動產生不重複的 ID 和時間戳記。舉例來說,建立新記錄時,Movie
和 Review
類型的 id
欄位會自動填入 UUID。
架構定義完成後,電影應用程式就能建立穩固的資料結構和關係!
5. 擷取熱門和最新電影
在本節中,您將在本機模擬器中插入模擬電影資料,然後實作連接器 (查詢) 和 TypeScript 程式碼,以便在網頁應用程式中呼叫這些連接器。完成後,應用程式就能直接從資料庫動態擷取及顯示最高評分和最新的電影。
插入模擬電影、演員和評論資料
- 在 VSCode 中開啟
dataconnect/moviedata_insert.gql
。確認 Firebase Data Connect 擴充功能中的模擬器是否正在執行。 - 檔案頂端應該會顯示「Run (local)」按鈕。按一下這個按鈕,即可將模擬電影資料插入資料庫。
- 查看 Data Connect Execution 終端機,確認資料是否已成功新增。
導入連接器
- 開啟
dataconnect/movie-connector/queries.gql
。您會在註解中看到基本ListMovies
查詢: 這個查詢會擷取所有電影及其詳細資料 (例如query ListMovies @auth(level: PUBLIC) { movies { id title imageUrl releaseYear genre rating tags description } }
id
、title
、releaseYear
)。不過,它不會為電影排序。 - 取代現有的
ListMovies
查詢,使用下列查詢來新增排序和限制選項:# List subset of fields for movies query ListMovies($orderByRating: OrderDirection, $orderByReleaseYear: OrderDirection, $limit: Int) @auth(level: PUBLIC) { movies( orderBy: [ { rating: $orderByRating }, { releaseYear: $orderByReleaseYear } ] limit: $limit ) { id title imageUrl releaseYear genre rating tags description } }
- 按一下「Run (local)」按鈕,對本機資料庫執行查詢。您也可以在執行前,在設定窗格中輸入查詢變數。
重點整理:
movies()
:GraphQL 查詢欄位,用於從資料庫擷取電影資料。orderByRating
:用來依評分排序電影的參數 (遞增/遞減)。orderByReleaseYear
:參數,可依電影上映年份排序 (遞增/遞減)。limit
:限制傳回的電影數量。
在網頁應用程式中整合查詢
在本程式碼研究室的這個部分,您將在網頁應用程式中使用前一個部分定義的查詢。Firebase Data Connect 模擬器會根據 .gql
檔案 (具體來說是 schema.gql
、queries.gql
、mutations.gql
) 和 connector.yaml
檔案中的資訊產生 SDK。這些 SDK 可直接在應用程式中呼叫。
- 在
MovieService
(app/src/lib/MovieService.tsx
) 中,將頂端的匯入陳述式取消註解: 函式import { listMovies, ListMoviesData, OrderDirection } from "@movie/dataconnect";
listMovies
、回應類型ListMoviesData
和列舉OrderDirection
都是 Firebase Data Connect 模擬器根據您先前定義的結構定義和查詢所產生的 SDK。 - 將
handleGetTopMovies
和handleGetLatestMovies
函式替換為以下程式碼:// Fetch top-rated movies export const handleGetTopMovies = async ( limit: number ): Promise<ListMoviesData["movies"] | null> => { try { const response = await listMovies({ orderByRating: OrderDirection.DESC, limit, }); return response.data.movies; } catch (error) { console.error("Error fetching top movies:", error); return null; } }; // Fetch latest movies export const handleGetLatestMovies = async ( limit: number ): Promise<ListMoviesData["movies"] | null> => { try { const response = await listMovies({ orderByReleaseYear: OrderDirection.DESC, limit, }); return response.data.movies; } catch (error) { console.error("Error fetching latest movies:", error); return null; } };
重點整理:
listMovies
:自動產生的函式,可呼叫listMovies
查詢來擷取電影清單。包括依評分或發行年份排序,以及限制結果數量的選項。ListMoviesData
:在應用程式首頁上顯示前 10 部熱門電影和最新電影的結果類型。
實例觀摩
重新載入網路應用程式,即可查看查詢的運作情形。首頁現在會動態顯示電影清單,並直接從本機資料庫擷取資料。系統會根據你剛剛設定的資料,流暢地顯示評價最高和最新的電影。
6. 顯示電影和演員詳細資料
在本節中,您將實作功能,以便使用電影或演員的專屬 ID 擷取詳細資訊。這不僅涉及從各自的資料表擷取資料,還要彙整相關資料表,以便顯示完整詳細資料,例如電影評論和演員的電影作品。
實作連接器
- 在專案中開啟
dataconnect/movie-connector/queries.gql
。 - 新增以下查詢,即可擷取電影和演員的詳細資料:
# Get movie by id query GetMovieById($id: UUID!) @auth(level: PUBLIC) { movie(id: $id) { id title imageUrl releaseYear genre rating description tags metadata: movieMetadatas_on_movie { director } mainActors: actors_via_MovieActor(where: { role: { eq: "main" } }) { id name imageUrl } supportingActors: actors_via_MovieActor( where: { role: { eq: "supporting" } } ) { id name imageUrl } reviews: reviews_on_movie { id reviewText reviewDate rating user { id username } } } } # Get actor by id query GetActorById($id: UUID!) @auth(level: PUBLIC) { actor(id: $id) { id name imageUrl mainActors: movies_via_MovieActor(where: { role: { eq: "main" } }) { id title genre tags imageUrl } supportingActors: movies_via_MovieActor( where: { role: { eq: "supporting" } } ) { id title genre tags imageUrl } } }
- 儲存變更並查看查詢。
重點整理:
movie()
/actor()
:GraphQL 查詢欄位,用於從Movies
或Actors
資料表擷取單一電影或演員。_on_
:可直接存取具有外鍵關係的關聯類型欄位。舉例來說,reviews_on_movie
會擷取與特定電影相關的所有評論。_via_
:用於透過彙整表格瀏覽多對多關係。舉例來說,actors_via_MovieActor
會透過MovieActor
彙整表存取Actor
類型,而where
條件會根據演員的角色 (例如「主角」或「配角」) 篩選演員。
輸入模擬資料來測試查詢
- 在 Data Connect 執行作業窗格中,您可以輸入模擬 ID 來測試查詢,例如:
{"id": "550e8400-e29b-41d4-a716-446655440000"}
- 按一下
GetMovieById
的「Run (local)」,擷取「Quantum Paradox」的詳細資料 (與上述 ID 相關的模擬電影)。
在網頁應用程式中整合查詢
- 在
MovieService
(app/src/lib/MovieService.tsx
) 中,將下列匯入作業取消註解:import { getMovieById, GetMovieByIdData } from "@movie/dataconnect"; import { GetActorByIdData, getActorById } from "@movie/dataconnect";
- 取代
handleGetMovieById
和handleGetActorById
函式,使用以下程式碼:// Fetch movie details by ID export const handleGetMovieById = async ( movieId: string ) => { try { const response = await getMovieById({ id: movieId }); if (response.data.movie) { return response.data.movie; } return null; } catch (error) { console.error("Error fetching movie:", error); return null; } }; // Calling generated SDK for GetActorById export const handleGetActorById = async ( actorId: string ): Promise<GetActorByIdData["actor"] | null> => { try { const response = await getActorById({ id: actorId }); if (response.data.actor) { return response.data.actor; } return null; } catch (error) { console.error("Error fetching actor:", error); return null; } };
重點整理:
getMovieById
/getActorById
:這些是自動產生的函式,可呼叫您定義的查詢,擷取特定電影或演員的詳細資訊。GetMovieByIdData
/GetActorByIdData
:這些是結果類型,用於在應用程式中顯示電影和演員詳細資料。
實例觀摩
接著,前往網頁應用程式的首頁。點選電影後,您就能查看所有詳細資料,包括演員和評論等資訊,這些資訊都是從相關資料表中擷取。同樣地,點選演員後,系統也會顯示他們參與的電影。
7. 處理使用者驗證
在本節中,您將使用 Firebase 驗證功能實作使用者登入和登出功能。您也可以使用 Firebase 驗證資料,直接在 Firebase DataConnect 中擷取或更新使用者資料,確保應用程式中的使用者管理機制安全無虞。
實作連接器
- 在
dataconnect/movie-connector/
中開啟mutations.gql
。 - 新增下列變異數,建立或更新目前已驗證的使用者:
# Create or update the current authenticated user mutation UpsertUser($username: String!) @auth(level: USER) { user_upsert( data: { id_expr: "auth.uid" username: $username } ) }
重點整理:
id_expr: "auth.uid"
:此選項會使用auth.uid
,而非使用者或應用程式提供的auth.uid
,可確保使用者 ID 安全且自動處理,進而提供額外的安全防護。
擷取目前使用者
- 在
dataconnect/movie-connector/
中開啟queries.gql
。 - 新增下列查詢,擷取目前使用者:
# Get user by ID query GetCurrentUser @auth(level: USER) { user(key: { id_expr: "auth.uid" }) { id username reviews: reviews_on_user { id rating reviewDate reviewText movie { id title } } favoriteMovies: favorite_movies_on_user { movie { id title genre imageUrl releaseYear rating description tags metadata: movieMetadatas_on_movie { director } } } } }
重點整理:
auth.uid
:這是直接從 Firebase 驗證中擷取,可確保安全存取使用者專屬資料。_on_
欄位:這些欄位代表彙整表格:reviews_on_user
:擷取與使用者相關的所有評論,包括電影的id
和title
。favorite_movies_on_user
:擷取使用者將所有電影標示為「我的最愛」的內容,包括genre
、releaseYear
、rating
和metadata
等詳細資訊。
在網頁應用程式中整合查詢
- 在
MovieService
(app/src/lib/MovieService.tsx
) 中,取消註解下列匯入作業:import { upsertUser } from "@movie/dataconnect"; import { getCurrentUser, GetCurrentUserData } from "@movie/dataconnect";
- 將
handleAuthStateChange
和handleGetCurrentUser
函式替換為以下程式碼:// Handle user authentication state changes and upsert user export const handleAuthStateChange = ( auth: any, setUser: (user: User | null) => void ) => { return onAuthStateChanged(auth, async (user) => { if (user) { setUser(user); const username = user.email?.split("@")[0] || "anon"; await upsertUser({ username }); } else { setUser(null); } }); }; // Fetch current user profile export const handleGetCurrentUser = async (): Promise< GetCurrentUserData["user"] | null > => { try { const response = await getCurrentUser(); return response.data.user; } catch (error) { console.error("Error fetching user profile:", error); return null; } };
重點整理:
handleAuthStateChange
:這個函式會監聽驗證狀態變更。使用者登入時,系統會設定使用者資料,並呼叫upsertUser
變異數,在資料庫中建立或更新使用者資訊。handleGetCurrentUser
:使用getCurrentUser
查詢來擷取目前使用者的個人資料,這項查詢會擷取使用者的評論和喜愛的電影。
實例觀摩
接著,請按一下導覽列中的「使用 Google 帳戶登入」按鈕。您可以使用 Firebase 驗證模擬器登入。登入後,按一下「我的個人資料」。這個資料夾目前會是空白,但您已為應用程式中的使用者資料處理作業奠定基礎。
8. 實作使用者互動
在本程式碼研究室的這個部分,您將在電影評論應用程式中實作使用者互動,具體來說,就是讓使用者管理喜愛的電影,並留下或刪除評論。
讓使用者將電影設為我的最愛
在本節中,您將設定資料庫,讓使用者將電影設為喜愛。
實作連接器
- 在
dataconnect/movie-connector/
中開啟mutations.gql
。 - 新增下列變異式,處理將電影設為喜愛的功能:
# Add a movie to the user's favorites list mutation AddFavoritedMovie($movieId: UUID!) @auth(level: USER) { favorite_movie_upsert(data: { userId_expr: "auth.uid", movieId: $movieId }) } # Remove a movie from the user's favorites list mutation DeleteFavoritedMovie($movieId: UUID!) @auth(level: USER) { favorite_movie_delete(key: { userId_expr: "auth.uid", movieId: $movieId }) }
重點整理:
userId_expr: "auth.uid"
:使用auth.uid
,這是由 Firebase 驗證直接提供,可確保只存取或修改已驗證使用者的資料。
檢查電影是否已加入收藏
- 在
dataconnect/movie-connector/
中開啟queries.gql
。 - 新增以下查詢,檢查電影是否已加入收藏:
query GetIfFavoritedMovie($movieId: UUID!) @auth(level: USER) { favorite_movie(key: { userId_expr: "auth.uid", movieId: $movieId }) { movieId } }
重點整理:
auth.uid
:使用 Firebase 驗證,確保可安全存取特定使用者資料。favorite_movie
:檢查favorite_movies
彙整表,查看目前使用者是否將特定電影標示為收藏。
在網頁應用程式中整合查詢
- 在
MovieService
(app/src/lib/MovieService.tsx
) 中,將下列匯入作業取消註解:import { addFavoritedMovie, deleteFavoritedMovie, getIfFavoritedMovie } from "@movie/dataconnect";
- 取代
handleAddFavoritedMovie
、handleDeleteFavoritedMovie
和handleGetIfFavoritedMovie
函式,使用以下程式碼:// Add a movie to user's favorites export const handleAddFavoritedMovie = async ( movieId: string ): Promise<void> => { try { await addFavoritedMovie({ movieId }); } catch (error) { console.error("Error adding movie to favorites:", error); throw error; } }; // Remove a movie from user's favorites export const handleDeleteFavoritedMovie = async ( movieId: string ): Promise<void> => { try { await deleteFavoritedMovie({ movieId }); } catch (error) { console.error("Error removing movie from favorites:", error); throw error; } }; // Check if the movie is favorited by the user export const handleGetIfFavoritedMovie = async ( movieId: string ): Promise<boolean> => { try { const response = await getIfFavoritedMovie({ movieId }); return !!response.data.favorite_movie; } catch (error) { console.error("Error checking if movie is favorited:", error); return false; } };
重點整理:
handleAddFavoritedMovie
和handleDeleteFavoritedMovie
:使用這兩個異動,即可安全地在使用者的收藏清單中新增或移除電影。handleGetIfFavoritedMovie
:使用getIfFavoritedMovie
查詢,檢查使用者是否將電影標示為收藏。
實例觀摩
現在只要在電影資訊卡和電影詳細資料頁面中點選愛心圖示,即可將電影加入或移出收藏清單。此外,你也可以在個人資料頁面上查看喜愛的電影。
讓使用者發表或刪除評論
接下來,您將在應用程式中實作管理使用者評論的部分。
實作連接器
在 mutations.gql
(dataconnect/movie-connector/mutations.gql
) 中:新增下列突變:
# Add a review for a movie
mutation AddReview($movieId: UUID!, $rating: Int!, $reviewText: String!)
@auth(level: USER) {
review_insert(
data: {
userId_expr: "auth.uid"
movieId: $movieId
rating: $rating
reviewText: $reviewText
reviewDate_date: { today: true }
}
)
}
# Delete a user's review for a movie
mutation DeleteReview($movieId: UUID!) @auth(level: USER) {
review_delete(key: { userId_expr: "auth.uid", movieId: $movieId })
}
重點整理:
userId_expr: "auth.uid"
:確保評論與已驗證的使用者相關聯。reviewDate_date: { today: true }
:使用 DataConnect 自動產生評論的目前日期,無須手動輸入。
在網頁應用程式中整合查詢
- 在
MovieService
(app/src/lib/MovieService.tsx
) 中,將下列匯入作業取消註解:import { addReview, deleteReview } from "@movie/dataconnect";
- 取代
handleAddReview
和handleDeleteReview
函式,使用以下程式碼:// Add a review to a movie export const handleAddReview = async ( movieId: string, rating: number, reviewText: string ): Promise<void> => { try { await addReview({ movieId, rating, reviewText }); } catch (error) { console.error("Error adding review:", error); throw error; } }; // Delete a review from a movie export const handleDeleteReview = async (movieId: string): Promise<void> => { try { await deleteReview({ movieId }); } catch (error) { console.error("Error deleting review:", error); throw error; } };
重點整理:
handleAddReview
:呼叫addReview
變換,為指定電影新增評論,並安全地將其連結至已驗證的使用者。handleDeleteReview
:使用deleteReview
變換來移除已驗證使用者對電影的評論。
實例觀摩
使用者現在可以在電影詳細資料頁面上為電影撰寫評論。使用者也可以在個人資料頁面上查看及刪除自己的評論,完全掌控與應用程式的互動情形。
9. 進階篩選器和部分文字比對
在本節中,您將實作進階搜尋功能,讓使用者可根據一系列評分和發行年份搜尋電影、依類型和標記篩選、在標題或說明中執行部分文字比對,甚至結合多個篩選器,以便取得更精確的結果。
實作連接器
- 在
dataconnect/movie-connector/
中開啟queries.gql
。 - 新增以下查詢,支援各種搜尋功能:
# Search for movies, actors, and reviews query SearchAll( $input: String $minYear: Int! $maxYear: Int! $minRating: Float! $maxRating: Float! $genre: String! ) @auth(level: PUBLIC) { moviesMatchingTitle: movies( where: { _and: [ { releaseYear: { ge: $minYear } } { releaseYear: { le: $maxYear } } { rating: { ge: $minRating } } { rating: { le: $maxRating } } { genre: { contains: $genre } } { title: { contains: $input } } ] } ) { id title genre rating imageUrl } moviesMatchingDescription: movies( where: { _and: [ { releaseYear: { ge: $minYear } } { releaseYear: { le: $maxYear } } { rating: { ge: $minRating } } { rating: { le: $maxRating } } { genre: { contains: $genre } } { description: { contains: $input } } ] } ) { id title genre rating imageUrl } actorsMatchingName: actors(where: { name: { contains: $input } }) { id name imageUrl } reviewsMatchingText: reviews(where: { reviewText: { contains: $input } }) { id rating reviewText reviewDate movie { id title } user { id username } } }
重點整理:
_and
運算子:在單一查詢中結合多個條件,讓搜尋結果可依據releaseYear
、rating
和genre
等多個欄位進行篩選。contains
運算子:搜尋欄位內的部分文字比對結果。在這個查詢中,系統會在title
、description
、name
或reviewText
中尋找符合項目。where
子句:指定篩選資料的條件。每個部分 (電影、演員、評論) 都會使用where
子句,定義搜尋的特定條件。
在網頁應用程式中整合查詢
- 在
MovieService
(app/src/lib/MovieService.tsx
) 中,將下列匯入作業取消註解:import { searchAll, SearchAllData } from "@movie/dataconnect";
- 取代
handleSearchAll
函式,使用以下程式碼:// Function to perform the search using the query and filters export const handleSearchAll = async ( searchQuery: string, minYear: number, maxYear: number, minRating: number, maxRating: number, genre: string ): Promise<SearchAllData | null> => { try { const response = await searchAll({ input: searchQuery, minYear, maxYear, minRating, maxRating, genre, }); return response.data; } catch (error) { console.error("Error performing search:", error); return null; } };
重點整理:
handleSearchAll
:這個函式會使用searchAll
查詢,根據使用者輸入的內容執行搜尋,並依據年份、評分、類型和部分文字比對等參數篩選結果。
實例觀摩
透過網頁應用程式的水平選單前往「進階搜尋」頁面,現在你可以使用各種篩選器和輸入內容搜尋電影、演員和評論,取得詳細且個人化的搜尋結果。
10. 選用:部署至雲端 (需要啟用帳單功能)
本機開發迭代作業完成後,現在是時候將結構定義、資料和查詢部署至伺服器。您可以使用 Firebase Data Connect VS Code 擴充功能或 Firebase CLI 執行這項操作。
升級 Firebase 定價方案
如要將 Firebase Data Connect 與 PostgreSQL 適用的 Cloud SQL 整合,Firebase 專案必須採用即付即用 (Blaze) 定價方案,也就是說,專案已連結至 Cloud Billing 帳戶。
- 您必須提供付款方式 (例如信用卡),才能建立 Cloud Billing 帳戶。
- 如果您剛開始使用 Firebase 和 Google Cloud,請確認是否符合 $300 美元抵免額和免費試用 Cloud Billing 帳戶的資格。
- 如果您是為了參加活動而進行這個程式碼研究室,請向活動主辦單位詢問是否有任何 Cloud 抵用金。
如要將專案升級至 Blaze 方案,請按照下列步驟操作:
- 在 Firebase 控制台中,選取「升級方案」。
- 選取 Blaze 方案。按照畫面上的指示將 Cloud Billing 帳戶連結至專案。
如果您需要在升級過程中建立 Cloud Billing 帳戶,可能需要返回 Firebase 控制台的升級流程,才能完成升級。
將網頁應用程式連結至 Firebase 專案
- 使用 Firebase 主控台在 Firebase 專案中註冊網頁應用程式:
- 開啟專案,然後按一下「新增應用程式」。
- 暫時忽略 SDK 設定和配置設定,但請務必複製產生的
firebaseConfig
物件。
- 取代
app/src/lib/firebase.tsx
中現有的firebaseConfig
,改用剛從 Firebase 主控台複製的設定。const firebaseConfig = { apiKey: "API_KEY", authDomain: "PROJECT_ID.firebaseapp.com", projectId: "PROJECT_ID", storageBucket: "PROJECT_ID.firebasestorage.app", messagingSenderId: "SENDER_ID", appId: "APP_ID" };
- 建構網頁應用程式:回到 VS Code,在
app
資料夾中使用 Vite 建構網頁應用程式,以便代管部署作業:cd app npm run build
在 Firebase 專案中設定 Firebase 驗證
- 設定 Firebase 驗證和 Google 登入機制。
- (選用) 使用 Firebase 控制台 (例如
http://127.0.0.1
) 允許 Firebase 驗證的網域。
使用 Firebase CLI 部署
- 在
dataconnect/dataconnect.yaml
中,請確認執行個體 ID、資料庫和服務 ID 與專案相符:specVersion: "v1alpha" serviceId: "your-service-id" location: "us-central1" schema: source: "./schema" datasource: postgresql: database: "your-database-id" cloudSql: instanceId: "your-instance-id" connectorDirs: ["./movie-connector"]
- 請確認您已在專案中設定 Firebase CLI:
npm i -g firebase-tools firebase login --reauth firebase use --add
- 在終端機中執行下列指令來部署:
firebase deploy --only dataconnect,hosting
- 執行下列指令,比較結構定義變更:
firebase dataconnect:sql:diff
- 如果變更內容可接受,請使用下列指令套用變更:
firebase dataconnect:sql:migrate
系統會使用最終部署的結構定義和資料更新 Cloud SQL for PostgreSQL 例項。您可以在 Firebase 主控台中監控狀態。
您現在應該可以在 your-project.web.app/
上看到應用程式。此外,您也可以在 Firebase Data Connect 面板中按一下「Run (Production)」,就像使用本機模擬器一樣,將資料新增至實際工作環境。
11. 選用:搭配 Firebase Data Connect 使用向量搜尋功能 (需要啟用帳單功能)
在本節中,您將使用 Firebase Data Connect 在電影評論應用程式中啟用向量搜尋功能。這項功能可讓您根據內容進行搜尋,例如使用向量嵌入功能找出說明相似的電影。
您必須先完成本程式碼研究室的最後一個步驟,才能部署至 Google Cloud。
更新結構定義,加入欄位的嵌入資料
在 dataconnect/schema/schema.gql
中,將 descriptionEmbedding
欄位新增至 Movie
資料表:
type Movie
# The below parameter values are generated by default with @table, and can be edited manually.
@table {
# implicitly calls @col to generates a column name. ex: @col(name: "movie_id")
id: UUID! @default(expr: "uuidV4()")
title: String!
imageUrl: String!
releaseYear: Int
genre: String
rating: Float
description: String
tags: [String]
descriptionEmbedding: Vector @col(size:768) # Enables vector search
}
重點整理:
descriptionEmbedding: Vector @col(size:768)
:這個欄位會儲存電影說明的語義嵌入資料,讓應用程式支援以向量為基礎的內容搜尋功能。
啟用 Vertex AI
- 請按照必要條件指南設定 Google Cloud 中的 Vertex AI API。這個步驟對於支援嵌入產生和向量搜尋功能至關重要。
- 重新部署結構定義:使用 Firebase Data Connect VS Code 擴充功能,按一下「Deploy to Production」(部署至正式環境),即可啟用
pgvector
和向量搜尋。
使用嵌入資料填充資料庫
- 在 VS Code 中開啟
dataconnect
資料夾。 - 按一下
optional_vector_embed.gql
中的「Run(local)」,將電影的嵌入資料填入資料庫。
新增向量搜尋查詢
在 dataconnect/movie-connector/queries.gql
中新增下列查詢,以執行向量搜尋:
# Search movie descriptions using L2 similarity with Vertex AI
query SearchMovieDescriptionUsingL2Similarity($query: String!)
@auth(level: PUBLIC) {
movies_descriptionEmbedding_similarity(
compare_embed: { model: "textembedding-gecko@003", text: $query }
method: L2
within: 2
limit: 5
) {
id
title
description
tags
rating
imageUrl
}
}
重點整理:
compare_embed
:指定要比較的嵌入模型 (textembedding-gecko@003
) 和輸入文字 ($query
)。method
:指定相似度方法 (L2
),代表歐氏距離。within
:將搜尋範圍限制為 L2 距離 2 以下的電影,著重於內容相符度較高的影片。limit
:限制傳回的結果數量為 5。
在應用程式中實作向量搜尋函式
架構和查詢都已設定完成,現在請將向量搜尋整合至應用程式的服務層。這個步驟可讓您從網路應用程式呼叫搜尋查詢。
- 在
app/src/lib/
MovieService.ts
中,將 SDK 中的以下匯入語句取消註解,這會像其他查詢一樣運作。import { searchMovieDescriptionUsingL2similarity, SearchMovieDescriptionUsingL2similarityData, } from "@movie/dataconnect";
- 新增下列函式,將以向量為基礎的搜尋功能整合至應用程式:
// Perform vector-based search for movies based on description export const searchMoviesByDescription = async ( query: string ): Promise< | SearchMovieDescriptionUsingL2similarityData["movies_descriptionEmbedding_similarity"] | null > => { try { const response = await searchMovieDescriptionUsingL2similarity({ query }); return response.data.movies_descriptionEmbedding_similarity; } catch (error) { console.error("Error fetching movie descriptions:", error); return null; } };
重點整理:
searchMoviesByDescription
:這個函式會呼叫searchMovieDescriptionUsingL2similarity
查詢,傳遞輸入文字來執行以向量為基礎的內容搜尋。
實例觀摩
前往網格選單中的「向量圖形搜尋」部分,然後輸入「浪漫和現代」等詞組。系統會顯示與你搜尋內容相符的電影清單。你也可以前往任何電影的詳細資料頁面,查看頁面底部的「類似電影」部分。
12. 結論
恭喜,您應該可以使用網頁應用程式了!如果你想使用自己的電影資料,請放心,只要模仿 _insert.gql
檔案,即可透過 Firebase Data Connect 擴充功能插入自己的資料,或是透過 VS Code 中的 Data Connect 執行窗格新增資料。