1. 始める前に
この Codelab では、Firebase Data Connect を Cloud SQL データベースと統合して、映画レビュー ウェブアプリを構築します。完成したアプリは、Firebase Data Connect が SQL を利用したアプリケーションの構築プロセスをどのように簡素化するかを示しています。これには次の機能が含まれます。
- 認証: アプリのクエリとミューテーションにカスタム認証を実装し、承認されたユーザーのみがデータとやり取りできるようにします。
- GraphQL スキーマ: 映画レビュー ウェブアプリのニーズに合わせて調整された柔軟な GraphQL スキーマを使用して、データ構造を作成して管理します。
- SQL クエリとミューテーション: GraphQL を利用したクエリとミューテーションを使用して、Cloud SQL のデータを取得、更新、管理します。
- 部分文字列一致による高度な検索: フィルタと検索オプションを使用して、タイトル、説明、タグなどのフィールドに基づいて映画を検索します。
- (省略可) ベクトル検索の統合: Firebase Data Connect のベクトル検索を使用してコンテンツ検索機能を追加し、入力と設定に基づいてリッチなユーザー エクスペリエンスを提供します。
前提条件
JavaScript の基本的な知識が必要です。
学習内容
- ローカル エミュレータを使用して Firebase Data Connect を設定します。
- Data Connect と GraphQL を使用してデータスキーマを設計します。
- 映画レビュー アプリのさまざまなクエリとミューテーションを作成してテストします。
- Firebase Data Connect がアプリで SDK を生成して使用する方法について説明します。
- スキーマをデプロイし、データベースを効率的に管理します。
必要なもの
- Git
- Visual Studio Code
- nvm-windows(Windows)または nvm(macOS/Linux)を使用して Node.js をインストールします。
- まだ作成していない場合は、Firebase コンソールで Firebase プロジェクトを作成します。
- (省略可) ベクトル検索の場合は、プロジェクトを従量課金制の Blaze のお支払いプランにアップグレードします。
2. 開発環境を設定する
この Codelab のステージでは、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 を開いて、ローカルで実行されているウェブアプリを確認します。これは、映画レビュー アプリを構築し、その機能を利用するためのフロントエンドとして機能します。
- Visual Studio Code を使用して、クローン作成した
codelab-dataconnect-web
フォルダを開きます。ここでは、スキーマを定義し、クエリを作成して、アプリの機能をテストします。 - Data Connect 機能を使用するには、Firebase Data Connect Visual Studio 拡張機能をインストールします。
または、Visual Studio Code Marketplace から拡張機能をインストールするか、VS Code 内で検索することもできます。 - Firebase コンソールで Firebase プロジェクトを開くか、新しいプロジェクトを作成します。
- Firebase プロジェクトを Firebase Data Connect VSCode 拡張機能に接続します。拡張機能で次の操作を行います。
- [ログイン] ボタンをクリックします。
- [Connect a Firebase Project] をクリックして、Firebase プロジェクトを選択します。
- Firebase Data Connect VS Code 拡張機能を使用して Firebase エミュレータを起動します。
[Start Emulators] をクリックし、ターミナルでエミュレータが実行されていることを確認します。
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 が含まれます。connector/connector.yaml
ファイルで SDK 生成の場所を編集できます。クエリまたはミューテーションを定義すると、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:
@default(expr: "uuidV4()")
を使用して生成された、映画ごとに一意の UUID。
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! を暗黙的に生成します。
- role: 映画における俳優の役柄を定義します(例: 「メイン」または「サポート」)。
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
タイプを多対多のリレーションシップでリンクします(1 人のユーザーが複数のレビューを残すことができ、各映画に複数のレビューを残すことができます)。
コード スニペットをコピーして 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")
}
重要なポイント:
- user: レビューを投稿したユーザーを参照します。
- movie: レビュー対象の映画を参照します。
- reviewDate:
@default(expr: "request.time")
を使用してクチコミが作成された時刻に自動的に設定されます。
自動生成されるフィールドとデフォルト値
スキーマでは、@default(expr: "uuidV4()")
などの式を使用して、一意の ID とタイムスタンプを自動的に生成します。たとえば、Movie
型と Review
型の id
フィールドには、新しいレコードが作成されると UUID が自動的に入力されます。
スキーマが定義されたので、映画アプリのデータ構造と関係の基盤ができました。
5. 人気映画と最新映画を取得する
このセクションでは、モックの映画データをローカル エミュレータに挿入し、コネクタ(クエリ)と、ウェブ アプリケーションでこれらのコネクタを呼び出す TypeScript コードを実装します。このチュートリアルを終えると、アプリでデータベースから直接、高評価の最新映画を動的に取得して表示できるようになります。
映画、俳優、レビューのモックデータを挿入する
- VSCode で、
dataconnect/moviedata_insert.gql
を開きます。Firebase Data Connect 拡張機能のエミュレータが実行されていることを確認します。 - ファイルの先頭に [Run (local)] ボタンが表示されます。これをクリックして、映画のモックデータをデータベースに挿入します。
- [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 エミュレータは、.gql
ファイル(具体的には schema.gql
、queries.gql
、mutations.gql
)と connector.yaml
ファイルの情報に基づいて SDK を生成します。これらの SDK は、アプリケーションで直接呼び出すことができます。
MovieService
(app/src/lib/MovieService.tsx
)で、先頭の import ステートメントのコメントを解除します。 関数import { listMovies, ListMoviesData, OrderDirection } from "@movie/dataconnect";
listMovies
、レスポンス タイプListMoviesData
、列挙型OrderDirection
はすべて、以前に定義したスキーマとクエリに基づいて Firebase Data Connect エミュレータによって生成された SDK です。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. 映画と俳優の詳細を表示する
このセクションでは、映画や俳優の一意の ID を使用して詳細情報を取得する機能を実装します。これには、それぞれのテーブルからデータを取得するだけでなく、関連するテーブルを結合して、映画のレビューや俳優のフィルモグラフィーなどの包括的な詳細を表示することも含まれます。
コネクタを実装する
- プロジェクトで
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()
:Movies
テーブルまたはActors
テーブルから単一の映画または俳優を取得するための GraphQL クエリ フィールド。_on_
: これにより、外部キー関係を持つ関連付けられた型からフィールドに直接アクセスできます。たとえば、reviews_on_movie
は特定の映画に関連するすべてのレビューを取得します。_via_
: 結合テーブルを介して多対多のリレーションシップをナビゲートするために使用されます。たとえば、actors_via_MovieActor
はMovieActor
結合テーブルを介してActor
型にアクセスし、where
条件はロール(「メイン」や「サポート」など)に基づいてアクターをフィルタします。
モックデータを入力してクエリをテストする
- [Data Connect の実行] ペインで、次のようなモック ID を入力してクエリをテストできます。
{"id": "550e8400-e29b-41d4-a716-446655440000"}
GetMovieById
の [Run (local)] をクリックして、「Quantum Paradox」(上記の ID に関連する架空の映画)の詳細を取得します。
ウェブアプリにクエリを統合する
MovieService
(app/src/lib/MovieService.tsx
)で、次の import のコメントを解除します。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 でユーザーデータを直接取得または更新し、アプリ内で安全なユーザー管理を実現します。
コネクタを実装する
dataconnect/movie-connector/
で Openmutations.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"
: ユーザーまたはアプリではなく、Firebase Authentication から直接提供されるauth.uid
を使用します。ユーザー ID が安全かつ自動的に処理されるようにすることで、セキュリティ レイヤを追加します。
現在のユーザーを取得する
dataconnect/movie-connector/
で Openqueries.gql
を開きます。- 次のクエリを追加して、現在のユーザーを取得します。
# 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 のコメントを解除します。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 Authentication エミュレータを使用してログインできます。ログインしたら、[プロフィール] をクリックします。現時点では空ですが、アプリでユーザー固有のデータを処理するための基盤をセットアップしました。
8. ユーザー操作を実装する
この Codelab のこのセクションでは、映画レビュー アプリでユーザー操作を実装します。具体的には、ユーザーが好きな映画を管理したり、レビューを残したり削除したりできるようにします。
ユーザーが映画をお気に入り登録できるようにする
このセクションでは、ユーザーが映画をお気に入り登録できるようにデータベースを設定します。
コネクタを実装する
dataconnect/movie-connector/
で Openmutations.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"
: Firebase Authentication から直接提供されるauth.uid
を使用し、認証されたユーザーのデータのみがアクセスまたは変更されるようにします。
映画がお気に入りに追加されているかどうかを確認する
dataconnect/movie-connector/
で Openqueries.gql
を開きます。- 映画がお気に入りかどうかを確認する次のクエリを追加します。
query GetIfFavoritedMovie($movieId: UUID!) @auth(level: USER) { favorite_movie(key: { userId_expr: "auth.uid", movieId: $movieId }) { movieId } }
重要なポイント:
auth.uid
: Firebase Authentication を使用して、ユーザー固有のデータへの安全なアクセスを確保します。favorite_movie
:favorite_movies
結合テーブルをチェックして、特定の映画が現在のユーザーのお気に入りとしてマークされているかどうかを確認します。
ウェブアプリにクエリを統合する
MovieService
(app/src/lib/MovieService.tsx
)で、次の import のコメントを解除します。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 のコメントを解除します。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. 高度なフィルタとテキストの部分一致
このセクションでは、高度な検索機能を実装します。ユーザーは、さまざまな評価や公開年に基づいて映画を検索したり、ジャンルやタグでフィルタしたり、タイトルや説明で部分一致検索を行ったりできます。複数のフィルタを組み合わせて、より正確な結果を得ることもできます。
コネクタを実装する
dataconnect/movie-connector/
でqueries.gql
を開きます。- さまざまな検索機能をサポートするため、次のクエリを追加します。
# 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
演算子: 複数の条件を 1 つのクエリに結合し、releaseYear
、rating
、genre
などの複数のフィールドで検索をフィルタできます。contains
演算子: フィールド内でテキストの部分一致を検索します。このクエリでは、title
、description
、name
、reviewText
内で一致するものを探します。where
句: データをフィルタする条件を指定します。各セクション(映画、俳優、レビュー)では、where
句を使用して検索の特定の条件を定義します。
ウェブアプリにクエリを統合する
MovieService
(app/src/lib/MovieService.tsx
)で、次の import のコメントを解除します。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. 省略可: Cloud にデプロイする(課金が必要)
ローカル開発のイテレーションが完了したので、スキーマ、データ、クエリをサーバーにデプロイします。これは、Firebase Data Connect VS Code 拡張機能または Firebase CLI を使用して行うことができます。
Firebase 料金プランをアップグレードする
Firebase Data Connect を Cloud SQL for PostgreSQL と統合するには、Firebase プロジェクトが従量課金制(Blaze)のお支払いプランに登録されている必要があります。つまり、Cloud 請求先アカウントにリンクされている必要があります。
- Cloud 請求先アカウントには、クレジット カードなどの支払い方法が必要です。
- Firebase と Google Cloud を初めて使用する場合は、$300 のクレジットと無料トライアル用 Cloud 請求先アカウントを利用できるかどうかご確認ください。
- この Codelab をイベントの一環として行う場合は、利用可能な Cloud クレジットがあるかどうかを主催者に確認してください。
プロジェクトを Blaze プランにアップグレードする手順は次のとおりです。
- Firebase コンソールで、プランをアップグレードします。
- Blaze プランを選択します。画面の指示に沿って、Cloud 請求先アカウントをプロジェクトにリンクします。
このアップグレードの一環として Cloud 請求先アカウントを作成する必要があった場合は、Firebase コンソールのアップグレード フローに戻ってアップグレードを完了する必要がある場合があります。
ウェブアプリを Firebase プロジェクトに接続する
- Firebase コンソールを使用して、Firebase プロジェクトにウェブアプリを登録します。
- プロジェクトを開き、[アプリを追加] をクリックします。
- SDK の設定と構成は、今のところ無視してかまいませんが、生成された
firebaseConfig
オブジェクトは必ずコピーしてください。
app/src/lib/firebase.tsx
の既存のfirebaseConfig
を、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 Authentication を設定する
- Google ログインを使用して Firebase Authentication を設定します。
- (省略可)Firebase コンソールを使用して、Firebase Authentication のドメイン(
http://127.0.0.1
など)を許可します。- [認証] 設定で、[認可済みドメイン] に移動します。
- [ドメインを追加] をクリックして、ローカル ドメインをリストに追加します。
Firebase CLI を使用してデプロイする
dataconnect/dataconnect.yaml
で、インスタンス ID、データベース、サービス ID がプロジェクトと一致していることを確認します。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 パネルで [Run (Production)] をクリックして、本番環境にデータを追加することもできます。
11. 省略可: Firebase Data Connect によるベクター検索(課金が必要)
このセクションでは、Firebase Data Connect を使用して映画レビュー アプリでベクトル検索を有効にします。この機能を使用すると、ベクトル エンベディングを使用して類似した説明の映画を見つけるなど、コンテンツ ベースの検索が可能になります。
この手順では、この Codelab の最後の手順を完了して Google Cloud にデプロイする必要があります。
フィールドのエンベディングを含むようにスキーマを更新する
dataconnect/schema/schema.gql
で、Movie
テーブルに descriptionEmbedding
フィールドを追加します。
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 を有効にする
- 前提条件ガイドに沿って、Google Cloud から Vertex AI API を設定します。このステップは、エンベディング生成とベクトル検索の機能をサポートするために不可欠です。
- Firebase Data Connect VS Code 拡張機能を使用して [Deploy to Production] をクリックし、
pgvector
とベクター検索を有効にするためにスキーマを再デプロイします。
エンベディングを使用してデータベースにデータを入力する
- VS Code で
dataconnect
フォルダを開きます。 optional_vector_embed.gql
で [Run(local)] をクリックして、映画のエンベディングをデータベースに入力します。
ベクトル検索クエリを追加する
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. まとめ
これでウェブアプリをご利用いただけるようになりました。独自の映画データを使用する場合は、_insert.gql
ファイルを模倣して Firebase Data Connect 拡張機能を使用して独自のデータを挿入するか、VS Code の Data Connect 実行ペインから追加してください。