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 с оплатой по мере использования.
2. Настройте среду разработки
На этом этапе работы с кодом вы настроите среду, чтобы приступить к созданию приложения для просмотра фильмов с помощью Firebase Data Connect.
- Клонируйте репозиторий проекта и установите необходимые зависимости:
git clone https://github.com/firebaseextended/codelab-dataconnect-web cd codelab-dataconnect-web cd ./app && npm i npm run dev
- После выполнения этих команд откройте http://localhost:5173 в своем браузере, чтобы увидеть, как веб-приложение работает локально. Это служит интерфейсом для создания приложения для обзора фильмов и взаимодействия с его функциями.
- Откройте клонированную папку
codelab-dataconnect-web
с помощью Visual Studio Code . Здесь вы определите свою схему, напишете запросы и протестируете функциональность приложения. - Чтобы использовать функции Data Connect, установите расширение Visual Studio Firebase Data Connect .
Кроме того, вы можете установить расширение из Visual Studio Code Marketplace или найти его в VS Code. - Откройте или создайте новый проект Firebase в консоли Firebase .
- Подключите свой проект Firebase к расширению Firebase Data Connect VSCode. В расширении сделайте следующее:
- Нажмите кнопку «Войти» .
- Нажмите «Подключить проект Firebase» и выберите свой проект Firebase.
- Запустите эмуляторы Firebase, используя расширение Firebase Data Connect VS Code:
Нажмите «Запустить эмуляторы» и подтвердите, что эмуляторы работают в терминале.
3. Просмотрите начальную кодовую базу
В этом разделе вы изучите ключевые области стартовой кодовой базы приложения. Хотя в приложении отсутствуют некоторые функции, полезно понять общую структуру.
Структура папок и файлов
В следующих подразделах представлен обзор структуры папок и файлов приложения.
Каталог dataconnect/
Содержит конфигурации Firebase Data Connect, соединители (которые определяют запросы и мутации) и файлы схемы.
-
schema/schema.gql
: определяет схему GraphQL. -
connector/queries.gql
: запросы, необходимые в вашем приложении. -
connector/mutations.gql
: мутации, необходимые в вашем приложении. -
connector/connector.yaml
: файл конфигурации для генерации SDK.
Каталог app/src/
Содержит логику приложения и взаимодействие с Firebase Data Connect.
-
firebase.ts
: конфигурация для подключения к приложению Firebase в вашем проекте Firebase. -
lib/dataconnect-sdk/
: содержит сгенерированный SDK. Вы можете изменить местоположение создания SDK в файлеconnector/connector.yaml
, и SDK будут создаваться автоматически каждый раз, когда вы определяете запрос или мутацию.
4. Определите схему обзоров фильмов
В этом разделе вы определите структуру и отношения между ключевыми объектами приложения фильма в схеме. Такие сущности, как Movie
, User
, Actor
и Review
сопоставляются с таблицами базы данных, а отношения устанавливаются с помощью Firebase Data Connect и директив схемы GraphQL. Как только оно будет установлено, ваше приложение будет готово выполнять все задачи: от поиска фильмов с самым высоким рейтингом и фильтрации по жанрам до предоставления пользователям возможности оставлять отзывы, отмечать избранное, просматривать похожие фильмы или находить рекомендованные фильмы на основе ввода текста с помощью векторного поиска.
Основные сущности и отношения
Тип Movie
содержит ключевые данные, такие как название, жанр и теги, которые приложение использует для поиска и профилей фильмов. Тип User
отслеживает действия пользователя, такие как обзоры и избранное. Reviews
знакомят пользователей с фильмами, позволяя приложению отображать оценки и отзывы, созданные пользователями.
Отношения между фильмами, актерами и пользователями делают приложение более динамичным. Таблица соединений MovieActor
помогает отображать сведения об актерах и фильмографии актеров. Тип FavoriteMovie
позволяет пользователям добавлять фильмы в избранное, поэтому приложение может отображать персонализированный список избранного и выделять популярные фильмы.
Настройте таблицу Movie
Тип Movie
определяет основную структуру сущности фильма, включая такие поля, как title
, genre
, releaseYear
и rating
.
Скопируйте и вставьте фрагмент кода в файл dataconnect/schema/schema.gql
:
type Movie
@table {
id: UUID! @default(expr: "uuidV4()")
title: String!
imageUrl: String!
releaseYear: Int
genre: String
rating: Float
description: String
tags: [String]
}
Ключевые выводы:
- id: уникальный UUID для каждого фильма, созданный с помощью
@default(expr: "uuidV4()")
.
Настройте таблицу MovieMetadata
Тип MovieMetadata
устанавливает связь «один к одному» с типом Movie
. Он включает в себя дополнительные данные, такие как режиссер фильма.
Скопируйте и вставьте фрагмент кода в файл dataconnect/schema/schema.gql
:
type MovieMetadata
@table {
# @ref creates a field in the current table (MovieMetadata)
# It is a reference that holds the primary key of the referenced type
# In this case, @ref(fields: "movieId", references: "id") is implied
movie: Movie! @ref
# movieId: UUID <- this is created by the above @ref
director: String
}
Ключевые выводы:
- Фильм! @ref: ссылается на тип
Movie
, устанавливая связь по внешнему ключу.
Настройте таблицу Actor
Скопируйте и вставьте фрагмент кода в файл dataconnect/schema/schema.gql
:
type Actor @table {
id: UUID!
imageUrl: String!
name: String! @col(name: "name", dataType: "varchar(30)")
}
Тип Actor
представляет актера в базе данных фильмов, где каждый актер может быть частью нескольких фильмов, образуя связь «многие ко многим».
Настройте таблицу MovieActor
Скопируйте и вставьте фрагмент кода в файл dataconnect/schema/schema.gql
:
type MovieActor @table(key: ["movie", "actor"]) {
# @ref creates a field in the current table (MovieActor) that holds the primary key of the referenced type
# In this case, @ref(fields: "id") is implied
movie: Movie!
# movieId: UUID! <- this is created by the implied @ref, see: implicit.gql
actor: Actor!
# actorId: UUID! <- this is created by the implied @ref, see: implicit.gql
role: String! # "main" or "supporting"
}
Ключевые выводы:
- фильм: ссылается на тип фильма, неявно генерирует внешний ключ movieId: UUID!.
- актер: ссылается на тип актера, неявно генерирует внешний ключ actorId: UUID!.
- роль: определяет роль актера в фильме (например, «главный» или «второстепенный»).
Настройте таблицу User
Тип User
определяет сущность пользователя, которая взаимодействует с фильмами, оставляя отзывы или добавляя фильмы в избранное.
Скопируйте и вставьте фрагмент кода в файл dataconnect/schema/schema.gql
:
type User
@table {
id: String! @col(name: "auth_uid")
username: String! @col(dataType: "varchar(50)")
# The following are generated from the @ref in the Review table
# reviews_on_user
# movies_via_Review
}
Настройте таблицу FavoriteMovie
Тип FavoriteMovie
— это таблица соединений, которая обрабатывает отношения «многие ко многим» между пользователями и их любимыми фильмами. Каждая таблица связывает User
с Movie
.
Скопируйте и вставьте фрагмент кода в файл dataconnect/schema/schema.gql
:
type FavoriteMovie
@table(name: "FavoriteMovies", singular: "favorite_movie", plural: "favorite_movies", key: ["user", "movie"]) {
# @ref is implicit
user: User!
movie: Movie!
}
Ключевые выводы:
- фильм: Ссылается на тип фильма, неявно генерирует внешний ключ.
movieId: UUID!
. - user: Ссылается на тип пользователя, неявно генерирует внешний ключ.
userId: UUID!
.
Настройте таблицу Review
Тип Review
представляет сущность обзора и связывает типы User
и Movie
отношением «многие ко многим» (один пользователь может оставить много обзоров, и каждый фильм может иметь много обзоров).
Скопируйте и вставьте фрагмент кода в файл dataconnect/schema/schema.gql
:
type Review @table(name: "Reviews", key: ["movie", "user"]) {
id: UUID! @default(expr: "uuidV4()")
user: User!
movie: Movie!
rating: Int
reviewText: String
reviewDate: Date! @default(expr: "request.time")
}
Ключевые выводы:
- пользователь: Ссылается на пользователя, оставившего отзыв.
- фильм: Ссылается на рецензируемый фильм.
- reviewDate: автоматически устанавливается на время создания обзора с помощью
@default(expr: "request.time")
.
Автоматически сгенерированные поля и значения по умолчанию
Схема использует такие выражения, как @default(expr: "uuidV4()")
для автоматического создания уникальных идентификаторов и временных меток. Например, поле id
в типах Movie
и Review
автоматически заполняется UUID при создании новой записи.
Теперь, когда схема определена, ваше приложение для просмотра фильмов имеет прочную основу для структуры данных и связей!
5. Получить лучшие и последние фильмы
В этом разделе вы вставите данные макета фильма в локальные эмуляторы, затем реализуете соединители (запросы) и код 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 } }
id
,title
,releaseYear
). Однако он не сортирует фильмы. - Замените существующий запрос
ListMovies
следующим запросом, чтобы добавить параметры сортировки и ограничения:# List subset of fields for movies query ListMovies($orderByRating: OrderDirection, $orderByReleaseYear: OrderDirection, $limit: Int) @auth(level: PUBLIC) { movies( orderBy: [ { rating: $orderByRating }, { releaseYear: $orderByReleaseYear } ] limit: $limit ) { id title imageUrl releaseYear genre rating tags description } }
- Нажмите кнопку «Выполнить (локально)» , чтобы выполнить запрос к вашей локальной базе данных. Вы также можете ввести переменные запроса на панели конфигурации перед запуском.
Ключевые выводы:
-
movies()
: поле запроса GraphQL для получения данных о фильме из базы данных. -
orderByRating
: параметр для сортировки фильмов по рейтингу (по возрастанию/убыванию). -
orderByReleaseYear
: параметр для сортировки фильмов по году выпуска (по возрастанию/убыванию). -
limit
: Ограничивает количество возвращаемых фильмов.
Интегрируйте запросы в веб-приложение
В этой части лаборатории кода вы будете использовать в своем веб-приложении запросы, определенные в предыдущем разделе. Эмуляторы Firebase Data Connect генерируют SDK на основе информации в файлах .gql
(в частности, schema.gql
, queries.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 лучших и последних фильмов на главной странице приложения.
Посмотрите это в действии
Перезагрузите веб-приложение, чтобы увидеть запрос в действии. На домашней странице теперь динамически отображается список фильмов, получая данные непосредственно из вашей локальной базы данных. Вы увидите, что фильмы с самым высоким рейтингом и новейшие фильмы отображаются плавно, отражая только что настроенные данные.
6. Отображение информации о фильме и актере
В этом разделе вы реализуете функцию получения подробной информации о фильме или актере, используя их уникальные идентификаторы. Это предполагает не только получение данных из соответствующих таблиц, но и объединение связанных таблиц для отображения подробной информации, такой как обзоры фильмов и фильмографии актеров.
Реализуйте соединители
- Откройте
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
: это типы результатов, используемые для отображения сведений о фильме и актере в приложении.
Посмотрите это в действии
Теперь перейдите на домашнюю страницу вашего веб-приложения. Нажмите на фильм, и вы сможете просмотреть все его детали, включая актеров и рецензии — информацию, полученную из связанных таблиц. Аналогично, если щелкнуть актера, отобразятся фильмы, в которых он участвовал.
7. Обработка аутентификации пользователей
В этом разделе вы реализуете функции входа и выхода пользователей с помощью аутентификации 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
: извлекает все отзывы, относящиеся к пользователю, включаяid
иtitle
фильма. -
favorite_movies_on_user
: извлекает все фильмы, отмеченные пользователем как избранные, включая подробную информацию, такую какgenre
,releaseYear
,rating
иmetadata
.
-
Интегрируйте запросы в веб-приложение
- В
MovieService
(app/src/lib/MovieService.tsx
) раскомментируйте следующий импорт:import { upsertUser } from "@movie/dataconnect"; import { getCurrentUser, GetCurrentUserData } from "@movie/dataconnect";
- Замените функции
handleAuthStateChange
иhandleGetCurrentUser
следующим кодом:// Handle user authentication state changes and upsert user export const handleAuthStateChange = ( auth: any, setUser: (user: User | null) => void ) => { return onAuthStateChanged(auth, async (user) => { if (user) { setUser(user); const username = user.email?.split("@")[0] || "anon"; await upsertUser({ username }); } else { setUser(null); } }); }; // Fetch current user profile export const handleGetCurrentUser = async (): Promise< GetCurrentUserData["user"] | null > => { try { const response = await getCurrentUser(); return response.data.user; } catch (error) { console.error("Error fetching user profile:", error); return null; } };
Ключевые выводы:
-
handleAuthStateChange
: эта функция прослушивает изменения состояния аутентификации. Когда пользователь входит в систему, он устанавливает данные пользователя и вызывает мутациюupsertUser
для создания или обновления информации о пользователе в базе данных. -
handleGetCurrentUser
: извлекает профиль текущего пользователя с помощью запросаgetCurrentUser
, который извлекает обзоры пользователя и любимые фильмы.
Посмотрите это в действии
Теперь нажмите кнопку «Войти через Google» на панели навигации. Вы можете войти в систему с помощью эмулятора аутентификации Firebase. После входа в систему нажмите «Мой профиль». На данный момент оно будет пустым, но вы заложили основу для обработки пользовательских данных в своем приложении.
8. Реализуйте взаимодействие с пользователем
В этом разделе лаборатории кода вы реализуете взаимодействие с пользователем в приложении для просмотра фильмов, в частности позволяя пользователям управлять своими любимыми фильмами и оставлять или удалять отзывы.
Позвольте пользователю добавить фильм в избранное
В этом разделе вы настроите базу данных, чтобы пользователи могли добавлять фильмы в избранное.
Реализуйте соединители
- Откройте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
для удаления рецензии на фильм, сделанной авторизованным пользователем.
Посмотрите это в действии
Пользователи теперь могут оставлять отзывы о фильмах на странице сведений о фильме. Они также могут просматривать и удалять свои отзывы на странице своего профиля, что дает им полный контроль над взаимодействием с приложением.
9. Расширенные фильтры и частичное сопоставление текста
В этом разделе вы реализуете расширенные возможности поиска, позволяющие пользователям искать фильмы по ряду рейтингов и лет выпуска, фильтровать по жанрам и тегам, выполнять частичное сопоставление текста в заголовках или описаниях и даже комбинировать несколько фильтров для более точного просмотра. результаты.
Реализуйте соединители
- Откройте
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
для выполнения поиска на основе введенных пользователем данных, фильтруя результаты по таким параметрам, как год, рейтинг, жанр и частичное совпадение текста.
Посмотрите это в действии
Перейдите на страницу «Расширенный поиск» с панели навигации веб-приложения. Теперь вы можете искать фильмы, актеров и обзоры, используя различные фильтры и входные данные, получая подробные и адаптированные результаты поиска.
10. Необязательно: развертывание в облаке (требуется оплата).
Теперь, когда вы прошли итерацию локальной разработки, пришло время развернуть вашу схему, данные и запросы на сервере. Это можно сделать с помощью расширения Firebase Data Connect VS Code или интерфейса командной строки Firebase.
Обновите тарифный план Firebase
Чтобы интегрировать Firebase Data Connect с Cloud SQL для PostgreSQL, ваш проект Firebase должен находиться на тарифном плане с оплатой по мере использования (Blaze) , что означает, что он связан с платежным аккаунтом Cloud .
- Для учетной записи Cloud Billing требуется способ оплаты, например кредитная карта.
- Если вы новичок в Firebase и Google Cloud, проверьте, имеете ли вы право на получение кредита в размере 300 долларов США и бесплатную пробную платежную учетную запись Cloud .
- Если вы выполняете эту лабораторную работу в рамках мероприятия, спросите у организатора, есть ли у него какие-либо облачные кредиты.
Чтобы обновить проект до плана Blaze, выполните следующие действия:
- В консоли Firebase выберите обновление плана .
- Выберите план Blaze. Следуйте инструкциям на экране, чтобы связать учетную запись Cloud Billing с вашим проектом.
Если вам нужно было создать учетную запись Cloud Billing в рамках этого обновления, вам может потребоваться вернуться к процессу обновления в консоли Firebase, чтобы завершить обновление.
Подключите свое веб-приложение к проекту Firebase
- Зарегистрируйте свое веб-приложение в проекте Firebase с помощью консоли Firebase :
- Откройте проект и нажмите «Добавить приложение» .
- На данный момент игнорируйте установку SDK и настройку конфигурации, но обязательно скопируйте сгенерированный объект
firebaseConfig
.
- Замените существующую
firebaseConfig
вapp/src/lib/firebase.tsx
конфигурацией, которую вы только что скопировали из консоли Firebase.const firebaseConfig = { apiKey: "API_KEY", authDomain: "PROJECT_ID.firebaseapp.com", projectId: "PROJECT_ID", storageBucket: "PROJECT_ID.firebasestorage.app", messagingSenderId: "SENDER_ID", appId: "APP_ID" };
- Создайте веб-приложение. Вернитесь в VS Code и в папке
app
используйте Vite для создания веб-приложения для развертывания на хостинге:cd app npm run build
Настройте аутентификацию Firebase в своем проекте Firebase
- Настройте аутентификацию Firebase с помощью входа в Google.
- (Необязательно) Разрешите доменам аутентификацию Firebase с помощью консоли Firebase (например,
http://127.0.0.1
).- В настройках аутентификации перейдите в раздел Авторизованные домены .
- Нажмите «Добавить домен» и добавьте свой локальный домен в список.
Развертывание с помощью 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, как и в случае с локальными эмуляторами, чтобы добавить данные в производственную среду.
11. Необязательно: векторный поиск с помощью Firebase Data Connect (требуется оплата).
В этом разделе вы включите векторный поиск в своем приложении для просмотра фильмов с помощью Firebase Data Connect. Эта функция позволяет осуществлять поиск по контенту, например, находить фильмы со схожим описанием с использованием векторных вложений.
Для этого шага необходимо, чтобы вы выполнили последний шаг этой лаборатории кода для развертывания в Google Cloud.
Обновите схему, чтобы включить встраивания для поля.
В dataconnect/schema/schema.gql
добавьте descriptionEmbedding
в таблицу Movie
:
type Movie
# The below parameter values are generated by default with @table, and can be edited manually.
@table {
# implicitly calls @col to generates a column name. ex: @col(name: "movie_id")
id: UUID! @default(expr: "uuidV4()")
title: String!
imageUrl: String!
releaseYear: Int
genre: String
rating: Float
description: String
tags: [String]
descriptionEmbedding: Vector @col(size:768) # Enables vector search
}
Ключевые выводы:
-
descriptionEmbedding: Vector @col(size:768)
: в этом поле хранятся семантические внедрения описаний фильмов, что позволяет осуществлять векторный поиск контента в вашем приложении.
Активировать Vertex AI
- Следуйте руководству по предварительным требованиям , чтобы настроить API-интерфейсы Vertex AI из Google Cloud. Этот шаг необходим для поддержки функций генерации встраивания и векторного поиска.
- Повторно разверните свою схему , чтобы активировать поиск
pgvector
и векторов, нажав «Развернуть в производство» с помощью расширения Firebase Data Connect VS Code.
Заполните базу данных встраиваниями
- Откройте папку
dataconnect
в VS Code. - Нажмите «Выполнить (локальный)» в файле
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
раскомментируйте следующие импортированные данные из SDK, это будет работать как любой другой запрос.import { searchMovieDescriptionUsingL2similarity, SearchMovieDescriptionUsingL2similarityData, } from "@movie/dataconnect";
- Добавьте следующую функцию для интеграции векторного поиска в приложение:
// Perform vector-based search for movies based on description export const searchMoviesByDescription = async ( query: string ): Promise< | SearchMovieDescriptionUsingL2similarityData["movies_descriptionEmbedding_similarity"] | null > => { try { const response = await searchMovieDescriptionUsingL2similarity({ query }); return response.data.movies_descriptionEmbedding_similarity; } catch (error) { console.error("Error fetching movie descriptions:", error); return null; } };
Ключевые выводы:
-
searchMoviesByDescription
: эта функция вызывает запросsearchMovieDescriptionUsingL2similarity
, передавая входной текст для выполнения векторного поиска контента.
Посмотрите это в действии
Перейдите в раздел «Векторный поиск» на панели навигации и введите такие фразы, как «романтичный и современный». Вы увидите список фильмов, соответствующих контенту, который вы ищете, или зайдите на страницу сведений о любом фильме и просмотрите раздел похожих фильмов внизу страницы.
12. Заключение
Поздравляем, вы сможете использовать веб-приложение! Если вы хотите поиграть со своими собственными данными фильма, не волнуйтесь, вставьте свои собственные данные с помощью расширения Firebase Data Connect, имитируя файлы _insert.gql
, или добавьте их через панель выполнения Data Connect в VS Code.