1. Trước khi bạn bắt đầu
Trong lớp học lập trình này, bạn sẽ tích hợp Firebase Data Connect với cơ sở dữ liệu Cloud SQL để tạo một ứng dụng web đánh giá phim. Ứng dụng hoàn chỉnh sẽ cho thấy cách Firebase Data Connect đơn giản hoá quy trình xây dựng các ứng dụng chạy bằng SQL. Gói này bao gồm các tính năng sau:
- Xác thực: Triển khai tính năng xác thực tuỳ chỉnh cho các truy vấn và đột biến của ứng dụng, đảm bảo chỉ những người dùng được uỷ quyền mới có thể tương tác với dữ liệu của bạn.
- Giản đồ GraphQL: Tạo và quản lý cấu trúc dữ liệu bằng cách sử dụng giản đồ GraphQL linh hoạt, phù hợp với nhu cầu của ứng dụng web đánh giá phim.
- Truy vấn và đột biến SQL: Truy xuất, cập nhật và quản lý dữ liệu trong Cloud SQL bằng các truy vấn và đột biến do GraphQL cung cấp.
- Tìm kiếm nâng cao bằng kiểu khớp một phần chuỗi: Sử dụng bộ lọc và các tuỳ chọn tìm kiếm để tìm phim dựa trên các trường như tiêu đề, nội dung mô tả hoặc thẻ.
- Không bắt buộc: Tích hợp tính năng Tìm kiếm vectơ: Thêm chức năng tìm kiếm nội dung bằng cách sử dụng tính năng tìm kiếm vectơ của Firebase Data Connect để mang lại trải nghiệm phong phú cho người dùng dựa trên dữ liệu đầu vào và lựa chọn ưu tiên.
Điều kiện tiên quyết
Bạn cần có kiến thức cơ bản về JavaScript.
Kiến thức bạn sẽ học được
- Thiết lập Firebase Data Connect bằng trình mô phỏng cục bộ.
- Thiết kế giản đồ dữ liệu bằng Data Connect và GraphQL.
- Viết và kiểm thử nhiều truy vấn và đột biến cho một ứng dụng đánh giá phim.
- Tìm hiểu cách Firebase Data Connect tạo và sử dụng SDK trong ứng dụng.
- Triển khai giản đồ và quản lý cơ sở dữ liệu một cách hiệu quả.
Những thông tin bạn cần
- Git
- Visual Studio Code
- Cài đặt Node.js bằng nvm-windows (Windows) hoặc nvm (macOS/Linux)
- Nếu chưa có, hãy tạo một dự án Firebase trong Bảng điều khiển Firebase
- (Không bắt buộc) Để tìm kiếm vectơ, hãy nâng cấp dự án của bạn lên gói Blaze
Thiết lập môi trường phát triển
Phần này sẽ hướng dẫn bạn thiết lập môi trường để bắt đầu xây dựng ứng dụng đánh giá phim bằng Firebase Data Connect.
Bước 1: Nhân bản Kho lưu trữ dự án
Bắt đầu bằng cách nhân bản kho lưu trữ dự án và cài đặt các phần phụ thuộc bắt buộc:
git clone https://github.com/firebaseextended/codelab-dataconnect-web cd codelab-dataconnect-web cd ./app && npm i npm run dev
- Sau khi chạy các lệnh này, hãy mở http://localhost:5173 trong trình duyệt để xem ứng dụng web đang chạy cục bộ. Đây là giao diện người dùng để xây dựng ứng dụng đánh giá phim và tương tác với các tính năng của ứng dụng.
Bước 2: Mở dự án trong Visual Studio Code
Mở thư mục codelab-dataconnect-web
được sao chép bằng Visual Studio Code. Đây là nơi bạn sẽ xác định giản đồ, ghi truy vấn và kiểm thử chức năng của ứng dụng.
Bước 3: Cài đặt Tiện ích Visual Studio Firebase Data Connect
Để sử dụng các tính năng của Data Connect, hãy cài đặt Tiện ích Visual Studio cho Firebase Data Connect.Hoặc: Cài đặt tiện ích này từ Visual Studio Code Marketplace hoặc tìm tiện ích này trong VS Code.
- Hoặc: Cài đặt từ Visual Studio Code Marketplace hoặc tìm kiếm trong VS Code.
Bước 4: Tạo dự án Firebase
Truy cập vào Bảng điều khiển Firebase để tạo dự án Firebase mới nếu bạn chưa có dự án. Sau đó, trong tiện ích Firebase Data Connect VSCode:
- Nhấp vào nút Đăng nhập.
- Nhấp vào Kết nối dự án Firebase rồi chọn dự án bạn đã tạo trong Bảng điều khiển của Firebase.
Bước 5: Khởi động trình mô phỏng Firebase
Trong tiện ích Firebase Data Connect VSCode, hãy nhấp vào Start Emulators (Bắt đầu trình mô phỏng) và xác nhận rằng trình mô phỏng đang chạy trong thiết bị đầu cuối.
2. Xem lại cơ sở mã khởi động
Trong phần này, bạn sẽ khám phá các phần chính của cơ sở mã ban đầu của ứng dụng. Mặc dù ứng dụng này thiếu một số chức năng, nhưng bạn vẫn nên tìm hiểu cấu trúc tổng thể của ứng dụng.
Cấu trúc thư mục và tệp
Dưới đây là thông tin tổng quan nhanh về thư mục và cấu trúc tệp của ứng dụng:
dataconnect/
Chứa các cấu hình Firebase Data Connect, trình kết nối (xác định truy vấn và đột biến) và tệp giản đồ.
schema/schema.gql
: Xác định giản đồ GraphQLconnector/queries.gql
: Các truy vấn cần thiết trong ứng dụng.connector/mutations.gql
: Các đột biến cần thiết trong ứng dụng.- Tệp cấu hình
connector/connector.yaml:
để tạo SDK
app/src/
Chứa logic ứng dụng và hoạt động tương tác với Firebase Data Connect.
firebase.ts
: Cấu hình để kết nối với một ứng dụng Firebase trên bảng điều khiển.lib/dataconnect-sdk/
: Thư mục này chứa SDK đã tạo. Bạn có thể chỉnh sửa vị trí tạo SDK trong tệp connector/connector.yaml và SDK sẽ được tạo tự động bất cứ khi nào bạn xác định một truy vấn hoặc đột biến.
3. Xác định giản đồ cho Bài đánh giá phim
Trong phần này, bạn sẽ xác định cấu trúc và mối quan hệ giữa các thực thể chính trong ứng dụng phim trong một giản đồ. Các thực thể như Movie
, User
, Actor
và Review
được liên kết với các bảng cơ sở dữ liệu, với các mối quan hệ được thiết lập bằng cách sử dụng lệnh Firebase Data Connect và giản đồ GraphQL. Sau khi triển khai, ứng dụng của bạn sẽ sẵn sàng xử lý mọi thứ, từ việc tìm kiếm phim có điểm xếp hạng cao nhất và lọc theo thể loại đến việc cho phép người dùng để lại bài đánh giá, đánh dấu phim yêu thích, khám phá các phim tương tự hoặc tìm phim được đề xuất dựa trên nội dung nhập bằng văn bản thông qua tính năng tìm kiếm vectơ.
Các thực thể và mối quan hệ cốt lõi
Loại Movie
chứa các thông tin chi tiết chính như tiêu đề, thể loại và thẻ mà ứng dụng sử dụng cho nội dung tìm kiếm và hồ sơ phim. Loại User
theo dõi các lượt tương tác của người dùng, chẳng hạn như bài đánh giá và lượt yêu thích. Reviews
kết nối người dùng với phim, cho phép ứng dụng hiển thị điểm xếp hạng và ý kiến phản hồi do người dùng tạo.
Mối quan hệ giữa phim, diễn viên và người dùng giúp ứng dụng trở nên linh động hơn. Bảng nối MovieActor
giúp hiển thị thông tin chi tiết về dàn diễn viên và danh sách phim của diễn viên. Loại FavoriteMovie
cho phép người dùng đánh dấu phim yêu thích để ứng dụng có thể hiển thị danh sách yêu thích được cá nhân hoá và làm nổi bật các lựa chọn phổ biến.
Bảng phim
Loại Movie xác định cấu trúc chính cho một thực thể phim, bao gồm các trường như title, genre, releaseYear và rating.
Sao chép và dán đoạn mã vào tệp 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]
}
Những điểm chính cần ghi nhớ:
- id: Một UUID duy nhất cho mỗi bộ phim, được tạo bằng
@default(expr: "uuidV4()")
.
Bảng MovieMetadata
Loại MovieMetadata thiết lập mối quan hệ một với một với loại Movie. Dữ liệu này bao gồm các dữ liệu bổ sung như đạo diễn của phim.
Sao chép và dán đoạn mã vào tệp 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
}
Những điểm chính cần ghi nhớ:
- Phim! @ref: Tham chiếu loại
Movie
, thiết lập mối quan hệ khoá ngoại.
Bảng diễn viên
Sao chép và dán đoạn mã vào tệp dataconnect/schema/schema.gql
:
type Actor @table {
id: UUID!
imageUrl: String!
name: String! @col(name: "name", dataType: "varchar(30)")
}
Loại Actor
đại diện cho một diễn viên trong cơ sở dữ liệu phim, trong đó mỗi diễn viên có thể tham gia nhiều bộ phim, tạo thành mối quan hệ nhiều với nhiều.
Bảng MovieActor
Sao chép và dán đoạn mã vào tệp 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"
}
Những điểm chính cần ghi nhớ:
- movie: Tham chiếu đến loại Movie (Phim), ngầm tạo một khoá ngoại movieId: UUID!.
- actor: Tham chiếu đến loại Actor, ngầm tạo một khoá ngoại actorId: UUID!.
- role: Xác định vai trò của diễn viên trong phim (ví dụ: "chính" hoặc "hỗ trợ").
Bảng người dùng
Loại User
xác định một thực thể người dùng tương tác với phim bằng cách để lại bài đánh giá hoặc thêm phim vào danh sách yêu thích.
Sao chép và dán đoạn mã vào tệp 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
}
Bảng FavoriteMovie
Loại FavoriteMovie
là một bảng nối xử lý mối quan hệ nhiều với nhiều giữa người dùng và bộ phim hoặc diễn viên mà họ yêu thích. Mỗi bảng liên kết một User
với một Movie
.
Sao chép và dán đoạn mã vào tệp 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!
}
Những điểm chính cần ghi nhớ:
- movie: Tham chiếu đến loại Movie (Phim), ngầm tạo một khoá ngoại movieId: UUID!.
- user: Tham chiếu đến loại người dùng, ngầm tạo một khoá ngoại userId: UUID!.
Bảng xem xét
Loại Bài đánh giá đại diện cho thực thể bài đánh giá và liên kết các loại Người dùng và Phim theo mối quan hệ nhiều với nhiều (một người dùng có thể để lại nhiều bài đánh giá và mỗi bộ phim có thể có nhiều bài đánh giá).
Sao chép và dán đoạn mã vào tệp 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")
}
Những điểm chính cần ghi nhớ:
- user: Tham chiếu đến người dùng đã để lại bài đánh giá.
- movie: Tham chiếu đến bộ phim đang được đánh giá.
- reviewDate: Tự động đặt thành thời gian tạo bài đánh giá bằng
@default(expr: "request.time")
.
Trường được tạo tự động và giá trị mặc định
Giản đồ sử dụng các biểu thức như @default(expr: "uuidV4()")
để tự động tạo mã nhận dạng và dấu thời gian duy nhất. Ví dụ: trường mã nhận dạng trong loại Phim và Bài đánh giá sẽ tự động được điền bằng UUID khi một bản ghi mới được tạo.
Giờ đây, khi đã xác định giản đồ, ứng dụng phim của bạn đã có nền tảng vững chắc cho cấu trúc và mối quan hệ dữ liệu!
4. Truy xuất phim hàng đầu và mới nhất
Trong phần này, bạn sẽ chèn dữ liệu phim mô phỏng vào trình mô phỏng cục bộ, sau đó triển khai các trình kết nối (truy vấn) và mã TypeScript để gọi các trình kết nối này trong ứng dụng web. Cuối cùng, ứng dụng của bạn sẽ có thể tự động tìm nạp và hiển thị các bộ phim mới nhất và được đánh giá cao nhất ngay trong cơ sở dữ liệu.
Chèn dữ liệu về phim, diễn viên và bài đánh giá giả
- Trong VSCode, hãy mở
dataconnect/moviedata_insert.gql
. Đảm bảo trình mô phỏng trong tiện ích Firebase Data Connect đang chạy. - Bạn sẽ thấy nút Run (local) (Chạy (trên máy)) ở đầu tệp. Nhấp vào nút này để chèn dữ liệu phim mô phỏng vào cơ sở dữ liệu.
- Kiểm tra thiết bị đầu cuối Data Connect Execution (Thực thi kết nối dữ liệu) để xác nhận rằng dữ liệu đã được thêm thành công.
Triển khai Trình kết nối
- Mở
dataconnect/movie-connector/queries.gql
. Bạn sẽ thấy một truy vấnListMovies
cơ bản trong phần nhận xét:
query ListMovies @auth(level: PUBLIC) {
movies {
id
title
imageUrl
releaseYear
genre
rating
tags
description
}
}
Truy vấn này tìm nạp tất cả phim và thông tin chi tiết về các phim đó (ví dụ: mã nhận dạng, tiêu đề, năm phát hành). Tuy nhiên, hàm này không sắp xếp các bộ phim.
- Thay thế truy vấn
ListMovies
bằng truy vấn bên dưới để thêm các tuỳ chọn sắp xếp và giới hạn:
# 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
}
}
Nhấp vào nút Run (local) (Chạy (cục bộ)) để thực thi truy vấn trên cơ sở dữ liệu cục bộ. Bạn cũng có thể nhập các biến truy vấn trong ngăn cấu hình trước khi chạy.
Những điểm chính cần ghi nhớ:
- movies(): Trường truy vấn GraphQL để tìm nạp dữ liệu phim từ cơ sở dữ liệu.
- orderByRating: Tham số để sắp xếp phim theo điểm xếp hạng (tăng dần/giảm dần).
- orderByReleaseYear: Tham số để sắp xếp phim theo năm phát hành (tăng dần/giảm dần).
- limit: Giới hạn số lượng phim được trả về.
Tích hợp truy vấn trong ứng dụng web
Trong phần này, bạn sẽ sử dụng các truy vấn được xác định trong phần trước trong ứng dụng web của mình. Trình mô phỏng Firebase Data Connect tạo SDK dựa trên thông tin trong các tệp .gql (schema.gql, queries.gql, mutations.gql) và connector.yaml. Bạn có thể gọi trực tiếp các SDK này trong ứng dụng.
- Trong
MovieService
(app/src/lib/MovieService.tsx
), hãy bỏ ghi chú câu lệnh nhập ở trên cùng:
import { listMovies, ListMoviesData, OrderDirection } from "@movie/dataconnect";
Hàm listMovies
, loại phản hồi ListMoviesData
và enum OrderDirection
đều là các SDK do trình mô phỏng Firebase Data Connect tạo ra dựa trên giản đồ và các truy vấn mà bạn đã xác định trước đó .
- Thay thế các hàm
handleGetTopMovies
vàhandleGetLatestMovies
bằng mã sau:
// 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;
}
};
Những điểm chính cần ghi nhớ:
- listMovies: Một hàm được tạo tự động gọi truy vấn listMovies để truy xuất danh sách phim. Trang này có các tuỳ chọn sắp xếp theo điểm xếp hạng hoặc năm phát hành và giới hạn số lượng kết quả.
- ListMoviesData: Loại kết quả dùng để hiển thị 10 phim hàng đầu và mới nhất trên trang chủ.
Xem ví dụ thực tế
Tải lại ứng dụng web để xem truy vấn hoạt động. Trang chủ hiện hiển thị linh động danh sách phim, tìm nạp dữ liệu trực tiếp từ cơ sở dữ liệu cục bộ. Bạn sẽ thấy các bộ phim mới nhất và được đánh giá cao nhất xuất hiện liền mạch, phản ánh dữ liệu bạn vừa thiết lập.
5. Hiển thị thông tin chi tiết về phim và diễn viên
Trong phần này, bạn sẽ triển khai chức năng truy xuất thông tin chi tiết về một bộ phim hoặc một diễn viên bằng mã nhận dạng duy nhất của họ. Việc này không chỉ liên quan đến việc tìm nạp dữ liệu từ các bảng tương ứng mà còn liên kết các bảng có liên quan để hiển thị thông tin chi tiết toàn diện, chẳng hạn như bài đánh giá phim và danh sách phim của diễn viên.
Triển khai trình kết nối
- Mở
dataconnect/movie-connector/queries.gql
trong dự án. - Thêm các truy vấn sau để truy xuất thông tin chi tiết về phim và diễn viên:
# 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
}
}
}
- Lưu các thay đổi và xem lại các truy vấn.
Những điểm chính cần ghi nhớ:
movie()
/actor()
: Các trường truy vấn GraphQL để tìm nạp một bộ phim hoặc diễn viên từ bảng Phim hoặc Diễn viên._on_
: Cho phép truy cập trực tiếp vào các trường từ một loại được liên kết có mối quan hệ khoá ngoại. Ví dụ:reviews_on_movie
tìm nạp tất cả bài đánh giá liên quan đến một bộ phim cụ thể._via_
: Dùng để điều hướng các mối quan hệ nhiều với nhiều thông qua bảng nối. Ví dụ:actors_via_MovieActor
truy cập vào loại Diễn viên thông qua bảng liên kết MovieActor và điều kiệnwhere
lọc diễn viên dựa trên vai trò của họ (ví dụ: "chính" hoặc "hỗ trợ").
Trong ngăn thực thi Data Connect, bạn có thể kiểm thử truy vấn bằng cách nhập mã nhận dạng mô phỏng, chẳng hạn như:
{"id": "550e8400-e29b-41d4-a716-446655440000"}
Nhấp vào Run (local) (Chạy (trên máy)) cho GetMovieById
để truy xuất thông tin chi tiết về "Quantum Paradox" (phim mô phỏng liên quan đến mã nhận dạng ở trên).
Tích hợp truy vấn trong ứng dụng web
- Trong
MovieService
(app/src/lib/MovieService.tsx
), hãy bỏ ghi chú các lệnh nhập sau:
import { getMovieById, GetMovieByIdData } from "@movie/dataconnect";
import { GetActorByIdData, getActorById } from "@movie/dataconnect";
- Thay thế các hàm
handleGetMovieById
vàhandleGetActorById
bằng mã sau:
// 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;
}
};
Những điểm chính cần ghi nhớ:
getMovieById
/getActorById
: Đây là các hàm được tạo tự động gọi các truy vấn mà bạn đã xác định, truy xuất thông tin chi tiết về một bộ phim hoặc diễn viên cụ thể.GetMovieByIdData
/GetActorByIdData
: Đây là các loại kết quả, dùng để hiển thị thông tin chi tiết về phim và diễn viên trong ứng dụng.
Xem ví dụ thực tế
Bây giờ, hãy chuyển đến trang chủ của ứng dụng web. Nhấp vào một bộ phim để xem tất cả thông tin chi tiết về bộ phim đó, bao gồm cả diễn viên và bài đánh giá. Đây là thông tin được lấy từ các bảng liên quan. Tương tự, khi bạn nhấp vào một diễn viên, những bộ phim mà họ tham gia sẽ xuất hiện.
6. Xử lý việc xác thực người dùng
Trong phần này, bạn sẽ triển khai chức năng đăng nhập và đăng xuất của người dùng bằng tính năng Xác thực Firebase. Bạn cũng sẽ sử dụng dữ liệu Xác thực Firebase để truy xuất hoặc chèn/cập nhật dữ liệu người dùng trong Firebase DataConnect, đảm bảo việc quản lý người dùng an toàn trong ứng dụng.
Triển khai trình kết nối
- Mở
mutations.gql
trongdataconnect/movie-connector/
. - Thêm đột biến sau để tạo hoặc cập nhật người dùng đã xác thực hiện tại:
# Create or update the current authenticated user
mutation UpsertUser($username: String!) @auth(level: USER) {
user_upsert(
data: {
id_expr: "auth.uid"
username: $username
}
)
}
Điểm chính cần ghi nhớ:
id_expr: "auth.uid"
: Phương thức này sử dụngauth.uid
do Xác thực Firebase trực tiếp cung cấp, chứ không phải do người dùng hoặc ứng dụng cung cấp, giúp tăng cường thêm một lớp bảo mật bằng cách đảm bảo mã nhận dạng người dùng được xử lý một cách an toàn và tự động.
Tiếp theo, hãy mở queries.gql
trong dataconnect/movie-connector/
.
Thêm truy vấn sau để tìm nạp người dùng hiện tại:
# 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
}
}
}
}
}
Những điểm chính cần ghi nhớ:
auth.uid
: Mã này được truy xuất trực tiếp từ tính năng Xác thực Firebase, đảm bảo quyền truy cập an toàn vào dữ liệu dành riêng cho người dùng.- Các trường
_on_
: Các trường này đại diện cho các bảng nối: reviews_on_user
: Truy xuất tất cả bài đánh giá liên quan đến người dùng, bao gồm cả mã nhận dạng và tiêu đề của phim.favorite_movies_on_user
: Truy xuất tất cả phim mà người dùng đánh dấu là phim yêu thích, bao gồm cả thông tin chi tiết như thể loại, năm phát hành, điểm xếp hạng và siêu dữ liệu.
Tích hợp truy vấn trong ứng dụng web
- Trong MovieService (
app/src/lib/MovieService.tsx
), hãy bỏ ghi chú các lệnh nhập sau:
import { upsertUser } from "@movie/dataconnect";
import { getCurrentUser, GetCurrentUserData } from "@movie/dataconnect";
- Thay thế các hàm
handleAuthStateChange
vàhandleGetCurrentUser
bằng mã sau:
// 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;
}
};
Những điểm chính cần ghi nhớ:
handleAuthStateChange
: Hàm này theo dõi các thay đổi về trạng thái xác thực. Khi người dùng đăng nhập, ứng dụng sẽ đặt dữ liệu của người dùng và gọi đột biếnupsertUser
để tạo hoặc cập nhật thông tin của người dùng trong cơ sở dữ liệu.handleGetCurrentUser
: Tìm nạp hồ sơ của người dùng hiện tại bằng cách sử dụng truy vấngetCurrentUser
. Truy vấn này sẽ truy xuất bài đánh giá và phim yêu thích của người dùng.
Xem ví dụ thực tế
Bây giờ, hãy nhấp vào nút "Đăng nhập bằng Google" trong thanh điều hướng. Bạn có thể đăng nhập bằng trình mô phỏng Firebase Auth. Sau khi đăng nhập, hãy nhấp vào "Hồ sơ của tôi". Hiện tại, phần này sẽ trống, nhưng bạn đã thiết lập nền tảng để xử lý dữ liệu dành riêng cho người dùng trong ứng dụng.
7. Triển khai hoạt động tương tác của người dùng
Trong phần này, bạn sẽ triển khai các hoạt động tương tác của người dùng trong ứng dụng đánh giá phim, cho phép người dùng quản lý các bộ phim mà họ yêu thích cũng như để lại hoặc xoá bài đánh giá.
Triển khai trình kết nối
- Mở
mutations.gql
trongdataconnect/movie-connector/
. - Thêm các đột biến sau để xử lý việc thêm phim vào danh sách yêu thích:
# 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 })
}
Những điểm chính cần ghi nhớ:
userId_expr: "auth.uid"
: Sử dụngauth.uid
do Xác thực Firebase trực tiếp cung cấp, đảm bảo rằng chỉ truy cập hoặc sửa đổi dữ liệu của người dùng đã xác thực.
- Tiếp theo, hãy mở
queries.gql
trongdataconnect/movie-connector/
. - Thêm truy vấn sau để kiểm tra xem một bộ phim có được thêm vào danh sách yêu thích hay không:
query GetIfFavoritedMovie($movieId: UUID!) @auth(level: USER) {
favorite_movie(key: { userId_expr: "auth.uid", movieId: $movieId }) {
movieId
}
}
Những điểm chính cần ghi nhớ:
auth.uid
: Đảm bảo quyền truy cập an toàn vào dữ liệu dành riêng cho người dùng bằng tính năng Xác thực Firebase.favorite_movie
: Kiểm tra bảng nốifavorite_movies
để xem người dùng hiện tại có đánh dấu một bộ phim cụ thể là phim yêu thích hay không.
Tích hợp truy vấn trong ứng dụng web
- Trong
MovieService
(app/src/lib/MovieService.tsx
), hãy bỏ ghi chú các lệnh nhập sau:
import { addFavoritedMovie, deleteFavoritedMovie, getIfFavoritedMovie } from "@movie/dataconnect";
- Thay thế các hàm
handleAddFavoritedMovie
,handleDeleteFavoritedMovie
vàhandleGetIfFavoritedMovie
bằng mã sau:
// 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;
}
};
Những điểm chính cần ghi nhớ:
handleAddFavoritedMovie
vàhandleDeleteFavoritedMovie
: Sử dụng các đột biến để thêm hoặc xoá một bộ phim khỏi danh sách yêu thích của người dùng một cách an toàn.handleGetIfFavoritedMovie
: Sử dụng truy vấngetIfFavoritedMovie
để kiểm tra xem người dùng có đánh dấu một bộ phim là yêu thích hay không.
Xem ví dụ thực tế
Giờ đây, bạn có thể thêm phim vào danh sách yêu thích hoặc xoá phim khỏi danh sách yêu thích bằng cách nhấp vào biểu tượng trái tim trên thẻ phim và trang chi tiết về phim. Ngoài ra, bạn có thể xem các bộ phim yêu thích trên trang hồ sơ của mình.
Triển khai bài đánh giá của người dùng
Tiếp theo, bạn sẽ triển khai phần quản lý bài đánh giá của người dùng trong ứng dụng.
Triển khai trình kết nối
- Trong
mutations.gql
(dataconnect/movie-connector/mutations.gql
): Thêm các đột biến sau:
# 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 })
}
Những điểm chính cần ghi nhớ:
userId_expr: "auth.uid"
: Đảm bảo rằng bài đánh giá được liên kết với người dùng đã xác thực.reviewDate_date: { today: true }
: Tự động tạo ngày hiện tại cho bài đánh giá bằng DataConnect, giúp bạn không cần phải nhập thủ công.
Tích hợp truy vấn trong ứng dụng web
- Trong
MovieService
(app/src/lib/MovieService.tsx
), hãy bỏ ghi chú các lệnh nhập sau:
import { addReview, deleteReview } from "@movie/dataconnect";
- Thay thế các hàm
handleAddReview
vàhandleDeleteReview
bằng mã sau:
// 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;
}
};
Những điểm chính cần ghi nhớ:
handleAddReview
: Gọi phương thức đột biếnaddReview
để thêm bài đánh giá cho bộ phim đã chỉ định, liên kết bài đánh giá đó một cách an toàn với người dùng đã xác thực.handleDeleteReview
: Sử dụng đột biếndeleteReview
để xoá bài đánh giá về một bộ phim do người dùng đã xác thực thực hiện.
Xem ví dụ thực tế
Giờ đây, người dùng có thể để lại bài đánh giá về phim trên trang chi tiết về phim. Người dùng cũng có thể xem và xoá bài đánh giá của mình trên trang hồ sơ, nhờ đó có toàn quyền kiểm soát các hoạt động tương tác với ứng dụng.
8. Bộ lọc nâng cao và tính năng khớp văn bản một phần
Trong phần này, bạn sẽ triển khai các tính năng tìm kiếm nâng cao, cho phép người dùng tìm kiếm phim dựa trên một loạt điểm xếp hạng và năm phát hành, lọc theo thể loại và thẻ, so khớp một phần văn bản trong tiêu đề hoặc nội dung mô tả, thậm chí kết hợp nhiều bộ lọc để có kết quả chính xác hơn.
Triển khai trình kết nối
- Mở
queries.gql
trongdataconnect/movie-connector/
. - Thêm truy vấn sau để hỗ trợ nhiều tính năng tìm kiếm:
# 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
}
}
}
Những điểm chính cần ghi nhớ:
- Toán tử
_and
: Kết hợp nhiều điều kiện trong một truy vấn, cho phép lọc kết quả tìm kiếm theo một số trường nhưreleaseYear
,rating
vàgenre
. - Toán tử
contains
: Tìm kiếm các kết quả khớp một phần văn bản trong các trường. Trong truy vấn này, hàm này tìm kiếm các kết quả trùng khớp trongtitle
,description
,name
hoặcreviewText
. - Mệnh đề
where
: Chỉ định các điều kiện để lọc dữ liệu. Mỗi phần (phim, diễn viên, bài đánh giá) sử dụng một mệnh đềwhere
để xác định tiêu chí cụ thể cho nội dung tìm kiếm.
Tích hợp truy vấn trong ứng dụng web
- Trong
MovieService
(app/src/lib/MovieService.tsx
), hãy bỏ ghi chú các lệnh nhập sau:
import { searchAll, SearchAllData } from "@movie/dataconnect";
- Thay thế hàm
handleSearchAll
bằng mã sau:
// 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;
}
};
Những điểm chính cần ghi nhớ:
handleSearchAll
: Hàm này sử dụng truy vấnsearchAll
để tìm kiếm dựa trên dữ liệu đầu vào của người dùng, lọc kết quả theo các tham số như năm, điểm xếp hạng, thể loại và kết quả khớp một phần văn bản.
Xem ví dụ thực tế
Chuyển đến trang "Tìm kiếm nâng cao" trên thanh điều hướng trong ứng dụng web. Giờ đây, bạn có thể tìm kiếm phim, diễn viên và bài đánh giá bằng nhiều bộ lọc và thông tin đầu vào, nhận kết quả tìm kiếm chi tiết và phù hợp.
9. Không bắt buộc: Triển khai lên đám mây (Bắt buộc phải có thông tin thanh toán)
Giờ đây, khi bạn đã hoàn tất quá trình lặp lại phát triển cục bộ, đã đến lúc triển khai giản đồ, dữ liệu và truy vấn cho máy chủ. Bạn có thể thực hiện việc này bằng cách sử dụng tiện ích Firebase Data Connect VS Code hoặc Firebase CLI.
Thêm ứng dụng web trong Bảng điều khiển của Firebase
- Tạo ứng dụng web trong Bảng điều khiển Firebase và ghi lại mã ứng dụng
- Thiết lập một ứng dụng web trong Bảng điều khiển của Firebase bằng cách nhấp vào "Thêm ứng dụng". Hiện tại, bạn có thể yên tâm bỏ qua việc thiết lập SDK và cấu hình, nhưng hãy lưu ý đến đối tượng
firebaseConfig
đã tạo. - Thay thế
firebaseConfig
trongapp/src/lib/firebase.tsx
:
const firebaseConfig = { apiKey: "API_KEY", authDomain: "PROJECT_ID.firebaseapp.com", projectId: "PROJECT_ID", storageBucket: "PROJECT_ID.appspot.com", messagingSenderId: "SENDER_ID", appId: "APP_ID" };
- Tạo ứng dụng web: Trong thư mục
app
, hãy sử dụng Vite để tạo ứng dụng web nhằm lưu trữ quá trình triển khai:
cd app npm run build
Thiết lập tính năng Xác thực Firebase trong Bảng điều khiển
- Thiết lập Firebase Auth bằng tính năng Đăng nhập bằng Google
- (Không bắt buộc) Cho phép các miền cho (Firebase Auth) [https://firebase.google.com/docs/auth/web/hosting] trong bảng điều khiển dự án (ví dụ:
http://127.0.0.1
):
- Trong phần Cài đặt xác thực, hãy chọn dự án của bạn rồi chuyển đến (Miền được uỷ quyền) [https://firebase.google.com/docs/auth/web/hosting]. Nhấp vào "Thêm miền" rồi thêm miền địa phương của bạn vào danh sách.
Triển khai bằng Firebase CLI
- Trong
dataconnect/dataconnect.yaml
, hãy đảm bảo rằng mã nhận dạng thực thể, cơ sở dữ liệu và mã nhận dạng dịch vụ khớp với dự án của bạn:
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"]
- Đảm bảo bạn đã thiết lập Firebase CLI với dự án
npm i -g firebase-tools
firebase login --reauth
firebase use --add
- Trong dòng lệnh, hãy chạy lệnh sau để triển khai:
firebase deploy --only dataconnect,hosting
- Chạy lệnh sau để so sánh các thay đổi về giản đồ:
firebase dataconnect:sql:diff
- Nếu bạn chấp nhận các thay đổi, hãy áp dụng bằng:
firebase dataconnect:sql:migrate
Phiên bản Cloud SQL cho PostgreSQL sẽ được cập nhật bằng giản đồ và dữ liệu đã triển khai cuối cùng. Bạn có thể theo dõi trạng thái trong Bảng điều khiển của Firebase.
Giờ đây, bạn có thể xem ứng dụng của mình trực tiếp tại your-project.web.app/
. Ngoài ra, bạn có thể nhấp vào Run (Production) (Chạy (Bản chính thức)) trong bảng điều khiển Firebase Data Connect (Kết nối dữ liệu Firebase), giống như cách bạn đã làm với trình mô phỏng cục bộ, để thêm dữ liệu vào môi trường chính thức.
10. Không bắt buộc: Tìm kiếm vectơ bằng Firebase Data Connect
Trong phần này, bạn sẽ bật tính năng tìm kiếm vectơ trong ứng dụng đánh giá phim bằng Firebase Data Connect. Tính năng này cho phép tìm kiếm dựa trên nội dung, chẳng hạn như tìm phim có nội dung mô tả tương tự bằng cách sử dụng các vectơ nhúng.
Cập nhật giản đồ để thêm các phần nhúng cho một trường
- Trong
dataconnect/schema/schema.gql
, hãy thêm trườngdescriptionEmbedding
vào bảngMovie
:
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
}
Điểm chính cần ghi nhớ:
descriptionEmbedding: Vector @col(size:768)
: Trường này lưu trữ các nội dung nhúng ngữ nghĩa của nội dung mô tả phim, cho phép tìm kiếm nội dung dựa trên vectơ trong ứng dụng.
Kích hoạt Vertex AI
- Làm theo hướng dẫn về điều kiện tiên quyết để thiết lập API Vertex AI bằng Google Cloud. Bước này rất quan trọng để hỗ trợ chức năng tạo tính năng nhúng và tìm kiếm vectơ.
- Triển khai lại giản đồ để kích hoạt
pgvector
và tính năng tìm kiếm vectơ bằng cách nhấp vào Triển khai công khai bằng tiện ích Firebase Data Connect VSCode.
Điền nội dung nhúng vào cơ sở dữ liệu
- Mở thư mục
dataconnect
trong VSCode rồi nhấp vào Run(local) (Chạy (cục bộ)) trongoptional_vector_embed.gql
để điền các nội dung nhúng cho phim vào cơ sở dữ liệu.
Thêm truy vấn Tìm kiếm vectơ
- Trong
dataconnect/movie-connector/queries.gql
, hãy thêm truy vấn sau để thực hiện tìm kiếm vectơ:
# 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
}
}
Những điểm chính cần ghi nhớ:
compare_embed
: Chỉ định mô hình nhúng (textembedding-gecko@003
) và văn bản đầu vào ($query
) để so sánh.method
: Chỉ định phương thức tương đồng (L2
) đại diện cho khoảng cách Euclide.within
: Giới hạn nội dung tìm kiếm ở những bộ phim có khoảng cách L2 từ 2 trở xuống, tập trung vào những nội dung trùng khớp gần.limit
: Giới hạn số lượng kết quả trả về ở mức 5.
Triển khai Hàm tìm kiếm vectơ trong ứng dụng
- Trong
app/src/lib/MovieService.ts
, hãy bỏ ghi chú các lệnh nhập sau:
Triển khai Hàm tìm kiếm vectơ trong ứng dụng
Giờ đây, khi đã thiết lập giản đồ và truy vấn, hãy tích hợp tính năng tìm kiếm vectơ vào lớp dịch vụ của ứng dụng. Bước này cho phép bạn gọi cụm từ tìm kiếm từ ứng dụng web.
Trong app/src/lib/
MovieService.ts
, hãy bỏ ghi chú các lệnh nhập sau đây từ SDK, lệnh này sẽ hoạt động như mọi truy vấn khác.
import {
searchMovieDescriptionUsingL2similarity,
SearchMovieDescriptionUsingL2similarityData,
} from "@movie/dataconnect";
Thêm hàm sau để tích hợp tính năng tìm kiếm dựa trên vectơ vào ứng dụng:
// 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;
}
};
Những điểm chính cần ghi nhớ:
searchMoviesByDescription
: Hàm này gọi truy vấnsearchMovieDescriptionUsingL2similarity
, truyền văn bản đầu vào để thực hiện tìm kiếm nội dung dựa trên vectơ.
Xem ví dụ thực tế
Chuyển đến phần "Tìm kiếm vectơ" trong thanh điều hướng rồi nhập các cụm từ như "lãng mạn và hiện đại". Bạn sẽ thấy danh sách phim phù hợp với nội dung bạn đang tìm kiếm. Ngoài ra, bạn có thể truy cập vào trang chi tiết của bất kỳ bộ phim nào rồi xem phần phim tương tự ở cuối trang.
11. Kết luận
Xin chúc mừng! Bạn có thể sử dụng ứng dụng web! Nếu bạn muốn chơi với dữ liệu phim của riêng mình, đừng lo, hãy chèn dữ liệu của riêng bạn bằng tiện ích FDC bằng cách mô phỏng các tệp _insert.gql hoặc thêm các tệp đó thông qua ngăn Thực thi kết nối dữ liệu.