1. Прежде чем начать
В этой лаборатории кода вы интегрируете Firebase Data Connect с базой данных Cloud SQL, чтобы создать веб-приложение для обзора фильмов. Готовое приложение демонстрирует, как Firebase Data Connect упрощает процесс создания приложений на базе SQL. Он включает в себя следующие функции:
- Аутентификация: реализуйте пользовательскую аутентификацию для запросов и изменений вашего приложения, гарантируя, что только авторизованные пользователи смогут взаимодействовать с вашими данными.
- Схема GraphQL: создавайте структуры данных и управляйте ими с помощью гибкой схемы GraphQL, адаптированной к потребностям веб-приложения для просмотра фильмов.
- SQL-запросы и мутации: извлекайте, обновляйте и управляйте данными в Cloud SQL с помощью запросов и мутаций на базе GraphQL.
- Расширенный поиск с частичным совпадением строк: используйте фильтры и параметры поиска для поиска фильмов по таким полям, как название, описание или теги.
- Необязательно: интеграция векторного поиска: добавьте функцию поиска по контенту с помощью векторного поиска Firebase Data Connect, чтобы обеспечить богатый пользовательский интерфейс на основе введенных данных и предпочтений.
Предварительные условия
Вам понадобится базовое понимание JavaScript .
Что вы узнаете
- Настройте Firebase Data Connect с помощью локальных эмуляторов.
- Спроектируйте схему данных с помощью Data Connect и GraphQL .
- Напишите и протестируйте различные запросы и мутации для приложения для обзора фильмов.
- Узнайте, как Firebase Data Connect генерирует и использует SDK в приложении.
- Разверните свою схему и эффективно управляйте базой данных.
Что вам понадобится
- Гит
- Код Visual Studio
- Установите Node.js с помощью nvm-windows (Windows) или nvm (macOS/Linux).
- Если вы еще этого не сделали, создайте проект 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.
Откройте клонированную папку codelab-dataconnect-web
с помощью Visual Studio Code . Здесь вы определите свою схему, напишите запросы и протестируете функциональность приложения.
Шаг 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 нажмите «Запустить эмуляторы» и убедитесь, что эмуляторы работают в терминале.
2. Просмотрите начальную кодовую базу
В этом разделе вы изучите ключевые области стартовой кодовой базы приложения. Хотя в приложении отсутствуют некоторые функции, полезно понять общую структуру.
Структура папок и файлов
Вот краткий обзор структуры папок и файлов приложения:
подключение к данным/
Содержит конфигурации Firebase Data Connect, соединители (которые определяют запросы и мутации) и файлы схемы.
-
schema/schema.gql
: определяет схему GraphQL. -
connector/queries.gql
: запросы, необходимые в вашем приложении. -
connector/mutations.gql
: мутации, необходимые в вашем приложении. -
connector/connector.yaml:
файл конфигурации для генерации SDK.
приложение/источник/
Содержит логику приложения и взаимодействие с Firebase Data Connect.
-
firebase.ts
: конфигурация для подключения к приложению Firebase на консоли. -
lib/dataconnect-sdk/
: эта папка содержит сгенерированный SDK. Вы можете изменить местоположение создания SDK в файле Connector/connector.yaml, и SDK будут создаваться автоматически каждый раз, когда вы определяете запрос или мутацию.
3. Определение схемы для обзора фильмов
В этом разделе вы определите структуру и отношения между ключевыми объектами приложения фильма в схеме. Такие сущности, как Movie
, User
, Actor
и Review
, сопоставляются с таблицами базы данных, а отношения устанавливаются с помощью Firebase Data Connect и директив схемы GraphQL. Как только оно будет установлено, ваше приложение будет готово выполнять все задачи: от поиска фильмов с самым высоким рейтингом и фильтрации по жанрам до предоставления пользователям возможности оставлять отзывы, отмечать избранное, просматривать похожие фильмы или находить рекомендованные фильмы на основе ввода текста с помощью векторного поиска.
Основные сущности и отношения
Тип Movie
содержит ключевые данные, такие как название, жанр и теги, которые приложение использует для поиска и профилей фильмов. Тип User
отслеживает действия пользователя, такие как обзоры и избранное. Reviews
знакомят пользователей с фильмами, позволяя приложению отображать оценки и отзывы, созданные пользователями.
Отношения между фильмами, актерами и пользователями делают приложение более динамичным. Таблица соединений MovieActor
помогает отображать сведения об актерах и фильмографии актеров. Тип FavoriteMovie
позволяет пользователям добавлять фильмы в избранное, поэтому приложение может отображать персонализированный список избранного и выделять популярные фильмы.
Таблица фильмов
Тип фильма определяет основную структуру сущности фильма, включая такие поля, как название, жанр, год выпуска и рейтинг.
Скопируйте и вставьте фрагмент кода в файл 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 устанавливает связь «один к одному» с типом 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
, устанавливая связь по внешнему ключу.
Таблица актеров
Скопируйте и вставьте фрагмент кода в файл dataconnect/schema/schema.gql
:
type Actor @table {
id: UUID!
imageUrl: String!
name: String! @col(name: "name", dataType: "varchar(30)")
}
Тип Actor
представляет актера в базе данных фильмов, где каждый актер может быть частью нескольких фильмов, образуя связь «многие ко многим».
Стол КиноАктера
Скопируйте и вставьте фрагмент кода в файл 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"
}
Ключевые выводы:
- фильм: ссылается на тип фильма, неявно генерирует внешний ключ movieId: UUID!.
- актер: ссылается на тип актера, неявно генерирует внешний ключ actorId: UUID!.
- роль: определяет роль актера в фильме (например, «главный» или «второстепенный»).
Таблица пользователей
Тип 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
— это таблица соединений, которая обрабатывает отношения «многие ко многим» между пользователями и их любимыми фильмами или актерами. Каждая таблица связывает 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!
}
Ключевые выводы:
- фильм: ссылается на тип фильма, неявно генерирует внешний ключ movieId: UUID!.
- user: Ссылается на тип пользователя, неявно генерирует внешний ключ userId: UUID!.
Обзорная таблица
Тип обзора представляет сущность обзора и связывает типы «Пользователь» и «Фильм» отношением «многие ко многим» (один пользователь может оставить много обзоров, и каждый фильм может иметь много обзоров).
Скопируйте и вставьте фрагмент кода в файл 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")
}
Ключевые выводы:
- пользователь: Ссылается на пользователя, оставившего отзыв.
- фильм: Ссылается на рецензируемый фильм.
- reviewDate: автоматически устанавливается на время создания обзора с помощью
@default(expr: "request.time")
.
Автоматически сгенерированные поля и значения по умолчанию
Схема использует такие выражения, как @default(expr: "uuidV4()")
для автоматического создания уникальных идентификаторов и временных меток. Например, поле id в типах «Фильм» и «Обзор» автоматически заполняется UUID при создании новой записи.
Теперь, когда схема определена, ваше приложение для просмотра фильмов имеет прочную основу для структуры данных и связей!
4. Получение лучших и последних фильмов
В этом разделе вы вставите данные макета фильма в локальные эмуляторы, затем реализуете соединители (запросы) и код TypeScript для вызова этих соединителей в веб-приложении. В конечном итоге ваше приложение сможет динамически получать и отображать самые популярные и новейшие фильмы прямо из базы данных.
Вставка макета фильма, актера и данных обзора
- В VSCode откройте
dataconnect/moviedata_insert.gql
. Убедитесь, что эмуляторы в расширении Firebase Data Connect работают. - В верхней части файла вы должны увидеть кнопку «Выполнить» (локальную) . Нажмите эту кнопку, чтобы вставить данные макета фильма в вашу базу данных.
- Проверьте терминал Data Connect Execution, чтобы убедиться, что данные были успешно добавлены.
Реализация соединителя
- Откройте
dataconnect/movie-connector/queries.gql
. В комментариях вы найдете базовый запросListMovies
:
query ListMovies @auth(level: PUBLIC) {
movies {
id
title
imageUrl
releaseYear
genre
rating
tags
description
}
}
Этот запрос извлекает все фильмы и их сведения (например, идентификатор, название, год выпуска). Однако он не сортирует фильмы.
- Замените запрос
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
}
}
Нажмите кнопку «Выполнить (локально)» , чтобы выполнить запрос к вашей локальной базе данных. Вы также можете ввести переменные запроса на панели конфигурации перед запуском.
Ключевые выводы:
- Movies(): поле запроса GraphQL для получения данных о фильме из базы данных.
- orderByRating: параметр для сортировки фильмов по рейтингу (по возрастанию/убыванию).
- orderByReleaseYear: параметр для сортировки фильмов по году выпуска (по возрастанию/убыванию).
- предел: Ограничивает количество возвращаемых фильмов.
Интеграция запросов в веб-приложение
В этой части вы будете использовать запросы, определенные в предыдущем разделе, в своем веб-приложении. Эмуляторы Firebase Data Connect генерируют SDK на основе информации в файлах .gql (schema.gql, query.gql,mutations.gql) и Connector.yaml. Эти SDK можно напрямую вызывать в вашем приложении.
- В
MovieService
(app/src/lib/MovieService.tsx
) раскомментируйте оператор импорта вверху:
import { listMovies, ListMoviesData, OrderDirection } from "@movie/dataconnect";
Функция listMovies
, тип ответа ListMoviesData
и перечисление OrderDirection
— это все SDK, созданные эмуляторами Firebase Data Connect на основе схемы и ранее определенных вами запросов.
- Замените функции
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. Отображение сведений о фильме и актере
В этом разделе вы реализуете функцию получения подробной информации о фильме или актере, используя их уникальные идентификаторы. Это предполагает не только получение данных из соответствующих таблиц, но и объединение связанных таблиц для отображения подробной информации, такой как обзоры фильмов и фильмографии актеров.
Реализация соединителей
- Откройте
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
получает доступ к типу Actor через таблицу соединений MovieActor, аwhere
фильтрует актеров на основе их роли (например, «главный» или «вспомогательный»).
На панели выполнения Data Connect вы можете протестировать запрос, введя фиктивные идентификаторы, например:
{"id": "550e8400-e29b-41d4-a716-446655440000"}
Нажмите «Выполнить» (локально) для GetMovieById
чтобы получить подробную информацию о «Квантовом парадоксе» (фиктивном фильме, к которому относится указанный выше идентификатор).
Интеграция запросов в веб-приложение
- В
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, обеспечивая безопасное управление пользователями в вашем приложении.
Реализация соединителей
- Откройтеmutations.gql в dataconnect/
mutations.gql
dataconnect/movie-connector/
. - Добавьте следующую мутацию, чтобы создать или обновить текущего аутентифицированного пользователя:
# 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, а не пользователем или приложением, что добавляет дополнительный уровень безопасности, гарантируя, что идентификатор пользователя обрабатывается безопасно и автоматически.
Затем откройте queries.gql
в dataconnect/movie-connector/
.
Добавьте следующий запрос для получения текущего пользователя:
# 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
: извлекает все отзывы, относящиеся к пользователю, включая идентификатор и название фильма. -
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 Auth. После входа нажмите «Мой профиль». На данный момент оно будет пустым, но вы заложили основу для обработки пользовательских данных в своем приложении.
7. Реализация взаимодействия с пользователем
В этом разделе вы реализуете взаимодействие с пользователем в приложении для просмотра фильмов, что позволит пользователям управлять своими любимыми фильмами, а также оставлять или удалять отзывы.
Реализация соединителей
- Откройтеmutations.gql в dataconnect/
mutations.gql
dataconnect/movie-connector/
. - Добавьте следующие мутации для обработки избранных фильмов:
# 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, гарантируя доступ или изменение только данных аутентифицированного пользователя.
- Затем откройте
queries.gql
вdataconnect/movie-connector/
. - Добавьте следующий запрос, чтобы проверить, добавлен ли фильм в избранное:
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. Расширенные фильтры и частичное сопоставление текста
В этом разделе вы реализуете расширенные возможности поиска, позволяющие пользователям искать фильмы по ряду рейтингов и лет выпуска, фильтровать по жанрам и тегам, выполнять частичное сопоставление текста в заголовках или описаниях и даже комбинировать несколько фильтров для более точного просмотра. результаты.
Реализация соединителей
- Откройте
queries.gql
вdataconnect/movie-connector/
. - Добавьте следующий запрос для поддержки различных возможностей поиска:
# 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.
Добавление веб-приложения в консоль Firebase
- Создайте веб-приложение в консоли Firebase и запишите свой идентификатор приложения.
- Настройте веб-приложение в консоли Firebase, нажав «Добавить приложение». На данный момент вы можете спокойно игнорировать установку SDK и настройку конфигурации, но обратите внимание на сгенерированный объект
firebaseConfig
. - Замените
firebaseConfig
вapp/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" };
- Создайте веб-приложение. В папке
app
используйте Vite для создания веб-приложения для развертывания хостинга:
cd app npm run build
Настройте аутентификацию Firebase в консоли
- Настройте Firebase Auth с помощью входа в Google
- (Необязательно) Разрешите домены для (Firebase Auth) [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
убедитесь, что идентификатор вашего экземпляра, базы данных и идентификатор службы соответствуют вашему проекту:
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 для PostgreSQL будет обновлен с учетом окончательной развернутой схемы и данных. Вы можете отслеживать статус в консоли Firebase.
Теперь вы сможете увидеть свое приложение вживую на your-project.web.app/
. Кроме того, вы можете нажать «Выполнить (производство)» на панели подключения к данным Firebase, как и в случае с локальными эмуляторами, чтобы добавить данные в производственную среду.
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
- Следуйте руководству по предварительным требованиям , чтобы настроить API-интерфейсы Vertex AI с помощью Google Cloud. Этот шаг необходим для поддержки функций генерации встраивания и векторного поиска.
- Повторно разверните свою схему , чтобы активировать поиск
pgvector
и векторов, нажав «Развернуть в производство» с помощью расширения Firebase Data Connect VSCode.
Заполнение базы данных внедрениями
- Откройте папку
dataconnect
в VSCode и нажмите «Выполнить (локально)» вoptional_vector_embed.gql
чтобы заполнить базу данных вставками для фильмов.
Добавьте векторный поисковый запрос
- В
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. Заключение
Поздравляем, вы сможете использовать веб-приложение! Если вы хотите работать с собственными данными фильма, не волнуйтесь, вставьте свои собственные данные, используя расширение FDC, имитируя файлы _insert.gql, или добавьте их через панель «Выполнение подключения к данным».