1. Vorbereitung
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
- Git
- Visual Studio Code
- Node.js mit nvm-windows (Windows) oder nvm (macOS/Linux) installieren
- Falls noch nicht geschehen, erstellen Sie ein Firebase-Projekt in der Firebase Console.
- Optional: Führen Sie für die Vektorsuche ein Upgrade auf den Blaze-Tarif durch.
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
- Ö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.
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.
- Alternativ: Installieren Sie es über den Visual Studio Code Marketplace oder suchen Sie in VS Code danach.
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.
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.
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-Schemaconnector/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
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
- Öffnen Sie in VS Code
dataconnect/moviedata_insert.gql
. Prüfen Sie, ob die Emulatoren in der Firebase Data Connect-Erweiterung ausgeführt werden. - 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.
- Prüfen Sie im Terminal Data Connect Execution, ob die Daten erfolgreich hinzugefügt wurden.
Connector implementieren
- Öffnen Sie
dataconnect/movie-connector/queries.gql
. In den Kommentaren finden Sie eine einfacheListMovies
-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.
- 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.
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.
- 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 .
- Ersetzen Sie die Funktionen
handleGetTopMovies
undhandleGetLatestMovies
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.
Connectors implementieren
- Öffnen Sie
dataconnect/movie-connector/queries.gql
in Ihrem Projekt. - 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
}
}
}
- 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. Mitreviews_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 greiftactors_via_MovieActor
über die Join-Tabelle „MovieActor“ auf den Schauspielertyp zu und die Bedingungwhere
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.
Abfragen in die Webanwendung einbinden
- 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";
- Ersetzen Sie die Funktionen
handleGetMovieById
undhandleGetActorById
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.
Connectors implementieren
- Öffnen Sie
mutations.gql
indataconnect/movie-connector/
. - 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 wirdauth.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
- 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";
- Ersetzen Sie die Funktionen
handleAuthStateChange
undhandleGetCurrentUser
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 dieupsertUser
-Mutation aufgerufen, um die Informationen des Nutzers in der Datenbank zu erstellen oder zu aktualisieren.handleGetCurrentUser
: Ruft das Profil des aktuellen Nutzers mit der AbfragegetCurrentUser
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.
Connectors implementieren
- Öffnen Sie
mutations.gql
indataconnect/movie-connector/
. - 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"
: Verwendetauth.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.
- Öffnen Sie als Nächstes
dataconnect/movie-connector/
und öffnen Siequeries.gql
. - 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ührungstabellefavorite_movies
, ob ein bestimmter Film vom aktuellen Nutzer als Favorit markiert wurde.
Abfragen in die Webanwendung einbinden
- Entfernen Sie in
MovieService
(app/src/lib/MovieService.tsx
) das Kommentarzeichen bei den folgenden Importen:
import { addFavoritedMovie, deleteFavoritedMovie, getIfFavoritedMovie } from "@movie/dataconnect";
- Ersetzen Sie die Funktionen
handleAddFavoritedMovie
,handleDeleteFavoritedMovie
undhandleGetIfFavoritedMovie
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
undhandleDeleteFavoritedMovie
: Mithilfe der Mutationen kannst du einen Film sicher zu den Favoriten des Nutzers hinzufügen oder daraus entfernen.handleGetIfFavoritedMovie
: Prüft mit der AbfragegetIfFavoritedMovie
, 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
- 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
- Entfernen Sie in
MovieService
(app/src/lib/MovieService.tsx
) das Kommentarzeichen bei den folgenden Importen:
import { addReview, deleteReview } from "@movie/dataconnect";
- Ersetzen Sie die Funktionen
handleAddReview
undhandleDeleteReview
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 dieaddReview
-Mutation auf, um eine Rezension für den angegebenen Film hinzuzufügen und sie sicher mit dem authentifizierten Nutzer zu verknüpfen.handleDeleteReview
: Mit derdeleteReview
-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.
Connectors implementieren
- Öffnen Sie
queries.gql
indataconnect/movie-connector/
. - 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 wiereleaseYear
,rating
undgenre
gefiltert werden kann. contains
-Operator: Es wird nach teilweisen Textübereinstimmungen in Feldern gesucht. In dieser Abfrage wird nach Übereinstimmungen intitle
,description
,name
oderreviewText
gesucht.where
-Klausel: Gibt die Bedingungen für das Filtern von Daten an. In jedem Bereich (Filme, Schauspieler, Rezensionen) werden mit einerwhere
-Klausel die spezifischen Kriterien für die Suche definiert.
Abfragen in die Webanwendung einbinden
- Entfernen Sie in
MovieService
(app/src/lib/MovieService.tsx
) das Kommentarzeichen bei den folgenden Importen:
import { searchAll, SearchAllData } from "@movie/dataconnect";
- 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 AbfragesearchAll
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
- Erstellen Sie eine Webanwendung in der Firebase Console und notieren Sie sich die App-ID.
- 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. - Ersetzen Sie das
firebaseConfig
inapp/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" };
- 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
- Firebase Auth mit Google Log-in einrichten
- 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.
Mit der Firebase CLI bereitstellen
- 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"]
- Prüfen Sie, ob die Firebase CLI für Ihr Projekt eingerichtet ist.
npm i -g firebase-tools
firebase login --reauth
firebase use --add
- Führen Sie im Terminal den folgenden Befehl aus, um die Bereitstellung vorzunehmen:
firebase deploy --only dataconnect,hosting
- Führen Sie diesen Befehl aus, um die Schemaänderungen zu vergleichen:
firebase dataconnect:sql:diff
- 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.
Schema aktualisieren, um Einbettungen für ein Feld einzubeziehen
- Fügen Sie in
dataconnect/schema/schema.gql
der TabelleMovie
das FelddescriptionEmbedding
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
- 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.
- 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
- Öffnen Sie den Ordner
dataconnect
in VS Code und klicken Sie inoptional_vector_embed.gql
auf Ausführen(lokal), um Ihre Datenbank mit Einbettungen für die Filme zu füllen.
Vektorsuchabfrage hinzufügen
- 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
- 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 diesearchMovieDescriptionUsingL2similarity
-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.
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.