Firebase Data Connect を使用して構築する

1. 始める前に

FriendlyMovies アプリ

この 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 プランにアップグレードする

開発環境の設定

このセクションでは、Firebase Data Connect を使用して映画レビュー アプリの作成を開始するための環境を設定します。

ステップ 1: プロジェクト リポジトリのクローンを作成する

まず、プロジェクト リポジトリのクローンを作成し、必要な依存関係をインストールします。

git clone https://github.com/firebaseextended/codelab-dataconnect-web
cd codelab-dataconnect-web
cd ./app && npm i
npm run dev
  1. これらのコマンドを実行したら、ブラウザで http://localhost:5173 を開き、ローカルで実行されているウェブアプリを確認します。これは、映画レビューアプリを作成し、その機能を操作するためのフロントエンドとして機能します。

93f6648a2532c606.png

ステップ 2: Visual Studio Code でプロジェクトを開く

Visual Studio Code を使用して、クローンを作成した codelab-dataconnect-web フォルダを開きます。ここでは、スキーマの定義、クエリの作成、アプリの機能のテストを行います。

ステップ 3: Firebase Data Connect Visual Studio 拡張機能をインストールする

Data Connect 機能を使用するには、Firebase Data Connect Visual Studio 拡張機能をインストールします。または、Visual Studio Code Marketplace からインストールするか、VS Code 内で検索します。

  1. または、Visual Studio Code Marketplace からインストールするか、VS Code 内で検索します。

b03ee38c9a81b648.png

ステップ 4: Firebase プロジェクトを作成する

新しい Firebase プロジェクトをまだ作成していない場合は、Firebase コンソールに移動して作成します。次に、Firebase Data Connect の VSCode 拡張機能で以下を行います。

  • [ログイン] ボタンをクリックします。
  • [Connect a Firebase Project] をクリックし、Firebase コンソールで作成したプロジェクトを選択します。

4bb2fbf8f9fac29b.png

ステップ 5: Firebase エミュレータを起動する

Firebase Data Connect VSCode 拡張機能で [Start Emulators] をクリックし、ターミナルでエミュレータが実行されていることを確認します。

6d3d95f4cb708db1.png

2. スターター コードベースを確認する

このセクションでは、アプリのスターター コードベースの主要な部分について説明します。アプリには一部の機能が欠けていますが、全体的な構造を理解しておくと役に立ちます。

フォルダとファイルの構造

アプリのフォルダとファイルの構造の概要は次のとおりです。

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 アプリに接続するための構成。
  • lib/dataconnect-sdk/: このフォルダには、生成された SDK が格納されます。SDK 生成の場所は connector/connector.yaml ファイルで編集できます。クエリまたはミューテーションを定義するたびに、SDK が自動的に生成されます。

3. 映画レビューのスキーマの定義

このセクションでは、映画アプリの主要なエンティティの構造と関係をスキーマで定義します。MovieUserActorReview などのエンティティは、Firebase Data Connect と GraphQL スキーマ ディレクティブを使用して確立された関係とともに、データベース テーブルにマッピングされます。実装が完了すると、高評価の映画の検索やジャンル別のフィルタリングから、ユーザーによるレビューの投稿、お気に入りのマーク付け、類似映画の探索、ベクトル検索によるテキスト入力に基づくおすすめ映画の検索まで、あらゆる処理をアプリで行うことができます。

コア エンティティと関係

Movie 型は、タイトル、ジャンル、タグなどの重要な詳細情報を保持します。アプリは、検索と映画のプロフィールにこの情報を使用します。User タイプは、レビューやお気に入りなどのユーザー操作を追跡します。Reviews: ユーザーと映画をつなぐことで、アプリでユーザーが作成した評価やフィードバックを表示できます。

映画、俳優、ユーザーの関係により、アプリはより動的になります。MovieActor 結合テーブルは、キャストの詳細と俳優のフィルモグラフィーを表示するのに役立ちます。FavoriteMovie タイプを使用すると、ユーザーは好きな映画を好きな映画に使えるようになり、カスタマイズされたお気に入りリストを表示して、人気の映画をハイライトすることができます。

Movie Table

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 型と1 対 1 の関係を確立します。映画の監督などの追加データが含まれます。

コード スニペットを 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: 映画での俳優の役割(例:「main」または「supporting」)。

ユーザー テーブル

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 型は、ユーザーとそのお気に入りの映画や俳優との多対多の関係を処理する結合テーブルです。各テーブルは UserMovie にリンクします。

コード スニペットを 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 タイプはレビュー エンティティを表し、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 が入力されます。

スキーマが定義されたので、映画アプリのデータ構造と関係の強固な基盤を築きました。

4. 人気の映画と最新の映画の取得

FriendlyMovies アプリ

このセクションでは、モック動画データをローカル エミュレータに挿入し、ウェブ アプリケーションでこれらのコネクタを呼び出すためのコネクタ(クエリ)と TypeScript コードを実装します。最終的には、アプリでデータベースから直接、評価の高い最新の映画を動的に取得して表示できるようになります。

模擬映画、俳優、レビューのデータを挿入する

  1. VSCode で dataconnect/moviedata_insert.gql開きます。Firebase Data Connect 拡張機能のエミュレータが実行されていることを確認します。
  2. ファイルの先頭に [Run (local)] ボタンが表示されます。これをクリックすると、モック映画データをデータベースに挿入できます。

e424f75e63bf2e10.png

  1. [Data Connect Execution] ターミナルで、データが正常に追加されたことを確認します。

e0943d7704fb84ea.png

コネクタの実装

  1. dataconnect/movie-connector/queries.gql を開きます。コメントに基本的な ListMovies クエリが含まれています。
query ListMovies @auth(level: PUBLIC) {
  movies {
    id
    title
    imageUrl
    releaseYear
    genre
    rating
    tags
    description
  }
}

このクエリは、すべての映画とその詳細(id、title、releaseYear など)を取得します。ただし、映画は並べ替えられません。

  1. 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
  }
}

[実行(ローカル)] ボタンをクリックして、ローカル データベースに対してクエリを実行します。実行する前に、構成ペインにクエリ変数を入力することもできます。

c4d947115bb11b16.png

重要ポイント:

  • movies(): データベースから映画データを取得するための GraphQL クエリフィールド。
  • orderByRating: 映画を評価(昇順 / 降順)で並べ替えるパラメータ。
  • orderByReleaseYear: 映画を公開年で並べ替えるパラメータ(昇順 / 降順)。
  • limit: 返される映画の数を制限します。

ウェブアプリでのクエリの統合

このパートでは、前のセクションで定義したクエリをウェブアプリで使用します。Firebase Data Connect エミュレータは、.gql ファイル(schema.gql、query.gql、mutations.gql)と connector.yaml 内の情報に基づいて SDK を生成します。これらの SDK は、アプリケーションで直接呼び出すことができます。

  1. MovieServiceapp/src/lib/MovieService.tsx)で、先頭の import ステートメントのコメント化を解除します。
import { listMovies, ListMoviesData, OrderDirection } from "@movie/dataconnect";

関数 listMovies、レスポンス タイプ ListMoviesData、列挙型 OrderDirection はすべて、以前に定義したスキーマとクエリに基づいて Firebase Data Connect エミュレータによって生成された SDK です。

  1. handleGetTopMovies 関数と handleGetLatestMovies 関数を次のコードに置き換えます。
// Fetch top-rated movies
export const handleGetTopMovies = async (
  limit: number
): Promise<ListMoviesData["movies"] | null> => {
  try {
    const response = await listMovies({
      orderByRating: OrderDirection.DESC,
      limit,
    });
    return response.data.movies;
  } catch (error) {
    console.error("Error fetching top movies:", error);
    return null;
  }
};

// Fetch latest movies
export const handleGetLatestMovies = async (
  limit: number
): Promise<ListMoviesData["movies"] | null> => {
  try {
    const response = await listMovies({
      orderByReleaseYear: OrderDirection.DESC,
      limit,
    });
    return response.data.movies;
  } catch (error) {
    console.error("Error fetching latest movies:", error);
    return null;
  }
};

重要ポイント:

  • listMovies: listMovies クエリを呼び出して映画のリストを取得する、自動生成された関数。評価やリリース年で並べ替えたり、検索結果の数を制限したりするオプションがあります。
  • ListMoviesData: トップページの上位 10 作品と最新映画の表示に使用される結果タイプ。

デモ動画を見る

ウェブアプリを再読み込みして、クエリの動作を確認します。ホームページに映画のリストが動的に表示され、ローカル データベースから直接データが取得されるようになりました。設定済みのデータが反映され、評価の高い最新の映画がシームレスに表示されます。

5. 映画と俳優の詳細を表示する

このセクションでは、一意の ID を使用して映画や俳優の詳細情報を取得する機能を実装します。これには、それぞれのテーブルからデータを取得するだけでなく、関連するテーブルを結合して、映画のレビューや俳優のフィルモグラフィーなどの包括的な詳細情報を表示することも含まれます。

ac7fefa7ff779231.png

コネクタの実装

  1. プロジェクトで dataconnect/movie-connector/queries.gql開きます
  2. 次のクエリを追加して、映画と俳優の詳細を取得します。
# 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
    }
  }
}
  1. 変更を保存してクエリを確認します。

重要ポイント:

  • movie()/actor(): Movies テーブルまたは Actors テーブルから単一の映画または俳優を取得するための GraphQL クエリ フィールド。
  • _on_: これにより、外部キー関係を持つ関連型のフィールドに直接アクセスできます。たとえば reviews_on_movie は特定の映画に関連するすべてのレビューをフェッチします。
  • _via_: 結合テーブルで多対多のリレーションを移動するために使用されます。たとえば、actors_via_MovieActor は MovieActor 結合テーブルを介してアクタータイプにアクセスします。where 条件は、ロール(「main」または「supporting」)。

Data Connect 実行ペインで、次のようなモック ID を入力してクエリをテストできます。

{"id": "550e8400-e29b-41d4-a716-446655440000"}

GetMovieById の [Run (local)] をクリックして、「Quantum Paradox」の詳細を取得します。(上記の ID に関連する疑似ムービー)。

1b08961891e44da2.png

ウェブアプリでのクエリの統合

  1. MovieServiceapp/src/lib/MovieService.tsx)で、次のインポートをコメント化解除します。
import { getMovieById, GetMovieByIdData } from "@movie/dataconnect";
import { GetActorByIdData, getActorById } from "@movie/dataconnect";
  1. handleGetMovieById 関数と handleGetActorById 関数を次のコードに置き換えます
// Fetch movie details by ID
export const handleGetMovieById = async (
  movieId: string
) => {
  try {
    const response = await getMovieById({ id: movieId });
    if (response.data.movie) {
      return response.data.movie;
    }
    return null;
  } catch (error) {
    console.error("Error fetching movie:", error);
    return null;
  }
};

// Calling generated SDK for GetActorById
export const handleGetActorById = async (
  actorId: string
): Promise<GetActorByIdData["actor"] | null> => {
  try {
    const response = await getActorById({ id: actorId });
    if (response.data.actor) {
      return response.data.actor;
    }
    return null;
  } catch (error) {
    console.error("Error fetching actor:", error);
    return null;
  }
};

重要ポイント:

  • getMovieById / getActorById: 定義したクエリを呼び出して、特定の映画や俳優の詳細情報を取得する自動生成関数です。
  • GetMovieByIdData / GetActorByIdData: 映画や俳優の詳細をアプリに表示するために使用される結果タイプです。

実例を見る

ウェブアプリのホームページにアクセスします。映画をクリックすると、俳優やレビューなど、関連するテーブルから取得した情報がすべて表示されます。同様に、俳優をクリックすると、その俳優が出演している映画が表示されます。

6. ユーザー認証の処理

このセクションでは、Firebase Authentication を使用してユーザーのログインとログアウト機能を実装します。また、Firebase Authentication のデータを使用して Firebase DataConnect でユーザーデータを直接取得またはアップサートすることで、アプリ内で安全なユーザー管理を実現します。

9890838045d5a00e.png

コネクタの実装

  1. dataconnect/movie-connector/mutations.gql開く
  2. 次のミューテーションを追加して、現在の認証済みユーザーを作成または更新します。
# 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/queries.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 とタイトルを含む)。
  • favorite_movies_on_user: ユーザーがお気に入りとしてマークしたすべての映画を取得します。これには、genre、releaseYear、rating、metadata などの詳細情報が含まれます。

ウェブアプリへのクエリの統合

  1. MovieService(app/src/lib/MovieService.tsx)で、次のインポートをコメント化解除します。
import { upsertUser } from "@movie/dataconnect";
import { getCurrentUser, GetCurrentUserData } from "@movie/dataconnect";
  1. handleAuthStateChange 関数と handleGetCurrentUser 関数を次のコードに置き換えます。
// Handle user authentication state changes and upsert user
export const handleAuthStateChange = (
  auth: any,
  setUser: (user: User | null) => void
) => {
  return onAuthStateChanged(auth, async (user) => {
    if (user) {
      setUser(user);
      const username = user.email?.split("@")[0] || "anon";
      await upsertUser({ username });
    } else {
      setUser(null);
    }
  });
};

// Fetch current user profile
export const handleGetCurrentUser = async (): Promise<
  GetCurrentUserData["user"] | null
> => {
  try {
    const response = await getCurrentUser();
    return response.data.user;
  } catch (error) {
    console.error("Error fetching user profile:", error);
    return null;
  }
};

重要ポイント:

  • handleAuthStateChange: この関数は、認証状態の変化をリッスンします。ユーザーがログインすると、ユーザーのデータが設定され、upsertUser ミューテーションが呼び出され、データベース内のユーザー情報を作成または更新します。
  • handleGetCurrentUser: getCurrentUser クエリを使用して現在のユーザーのプロフィールを取得します。これにより、ユーザーのレビューとお気に入りの映画が取得されます。

デモ動画を見る

[Google でログイン] をクリックします。クリックします。Firebase Auth エミュレータを使用してログインできます。ログインしたら、[プロフィール] をクリックします。現時点では空ですが、アプリでユーザー固有のデータ処理の基盤が設定されました。

7. ユーザー操作の実装

このセクションでは、映画レビュー アプリにユーザー操作を実装して、ユーザーがお気に入りの映画を管理したり、レビューを投稿または削除したりできるようにします。

b3d0ac1e181c9de9.png

コネクタの実装

  1. dataconnect/movie-connector/mutations.gql開きます
  2. お気に入りの映画を処理するために、次のミューテーションを追加します。
# 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 を使用します。これにより、認証されたユーザーのデータのみがアクセスまたは変更されます。
  1. 次に、dataconnect/movie-connector/queries.gql開きます
  2. 次のクエリを追加して、映画がお気に入りに追加されたかどうかを確認します。
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 結合テーブルを調べて、現在のユーザーが特定の映画をお気に入りとしてマークしているかどうかを確認します。

ウェブアプリでのクエリの統合

  1. MovieServiceapp/src/lib/MovieService.tsx)で、次のインポートをコメント化解除します。
import { addFavoritedMovie, deleteFavoritedMovie, getIfFavoritedMovie } from "@movie/dataconnect";
  1. handleAddFavoritedMoviehandleDeleteFavoritedMoviehandleGetIfFavoritedMovie 関数を次のコードに置き換えます
// 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;
  }
};

重要ポイント:

  • handleAddFavoritedMoviehandleDeleteFavoritedMovie: ミューテーションを使用して、ユーザーのお気に入りから映画を安全に追加または削除します。
  • handleGetIfFavoritedMovie: getIfFavoritedMovie クエリを使用して、映画がユーザーによってお気に入りとしてマークされているかどうかを確認します。

デモ動画を見る

映画をお気に入りに追加したり、お気に入りから削除したりするには、映画カードと映画の詳細ページにあるハートアイコンをクリックします。また、お気に入りの映画はプロフィール ページで確認できます。

ユーザー レビューの実装

次に、アプリにユーザー レビューを管理するセクションを実装します。

コネクタの実装

  1. mutations.gqldataconnect/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 を使用して審査の現在の日付を自動的に生成します。手動で入力する必要はありません。

ウェブアプリにクエリを統合する

  1. MovieServiceapp/src/lib/MovieService.tsx)で、次のインポートをコメント化解除します。
import { addReview, deleteReview } from "@movie/dataconnect";
  1. handleAddReview 関数と handleDeleteReview 関数を次のコードに置き換えます
// Add a review to a movie
export const handleAddReview = async (
  movieId: string,
  rating: number,
  reviewText: string
): Promise<void> => {
  try {
    await addReview({ movieId, rating, reviewText });
  } catch (error) {
    console.error("Error adding review:", error);
    throw error;
  }
};

// Delete a review from a movie
export const handleDeleteReview = async (movieId: string): Promise<void> => {
  try {
    await deleteReview({ movieId });
  } catch (error) {
    console.error("Error deleting review:", error);
    throw error;
  }
};

重要ポイント:

  • handleAddReview: addReview ミューテーションを呼び出して、指定された映画のレビューを追加し、認証済みユーザーに安全にリンクします。
  • handleDeleteReview: deleteReview ミューテーションを使用して、認証されたユーザーによる映画のレビューを削除します。

実例を見る

ユーザーは映画の詳細ページで映画のレビューを投稿できるようになりました。また、プロフィール ページでレビューを表示、削除することもできます。これにより、アプリとのやり取りを完全に管理できます。

8. 高度なフィルタとテキストの部分一致

このセクションでは、高度な検索機能を実装します。これにより、ユーザーはさまざまな評価や公開年に基づいて映画を検索したり、ジャンルやタグでフィルタしたり、タイトルや説明で部分一致検索を行ったり、複数のフィルタを組み合わせてより正確な結果を得たりできるようになります。

ece70ee0ab964e28.png

コネクタの実装

  1. dataconnect/movie-connector/queries.gql を開きます。
  2. さまざまな検索機能をサポートするには、次のクエリを追加します。
# 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 つのクエリに結合し、releaseYearratinggenre などの複数のフィールドで検索をフィルタできます。
  • contains 演算子: フィールド内のテキストの部分一致を検索します。このクエリでは、titledescriptionnamereviewText 内で一致するものを探します。
  • where 句: データのフィルタ条件を指定します。各セクション(映画、俳優、レビュー)では、where 句を使用して検索の特定の条件を定義します。

ウェブアプリにクエリを統合する

  1. MovieServiceapp/src/lib/MovieService.tsx)で、次のインポートをコメント化解除します。
import { searchAll, SearchAllData } from "@movie/dataconnect";
  1. handleSearchAll 関数を次のコードに置き換えます
// Function to perform the search using the query and filters
export const handleSearchAll = async (
  searchQuery: string,
  minYear: number,
  maxYear: number,
  minRating: number,
  maxRating: number,
  genre: string
): Promise<SearchAllData | null> => {
  try {
    const response = await searchAll({
      input: searchQuery,
      minYear,
      maxYear,
      minRating,
      maxRating,
      genre,
    });

    return response.data;
  } catch (error) {
    console.error("Error performing search:", error);
    return null;
  }
};

重要ポイント:

  • handleSearchAll: この関数は、searchAll クエリを使用してユーザーの入力に基づいて検索を行い、年、評価、ジャンル、テキストの部分一致などのパラメータで結果をフィルタします。

デモ動画を見る

[検索オプション]に移動しますアクセスできます。さまざまなフィルタや入力を使用して映画、俳優、レビューを検索し、詳細かつカスタマイズされた検索結果を取得できるようになりました。

9. 省略可: クラウドにデプロイする(有料)

ローカル開発のイテレーションを行ったので、次はスキーマ、データ、クエリをサーバーにデプロイします。これを行うには、Firebase Data Connect VS Code 拡張機能または Firebase CLI を使用します。

Firebase コンソールでウェブアプリを追加する

  1. Firebase コンソールウェブアプリを作成し、アプリ ID をメモします。

7030822793e4d75b.png

  1. Firebase コンソールで [アプリを追加] をクリックして、ウェブアプリを設定します。現時点では、SDK の設定と構成の設定は無視してかまいません。ただし、生成された firebaseConfig オブジェクトに注意してください。
  2. app/src/lib/firebase.tsxfirebaseConfig置き換えます
const firebaseConfig = {
  apiKey: "API_KEY",
  authDomain: "PROJECT_ID.firebaseapp.com",
  projectId: "PROJECT_ID",
  storageBucket: "PROJECT_ID.appspot.com",
  messagingSenderId: "SENDER_ID",
  appId: "APP_ID"
};
  1. ウェブアプリをビルドする: app フォルダで、Vite を使用してデプロイをホストするウェブアプリをビルドします。
cd app
npm run build

コンソールで Firebase Authentication を設定する

  1. Google ログインを使用して Firebase Auth を設定する

62af2f225e790ef6.png

  1. (省略可)プロジェクト コンソールで、(Firebase Auth)[https://firebase.google.com/docs/auth/web/hosting] のドメインを許可します(例: http://127.0.0.1):

c255098f12549886.png

Firebase CLI を使用してデプロイする

  1. 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"]
  1. プロジェクトで Firebase CLI が設定されていることを確認します。
npm i -g firebase-tools
firebase login --reauth
firebase use --add
  1. ターミナルで次のコマンドを実行してデプロイします。
firebase deploy --only dataconnect,hosting
  1. 次のコマンドを実行して、スキーマの変更を比較します。
firebase dataconnect:sql:diff
  1. 変更を受け入れる場合は、次のコマンドで適用します。
firebase dataconnect:sql:migrate

Cloud SQL for PostgreSQL インスタンスが更新され、最終的にデプロイされたスキーマとデータが使用されます。ステータスは Firebase コンソールでモニタリングできます。

これで your-project.web.app/ でアプリを公開できるようになりました。また、ローカル エミュレータと同様に、Firebase Data Connect パネルで [実行(本番環境)] をクリックして、本番環境にデータを追加することもできます。

10. 省略可: Firebase Data Connect を使用したベクトル検索

このセクションでは、Firebase Data Connect を使用して、映画レビュー アプリでベクトル検索を有効にします。この機能を使用すると、ベクトル エンベディングを使用して説明が類似した映画を見つけるなど、コンテンツベースの検索を行うことができます。

4b5aca5a447d2feb.png

スキーマを更新してフィールドのエンベディングを含める

  1. 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 の有効化

  1. 前提条件ガイドに沿って、Google Cloud で Vertex AI API を設定します。このステップは、エンベディング生成とベクトル検索機能をサポートするうえで不可欠です。
  2. スキーマを再デプロイし、Firebase Data Connect VSCode 拡張機能を使用して [Deploy to Production] をクリックして pgvector とベクトル検索を有効にします。

データベースにエンベディングを入力する

  1. VSCode で dataconnect フォルダを開きoptional_vector_embed.gql で [Run(local)] をクリックして、映画のエンベディングをデータベースに入力します。

b858da780f6ec103.png

ベクトル検索クエリを追加する

  1. 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 に制限します。

アプリにベクトル検索機能を実装する

  1. app/src/lib/MovieService.ts で、次のインポートをコメント化解除します。

アプリにベクトル検索関数を実装する

スキーマとクエリを設定したら、ベクトル検索をアプリのサービスレイヤに統合します。この手順により、ウェブアプリから検索クエリを呼び出せるようになります。

app/src/lib/ MovieService.ts で、SDK から次のインポートをコメント化解除します。これは他のクエリと同様に機能します。

import {
  searchMovieDescriptionUsingL2similarity,
  SearchMovieDescriptionUsingL2similarityData,
} from "@movie/dataconnect";

ベクトルベースの検索をアプリに統合するために、次の関数を追加します。

// Perform vector-based search for movies based on description
export const searchMoviesByDescription = async (
  query: string
): Promise<
  | SearchMovieDescriptionUsingL2similarityData["movies_descriptionEmbedding_similarity"]
  | null
> => {
  try {
    const response = await searchMovieDescriptionUsingL2similarity({ query });
    return response.data.movies_descriptionEmbedding_similarity;
  } catch (error) {
    console.error("Error fetching movie descriptions:", error);
    return null;
  }
};


重要ポイント:

  • searchMoviesByDescription: この関数は searchMovieDescriptionUsingL2similarity クエリを呼び出し、入力テキストを渡してベクトルベースのコンテンツ検索を実行します。

実例を見る

ナビゲーション バーの [ベクトル検索] セクションに移動し、「ロマンチックでモダン」などのフレーズを入力します。検索しているコンテンツに一致する映画のリストが表示されます。または、映画の詳細ページに移動し、ページの下部にある類似映画のセクションを確認してください。

7b71f1c75633c1be.png

11. まとめ

これで、ウェブアプリをご利用いただけるようになりました。独自の映画データで遊びたい場合は、_insert.gql ファイルを模倣して FDC 拡張機能を使用して独自のデータを挿入するか、Data Connect 実行ペインから追加します。

詳細