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 Code
- Установите 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 установите расширение Firebase Data Connect для Visual Studio .
В качестве альтернативы вы можете установить расширение из магазина расширений Visual Studio Code или найти его в самом 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. Разработайте схему для обзоров фильмов.
In this section, you'll define the structure and relationships between the key entities in the movie application in a schema. Entities such as Movie , User , Actor , and Review are mapped to database tables, with relationships established using Firebase Data Connect and GraphQL schema directives. Once it's in place, your app will be ready to handle everything from searching for top-rated movies and filtering by genre to letting users leave reviews, mark favorites, explore similar movies, or find recommended movies based on text input through vector search.
Основные сущности и взаимоотношения
Тип 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"
}
Основные выводы:
- movie: Ссылается на тип Movie, неявно генерирует внешний ключ movieId: UUID!.
- actor: Ссылается на тип Actor, неявно генерирует внешний ключ actorId: UUID!.
- Роль: Определяет роль актера в фильме (например, «главный» или «второстепенный»).
Настройте таблицу User .
Тип User определяет сущность пользователя, который взаимодействует с фильмами, оставляя отзывы или добавляя фильмы в избранное.
Скопируйте и вставьте фрагмент кода в файл dataconnect/schema/schema.gql :
type User
@table {
id: String! @col
username: String! @col(dataType: "varchar(50)")
# The following are generated from the @ref in the Review table
# reviews_on_user
# movies_via_Review
}
Настройте таблицу FavoriteMovie .
Тип FavoriteMovie — это промежуточная таблица, которая обрабатывает связи «многие ко многим» между пользователями и их любимыми фильмами. Каждая таблица связывает User с Movie .
Скопируйте и вставьте фрагмент кода в файл dataconnect/schema/schema.gql :
type FavoriteMovie
@table(name: "FavoriteMovies", singular: "favorite_movie", plural: "favorite_movies", key: ["user", "movie"]) {
# @ref is implicit
user: User!
movie: Movie!
}
Основные выводы:
- movie: Ссылается на тип Movie, неявно генерирует внешний ключ
movieId: UUID!. - user: Ссылается на тип пользователя, неявно генерирует внешний ключ.
userId: UUID!.
Настройте таблицу Review .
Тип 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 запущены. - В верхней части файла вы должны увидеть кнопку « Запустить (локально)» . Нажмите на неё, чтобы вставить данные фиктивного фильма в вашу базу данных.

- Проверьте терминал выполнения подключения данных , чтобы убедиться в успешном добавлении данных.

Реализуйте коннектор
- Откройте
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 Authentication. Вы также будете использовать данные Firebase Authentication для прямого получения или обновления данных пользователей в Firebase DataConnect, обеспечивая безопасное управление пользователями в вашем приложении.

Внедрить коннекторы
- Откройте
mutations.gqlвdataconnect/movie-connector/. - Добавьте следующую мутацию для создания или обновления текущего аутентифицированного пользователя:
# Create or update the current authenticated user mutation UpsertUser($username: String!) @auth(level: USER) { user_upsert( data: { id_expr: "auth.uid" username: $username } ) }
Основные выводы:
-
id_expr: "auth.uid": Здесь используетсяauth.uid, который предоставляется непосредственно Firebase Authentication, а не пользователем или приложением, что обеспечивает дополнительный уровень безопасности за счет безопасной и автоматической обработки идентификатора пользователя.
Получить текущего пользователя
- Откройте
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 Authentication, обеспечивая безопасный доступ к данным конкретного пользователя. - Поля
_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/. - Добавьте следующие изменения для обработки добавления фильмов в избранное:
# 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 Authentication, гарантируя, что доступ к данным или их изменение будут осуществляться только для аутентифицированного пользователя.
Проверьте, добавлен ли фильм в избранное.
- Откройте
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 CLI.
Обновите свой тарифный план Firebase.
Для интеграции Firebase Data Connect с Cloud SQL for PostgreSQL ваш проект Firebase должен использовать тарифный план с оплатой по мере использования (Blaze) , то есть быть привязан к учетной записи Cloud Billing .
- Для использования учетной записи Cloud Billing требуется способ оплаты, например, кредитная карта.
- Если вы новичок в Firebase и Google Cloud, проверьте, имеете ли вы право на получение кредита в размере 300 долларов США и бесплатной пробной версии учетной записи Cloud Billing .
- Если вы выполняете этот практический семинар в рамках мероприятия, уточните у организатора, есть ли возможность получить облачные кредиты.
Чтобы перейти на тарифный план 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 for PostgreSQL будет обновлен с использованием окончательно развернутой схемы и данных. Вы можете отслеживать статус в консоли Firebase.
Теперь вы сможете увидеть свое приложение в рабочем режиме по адресу your-project.web.app/ . Кроме того, вы можете нажать кнопку «Запустить (Производство)» на панели Firebase Data Connect, как и в случае с локальными эмуляторами, чтобы добавить данные в производственную среду.
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. - Нажмите кнопку 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.
Реализуйте функцию векторного поиска в вашем приложении.
Теперь, когда схема и запрос настроены, интегрируйте векторный поиск в сервисный слой вашего приложения. Этот шаг позволит вам вызывать поисковый запрос из вашего веб-приложения.
- В
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.