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 生成位置,每當您定義查詢或突變時,系統就會自動生成 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
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
條件會根據演員的角色 (例如「主要」或「配角」) 篩選演員。
輸入模擬資料來測試查詢
- 在「資料連結」執行窗格中,您可以輸入模擬 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
,由 Firebase Authentication 直接提供,而非使用者或應用程式提供,可確保使用者 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. 選用:部署至 Cloud (須付費)
完成本機開發疊代後,現在要將結構定義、資料和查詢部署至伺服器。您可以使用 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 Authentication
- 使用 Google 登入設定 Firebase 驗證。
- (選用) 使用 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
系統會使用最終部署的結構定義和資料更新 PostgreSQL 適用的 Cloud SQL 執行個體。您可以在 Firebase 控制台中監控狀態。
現在您應該可以在 your-project.web.app/
看到應用程式。此外,您也可以在 Firebase Data Connect 面板中點選「執行 (正式環境)」,就像使用本機模擬器一樣,將資料新增至正式環境。
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 個。
在應用程式中實作向量搜尋功能
現在已設定好結構定義和查詢,接下來請將向量搜尋功能整合至應用程式的服務層。這個步驟可讓您從 Web 應用程式呼叫搜尋查詢。
- 在
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
查詢,傳遞輸入文字來執行以向量為基礎的內容搜尋。
觀看實際使用教學
前往導覽列中的「Vector Search」部分,然後輸入「浪漫且現代」等詞組。系統會顯示符合搜尋內容的電影清單,或者,你也可以進入任一電影的詳細資料頁面,查看頁面底部的「類似電影」專區。
12. 結論
恭喜,您現在應該可以使用網頁應用程式了!如要使用自己的電影資料,請放心,只要模仿 _insert.gql
檔案,即可使用 Firebase Data Connect 擴充功能插入自己的資料,或透過 VS Code 中的 Data Connect 執行窗格新增資料。