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 方案
設定開發環境
本節將引導您設定環境,以便開始使用 Firebase Data Connect 建構電影評論應用程式。
步驟 1:複製專案存放區
首先複製專案存放區,並安裝必要的依附元件:
git clone https://github.com/firebaseextended/codelab-dataconnect-web cd codelab-dataconnect-web cd ./app && npm i npm run dev
- 執行這些指令後,請在瀏覽器中開啟 http://localhost:5173,查看在本機上執行的網路應用程式。這會成為您建構電影評論應用程式和與其功能互動的前端。
步驟 2:在 Visual Studio Code 中開啟專案
使用 Visual Studio Code 開啟複製的 codelab-dataconnect-web
資料夾。您可以在這個畫面中定義結構定義、編寫查詢,以及測試應用程式功能。
步驟 3:安裝 Firebase Data Connect Visual Studio 擴充功能
如要使用 Data Connect 功能,請安裝 Firebase Data Connect Visual Studio 擴充功能。或者,您也可以從 Visual Studio Code Marketplace 安裝這項擴充功能,或在 VS Code 中搜尋。
- 或:從 Visual Studio Code Marketplace 安裝,或在 VS Code 中搜尋。
步驟 4:建立 Firebase 專案
如果您沒有 Firebase 專案,請前往 Firebase 控制台建立新的專案。接著,在 Firebase Data Connect VSCode 擴充功能中:
- 按一下「登入」按鈕。
- 按一下「連結 Firebase 專案」,然後選取您在 Firebase 控制台中建立的專案。
步驟 5:啟動 Firebase 模擬器
在 Firebase Data Connect VSCode 擴充功能中,按一下「Start Emulators」,並確認終端機中是否正在執行模擬器。
2. 查看範例程式碼
在本節中,您將探索應用程式啟動程式碼集的幾個重點領域。雖然應用程式缺少部分功能,但有助於您瞭解整體結構。
資料夾和檔案結構
以下是應用程式資料夾和檔案結構的簡要說明:
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 應用程式的設定。lib/dataconnect-sdk/
:這個資料夾包含產生的 SDK。您可以在 connector/connector.yaml 檔案中編輯 SDK 產生位置,系統會在您定義查詢或 mutation 時自動產生 SDK。
3. 定義電影評論的結構定義
在本節中,您將在結構定義中定義電影應用程式中關鍵實體之間的關係。Movie
、User
、Actor
和 Review
等實體會對應至資料庫表格,並使用 Firebase Data Connect 和 GraphQL 架構指令建立關係。完成後,應用程式就能處理所有相關作業,包括搜尋高評分電影、依類型篩選內容、讓使用者留下評論、標記喜愛的內容、探索類似電影,或透過向量搜尋功能,根據文字輸入內容找出推薦電影。
核心實體和關係
Movie
類型會保留標題、類型和標記等重要詳細資料,應用程式會用於搜尋和電影設定檔。User
類型會追蹤使用者互動,例如評論和收藏。Reviews
將使用者連結至電影,讓應用程式顯示使用者提供的評分和意見回饋。
電影、演員和使用者之間的關係,可讓應用程式更具動態性。MovieActor
彙整表格可協助顯示演員陣容詳細資料和演員的電影作品。FavoriteMovie
類型可讓使用者將電影設為喜愛項目,讓應用程式顯示個人化的喜愛項目清單,並突顯熱門選項。
Movie 表
電影類型會定義電影實體的主要結構,包括片名、類型、發行年份和評分等欄位。
複製程式碼片段,然後貼到 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
類型定義了使用者實體,這些使用者會透過留下評論或將電影設為喜好項目,與電影互動。
複製程式碼片段,然後貼到 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」類型代表評論實體,並將「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。
架構定義完成後,電影應用程式就能建立穩固的資料結構和關係!
4. 擷取熱門和最新電影
在本節中,您將在本機模擬器中插入模擬電影資料,然後實作連接器 (查詢) 和 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、標題、發行年份)。不過,系統不會排序電影。
- 取代
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 部熱門電影和最新電影的結果類型。
實例觀摩
重新載入網路應用程式,即可查看查詢的運作情形。首頁現在會動態顯示電影清單,並直接從本機資料庫擷取資料。系統會根據你剛剛設定的資料,流暢地顯示評價最高和最新的電影。
5. 顯示電影和演員詳細資料
在本節中,您將實作功能,以便使用電影或演員的專屬 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 查詢欄位,用於從「電影」或「演員」資料表擷取單一電影或演員。_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
:這些是結果類型,用於在應用程式中顯示電影和演員詳細資料。
實例觀摩
接著,前往網頁應用程式的首頁。點選電影後,您就能查看所有詳細資料,包括演員和評論等資訊,這些資訊都是從相關資料表中擷取。同樣地,點選演員後,系統也會顯示他們參與的電影。
6. 處理使用者驗證
在本節中,您將使用 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 和標題。favorite_movies_on_user
:擷取使用者將其標示為常用項目的所有電影,包括類型、發行年份、評分和中繼資料等詳細資訊。
在網頁應用程式中整合查詢
- 在 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 驗證模擬器登入。登入後,按一下「我的個人資料」。這個資料夾目前會是空白,但您已為應用程式中的使用者資料處理作業奠定基礎。
7. 實作使用者互動
在本節中,您將在電影評論應用程式中實作使用者互動,讓使用者管理喜愛的電影,並留下或刪除評論。
實作連接器
- 在
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
異動來移除已驗證使用者對電影的評論。
實例觀摩
使用者現在可以在電影詳細資料頁面上為電影撰寫評論。使用者也可以在個人資料頁面上查看及刪除自己的評論,完全掌控與應用程式的互動情形。
8. 進階篩選器和部分文字比對
在本節中,您將實作進階搜尋功能,讓使用者可根據一系列評分和發行年份搜尋電影、依類型和標記篩選、在標題或說明中執行部分文字比對,甚至結合多個篩選器,以便取得更精確的結果。
實作連接器
- 在
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
查詢,根據使用者輸入內容執行搜尋,並依據年份、評分、類型和部分文字比對等參數篩選結果。
實例觀摩
透過網頁應用程式的水平選單前往「進階搜尋」頁面,現在你可以使用各種篩選器和輸入內容搜尋電影、演員和評論,取得詳細且個人化的搜尋結果。
9. 選用步驟:部署至雲端 (需要啟用帳單功能)
本機開發迭代作業完成後,現在是時候將結構定義、資料和查詢部署至伺服器。您可以使用 Firebase Data Connect VS Code 擴充功能或 Firebase CLI 執行這項操作。
在 Firebase 控制台中新增網頁應用程式
- 在 Firebase 主控台建立網頁應用程式,並記下應用程式 ID
- 按一下「Add App」(新增應用程式),在 Firebase 主控台中設定網頁應用程式。您可以暫時忽略 SDK 設定和配置設定,但請記下產生的
firebaseConfig
物件。 - 取代
app/src/lib/firebase.tsx
中的firebaseConfig
:
const firebaseConfig = { apiKey: "API_KEY", authDomain: "PROJECT_ID.firebaseapp.com", projectId: "PROJECT_ID", storageBucket: "PROJECT_ID.appspot.com", messagingSenderId: "SENDER_ID", appId: "APP_ID" };
- 建構網頁應用程式:在
app
資料夾中,使用 Vite 建構網頁應用程式,以便代管部署作業:
cd app npm run build
在控制台中設定 Firebase 驗證
- 使用 Google 登入功能設定 Firebase 驗證
- (選用) 在專案控制台中允許 (Firebase 驗證) [https://firebase.google.com/docs/auth/web/hosting] 的網域 (例如
http://127.0.0.1
):
- 在驗證設定中選取專案,然後前往「(已授權的網域)」 [https://firebase.google.com/docs/auth/web/hosting]。按一下「新增網域」,並將本機網域加入清單。
使用 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)」,就像使用本機模擬器一樣,將資料新增至實際工作環境。
10. 選用:使用 Firebase Data Connect 進行向量搜尋
在本節中,您將使用 Firebase Data Connect 在電影評論應用程式中啟用向量搜尋功能。這項功能可讓您根據內容進行搜尋,例如使用向量嵌入功能找出說明相似的電影。
更新結構定義,加入欄位的嵌入內容
- 在
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 VSCode 擴充功能,按一下「部署至正式版」即可啟用
pgvector
和向量搜尋功能。
使用嵌入內容填入資料庫
- 開啟 VSCode 中的
dataconnect
資料夾,然後按一下optional_vector_embed.gql
中的「Run(local)」,將電影的嵌入資料填入資料庫。
新增 Vector Search 查詢
- 在
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
中,取消註解以下匯入項目:
在應用程式中實作向量搜尋功能
架構和查詢都已設定完成,現在請將向量搜尋整合至應用程式的服務層。這個步驟可讓您從網路應用程式呼叫搜尋查詢。
在 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
查詢,傳遞輸入文字來執行以向量為基礎的內容搜尋。
實例觀摩
前往網格選單中的「向量圖形搜尋」部分,然後輸入「浪漫和現代」之類的詞組。系統會顯示與你搜尋內容相符的電影清單。你也可以前往任何電影的詳細資料頁面,查看頁面底部的「類似電影」專區。
11. 結論
恭喜,您應該可以使用網頁應用程式了!如果你想使用自己的電影資料,別擔心,只要模仿 _insert.gql 檔案,就能使用 FDC 擴充功能插入自己的資料,或是透過資料連結執行窗格新增資料。