Mit Firebase Data Connect entwickeln

1. Vorbereitung

FriendlyMovies App

In diesem Codelab integrieren Sie Firebase Data Connect in eine Cloud SQL-Datenbank, um eine Webanwendung für Filmbewertungen zu erstellen. Die fertige App zeigt, wie Firebase Data Connect das Erstellen von SQL-basierten Anwendungen vereinfacht. Sie umfasst folgende Funktionen:

  • Authentifizierung:Implementieren Sie eine benutzerdefinierte Authentifizierung für die Abfragen und Mutationen Ihrer App, damit nur autorisierte Nutzer mit Ihren Daten interagieren können.
  • GraphQL-Schema: Erstellen und verwalten Sie Ihre Datenstrukturen mit einem flexiblen GraphQL-Schema, das auf die Anforderungen einer Web-App für Filmbewertungen zugeschnitten ist.
  • SQL-Abfragen und ‑Mutationen:Mithilfe von Abfragen und Mutationen, die auf GraphQL basieren, können Sie Daten in Cloud SQL abrufen, aktualisieren und verwalten.
  • Erweiterte Suche mit teilweiser Übereinstimmung:Mithilfe von Filtern und Suchoptionen kannst du Filme anhand von Feldern wie Titel, Beschreibung oder Tags finden.
  • Optional: Vektorsuche integrieren:Fügen Sie mit der Vektorsuche von Firebase Data Connect eine Inhaltssuchfunktion hinzu, um eine umfassende Nutzererfahrung basierend auf Eingaben und Einstellungen zu ermöglichen.

Voraussetzungen

Sie benötigen grundlegende Kenntnisse von JavaScript.

Lerninhalte

  • Richten Sie Firebase Data Connect mit lokalen Emulatoren ein.
  • Erstellen Sie ein Datenschema mit Data Connect und GraphQL.
  • Verschiedene Abfragen und Mutationen für eine Filmbewertungs-App schreiben und testen
  • Informationen dazu, wie Firebase Data Connect das SDK in der App generiert und verwendet
  • Schema bereitstellen und Datenbank effizient verwalten

Voraussetzungen

Entwicklungsumgebung einrichten

In diesem Abschnitt erfahren Sie, wie Sie die Umgebung einrichten, um mit Firebase Data Connect Ihre Filmrezensions-App zu erstellen.

Schritt 1: Projekt-Repository klonen

Klonen Sie zuerst das Projekt-Repository und installieren Sie die erforderlichen Abhängigkeiten:

git clone https://github.com/firebaseextended/codelab-dataconnect-web
cd codelab-dataconnect-web
cd ./app && npm i
npm run dev
  1. Öffnen Sie nach Ausführung dieser Befehle http://localhost:5173 in Ihrem Browser, um die lokal ausgeführte Webanwendung zu sehen. Dies dient als Frontend für die Erstellung der Filmrezensions-App und die Interaktion mit ihren Funktionen.

93f6648a2532c606.png

Schritt 2: Projekt in Visual Studio Code öffnen

Öffnen Sie den geklonten Ordner codelab-dataconnect-web mit Visual Studio Code. Hier definieren Sie Ihr Schema, schreiben Abfragen und testen die Funktionen der App.

Schritt 3: Visual Studio-Erweiterung für Firebase Data Connect installieren

Wenn Sie die Data Connect-Funktionen verwenden möchten, installieren Sie die Visual Studio-Erweiterung für Firebase Data Connect.Sie können sie auch über den Visual Studio Code Marketplace installieren oder in VS Code danach suchen.

  1. Alternativ: Installieren Sie es über den Visual Studio Code Marketplace oder suchen Sie in VS Code danach.

b03ee38c9a81b648.png

Schritt 4: Firebase-Projekt erstellen

Rufen Sie die Firebase Console auf, um ein neues Firebase-Projekt zu erstellen, falls noch keines vorhanden ist. Führen Sie dann in der VSCode-Erweiterung „Firebase Data Connect“ die folgenden Schritte aus:

  • Klicken Sie auf die Schaltfläche Anmelden.
  • Klicken Sie auf Firebase-Projekt verknüpfen und wählen Sie das Projekt aus, das Sie in der Firebase Console erstellt haben.

4bb2fbf8f9fac29b.png

Schritt 5: Firebase-Emulatoren starten

Klicken Sie in der VSCode-Erweiterung „Firebase Data Connect“ auf „Emulatoren starten“ und prüfen Sie, ob die Emulatoren im Terminal ausgeführt werden.

6d3d95f4cb708db1.png

2. Starter-Codebasis ansehen

In diesem Abschnitt lernen Sie die wichtigsten Bereiche der Start-Codebasis der App kennen. Auch wenn der App einige Funktionen fehlen, ist es hilfreich, die Gesamtstruktur zu verstehen.

Ordner- und Dateistruktur

Hier ist eine kurze Übersicht über die Ordner- und Dateistruktur der App:

dataconnect/

Enthält Firebase Data Connect-Konfigurationen, Connectors (mit denen Abfragen und Mutationen definiert werden) und Schemadateien.

  • schema/schema.gql: Definiert das GraphQL-Schema
  • connector/queries.gql: Abfragen, die in Ihrer App benötigt werden.
  • connector/mutations.gql: In Ihrer App erforderliche Mutationen.
  • connector/connector.yaml: Konfigurationsdatei für die SDK-Generierung

app/src/

Enthält die Anwendungslogik und die Interaktion mit Firebase Data Connect.

  • firebase.ts: Konfiguration für die Verbindung zu einer Firebase-App in der Console.
  • lib/dataconnect-sdk/: Dieser Ordner enthält das generierte SDK. Sie können den Speicherort der SDK-Generierung in der Datei „connector/connector.yaml“ bearbeiten. SDKs werden dann automatisch generiert, wenn Sie eine Abfrage oder Mutation definieren.

3. Schema für Filmrezensionen definieren

In diesem Abschnitt definieren Sie die Struktur und die Beziehungen zwischen den wichtigsten Entitäten in der Filmanwendung in einem Schema. Entitäten wie Movie, User, Actor und Review werden Datenbanktabellen zugeordnet. Die Beziehungen werden mit Firebase Data Connect- und GraphQL-Schemarichtlinien hergestellt. Sobald die Funktion eingerichtet ist, kann Ihre App alles verarbeiten, von der Suche nach den bestbewerteten Filmen und dem Filtern nach Genre bis hin zur Möglichkeit für Nutzer, Rezensionen zu verfassen, Favoriten zu markieren, ähnliche Filme zu entdecken oder empfohlene Filme anhand von Texteingaben über die Vektorsuche zu finden.

Wichtige Entitäten und Beziehungen

Der Typ Movie enthält wichtige Details wie Titel, Genre und Tags, die in der App für Suchanfragen und Filmprofile verwendet werden. Der Typ User erfasst Nutzerinteraktionen wie Rezensionen und Favoriten. Reviews Nutzer mit Filmen verbinden, sodass die App von Nutzern erstellte Bewertungen und Feedback anzeigt.

Die Beziehungen zwischen Filmen, Schauspielern und Nutzern machen die App dynamischer. Die MovieActor-Join-Tabelle hilft, Details zur Besetzung und Filmografie der Schauspieler anzuzeigen. Mit dem Typ FavoriteMovie können Nutzer Filme zu ihren Favoriten hinzufügen. So kann die App eine personalisierte Favoritenliste anzeigen und beliebte Titel hervorheben.

Filmtabelle

Der Filmtyp definiert die Hauptstruktur einer Filmentität, einschließlich Feldern wie „Titel“, „Genre“, „Jahr der Veröffentlichung“ und „Altersfreigabe“.

Kopieren Sie das Code-Snippet und fügen Sie es in die Datei dataconnect/schema/schema.gql ein:

type Movie
  @table {
  id: UUID! @default(expr: "uuidV4()")
  title: String!
  imageUrl: String!
  releaseYear: Int
  genre: String
  rating: Float
  description: String
  tags: [String]
}

Zusammenfassung:

  • id:Eine eindeutige UUID für jeden Film, die mit @default(expr: "uuidV4()") generiert wird.

Tabelle „MovieMetadata“

Der Typ „MovieMetadata“ stellt eine Eins-zu-eins-Beziehung zum Typ „Movie“ her. Sie enthält zusätzliche Daten wie den Regisseur des Films.

Kopieren Sie das Code-Snippet und fügen Sie es in die Datei dataconnect/schema/schema.gql ein:

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
}

Zusammenfassung:

  • Film! @ref: verweist auf den Typ Movie und stellt eine Fremdschlüsselbeziehung her.

Akteurtabelle

Kopieren Sie das Code-Snippet und fügen Sie es in die Datei dataconnect/schema/schema.gql ein:

type Actor @table {
  id: UUID!
  imageUrl: String! 
  name: String! @col(name: "name", dataType: "varchar(30)")
}

Der Typ Actor steht für einen Schauspieler in der Filmdatenbank. Jeder Schauspieler kann an mehreren Filmen mitwirken, was eine Beziehung vom Typ „Viele zu Viele“ bildet.

Tabelle „MovieActor“

Kopieren Sie das Code-Snippet und fügen Sie es in die Datei dataconnect/schema/schema.gql ein:

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"
}

Zusammenfassung:

  • movie:Verweist auf den Filmtyp und generiert implizit einen Fremdschlüssel „movieId: UUID!“.
  • actor:Verweist auf den Actor-Typ und generiert implizit einen Fremdschlüssel „actorId: UUID!“.
  • role:Definiert die Rolle des Schauspielers im Film (z.B. „main“ oder „supporting“).

Nutzertabelle

Der Typ User definiert eine Nutzerentität, die mit Filmen interagiert, indem sie Rezensionen hinterlässt oder Filme zu ihren Favoriten hinzufügt.

Kopieren Sie das Code-Snippet und fügen Sie es in die Datei dataconnect/schema/schema.gql ein:

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
}

Tabelle „FavoriteMovie“

Der Typ FavoriteMovie ist eine Join-Tabelle, die m:n-Beziehungen zwischen Nutzern und ihren Lieblingsfilmen oder ‑schauspielern verarbeitet. Jede Tabelle verknüpft eine User mit einer Movie.

Kopieren Sie das Code-Snippet und fügen Sie es in die Datei dataconnect/schema/schema.gql ein:

type FavoriteMovie
  @table(name: "FavoriteMovies", singular: "favorite_movie", plural: "favorite_movies", key: ["user", "movie"]) {
  # @ref is implicit
  user: User!
  movie: Movie!
}

Zusammenfassung:

  • movie:Verweist auf den Filmtyp und generiert implizit einen Fremdschlüssel „movieId: UUID!“.
  • user:Verweist auf den Nutzertyp und generiert implizit einen Fremdschlüssel „userId: UUID!“.

Überprüfungstabelle

Der Rezensionstyp stellt die Rezensions-Entität dar und verknüpft die Typen „Nutzer“ und „Film“ in einer m:n-Beziehung (ein Nutzer kann viele Rezensionen hinterlassen und jeder Film kann viele Rezensionen haben).

Kopieren Sie das Code-Snippet und fügen Sie es in die Datei dataconnect/schema/schema.gql ein:

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")
}

Zusammenfassung:

  • user:Verweis auf den Nutzer, der die Rezension abgegeben hat.
  • movie:Verweis auf den Film, der rezensiert wird.
  • reviewDate:Wird automatisch auf die Uhrzeit festgelegt, zu der die Rezension mit @default(expr: "request.time") erstellt wurde.

Automatisch generierte Felder und Standardwerte

Im Schema werden Ausdrücke wie @default(expr: "uuidV4()") verwendet, um automatisch eindeutige IDs und Zeitstempel zu generieren. Beispielsweise wird das Feld „id“ in den Typen „Film“ und „Rezension“ automatisch mit einer UUID ausgefüllt, wenn ein neuer Datensatz erstellt wird.

Nachdem das Schema definiert wurde, hat Ihre Film-App eine solide Grundlage für die Datenstruktur und die Beziehungen.

4. Top- und aktuelle Filme abrufen

FriendlyMovies App

In diesem Abschnitt fügen Sie den lokalen Emulatoren Mock-Filmdaten hinzu und implementieren dann die Connectors (Abfragen) und den TypeScript-Code, um diese Connectors in der Webanwendung aufzurufen. Am Ende kann Ihre App die bestbewerteten und neuesten Filme dynamisch direkt aus der Datenbank abrufen und anzeigen.

Mock-Film-, Schauspieler- und Rezensionsdaten einfügen

  1. Öffnen Sie in VS Code dataconnect/moviedata_insert.gql . Prüfen Sie, ob die Emulatoren in der Firebase Data Connect-Erweiterung ausgeführt werden.
  2. Oben in der Datei sollte die Schaltfläche Ausführen (lokal) angezeigt werden. Klicken Sie darauf, um die simulierten Filmdaten in Ihre Datenbank einzufügen.

e424f75e63bf2e10.png

  1. Prüfen Sie im Terminal Data Connect Execution, ob die Daten erfolgreich hinzugefügt wurden.

e0943d7704fb84ea.png

Connector implementieren

  1. Öffnen Sie dataconnect/movie-connector/queries.gql. In den Kommentaren finden Sie eine einfache ListMovies-Abfrage:
query ListMovies @auth(level: PUBLIC) {
  movies {
    id
    title
    imageUrl
    releaseYear
    genre
    rating
    tags
    description
  }
}

Mit dieser Abfrage werden alle Filme und ihre Details (z.B. ID, Titel, Erscheinungsjahr) abgerufen. Die Filme werden jedoch nicht sortiert.

  1. Ersetzen Sie die ListMovies-Abfrage durch die folgende, um Sortier- und Begrenzungsoptionen hinzuzufügen:
# 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
  }
}

Klicken Sie auf die Schaltfläche Ausführen (lokal), um die Abfrage in Ihrer lokalen Datenbank auszuführen. Sie können die Abfragevariablen auch vor der Ausführung im Konfigurationsbereich eingeben.

c4d947115bb11b16.png

Zusammenfassung:

  • movies(): GraphQL-Abfragefeld zum Abrufen von Filmdaten aus der Datenbank.
  • orderByRating:Parameter zum Sortieren von Filmen nach Bewertung (aufsteigend/absteigend).
  • orderByReleaseYear:Parameter zum Sortieren von Filmen nach Erscheinungsjahr (aufsteigend/absteigend).
  • limit:Begrenzt die Anzahl der zurückgegebenen Filme.

Abfragen in die Webanwendung einbinden

In diesem Teil verwenden Sie die im vorherigen Abschnitt definierten Abfragen in Ihrer Webanwendung. Die Firebase Data Connect-Emulatoren generieren SDKs basierend auf den Informationen in den .gql-Dateien (schema.gql, queries.gql, mutations.gql) und connector.yaml. Diese SDKs können direkt in Ihrer Anwendung aufgerufen werden.

  1. Entfernen Sie in MovieService (app/src/lib/MovieService.tsx) oben den Kommentar aus der Importanweisung:
import { listMovies, ListMoviesData, OrderDirection } from "@movie/dataconnect";

Die Funktion listMovies, der Antworttyp ListMoviesData und das Enum OrderDirection sind SDKs, die von den Firebase Data Connect-Emulatoren basierend auf dem Schema und den zuvor definierten Abfragen generiert wurden .

  1. Ersetzen Sie die Funktionen handleGetTopMovies und handleGetLatestMovies durch den folgenden Code:
// Fetch top-rated movies
export const handleGetTopMovies = async (
  limit: number
): Promise<ListMoviesData["movies"] | null> => {
  try {
    const response = await listMovies({
      orderByRating: OrderDirection.DESC,
      limit,
    });
    return response.data.movies;
  } catch (error) {
    console.error("Error fetching top movies:", error);
    return null;
  }
};

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

Zusammenfassung:

  • listMovies:Eine automatisch generierte Funktion, die die Abfrage „listMovies“ aufruft, um eine Liste von Filmen abzurufen. Sie haben die Möglichkeit, nach Bewertung oder Erscheinungsjahr zu sortieren und die Anzahl der Ergebnisse einzuschränken.
  • ListMoviesData:Der Ergebnistyp, mit dem die Top 10 und die neuesten Filme auf der Startseite angezeigt werden.

In der Praxis

Aktualisieren Sie Ihre Webanwendung, um die Abfrage in Aktion zu sehen. Die Startseite zeigt jetzt dynamisch die Liste der Filme an und ruft die Daten direkt aus Ihrer lokalen Datenbank ab. Die meistbewerteten und neuesten Filme werden nahtlos angezeigt und entsprechen den Daten, die Sie gerade eingerichtet haben.

5. Film- und Schauspielerdetails anzeigen

In diesem Abschnitt implementieren Sie die Funktion zum Abrufen detaillierter Informationen zu einem Film oder Schauspieler anhand ihrer eindeutigen IDs. Dazu werden nicht nur Daten aus den jeweiligen Tabellen abgerufen, sondern auch zugehörige Tabellen zusammengeführt, um umfassende Details wie Filmrezensionen und Filmografie der Schauspieler anzuzeigen.

ac7fefa7ff779231.png

Connectors implementieren

  1. Öffnen Sie dataconnect/movie-connector/queries.gql in Ihrem Projekt.
  2. Fügen Sie die folgenden Abfragen hinzu, um Film- und Schauspielerdetails abzurufen:
# 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. Speichern Sie die Änderungen und überprüfen Sie die Abfragen.

Zusammenfassung:

  • movie()/actor(): GraphQL-Abfragefelder zum Abrufen eines einzelnen Films oder Schauspielers aus der Tabelle „Filme“ oder „Schauspieler“.
  • _on_: Dadurch ist der direkte Zugriff auf Felder eines verknüpften Typs mit einer Fremdschlüsselbeziehung möglich. Mit reviews_on_movie werden beispielsweise alle Rezensionen zu einem bestimmten Film abgerufen.
  • _via_: Wird verwendet, um n:m-Beziehungen über eine Join-Tabelle zu steuern. Beispielsweise greift actors_via_MovieActor über die Join-Tabelle „MovieActor“ auf den Schauspielertyp zu und die Bedingung where filtert Schauspieler nach ihrer Rolle (z.B. „main“ oder „supporting“).

Im Ausführungsbereich von Data Connect können Sie die Abfrage testen, indem Sie Mock-IDs eingeben, z. B.:

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

Klicken Sie für GetMovieById auf Ausführen (lokal), um die Details zu „Quantum Paradox“ abzurufen, dem Mock-Film, auf den sich die obige ID bezieht.

1b08961891e44da2.png

Abfragen in die Webanwendung einbinden

  1. Entfernen Sie in MovieService (app/src/lib/MovieService.tsx) das Kommentarzeichen bei den folgenden Importen:
import { getMovieById, GetMovieByIdData } from "@movie/dataconnect";
import { GetActorByIdData, getActorById } from "@movie/dataconnect";
  1. Ersetzen Sie die Funktionen handleGetMovieById und handleGetActorById durch den folgenden Code:
// Fetch movie details by ID
export const handleGetMovieById = async (
  movieId: string
) => {
  try {
    const response = await getMovieById({ id: movieId });
    if (response.data.movie) {
      return response.data.movie;
    }
    return null;
  } catch (error) {
    console.error("Error fetching movie:", error);
    return null;
  }
};

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

Zusammenfassung:

  • getMovieById / getActorById: Das sind automatisch generierte Funktionen, die die von dir definierten Abfragen aufrufen und detaillierte Informationen zu einem bestimmten Film oder Schauspieler abrufen.
  • GetMovieByIdData / GetActorByIdData: Dies sind die Ergebnistypen, mit denen Film- und Schauspielerdetails in der App angezeigt werden.

In der Praxis

Rufen Sie nun die Startseite Ihrer Webanwendung auf. Wenn du auf einen Film klickst, werden alle Details angezeigt, einschließlich Schauspieler und Rezensionen. Diese Informationen werden aus verknüpften Tabellen abgerufen. Wenn du auf einen Schauspieler klickst, werden die Filme angezeigt, in denen er mitgespielt hat.

6. Nutzerauthentifizierung

In diesem Abschnitt implementieren Sie die Funktionen für die Nutzeranmeldung und ‑abmeldung mit Firebase Authentication. Außerdem verwenden Sie Firebase Authentication-Daten, um Nutzerdaten direkt in Firebase DataConnect abzurufen oder zu aktualisieren. So wird eine sichere Nutzerverwaltung in Ihrer App gewährleistet.

9890838045d5a00e.png

Connectors implementieren

  1. Öffnen Sie mutations.gql in dataconnect/movie-connector/.
  2. Fügen Sie die folgende Mutation hinzu, um den aktuell authentifizierten Nutzer zu erstellen oder zu aktualisieren:
# Create or update the current authenticated user
mutation UpsertUser($username: String!) @auth(level: USER) {
  user_upsert(
    data: {
      id_expr: "auth.uid"
      username: $username
    }
  )
}

Wichtige Erkenntnisse:

  • id_expr: "auth.uid": Hier wird auth.uid verwendet, das direkt von Firebase Authentication und nicht vom Nutzer oder der App bereitgestellt wird. Dadurch wird eine zusätzliche Sicherheitsebene hinzugefügt, da die Nutzer-ID sicher und automatisch verarbeitet wird.

Öffnen Sie als Nächstes dataconnect/movie-connector/ und queries.gql .

Fügen Sie die folgende Abfrage hinzu, um den aktuellen Nutzer abzurufen:

# 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
        }
      }
    }
  }
}

Die wichtigsten Erkenntnisse:

  • auth.uid: Dieser wird direkt aus Firebase Authentication abgerufen, um einen sicheren Zugriff auf nutzerspezifische Daten zu ermöglichen.
  • _on_ Felder: Diese Felder stellen die Join-Tabellen dar:
  • reviews_on_user: Hiermit werden alle Rezensionen abgerufen, die sich auf den Nutzer beziehen, einschließlich der ID und des Titels des Films.
  • favorite_movies_on_user: Ruft alle Filme ab, die vom Nutzer als Favoriten markiert wurden, einschließlich detaillierter Informationen wie Genre, Erscheinungsjahr, Altersfreigabe und Metadaten.

Abfragen in die Webanwendung einbinden

  1. Entfernen Sie in MovieService (app/src/lib/MovieService.tsx) die Kommentarzeichen vor den folgenden Importen:
import { upsertUser } from "@movie/dataconnect";
import { getCurrentUser, GetCurrentUserData } from "@movie/dataconnect";
  1. Ersetzen Sie die Funktionen handleAuthStateChange und handleGetCurrentUser durch den folgenden Code:
// Handle user authentication state changes and upsert user
export const handleAuthStateChange = (
  auth: any,
  setUser: (user: User | null) => void
) => {
  return onAuthStateChanged(auth, async (user) => {
    if (user) {
      setUser(user);
      const username = user.email?.split("@")[0] || "anon";
      await upsertUser({ username });
    } else {
      setUser(null);
    }
  });
};

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

Zusammenfassung:

  • handleAuthStateChange: Diese Funktion überwacht Änderungen des Authentifizierungsstatus. Wenn sich ein Nutzer anmeldet, werden seine Daten festgelegt und die upsertUser-Mutation aufgerufen, um die Informationen des Nutzers in der Datenbank zu erstellen oder zu aktualisieren.
  • handleGetCurrentUser: Ruft das Profil des aktuellen Nutzers mit der Abfrage getCurrentUser ab, über die die Rezensionen und Lieblingsfilme des Nutzers abgerufen werden.

In der Praxis

Klicken Sie jetzt in der Navigationsleiste auf die Schaltfläche „Über Google anmelden“. Sie können sich mit dem Firebase Auth-Emulator anmelden. Klicken Sie nach der Anmeldung auf „Mein Profil“. Sie ist vorerst leer, aber Sie haben die Grundlage für die nutzerspezifische Datenverarbeitung in Ihrer App geschaffen.

7. Nutzerinteraktionen implementieren

In diesem Abschnitt implementieren Sie Nutzerinteraktionen in der Filmrezensions-App, damit Nutzer ihre Lieblingsfilme verwalten und Rezensionen hinterlassen oder löschen können.

b3d0ac1e181c9de9.png

Connectors implementieren

  1. Öffnen Sie mutations.gql in dataconnect/movie-connector/.
  2. Fügen Sie die folgenden Mutationen hinzu, um Filme zu den Favoriten hinzuzufügen:
# 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 })
}

Zusammenfassung:

  • userId_expr: "auth.uid": Verwendet auth.uid, das direkt von Firebase Authentication bereitgestellt wird. So wird sichergestellt, dass nur auf die Daten des authentifizierten Nutzers zugegriffen oder diese nur von ihm geändert werden.
  1. Öffnen Sie als Nächstes dataconnect/movie-connector/ und öffnen Sie queries.gql.
  2. Füge die folgende Abfrage hinzu, um zu prüfen, ob ein Film zu den Favoriten gehört:
query GetIfFavoritedMovie($movieId: UUID!) @auth(level: USER) {
  favorite_movie(key: { userId_expr: "auth.uid", movieId: $movieId }) {
    movieId
  }
}

Zusammenfassung:

  • auth.uid: Sorgt mit Firebase Authentication für sicheren Zugriff auf nutzerspezifische Daten.
  • favorite_movie: Prüft in der Zusammenführungstabelle favorite_movies, ob ein bestimmter Film vom aktuellen Nutzer als Favorit markiert wurde.

Abfragen in die Webanwendung einbinden

  1. Entfernen Sie in MovieService (app/src/lib/MovieService.tsx) das Kommentarzeichen bei den folgenden Importen:
import { addFavoritedMovie, deleteFavoritedMovie, getIfFavoritedMovie } from "@movie/dataconnect";
  1. Ersetzen Sie die Funktionen handleAddFavoritedMovie, handleDeleteFavoritedMovie und handleGetIfFavoritedMovie durch den folgenden Code:
// Add a movie to user's favorites
export const handleAddFavoritedMovie = async (
  movieId: string
): Promise<void> => {
  try {
    await addFavoritedMovie({ movieId });
  } catch (error) {
    console.error("Error adding movie to favorites:", error);
    throw error;
  }
};

// Remove a movie from user's favorites
export const handleDeleteFavoritedMovie = async (
  movieId: string
): Promise<void> => {
  try {
    await deleteFavoritedMovie({ movieId });
  } catch (error) {
    console.error("Error removing movie from favorites:", error);
    throw error;
  }
};

// Check if the movie is favorited by the user
export const handleGetIfFavoritedMovie = async (
  movieId: string
): Promise<boolean> => {
  try {
    const response = await getIfFavoritedMovie({ movieId });
    return !!response.data.favorite_movie;
  } catch (error) {
    console.error("Error checking if movie is favorited:", error);
    return false;
  }
};

Zusammenfassung:

  • handleAddFavoritedMovie und handleDeleteFavoritedMovie: Mithilfe der Mutationen kannst du einen Film sicher zu den Favoriten des Nutzers hinzufügen oder daraus entfernen.
  • handleGetIfFavoritedMovie: Prüft mit der Abfrage getIfFavoritedMovie, ob ein Film vom Nutzer als Favorit markiert wurde.

In der Praxis

Du kannst Filme jetzt als Favoriten hinzufügen oder die Markierung aufheben, indem du auf den Filmkarten und auf der Detailseite des Films auf das Herzsymbol klickst. Außerdem kannst du dir deine Lieblingsfilme auf deiner Profilseite ansehen.

Rezensionen von Nutzern implementieren

Als Nächstes implementieren Sie den Bereich zum Verwalten von Nutzerrezensionen in der App.

Connectors implementieren

  1. In mutations.gql (dataconnect/movie-connector/mutations.gql): Fügen Sie die folgenden Mutationen hinzu:
# 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 })
}

Zusammenfassung:

  • userId_expr: "auth.uid": Sorgt dafür, dass Rezensionen dem authentifizierten Nutzer zugeordnet werden.
  • reviewDate_date: { today: true }: Das aktuelle Datum für die Rezension wird automatisch mit DataConnect generiert, sodass keine manuelle Eingabe erforderlich ist.

Abfragen in die Webanwendung einbinden

  1. Entfernen Sie in MovieService (app/src/lib/MovieService.tsx) das Kommentarzeichen bei den folgenden Importen:
import { addReview, deleteReview } from "@movie/dataconnect";
  1. Ersetzen Sie die Funktionen handleAddReview und handleDeleteReview durch den folgenden Code:
// Add a review to a movie
export const handleAddReview = async (
  movieId: string,
  rating: number,
  reviewText: string
): Promise<void> => {
  try {
    await addReview({ movieId, rating, reviewText });
  } catch (error) {
    console.error("Error adding review:", error);
    throw error;
  }
};

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

Zusammenfassung:

  • handleAddReview: Ruft die addReview-Mutation auf, um eine Rezension für den angegebenen Film hinzuzufügen und sie sicher mit dem authentifizierten Nutzer zu verknüpfen.
  • handleDeleteReview: Mit der deleteReview-Mutation wird eine Rezension für einen Film durch den authentifizierten Nutzer entfernt.

In der Praxis

Nutzer können jetzt auf der Detailseite eines Films Rezensionen hinterlassen. Außerdem können sie ihre Rezensionen auf ihrer Profilseite ansehen und löschen. So haben sie die volle Kontrolle über ihre Interaktionen mit der App.

8. Erweiterte Filter und teilweise Textübereinstimmung

In diesem Abschnitt implementieren Sie erweiterte Suchfunktionen, mit denen Nutzer Filme anhand einer Reihe von Bewertungen und Erscheinungsjahren suchen, nach Genres und Tags filtern, eine teilweise Textübereinstimmung in Titeln oder Beschreibungen durchführen und sogar mehrere Filter kombinieren können, um genauere Ergebnisse zu erhalten.

ece70ee0ab964e28.png

Connectors implementieren

  1. Öffnen Sie queries.gql in dataconnect/movie-connector/.
  2. Fügen Sie die folgende Abfrage hinzu, um verschiedene Suchfunktionen zu unterstützen:
# 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
    }
  }
}

Zusammenfassung:

  • Operator _and: Hiermit werden mehrere Bedingungen in einer einzigen Abfrage kombiniert, sodass die Suche nach mehreren Feldern wie releaseYear, rating und genre gefiltert werden kann.
  • contains-Operator: Es wird nach teilweisen Textübereinstimmungen in Feldern gesucht. In dieser Abfrage wird nach Übereinstimmungen in title, description, name oder reviewText gesucht.
  • where-Klausel: Gibt die Bedingungen für das Filtern von Daten an. In jedem Bereich (Filme, Schauspieler, Rezensionen) werden mit einer where-Klausel die spezifischen Kriterien für die Suche definiert.

Abfragen in die Webanwendung einbinden

  1. Entfernen Sie in MovieService (app/src/lib/MovieService.tsx) das Kommentarzeichen bei den folgenden Importen:
import { searchAll, SearchAllData } from "@movie/dataconnect";
  1. Ersetzen Sie die Funktion handleSearchAll durch den folgenden Code:
// Function to perform the search using the query and filters
export const handleSearchAll = async (
  searchQuery: string,
  minYear: number,
  maxYear: number,
  minRating: number,
  maxRating: number,
  genre: string
): Promise<SearchAllData | null> => {
  try {
    const response = await searchAll({
      input: searchQuery,
      minYear,
      maxYear,
      minRating,
      maxRating,
      genre,
    });

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

Zusammenfassung:

  • handleSearchAll: Bei dieser Funktion wird die Abfrage searchAll verwendet, um eine Suche basierend auf der Eingabe des Nutzers durchzuführen. Die Ergebnisse werden nach Parametern wie Jahr, Bewertung, Genre und teilweisen Textübereinstimmungen gefiltert.

In der Praxis

Rufe in der Web-App über die Navigationsleiste die Seite „Erweiterte Suche“ auf. Du kannst jetzt mithilfe verschiedener Filter und Eingaben nach Filmen, Schauspielern und Rezensionen suchen und erhältst detaillierte und personalisierte Suchergebnisse.

9. Optional: In der Cloud bereitstellen (Abrechnung erforderlich)

Nachdem Sie die Iteration der lokalen Entwicklung durchlaufen haben, ist es an der Zeit, Ihr Schema, Ihre Daten und Abfragen auf dem Server bereitzustellen. Sie können dazu die VS Code-Erweiterung „Firebase Data Connect“ oder die Firebase CLI verwenden.

Web-App in der Firebase Console hinzufügen

  1. Erstellen Sie eine Webanwendung in der Firebase Console und notieren Sie sich die App-ID.

7030822793e4d75b.png

  1. Richten Sie eine Webanwendung in der Firebase Console ein, indem Sie auf „App hinzufügen“ klicken. Sie können die SDK-Einrichtung und -Konfiguration vorerst ignorieren, aber notieren Sie sich das generierte firebaseConfig-Objekt.
  2. Ersetzen Sie das firebaseConfig in app/src/lib/firebase.tsx:
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. Webanwendung erstellen:Erstellen Sie im Ordner app mit Vite die Webanwendung für das Hosting-Deployment:
cd app
npm run build

Firebase Authentication in der Console einrichten

  1. Firebase Auth mit Google Log-in einrichten

62af2f225e790ef6.png

  1. Optional: Erlauben Sie in der Projektkonsole Domains für (Firebase Auth) [https://firebase.google.com/docs/auth/web/hosting] (z.B. http://127.0.0.1):
  • Wählen Sie in den Auth-Einstellungen Ihr Projekt aus und gehen Sie zu „Authorized Domains“ (Autorisierte Domains) [https://firebase.google.com/docs/auth/web/hosting]. Klicken Sie auf „Domain hinzufügen“ und fügen Sie Ihre lokale Domain der Liste hinzu.

c255098f12549886.png

Mit der Firebase CLI bereitstellen

  1. Achten Sie darauf, dass die Instanz-ID, Datenbank-ID und Dienst-ID in dataconnect/dataconnect.yaml mit Ihrem Projekt übereinstimmen:
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. Prüfen Sie, ob die Firebase CLI für Ihr Projekt eingerichtet ist.
npm i -g firebase-tools
firebase login --reauth
firebase use --add
  1. Führen Sie im Terminal den folgenden Befehl aus, um die Bereitstellung vorzunehmen:
firebase deploy --only dataconnect,hosting
  1. Führen Sie diesen Befehl aus, um die Schemaänderungen zu vergleichen:
firebase dataconnect:sql:diff
  1. Wenn die Änderungen akzeptabel sind, wenden Sie sie mit folgendem Befehl an:
firebase dataconnect:sql:migrate

Ihre Cloud SQL for PostgreSQL-Instanz wird mit dem endgültig bereitgestellten Schema und den Daten aktualisiert. Sie können den Status in der Firebase Console prüfen.

Sie sollten Ihre App jetzt unter your-project.web.app/ live sehen können. Außerdem können Sie im Bereich „Firebase Data Connect“ auf Ausführen (Produktion) klicken, um der Produktionsumgebung Daten hinzuzufügen, genau wie bei den lokalen Emulatoren.

10. Optional: Vektorsuche mit Firebase Data Connect

In diesem Abschnitt aktivieren Sie die Vektorsuche in Ihrer Filmrezensions-App mit Firebase Data Connect. Mit dieser Funktion sind inhaltsbasierte Suchanfragen möglich, z. B. die Suche nach Filmen mit ähnlichen Beschreibungen mithilfe von Vektoreinbettungen.

4b5aca5a447d2feb.png

Schema aktualisieren, um Einbettungen für ein Feld einzubeziehen

  1. Fügen Sie in dataconnect/schema/schema.gql der Tabelle Movie das Feld descriptionEmbedding hinzu:
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
}

Wichtige Erkenntnisse:

  • descriptionEmbedding: Vector @col(size:768): In diesem Feld werden die semantischen Einbettungen von Filmbeschreibungen gespeichert, wodurch eine vektorbasierte Inhaltssuche in Ihrer App möglich ist.

Vertex AI aktivieren

  1. Folgen Sie der Anleitung zu den Voraussetzungen, um Vertex AI APIs mit Google Cloud einzurichten. Dieser Schritt ist für die Unterstützung der Funktionen zur Generierung von Einbettungen und zur Vektorsuche erforderlich.
  2. Bereitstellen Sie Ihr Schema noch einmal, um pgvector und die Vektorsuche zu aktivieren. Klicken Sie dazu mit der VSCode-Erweiterung „Firebase Data Connect“ auf „In Produktion bereitstellen“.

Datenbank mit Einbettungen füllen

  1. Öffnen Sie den Ordner dataconnect in VS Code und klicken Sie in optional_vector_embed.gql auf Ausführen(lokal), um Ihre Datenbank mit Einbettungen für die Filme zu füllen.

b858da780f6ec103.png

Vektorsuchabfrage hinzufügen

  1. Fügen Sie in dataconnect/movie-connector/queries.gql die folgende Abfrage hinzu, um Vektorsuchen durchzuführen:
# 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
  }
}

Zusammenfassung:

  • compare_embed: Gibt das Einbettungsmodell (textembedding-gecko@003) und den Eingabetext ($query) für den Vergleich an.
  • method: Gibt die Ähnlichkeitsmethode (L2) an, die der euklidischen Entfernung entspricht.
  • within: Die Suche wird auf Filme mit einer L2-Distanz von maximal 2 beschränkt. Dabei wird der Schwerpunkt auf ähnliche Inhalte gelegt.
  • limit: Die Anzahl der zurückgegebenen Ergebnisse wird auf 5 beschränkt.

Vektorsuchfunktion in der App implementieren

  1. Entfernen Sie in app/src/lib/MovieService.ts das Kommentarzeichen bei den folgenden Importen:

Vektorsuchfunktion in der App implementieren

Nachdem Sie das Schema und die Abfrage eingerichtet haben, können Sie die Vektorsuche in die Dienstebene Ihrer App einbinden. Mit diesem Schritt können Sie die Suchanfrage über Ihre Webanwendung aufrufen.

Entfernen Sie in app/src/lib/ MovieService.ts die Kommentarzeichen vor den folgenden Importen aus den SDKs. Die Abfrage funktioniert dann wie jede andere Abfrage.

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

Fügen Sie die folgende Funktion hinzu, um die vektorbasierte Suche in die App einzubinden:

// 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;
  }
};


Zusammenfassung:

  • searchMoviesByDescription: Diese Funktion ruft die searchMovieDescriptionUsingL2similarity-Abfrage auf und übergibt den Eingabetext, um eine vektorbasierte Inhaltssuche durchzuführen.

In der Praxis

Gehen Sie in der Navigationsleiste zum Bereich „Vector Search“ (Vektorsuche) und geben Sie Begriffe wie „romantisch und modern“ ein. Es wird eine Liste mit Filmen angezeigt, die den von dir gesuchten Inhalten entsprechen. Du kannst auch die Detailseite eines Films aufrufen und unten im Bereich „Ähnliche Filme“ nachsehen.

7b71f1c75633c1be.png

11. Fazit

Glückwunsch, Sie sollten die Web-App jetzt verwenden können. Wenn Sie mit Ihren eigenen Filmdaten experimentieren möchten, können Sie diese mithilfe der FDC-Erweiterung einfügen, indem Sie die _insert.gql-Dateien nachahmen, oder sie über den Bereich „Data Connect Execution“ hinzufügen.

Weitere Informationen