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/movie-connector/
mutations.gql
- Добавьте следующую мутацию, чтобы создать или обновить текущего аутентифицированного пользователя:
# Create or update the current authenticated user mutation UpsertUser($username: String!) @auth(level: USER) { user_upsert( data: { id_expr: "auth.uid" username: $username } ) }
Ключевые выводы:
-
id_expr: "auth.uid"
: используетсяauth.uid
, который предоставляется непосредственно аутентификацией Firebase, а не пользователем или приложением, что добавляет дополнительный уровень безопасности, гарантируя, что идентификатор пользователя обрабатывается безопасно и автоматически.
Получить текущего пользователя
- Откройте
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/movie-connector/
mutations.gql
- Добавьте следующие мутации для обработки избранных фильмов:
# Add a movie to the user's favorites list mutation AddFavoritedMovie($movieId: UUID!) @auth(level: USER) { favorite_movie_upsert(data: { userId_expr: "auth.uid", movieId: $movieId }) } # Remove a movie from the user's favorites list mutation DeleteFavoritedMovie($movieId: UUID!) @auth(level: USER) { favorite_movie_delete(key: { userId_expr: "auth.uid", movieId: $movieId }) }
Ключевые выводы:
-
userId_expr: "auth.uid"
: используетauth.uid
, который предоставляется непосредственно системой аутентификации Firebase, гарантируя доступ или изменение только данных аутентифицированного пользователя.
Проверьте, есть ли фильм в избранном
- Откройте
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.
Узнать больше
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!.
- Актер: Ссылка на тип актера, неявно генерирует акторид иностранного ключа: uuid!.
- Роль: определяет роль актера в фильме (например, «Main» или «поддержка»).
Установите 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!
. - Пользователь: ссылается на тип пользователя, неявно генерирует внешний ключ
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. Получить топ и последние фильмы
В этом разделе вы будете вставить данные о фиксации в локальные эмуляторы, а затем реализовать разъемы (запросы) и код типографии для вызова этих разъемов в веб -приложении. К концу, ваше приложение сможет динамически выбрать и отображать высшие и последние фильмы непосредственно из базы данных.
Вставьте Mock Movie, актер и данные обзора
- В vscode откройте
dataconnect/moviedata_insert.gql
. Убедитесь, что эмуляторы в расширении данных Firebase Connect Connect. - Вы должны увидеть кнопку запуска (локальный) в верхней части файла. Нажмите на это, чтобы вставить данные из фиктивных фильмов в свою базу данных.
- Проверьте терминал выполнения Data Connect, чтобы подтвердить, что данные были успешно добавлены.
Реализовать разъем
- Откройте
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
: ограничивает количество возвращенных фильмов.
Интегрировать запросы в веб -приложение
В этой части CodeLab вы будете использовать запросы, определенные в предыдущем разделе в вашем веб -приложении. Данные Firebase Data Connect Emulators генерируют 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
и EnumOrderDirection
- все SDK, сгенерированные данными Firebase Data Connect Emulators на основе схемы и запросов, которые вы ранее определили. - Замените функции
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
условия фильтры, основанные на их роли (например, «Main» или «поддержка»).
Протестируйте запрос, введя фиктивные данные
- На панели выполнения Data Connect вы можете протестировать запрос, введя макет, такие как:
{"id": "550e8400-e29b-41d4-a716-446655440000"}
- Нажмите Run (Local) для
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 для непосредственного извлечения или Upsert пользовательских данных в Dataconnect Firebase, обеспечивая безопасное управление пользователями в вашем приложении.
Реализация разъемов
- Open
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, а не пользователем или приложением, добавляя дополнительный уровень безопасности, обеспечивая безопасное и автоматически идентификатор пользователя.
Принесите текущего пользователя
- Open
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» в Navbar. Вы можете подписаться, используя эмулятор аутентификации Firebase. После входа нажмите «Мой профиль». На данный момент он будет пустым, но вы настроили основу для обработки данных для конкретного пользователя в вашем приложении.
8. Реализовать взаимодействие с пользователями
В этом разделе CodeLab вы будете реализовать взаимодействие с пользователями в приложении для обзора фильмов, в частности, позволяя пользователям управлять своими любимыми фильмами и оставлять или удалять отзывы.
Позвольте пользователю любить фильм
В этом разделе вы настроите базу данных, чтобы позволить пользователям любить фильм.
Реализация разъемов
- Open
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, обеспечивая доступ только данные аутентифицированного пользователя или изменены.
Проверьте, является ли фильм любимым
- Open
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. Усовершенствованные фильтры и частичное сопоставление текста
В этом разделе вы реализуете расширенные возможности поиска, позволяя пользователям поиск фильмов на основе ряда рейтингов и лет выпуска, фильтрации по жанрам и тегам, выполнять частичное сопоставление текста в названиях или описаниях и даже объединить несколько фильтров для более точных результатов.
Реализация разъемов
- Open
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
для выполнения поиска на основе ввода пользователя, фильтрации результатов по таким параметрам, как год, рейтинг, жанр и частичный текст.
Увидеть это в действии
Перейдите на страницу «Advanced Search» из Navbar в веб -приложении. Теперь вы можете искать фильмы, актеров и обзоры, используя различные фильтры и входы, получая подробные и адаптированные результаты поиска.
10. Необязательно: развернуть в облаке (требуется выставление счетов)
Теперь, когда вы работали через локальную итерацию разработки, пришло время развернуть вашу схему, данные и запросы на сервер. Это может быть сделано с помощью Data Firebase Data Connect по сравнению с расширением кода или CLI Firebase.
Обновите план ценообразования пожарной базы
Для интеграции данных Firebase Connect с Cloud SQL для PostgreSQL ваш проект Firebase должен быть в плане ценообразования с оплатой как вы (Blaze) , что означает, что он связан с аккаунтом по облачным выставлению .
- Облачный счет выставления счетов требует метода оплаты, например, кредитной карты.
- Если вы новичок в Firebase и Google Cloud, проверьте, имеете ли вы право на получение кредита в размере 300 долларов США и бесплатный аккаунт по биллингу в области проб .
- Если вы делаете этот CodeLab как часть события, спросите своего организатора, есть ли доступные облачные кредиты.
Чтобы обновить свой проект до плана Blaze, выполните эти шаги:
- В консоли Firebase выберите, чтобы обновить свой план .
- Выберите план пламени. Следуйте инструкциям на экране, чтобы связать учетную запись облачного выставления счетов с вашим проектом.
Если вам нужно было создать облачную биллинговую учетную запись в рамках этого обновления, вам может потребоваться перейти обратно к потоку обновления в консоли 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, в папке
app
, используйте Vite для создания веб -приложения для развертывания хостинга:cd app npm run build
Установите аутентификацию Firebase в вашем проекте Firebase
- Установите аутентификацию Firebase с помощью входа в Google.
- (Необязательно) Разрешить домены для аутентификации Firebase с использованием консоли Firebase (например,
http://127.0.0.1
).- В настройках аутентификации перейдите в авторизованные домены .
- Нажмите «Добавить домен» и включите свой локальный домен в список.
Развернуть с помощью CLI Firebase
- В
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"]
- Убедитесь, что у вас есть CLI 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
Ваш облачный SQL для экземпляра PostgreSQL будет обновлен с помощью окончательной развернутой схемы и данных. Вы можете отслеживать статус в консоли Firebase.
Теперь вы должны увидеть ваше приложение в прямом эфире на your-project.web.app/
. Кроме того, вы можете щелкнуть запуск (производство) на панели подключения данных Firebase, как и с локальными эмуляторами, чтобы добавить данные в производственную среду.
11. Необязательно: векторный поиск с помощью данных Firebase Connect (требуется выставление счетов)
В этом разделе вы включите Vector Search в вашем приложении для обзора фильмов, используя Connect Data Firebase Data. Эта функция обеспечивает поиск на основе контента, такие как поиск фильмов с аналогичными описаниями с использованием векторных встроений.
Этот шаг требует, чтобы вы выполнили последний шаг этого CodeLab для развертывания в 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)
: В этом поле хранятся семантические встроенные встроенные введения описания фильмов, включающие поиск контента на основе вектора в вашем приложении.
Активировать вершину AI
- Следуйте руководству по предварительным условиям , чтобы настроить API Vertex AI из Google Cloud. Этот шаг важен для поддержки встраивания генерации и функциональности поиска вектора.
- Пересмотрите схему для активации
pgvector
и Vector Search, нажав на «развертывание на производство», используя подключение данных Firebase Data VS.
Заполнить базу данных вторжениями
- Откройте папку
dataconnect
в коде VS. - Нажмите Run (Local) в
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.
Реализуйте функцию Vector Search в вашем приложении
Теперь, когда схема и запрос настроены, интегрируйте векторный поиск в уровень обслуживания вашего приложения. Этот шаг позволяет вам позвонить в поисковый запрос из вашего веб -приложения.
- В
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
, передавая входной текст для выполнения поиска контента на основе вектора.
Увидеть это в действии
Перейдите в раздел «Вектор поиска» в Navbar и введите в такие фразы, как «романтический и современный». Вы увидите список фильмов, которые соответствуют контенту, который вы ищете, или перейдите на страницу сведений о фильме любого фильма, и посмотрите раздел «Подобный фильм» в нижней части страницы.
12. Заключение
Поздравляю, вы должны иметь возможность использовать веб -приложение! Если вы хотите играть со своими собственными данными фильма, не беспокойтесь, вставьте свои собственные данные, используя расширение данных Firebase Data Connect, имитируя файлы _insert.gql
, или добавьте их через панель выполнения Data Connect в коде VS.
Узнать больше
1. Прежде чем начать
В этом CodeLab вы интегрируете данные Firebase, соединяющие базу данных SQL, чтобы создать веб -приложение для обзора фильмов. Завершенное приложение демонстрирует, как данные Firebase Connect упрощает процесс создания приложений с синхронизацией SQL. Он включает в себя эти функции:
- Аутентификация: реализуйте пользовательскую аутентификацию для запросов и мутаций вашего приложения, обеспечивая взаимодействие только авторизованные пользователи.
- Схема GraphQL: Создайте и управляйте структурами данных, используя гибкую схему GraphQL, адаптированную для потребностей веб -приложения для обзора фильма.
- Запросы и мутации SQL: извлечь, обновлять и управлять данными в Cloud SQL с использованием запросов и мутаций, работающих на графике.
- Расширенный поиск с частичным соответствием строки: используйте фильтры и параметры поиска, чтобы найти фильмы на основе таких полей, как заголовок, описание или теги.
- (Необязательно) Интеграция векторного поиска: добавьте функциональность поиска контента с использованием векторного поиска Data Connect Connect для обеспечения богатого пользовательского опыта на основе ввода и предпочтений.
Предварительные условия
Вам понадобится базовое понимание JavaScript .
Чему вы узнаете
- Установите данные Firebase Connect с локальными эмуляторами.
- Разработайте схему данных, используя Data Connect и GraphQL .
- Напишите и проверяйте различные запросы и мутации для приложения для обзора фильма.
- Узнайте, как данные Firebase Connect создают и используют SDK в приложении.
- Разверните свою схему и эффективно управляйте базой данных.
Что вам понадобится
- Git
- Visual Studio Code
- Установить node.js с использованием NVM-Windows (Windows) или NVM (macos/linux)
- Если вы еще этого не сделали, создайте проект Firebase в консоли Firebase
- (Необязательно) Для векторного поиска обновите свой проект до плана платного ценообразования оплаты
2. Настройте среду разработки
На этом этапе CodeLab будет направлена на создание среды, чтобы начать создавать приложение для обзора фильма, используя Data 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 . Здесь вы определите свою схему, напишите запросы и проверьте функциональность приложения. - Чтобы использовать функции Data Connect, установите расширение данных Firebase Data Connect Visual Studio .
В качестве альтернативы, вы можете установить расширение с рынка кодов Visual Studio или найти его в коде VS. - Откройте или создайте новый проект Firebase в консоли Firebase .
- Подключите свой проект Firebase к данным Firebase Подключить расширение VSCODE. В расширении сделайте следующее:
- Нажмите кнопку «Вин в» .
- Нажмите подключить проект Firebase и выберите свой проект Firebase.
- Запустите эмуляторы Firebase, используя подключение данных Firebase Data VS Code:
Нажмите «Начать эмуляторы» , а затем подтвердите, что эмуляторы работают в терминале.
3. Просмотрите начальную кодовую базу
В этом разделе вы изучите ключевые области стартовой кодовой базы приложения. В то время как в приложении не хватает некоторой функциональности, полезно понять общую структуру.
Структура папки и файла
Следующие подразделы предоставляют обзор папки и структуры файла приложения.
dataconnect/
Directory
Содержит данные Firebase Data Connect Configurations, разъемы (которые определяют запросы и мутации) и файлы схемы.
-
schema/schema.gql
: определяет схему graphql -
connector/queries.gql
: запросы, необходимые в вашем приложении -
connector/mutations.gql
: мутации, необходимые в вашем приложении -
connector/connector.yaml
: файл конфигурации для генерации SDK
app/src/
каталог
Содержит логику приложения и взаимодействие с Data Firebase 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!.
- Актер: Ссылка на тип актера, неявно генерирует акторид иностранного ключа: uuid!.
- Роль: определяет роль актера в фильме (например, «Main» или «поддержка»).
Установите 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!
. - Пользователь: ссылается на тип пользователя, неявно генерирует внешний ключ
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. Получить топ и последние фильмы
В этом разделе вы будете вставить данные о фиксации в локальные эмуляторы, а затем реализовать разъемы (запросы) и код типографии для вызова этих разъемов в веб -приложении. К концу, ваше приложение сможет динамически выбрать и отображать высшие и последние фильмы непосредственно из базы данных.
Вставьте Mock Movie, актер и данные обзора
- В vscode откройте
dataconnect/moviedata_insert.gql
. Убедитесь, что эмуляторы в расширении данных Firebase Connect Connect. - Вы должны увидеть кнопку запуска (локальный) в верхней части файла. Нажмите на это, чтобы вставить данные из фиктивных фильмов в свою базу данных.
- Проверьте терминал выполнения Data Connect, чтобы подтвердить, что данные были успешно добавлены.
Implement the connector
- Open
dataconnect/movie-connector/queries.gql
. You'll find a basicListMovies
query in the comments: This query fetches all movies and their details (for example,query ListMovies @auth(level: PUBLIC) { movies { id title imageUrl releaseYear genre rating tags description } }
id
,title
,releaseYear
). However, it does not sort the movies. - Replace the existing
ListMovies
query with the following query to add sorting and limit options:# 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 } }
- Click the Run (local) button to execute the query against your local database. You can also input the query variables in the configuration pane before running.
Ключевые выводы:
-
movies()
: GraphQL query field for fetching movie data from the database. -
orderByRating
: Parameter to sort movies by rating (ascending/descending). -
orderByReleaseYear
: Parameter to sort movies by release year (ascending/descending). -
limit
: Restricts the number of movies returned.
Integrate queries in the web app
In this part of the codelab, you'll use the queries defined in the previous section in your web app. The Firebase Data Connect emulators generate SDKs based on the information in the .gql
files (specifically, schema.gql
, queries.gql
, mutations.gql
) and the connector.yaml
file. These SDKs can be directly called in your application.
- In
MovieService
(app/src/lib/MovieService.tsx
), uncomment the import statement at the top: The functionimport { listMovies, ListMoviesData, OrderDirection } from "@movie/dataconnect";
listMovies
, the response typeListMoviesData
, and the enumOrderDirection
are all SDKs generated by the Firebase Data Connect emulators based on the schema and the queries you've previously defined . - Replace the
handleGetTopMovies
andhandleGetLatestMovies
functions with the following code:// 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
: An auto-generated function that calls thelistMovies
query to retrieve a list of movies. It includes options for sorting by rating or release year and limiting the number of results. -
ListMoviesData
: The result type used to display the top 10 and latest movies on the app's homepage.
See it in action
Reload your web app to see the query in action. The homepage now dynamically displays the list of movies, fetching data directly from your local database. You'll see the top-rated and latest movies appear seamlessly, reflecting the data you've just set up.
6. Display movie and actor details
In this section, you'll implement the functionality to retrieve detailed information for a movie or an actor using their unique IDs. This involves not only fetching data from their respective tables but also joining related tables to display comprehensive details, such as movie reviews and actor filmographies.
Implement connectors
- Open
dataconnect/movie-connector/queries.gql
in your project. - Add the following queries to retrieve movie and actor details:
# 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 } } }
- Save your changes and review the queries.
Ключевые выводы:
-
movie()
/actor()
: GraphQL query fields for fetching a single movie or actor from theMovies
orActors
table. -
_on_
: This allows direct access to fields from an associated type that has a foreign key relationship. For example,reviews_on_movie
fetches all reviews related to a specific movie. -
_via_
: Used to navigate many-to-many relationships through a join table. For instance,actors_via_MovieActor
accesses theActor
type through theMovieActor
join table, and thewhere
condition filters actors based on their role (for example, "main" or "supporting").
Test the query by inputting mock data
- In the Data Connect execution pane, you can test the query by inputting mock IDs, such as:
{"id": "550e8400-e29b-41d4-a716-446655440000"}
- Click Run (local) for
GetMovieById
to retrieve the details about "Quantum Paradox" (the mock movie that the above ID relates to).
Integrate queries in the web app
- In
MovieService
(app/src/lib/MovieService.tsx
), uncomment the following imports:import { getMovieById, GetMovieByIdData } from "@movie/dataconnect"; import { GetActorByIdData, getActorById } from "@movie/dataconnect";
- Replace the
handleGetMovieById
andhandleGetActorById
functions with the following code:// 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
: These are auto-generated functions that call the queries you defined, retrieving detailed information for a specific movie or actor. -
GetMovieByIdData
/GetActorByIdData
: These are the result types, used to display movie and actor details in the app.
See it in action
Now, go to your web app's homepage. Click on a movie, and you'll be able to view all its details, including actors and reviews—information pulled from related tables. Similarly, clicking on an actor will display the movies they were part of.
7. Handle user authentication
In this section, you'll implement user sign-in and sign-out functionality using Firebase Authentication. You'll also use Firebase Authentication data to directly retrieve or upsert user data in Firebase DataConnect, ensuring secure user management within your app.
Implement connectors
- Open
mutations.gql
indataconnect/movie-connector/
. - Add the following mutation to create or update the current authenticated user:
# 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"
: This usesauth.uid
, which is provided directly by Firebase Authentication, not by the user or the app, adding an extra layer of security by ensuring the user ID is handled securely and automatically.
Fetch the current user
- Open
queries.gql
indataconnect/movie-connector/
. - Add the following query to fetch the current user:
# 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
: This is retrieved directly from Firebase Authentication, ensuring secure access to user-specific data. -
_on_
fields: These fields represent the join tables:-
reviews_on_user
: Fetches all reviews related to the user, including the movie'sid
andtitle
. -
favorite_movies_on_user
: Retrieves all movies marked as favorites by the user, including detailed information likegenre
,releaseYear
,rating
, andmetadata
.
-
Integrate queries in the web app
- In
MovieService
(app/src/lib/MovieService.tsx
), uncomment the following imports:import { upsertUser } from "@movie/dataconnect"; import { getCurrentUser, GetCurrentUserData } from "@movie/dataconnect";
- Replace the
handleAuthStateChange
andhandleGetCurrentUser
functions with the following code:// 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
: This function listens for authentication state changes. When a user signs in, it sets the user's data and calls theupsertUser
mutation to create or update the user's information in the database. -
handleGetCurrentUser
: Fetches the current user's profile using thegetCurrentUser
query, which retrieves the user's reviews and favorite movies.
See it in action
Now, click on the "Sign in with Google" button in the navbar. You can sign in using the Firebase Authentication emulator. After signing in, click on "My Profile". It will be empty for now, but you've set up the foundation for user-specific data handling in your app.
8. Implement user interactions
In this section of the codelab, you'll implement user interactions in the movie review app, specifically letting users manage their favorite movies and leave or delete reviews.
Let a user favorite a movie
In this section, you'll set up the database to let users favorite a movie.
Implement connectors
- Open
mutations.gql
indataconnect/movie-connector/
. - Add the following mutations to handle favoriting movies:
# 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"
: Usesauth.uid
, which is provided directly by Firebase Authentication, ensuring that only the authenticated user's data is accessed or modified.
Check if a movie is favorited
- Open
queries.gql
indataconnect/movie-connector/
. - Add the following query to check if a movie is favorited:
query GetIfFavoritedMovie($movieId: UUID!) @auth(level: USER) { favorite_movie(key: { userId_expr: "auth.uid", movieId: $movieId }) { movieId } }
Ключевые выводы:
-
auth.uid
: Ensures secure access to user-specific data using Firebase Authentication. -
favorite_movie
: Checks thefavorite_movies
join table to see if a specific movie is marked as a favorite by the current user.
Integrate queries in the web app
- In
MovieService
(app/src/lib/MovieService.tsx
), uncomment the following imports:import { addFavoritedMovie, deleteFavoritedMovie, getIfFavoritedMovie } from "@movie/dataconnect";
- Replace the
handleAddFavoritedMovie
,handleDeleteFavoritedMovie
, andhandleGetIfFavoritedMovie
functions with the following code:// 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
andhandleDeleteFavoritedMovie
: Use the mutations to add or remove a movie from the user's favorites securely. -
handleGetIfFavoritedMovie
: Uses thegetIfFavoritedMovie
query to check if a movie is marked as a favorite by the user.
See it in action
Now, you can favorite or unfavorite movies by clicking the heart icon on the movie cards and the movie details page. Additionally, you can view your favorite movies on your profile page.
Let users leave or delete reviews
Next, you'll implement the section for managing user reviews in the app.
Implement connectors
In mutations.gql
( dataconnect/movie-connector/mutations.gql
): Add the following mutations:
# 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"
: Ensures that reviews are associated with the authenticated user. -
reviewDate_date: { today: true }
: Automatically generates the current date for the review using DataConnect, eliminating the need for manual input.
Integrate queries in the web app
- In
MovieService
(app/src/lib/MovieService.tsx
), uncomment the following imports:import { addReview, deleteReview } from "@movie/dataconnect";
- Replace the
handleAddReview
andhandleDeleteReview
functions with the following code:// 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
: Calls theaddReview
mutation to add a review for the specified movie, securely linking it to the authenticated user. -
handleDeleteReview
: Uses thedeleteReview
mutation to remove a review for a movie by the authenticated user.
See it in action
Users can now leave reviews for movies on the movie details page. They can also view and delete their reviews on their profile page, giving them full control over their interactions with the app.
9. Advanced filters and partial text matching
In this section, you'll implement advanced search capabilities, allowing users to search movies based on a range of ratings and release years, filter by genres and tags, perform partial text matching in titles or descriptions, and even combine multiple filters for more precise results.
Implement connectors
- Open
queries.gql
indataconnect/movie-connector/
. - Add the following query to support various search capabilities:
# 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
operator: Combines multiple conditions in a single query, allowing the search to be filtered by several fields likereleaseYear
,rating
, andgenre
. -
contains
operator: Searches for partial text matches within fields. In this query, it looks for matches withintitle
,description
,name
, orreviewText
. -
where
clause: Specifies the conditions for filtering data. Each section (movies, actors, reviews) uses awhere
clause to define the specific criteria for the search.
Integrate queries in the web app
- In
MovieService
(app/src/lib/MovieService.tsx
), uncomment the following imports:import { searchAll, SearchAllData } from "@movie/dataconnect";
- Replace the
handleSearchAll
function with the following code:// 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
: This function uses thesearchAll
query to perform a search based on the user's input, filtering results by parameters like year, rating, genre, and partial text matches.
See it in action
Head over to the "Advanced Search" page from the navbar in the web app. You can now search for movies, actors, and reviews using various filters and inputs, getting detailed and tailored search results.
10. Optional: Deploy to Cloud (billing required)
Now that you've worked through the local development iteration, it's time to deploy your schema, data, and queries to the server. This can be done using the Firebase Data Connect VS Code extension or the Firebase CLI.
Upgrade your Firebase pricing plan
To integrate Firebase Data Connect with Cloud SQL for PostgreSQL, your Firebase project needs to be on the pay-as-you go (Blaze) pricing plan , which means it's linked to a Cloud Billing account .
- A Cloud Billing account requires a payment method, like a credit card.
- If you're new to Firebase and Google Cloud, check if you're eligible for a $300 credit and a Free Trial Cloud Billing account .
- If you're doing this codelab as part of an event, ask your organizer if there are any Cloud credits available.
To upgrade your project to the Blaze plan, follow these steps:
- In the Firebase console, select to upgrade your plan .
- Select the Blaze plan. Follow the on-screen instructions to link a Cloud Billing account to your project.
If you needed to create a Cloud Billing account as part of this upgrade, you might need to navigate back to the upgrade flow in the Firebase console to complete the upgrade.
Connect your web app to your Firebase project
- Register your web app in your Firebase project using the Firebase console :
- Open your project, and then click Add App .
- Ignore the SDK setup and configuration setup for now, but make sure to copy the generated
firebaseConfig
object.
- Replace the existing
firebaseConfig
inapp/src/lib/firebase.tsx
with the configuration you just copied from the Firebase console.const firebaseConfig = { apiKey: "API_KEY", authDomain: "PROJECT_ID.firebaseapp.com", projectId: "PROJECT_ID", storageBucket: "PROJECT_ID.firebasestorage.app", messagingSenderId: "SENDER_ID", appId: "APP_ID" };
- Build the web app: Back in VS Code, in the
app
folder, use Vite to build the web app for hosting deployment:cd app npm run build
Set up Firebase Authentication in your Firebase project
- Set up Firebase Authentication with Google Sign-In.
- (Optional) Allow domains for Firebase Authentication using the Firebase console (for example,
http://127.0.0.1
).- In the Authentication settings, go to Authorized Domains .
- Click "Add Domain" and include your local domain in the list.
Deploy with the Firebase CLI
- In
dataconnect/dataconnect.yaml
, ensure that your instance ID, database, and service ID match your project: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"]
- Make sure that you have the Firebase CLI set up with your project:
npm i -g firebase-tools firebase login --reauth firebase use --add
- In your terminal, run the following command to deploy:
firebase deploy --only dataconnect,hosting
- Run this command to compare your schema changes:
firebase dataconnect:sql:diff
- If the changes are acceptable, apply them with:
firebase dataconnect:sql:migrate
Your Cloud SQL for PostgreSQL instance will be updated with the final deployed schema and data. You can monitor the status in the Firebase console.
You should now be able to see your app live at your-project.web.app/
. Additionally, you can click Run (Production) in the Firebase Data Connect panel, just as you did with the local emulators, to add data to the production environment.
11. Optional: Vector search with Firebase Data Connect (billing required)
In this section, you'll enable vector search in your movie review app using Firebase Data Connect. This feature allows for content-based searches, such as finding movies with similar descriptions using vector embeddings.
This step requires that you completed the last step of this codelab to deploy to Google Cloud.
Update the schema to include embeddings for a field
In dataconnect/schema/schema.gql
, add the descriptionEmbedding
field to the Movie
table:
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)
: This field stores the semantic embeddings of movie descriptions, enabling vector-based content search in your app.
Activate Vertex AI
- Follow the prerequisites guide to set up Vertex AI APIs from Google Cloud. This step is essential to support the embedding generation and vector search functionality.
- Re-deploy your schema to activate
pgvector
and vector search by clicking on "Deploy to Production" using the Firebase Data Connect VS Code extension.
Populate the database with embeddings
- Open the
dataconnect
folder in VS Code. - Click Run(local) in
optional_vector_embed.gql
to populate your database with embeddings for the movies.
Add a vector search query
In dataconnect/movie-connector/queries.gql
, add the following query to perform vector searches:
# 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
: Specifies the embedding model (textembedding-gecko@003
) and the input text ($query
) for comparison. -
method
: Specifies the similarity method (L2
), which represents the Euclidean distance. -
within
: Limits the search to movies with an L2 distance of 2 or less, focusing on close content matches. -
limit
: Restricts the number of results returned to 5.
Implement the vector search function in your app
Now that the schema and query are set up, integrate the vector search into your app's service layer. This step allows you to call the search query from your web app.
- In
app/src/lib/
MovieService.ts
, uncomment the following imports from the SDKs, this will work like any other query.import { searchMovieDescriptionUsingL2similarity, SearchMovieDescriptionUsingL2similarityData, } from "@movie/dataconnect";
- Add the following function to integrate vector-based search into the app:
// 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
: This function calls thesearchMovieDescriptionUsingL2similarity
query, passing the input text to perform a vector-based content search.
See it in action
Navigate to the "Vector Search" section in the navbar and type in phrases like "romantic and modern". You'll see a list of movies that match the content you're searching for, or, go into the movie details page of any movie, and check out the similar movies section at the bottom of the page.
12. Conclusion
Congratulations, you should be able to use the web app! If you want to play with your own movie data, don't worry, insert your own data using the Firebase Data Connect extension by mimicking the _insert.gql
files, or add them through the Data Connect execution pane in VS Code.