Tworzenie za pomocą Firebase Data Connect (w przeglądarce)

1. Zanim zaczniesz

Aplikacja FriendlyMovies

W tym laboratorium kodowania zintegrujesz Firebase Data Connect z bazą danych Cloud SQL, aby utworzyć aplikację internetową do pisania recenzji filmów. Gotowa aplikacja pokazuje, jak Firebase Data Connect upraszcza proces tworzenia aplikacji opartych na SQL. Obejmuje on te funkcje:

  • Uwierzytelnianie: wdróż niestandardowe uwierzytelnianie zapytań i mutacji w aplikacji, aby mieć pewność, że tylko upoważnieni użytkownicy mogą wchodzić w interakcje z Twoimi danymi.
  • Schemat GraphQL: twórz struktury danych i zarządzaj nimi za pomocą elastycznego schematu GraphQL dostosowanego do potrzeb aplikacji internetowej do recenzowania filmów.
  • Zapytania i mutacje SQL: pobieraj, aktualizuj i zarządzaj danymi w Cloud SQL za pomocą zapytań i mutacji obsługiwanych przez GraphQL.
  • Wyszukiwanie zaawansowane z dopasowaniem częściowym: używaj filtrów i opcji wyszukiwania, aby znajdować filmy na podstawie takich pól jak tytuł, opis czy tagi.
  • (Opcjonalnie) Integracja wyszukiwania wektorowego: dodaj funkcję wyszukiwania treści za pomocą wyszukiwania wektorowego w Firebase Data Connect, aby zapewnić użytkownikom bogate doświadczenia na podstawie danych wejściowych i preferencji.

Wymagania wstępne

Wymagana jest podstawowa znajomość JavaScriptu.

Czego się nauczysz

  • Skonfiguruj Firebase Data Connect za pomocą emulatorów lokalnych.
  • Zaprojektuj schemat danych za pomocą Data Connect i GraphQL.
  • Pisz i testuj różne zapytania i mutacje w aplikacji do recenzowania filmów.
  • Dowiedz się, jak Firebase Data Connect generuje i wykorzystuje pakiet SDK w aplikacji.
  • Wdrażaj schemat i skutecznie zarządzaj bazą danych.

Czego potrzebujesz

  • Git
  • Visual Studio Code
  • Zainstaluj Node.js za pomocą nvm-windows (Windows) lub nvm (macOS/Linux).
  • Jeśli jeszcze tego nie zrobiono, utwórz projekt Firebase w konsoli Firebase.
  • (Opcjonalnie) W przypadku wyszukiwania wektorowego przenieś projekt na abonament Blaze z płatnością według wykorzystania.

2. Konfigurowanie środowiska programistycznego

Na tym etapie laboratorium dowiesz się, jak skonfigurować środowisko, aby rozpocząć tworzenie aplikacji do recenzowania filmów za pomocą Firebase Data Connect.

  1. Sklonuj repozytorium projektu i zainstaluj wymagane zależności:
    git clone https://github.com/firebaseextended/codelab-dataconnect-web
    cd codelab-dataconnect-web
    cd ./app && npm i
    npm run dev
    
  2. Po uruchomieniu tych poleceń otwórz w przeglądarce adres http://localhost:5173, aby zobaczyć działającą lokalnie aplikację internetową. Jest to interfejs użytkownika, który umożliwia tworzenie aplikacji do oceniania filmów i korzystanie z jej funkcji.93f6648a2532c606.png
  3. Otwórz sklonowany folder codelab-dataconnect-web w Visual Studio Code. W tym miejscu możesz zdefiniować schemat, napisać zapytania i przetestować funkcje aplikacji.
  4. Aby korzystać z funkcji Data Connect, zainstaluj rozszerzenie Firebase Data Connect Visual Studio.
    Możesz też zainstalować rozszerzenie z Visual Studio Code Marketplace lub wyszukać je w VS Code.b03ee38c9a81b648.png
  5. Otwórz lub utwórz nowy projekt Firebase w konsoli Firebase.
  6. Połącz projekt Firebase z rozszerzeniem Firebase Data Connect VSCode. W rozszerzeniu wykonaj te czynności:
    1. Kliknij przycisk Zaloguj się.
    2. Kliknij Połącz projekt Firebase i wybierz projekt Firebase.
    4bb2fbf8f9fac29b.png
  7. Uruchom emulatory Firebase za pomocą rozszerzenia Firebase Data Connect do VS Code:
    kliknij Start Emulators, a potem sprawdź, czy emulatory działają w terminalu.6d3d95f4cb708db1.png

3. Sprawdź początkową bazę kodu

W tej sekcji poznasz kluczowe obszary początkowej bazy kodu aplikacji. Chociaż w aplikacji brakuje niektórych funkcji, warto poznać jej ogólną strukturę.

Struktura folderów i plików

W podsekcjach poniżej znajdziesz omówienie struktury folderów i plików aplikacji.

Katalog dataconnect/

Zawiera konfiguracje Firebase Data Connect, łączniki (które definiują zapytania i mutacje) oraz pliki schematu.

  • schema/schema.gql: definiuje schemat GraphQL.
  • connector/queries.gql: zapytania potrzebne w aplikacji
  • connector/mutations.gql: zmiany wymagane w aplikacji
  • connector/connector.yaml: plik konfiguracyjny do generowania pakietu SDK

Katalog app/src/

Zawiera logikę aplikacji i interakcję z Firebase Data Connect.

  • firebase.ts: konfiguracja połączenia z aplikacją Firebase w projekcie Firebase.
  • lib/dataconnect-sdk/: zawiera wygenerowany pakiet SDK. Możesz edytować lokalizację generowania pakietu SDK w pliku connector/connector.yaml, a pakiety SDK będą generowane automatycznie za każdym razem, gdy zdefiniujesz zapytanie lub mutację.

4. Określanie schematu recenzji filmów

W tej sekcji zdefiniujesz strukturę i relacje między kluczowymi elementami aplikacji do obsługi filmów w schemacie. Jednostki takie jak Movie, User, ActorReview są mapowane na tabele bazy danych, a relacje są tworzone za pomocą dyrektyw schematu Firebase Data Connect i GraphQL. Po wdrożeniu tej funkcji aplikacja będzie gotowa do obsługi wszystkich działań, od wyszukiwania najlepiej ocenianych filmów i filtrowania ich według gatunku po umożliwianie użytkownikom dodawania opinii, oznaczania ulubionych filmów, przeglądania podobnych filmów i znajdowania polecanych filmów na podstawie wpisanego tekstu za pomocą wyszukiwania wektorowego.

Podstawowe elementy i relacje

Typ Movie zawiera kluczowe informacje, takie jak tytuł, gatunek i tagi, których aplikacja używa do wyszukiwania i tworzenia profili filmów. Typ User śledzi interakcje użytkowników, takie jak opinie i dodawanie do ulubionych. Reviews łączyć użytkowników z filmami, umożliwiając aplikacji wyświetlanie ocen i opinii generowanych przez użytkowników;

Relacje między filmami, aktorami i użytkownikami sprawiają, że aplikacja jest bardziej dynamiczna. Tabela MovieActor dołączania pomaga wyświetlać szczegóły obsady i filmografię aktorów. Typ FavoriteMovie umożliwia użytkownikom dodawanie filmów do ulubionych, dzięki czemu aplikacja może wyświetlać spersonalizowaną listę ulubionych i wyróżniać popularne wybory.

Konfigurowanie tabeli Movie

Typ Movie określa główną strukturę elementu filmu, w tym pola takie jak title, genre, releaseYear i rating.

Skopiuj i wklej fragment kodu do pliku 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]
}

Podsumowanie:

  • id: unikalny identyfikator UUID każdego filmu wygenerowany za pomocą funkcji @default(expr: "uuidV4()").

Konfigurowanie tabeli MovieMetadata

Typ MovieMetadata tworzy relację jeden do jednego z typem Movie. Zawiera dodatkowe dane, np. imię i nazwisko reżysera filmu.

Skopiuj i wklej fragment kodu do pliku 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
}

Podsumowanie:

  • Film! @ref: odwołuje się do typu Movie, tworząc relację klucza obcego.

Konfigurowanie tabeli Actor

Skopiuj i wklej fragment kodu do pliku dataconnect/schema/schema.gql:

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

Typ Actor reprezentuje aktora w bazie filmów, gdzie każdy aktor może być częścią wielu filmów, tworząc relację wiele-do-wielu.

Konfigurowanie tabeli MovieActor

Skopiuj i wklej fragment kodu do pliku 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"
}

Podsumowanie:

  • movie: odwołuje się do typu Movie, niejawnie generuje klucz obcy movieId: UUID!.
  • actor: odwołuje się do typu Actor, niejawnie generuje klucz obcy actorId: UUID!.
  • role:określa rolę aktora w filmie (np. „główny” lub „pomocniczy”).

Konfigurowanie tabeli User

Typ User określa użytkownika, który wchodzi w interakcję z filmami, dodając opinie lub oznaczając je jako ulubione.

Skopiuj i wklej fragment kodu do pliku dataconnect/schema/schema.gql:

type User
  @table {
  id: String! @col
  username: String! @col(dataType: "varchar(50)")
  # The following are generated from the @ref in the Review table
  # reviews_on_user
  # movies_via_Review
}

Konfigurowanie tabeli FavoriteMovie

Typ FavoriteMovie to tabela złączeń, która obsługuje relacje wiele do wielu między użytkownikami a ich ulubionymi filmami. Każda tabela łączy UserMovie.

Skopiuj i wklej fragment kodu do pliku 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!
}

Podsumowanie:

  • movie: odwołuje się do typu Movie i niejawnie generuje klucz obcy movieId: UUID!.
  • user: odnosi się do typu użytkownika i niejawnie generuje klucz obcy userId: UUID!.

Konfigurowanie tabeli Review

Typ Review reprezentuje encję opinii i łączy typy UserMovierelacji wiele do wielu (jeden użytkownik może zostawić wiele opinii, a każdy film może mieć wiele opinii).

Skopiuj i wklej fragment kodu do pliku 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")
}

Podsumowanie:

  • user:odnosi się do użytkownika, który dodał opinię.
  • movie: odnosi się do recenzowanego filmu.
  • reviewDate: automatycznie ustawiana na czas utworzenia opinii za pomocą @default(expr: "request.time").

Pola generowane automatycznie i wartości domyślne

Schemat używa wyrażeń takich jak @default(expr: "uuidV4()") do automatycznego generowania unikalnych identyfikatorów i sygnatur czasowych. Na przykład pole id w typach Movie i Review jest automatycznie wypełniane identyfikatorem UUID podczas tworzenia nowego rekordu.

Teraz, gdy schemat jest zdefiniowany, aplikacja do filmów ma solidną podstawę dla struktury danych i relacji.

5. Pobieranie najpopularniejszych i najnowszych filmów

Aplikacja FriendlyMovies

W tej sekcji wstawisz przykładowe dane filmów do lokalnych emulatorów, a następnie zaimplementujesz konektory (zapytania) i kod TypeScript, aby wywoływać te konektory w aplikacji internetowej. Na koniec Twoja aplikacja będzie mogła dynamicznie pobierać i wyświetlać najwyżej oceniane i najnowsze filmy bezpośrednio z bazy danych.

Wstaw przykładowe dane filmu, aktora i recenzji

  1. W VSCode otwórz dataconnect/moviedata_insert.gql. Sprawdź, czy emulatory w rozszerzeniu Firebase Data Connect działają.
  2. U góry pliku powinien być widoczny przycisk Uruchom (lokalnie). Kliknij ten przycisk, aby wstawić przykładowe dane filmów do bazy danych.
    e424f75e63bf2e10.png
  3. Sprawdź terminal Data Connect Execution, aby potwierdzić, że dane zostały dodane.
    e0943d7704fb84ea.png

Implementowanie oprogramowania sprzęgającego

  1. Otwórz pokój dataconnect/movie-connector/queries.gql. W komentarzach znajdziesz podstawowe zapytanie ListMovies:
    query ListMovies @auth(level: PUBLIC) {
      movies {
        id
        title
        imageUrl
        releaseYear
        genre
        rating
        tags
        description
      }
    }
    
    To zapytanie pobiera wszystkie filmy i ich szczegóły (np. id, title, releaseYear). Nie sortuje jednak filmów.
  2. Zastąp istniejące zapytanie ListMovies poniższym zapytaniem, aby dodać opcje sortowania i limitu:
    # 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
      }
    }
    
  3. Aby wykonać zapytanie w lokalnej bazie danych, kliknij przycisk Uruchom (lokalnie). Zanim uruchomisz zapytanie, możesz też wpisać zmienne w panelu konfiguracji.
    c4d947115bb11b16.png

Podsumowanie:

  • movies(): pole zapytania GraphQL do pobierania danych o filmach z bazy danych.
  • orderByRating: parametr sortowania filmów według oceny (rosnąco lub malejąco).
  • orderByReleaseYear: parametr sortowania filmów według roku premiery (rosnąco lub malejąco).
  • limit: ogranicza liczbę zwracanych filmów.

Integrowanie zapytań w aplikacji internetowej

W tej części kursu użyjesz w aplikacji internetowej zapytań zdefiniowanych w poprzedniej sekcji. Emulatory Firebase Data Connect generują pakiety SDK na podstawie informacji zawartych w plikach .gql (a konkretnie w plikach schema.gql, queries.gqlmutations.gql) oraz w pliku connector.yaml. Możesz wywoływać te pakiety SDK bezpośrednio w aplikacji.

  1. W pliku MovieService (app/src/lib/MovieService.tsx) usuń znak komentarza ze stwierdzenia importu u góry:
    import { listMovies, ListMoviesData, OrderDirection } from "@movie/dataconnect";
    
    Funkcja listMovies, typ odpowiedzi ListMoviesData i wyliczenie OrderDirection to pakiety SDK wygenerowane przez emulatory Firebase Data Connect na podstawie schematu i zdefiniowanych wcześniej zapytań .
  2. Zastąp funkcje handleGetTopMovieshandleGetLatestMovies tym kodem:
    // 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;
      }
    };
    

Podsumowanie:

  • listMovies: automatycznie wygenerowana funkcja, która wywołuje zapytanie listMovies w celu pobrania listy filmów. Obejmuje opcje sortowania według oceny lub roku premiery oraz ograniczenia liczby wyników.
  • ListMoviesData: typ wyniku używany do wyświetlania 10 najpopularniejszych i najnowszych filmów na stronie głównej aplikacji.

Zobacz, jak to działa

Aby zobaczyć działanie zapytania, ponownie załaduj aplikację internetową. Strona główna wyświetla teraz dynamiczną listę filmów, pobierając dane bezpośrednio z lokalnej bazy danych. Najwyżej oceniane i najnowsze filmy będą wyświetlane bezproblemowo, odzwierciedlając dane, które właśnie zostały skonfigurowane.

6. Wyświetlanie szczegółów filmu i aktora

W tej sekcji zaimplementujesz funkcję pobierania szczegółowych informacji o filmie lub aktorze za pomocą ich unikalnych identyfikatorów. Obejmuje to nie tylko pobieranie danych z odpowiednich tabel, ale także łączenie powiązanych tabel w celu wyświetlania szczegółowych informacji, takich jak recenzje filmów i filmografie aktorów.

ac7fefa7ff779231.png

Implementowanie złączy

  1. Otwórz dataconnect/movie-connector/queries.gql w projekcie.
  2. Dodaj te zapytania, aby pobrać szczegóły filmu i aktora:
    # 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
        }
      }
    }
    
  3. Zapisz zmiany i sprawdź zapytania.

Podsumowanie:

  • movie()/actor(): pola zapytania GraphQL do pobierania pojedynczego filmu lub aktora z tabeli Movies lub Actors.
  • _on_: umożliwia bezpośredni dostęp do pól z powiązanego typu, który ma relację klucza obcego. Na przykład reviews_on_movie pobiera wszystkie recenzje związane z określonym filmem.
  • _via_: służy do poruszania się po relacjach wiele-do-wielu za pomocą tabeli złączeń. Na przykład actors_via_MovieActor uzyskuje dostęp do typu Actor za pomocą tabeli złączeń MovieActor, a warunek where filtruje aktorów na podstawie ich roli (np. „główna” lub „drugoplanowa”).

Testowanie zapytania przez wprowadzenie danych testowych

  1. W panelu wykonania Data Connect możesz przetestować zapytanie, wpisując przykładowe identyfikatory, takie jak:
    {"id": "550e8400-e29b-41d4-a716-446655440000"}
    
  2. Kliknij Uruchom (lokalnie) w przypadku GetMovieById, aby pobrać szczegóły filmu „Paradoks kwantowy” (fikcyjnego filmu, z którym powiązany jest powyższy identyfikator).

1b08961891e44da2.png

Integrowanie zapytań w aplikacji internetowej

  1. W pliku MovieService (app/src/lib/MovieService.tsx) usuń komentarz z tych importów:
    import { getMovieById, GetMovieByIdData } from "@movie/dataconnect";
    import { GetActorByIdData, getActorById } from "@movie/dataconnect";
    
  2. Zastąp funkcje handleGetMovieByIdhandleGetActorById tym kodem:
    // 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;
      }
    };
    

Podsumowanie:

  • getMovieById / getActorById: są to generowane automatycznie funkcje, które wywołują zdefiniowane przez Ciebie zapytania, aby pobrać szczegółowe informacje o konkretnym filmie lub aktorze.
  • GetMovieByIdData / GetActorByIdData: to typy wyników używane do wyświetlania w aplikacji szczegółów dotyczących filmów i aktorów.

Zobacz, jak to działa

Teraz otwórz stronę główną aplikacji internetowej. Kliknij film, aby wyświetlić wszystkie jego szczegóły, w tym aktorów i recenzje – informacje pobrane z powiązanych tabel. Podobnie kliknięcie aktora spowoduje wyświetlenie filmów, w których wystąpił.

7. Obsługa uwierzytelniania użytkowników

W tej sekcji zaimplementujesz funkcje logowania i wylogowywania użytkowników za pomocą uwierzytelniania Firebase. Będziesz też używać danych Uwierzytelniania Firebase do bezpośredniego pobierania lub wstawiania danych użytkowników w Firebase DataConnect, co zapewni bezpieczne zarządzanie użytkownikami w aplikacji.

9890838045d5a00e.png

Implementowanie złączy

  1. Otwórz mutations.gql w dataconnect/movie-connector/.
  2. Aby utworzyć lub zaktualizować bieżącego uwierzytelnionego użytkownika, dodaj tę mutację:
    # Create or update the current authenticated user
    mutation UpsertUser($username: String!) @auth(level: USER) {
      user_upsert(
        data: {
          id_expr: "auth.uid"
          username: $username
        }
      )
    }
    

Podsumowanie:

  • id_expr: "auth.uid": korzysta z auth.uid, który jest dostarczany bezpośrednio przez Uwierzytelnianie Firebase, a nie przez użytkownika lub aplikację. Zapewnia to dodatkową warstwę zabezpieczeń, ponieważ identyfikator użytkownika jest obsługiwany bezpiecznie i automatycznie.

Pobieranie bieżącego użytkownika

  1. Otwórz queries.gql w dataconnect/movie-connector/.
  2. Dodaj to zapytanie, aby pobrać bieżącego użytkownika:
    # 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
            }
          }
        }
      }
    }
    

Podsumowanie:

  • auth.uid: jest pobierany bezpośrednio z usługi Uwierzytelnianie Firebase, co zapewnia bezpieczny dostęp do danych konkretnego użytkownika.
  • _on_ pola: te pola reprezentują tabele złączeń:
    • : pobiera wszystkie recenzje związane z użytkownikiem, w tym idtitle filmu.reviews_on_user
    • favorite_movies_on_user: Pobiera wszystkie filmy oznaczone przez użytkownika jako ulubione, w tym szczegółowe informacje, takie jak genre, releaseYear, ratingmetadata.

Integrowanie zapytań w aplikacji internetowej

  1. W pliku MovieService (app/src/lib/MovieService.tsx) usuń komentarz z tych importów:
    import { upsertUser } from "@movie/dataconnect";
    import { getCurrentUser, GetCurrentUserData } from "@movie/dataconnect";
    
  2. Zastąp funkcje handleAuthStateChangehandleGetCurrentUser tym kodem:
    // 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;
      }
    };
    

Podsumowanie:

  • handleAuthStateChange: ta funkcja nasłuchuje zmian stanu uwierzytelniania. Gdy użytkownik się zaloguje, usługa ustawia jego dane i wywołuje mutację upsertUser, aby utworzyć lub zaktualizować informacje o użytkowniku w bazie danych.
  • handleGetCurrentUser: pobiera profil bieżącego użytkownika za pomocą zapytania getCurrentUser, które zwraca opinie użytkownika i jego ulubione filmy.

Zobacz, jak to działa

Teraz na pasku nawigacyjnym kliknij przycisk „Zaloguj się przez Google”. Możesz zalogować się przy użyciu emulatora Uwierzytelniania Firebase. Po zalogowaniu się kliknij „Mój profil”. Na razie będzie ona pusta, ale masz już podstawy do obsługi danych specyficznych dla użytkownika w aplikacji.

8. Wdrażanie interakcji użytkownika

W tej sekcji laboratorium kodu zaimplementujesz interakcje użytkownika w aplikacji do oceniania filmów, w szczególności umożliwisz użytkownikom zarządzanie ulubionymi filmami oraz dodawanie i usuwanie opinii.

b3d0ac1e181c9de9.png

Umożliwianie użytkownikowi dodawania filmu do ulubionych

W tej sekcji skonfigurujesz bazę danych, aby umożliwić użytkownikom dodawanie filmów do ulubionych.

Implementowanie złączy

  1. Otwórz mutations.gql w dataconnect/movie-connector/.
  2. Dodaj te mutacje, aby obsługiwać dodawanie filmów do ulubionych:
    # 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 })
    }
    
    

Podsumowanie:

  • userId_expr: "auth.uid": korzysta z auth.uid, który jest dostarczany bezpośrednio przez Uwierzytelnianie Firebase, co gwarantuje, że dostęp do danych uwierzytelnionego użytkownika lub ich modyfikacja są możliwe tylko dla tego użytkownika.

Sprawdzanie, czy film został dodany do ulubionych

  1. Otwórz queries.gql w dataconnect/movie-connector/.
  2. Dodaj to zapytanie, aby sprawdzić, czy film jest oznaczony jako ulubiony:
    query GetIfFavoritedMovie($movieId: UUID!) @auth(level: USER) {
      favorite_movie(key: { userId_expr: "auth.uid", movieId: $movieId }) {
        movieId
      }
    }
    

Podsumowanie:

  • auth.uid: zapewnia bezpieczny dostęp do danych konkretnych użytkowników za pomocą Uwierzytelniania Firebase.
  • favorite_movie: sprawdza favorite_movies tabelę złączeń, aby zobaczyć, czy określony film jest oznaczony jako ulubiony przez bieżącego użytkownika.

Integrowanie zapytań w aplikacji internetowej

  1. W pliku MovieService (app/src/lib/MovieService.tsx) usuń komentarz z tych importów:
    import { addFavoritedMovie, deleteFavoritedMovie, getIfFavoritedMovie } from "@movie/dataconnect";
    
  2. Zastąp funkcje handleAddFavoritedMovie, handleDeleteFavoritedMoviehandleGetIfFavoritedMovie tym kodem:
    // 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;
      }
    };
    

Podsumowanie:

  • handleAddFavoritedMoviehandleDeleteFavoritedMovie: użyj mutacji, aby bezpiecznie dodać lub usunąć film z ulubionych użytkownika.
  • handleGetIfFavoritedMovie: używa zapytania getIfFavoritedMovie, aby sprawdzić, czy film jest oznaczony przez użytkownika jako ulubiony.

Zobacz, jak to działa

Teraz możesz dodawać filmy do ulubionych i usuwać je z nich, klikając ikonę serca na kartach filmów i na stronie z informacjami o filmie. Na stronie profilu możesz też wyświetlić ulubione filmy.

Zezwalanie użytkownikom na dodawanie i usuwanie opinii

Następnie zaimplementujesz w aplikacji sekcję do zarządzania opiniami użytkowników.

Implementowanie złączy

W mutations.gql (dataconnect/movie-connector/mutations.gql) dodaj te mutacje:

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

Podsumowanie:

  • userId_expr: "auth.uid": zapewnia powiązanie opinii z uwierzytelnionym użytkownikiem.
  • reviewDate_date: { today: true }: automatycznie generuje bieżącą datę opinii za pomocą DataConnect, co eliminuje konieczność ręcznego wprowadzania danych.

Integrowanie zapytań w aplikacji internetowej

  1. W pliku MovieService (app/src/lib/MovieService.tsx) usuń komentarz z tych importów:
    import { addReview, deleteReview } from "@movie/dataconnect";
    
  2. Zastąp funkcje handleAddReviewhandleDeleteReview tym kodem:
    // 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;
      }
    };
    

Podsumowanie:

  • handleAddReview: wywołuje mutację addReview, aby dodać opinię o określonym filmie, bezpiecznie łącząc ją z uwierzytelnionym użytkownikiem.
  • handleDeleteReview: używa mutacji deleteReview, aby usunąć recenzję filmu napisaną przez uwierzytelnionego użytkownika.

Zobacz, jak to działa

Użytkownicy mogą teraz dodawać opinie o filmach na stronie z informacjami o filmie. Mogą też przeglądać i usuwać swoje opinie na stronie profilu, co daje im pełną kontrolę nad interakcjami z aplikacją.

9. Filtry zaawansowane i dopasowanie częściowe tekstu

W tej sekcji wdrożysz zaawansowane funkcje wyszukiwania, które pozwolą użytkownikom wyszukiwać filmy na podstawie zakresu ocen i lat premiery, filtrować je według gatunków i tagów, wykonywać częściowe dopasowywanie tekstu w tytułach lub opisach, a nawet łączyć wiele filtrów, aby uzyskać bardziej precyzyjne wyniki.

ece70ee0ab964e28.png

Implementowanie złączy

  1. Otwórz plik queries.gql w usłudze dataconnect/movie-connector/.
  2. Dodaj to zapytanie, aby obsługiwać różne funkcje wyszukiwania:
    # 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
        }
      }
    }
    

Podsumowanie:

  • Operator _and: łączy kilka warunków w jednym zapytaniu, umożliwiając filtrowanie wyszukiwania według kilku pól, takich jak releaseYear, ratinggenre.
  • Operator contains: wyszukuje częściowe dopasowania tekstu w polach. W tym zapytaniu wyszukiwane są dopasowania w polach title, description, name lub reviewText.
  • where clause: określa warunki filtrowania danych. Każda sekcja (filmy, aktorzy, recenzje) używa klauzuli where, aby zdefiniować konkretne kryteria wyszukiwania.

Integrowanie zapytań w aplikacji internetowej

  1. W pliku MovieService (app/src/lib/MovieService.tsx) usuń komentarz z tych importów:
    import { searchAll, SearchAllData } from "@movie/dataconnect";
    
  2. Zastąp funkcję handleSearchAll tym kodem:
    // 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;
      }
    };
    

Podsumowanie:

  • handleSearchAll: ta funkcja używa zapytania searchAll do wyszukiwania na podstawie danych wprowadzonych przez użytkownika i filtrowania wyników według parametrów takich jak rok, ocena, gatunek i częściowe dopasowania tekstu.

Zobacz, jak to działa

W aplikacji internetowej otwórz stronę „Szukanie zaawansowane” na pasku nawigacyjnym. Możesz teraz wyszukiwać filmy, aktorów i recenzje, korzystając z różnych filtrów i danych wejściowych, aby uzyskać szczegółowe i dopasowane wyniki wyszukiwania.

10. Opcjonalnie: wdróż w Cloud (wymagane płatności)

Po zakończeniu iteracji lokalnego programowania przyszedł czas na wdrożenie schematu, danych i zapytań na serwerze. Możesz to zrobić za pomocą rozszerzenia Firebase Data Connect VS Code lub wiersza poleceń Firebase.

Uaktualnianie abonamentu Firebase

Aby zintegrować Firebase Data Connect z Cloud SQL for PostgreSQL, Twój projekt Firebase musi korzystać z abonamentu płatności za wykorzystanie (Blaze), co oznacza, że jest połączony z kontem rozliczeniowym Cloud.

  • Konto rozliczeniowe Cloud wymaga formy płatności, np. karty kredytowej.
  • Jeśli dopiero zaczynasz korzystać z Firebase i Google Cloud, sprawdź, czy możesz otrzymać środki w wysokości 300 USD i bezpłatne konto rozliczeniowe Cloud.
  • Jeśli wykonujesz te ćwiczenia w ramach wydarzenia, zapytaj organizatora, czy są dostępne środki w Google Cloud.

Aby przenieść projekt na abonament Blaze:

  1. W konsoli Firebase wybierz przejście na wyższy abonament.
  2. Wybierz pakiet Blaze. Postępuj zgodnie z instrukcjami wyświetlanymi na ekranie, aby połączyć konto rozliczeniowe Cloud z projektem.
    Jeśli w ramach tego przejścia na wyższy abonament konieczne było utworzenie konta rozliczeniowego Cloud, może być konieczne powrócenie do procesu przejścia na wyższy abonament w konsoli Firebase, aby go dokończyć.

Łączenie aplikacji internetowej z projektem Firebase

  1. Zarejestruj aplikację internetową w projekcie Firebase za pomocą konsoli Firebase:
    1. Otwórz projekt, a następnie kliknij Dodaj aplikację.
    2. Na razie zignoruj instalację i konfigurację pakietu SDK, ale skopiuj wygenerowany obiekt firebaseConfig.
    7030822793e4d75b.png
  2. Zastąp istniejący ciąg firebaseConfig w pliku app/src/lib/firebase.tsx konfiguracją skopiowaną z konsoli Firebase.
    const firebaseConfig = {
      apiKey: "API_KEY",
      authDomain: "PROJECT_ID.firebaseapp.com",
      projectId: "PROJECT_ID",
      storageBucket: "PROJECT_ID.firebasestorage.app",
      messagingSenderId: "SENDER_ID",
      appId: "APP_ID"
    };
    
  3. Utwórz aplikację internetową: wróć do VS Code i w folderze app użyj Vite, aby utworzyć aplikację internetową na potrzeby wdrożenia hostingu:
    cd app
    npm run build
    

Konfigurowanie uwierzytelniania Firebase w projekcie Firebase

  1. Skonfiguruj Firebase Authentication za pomocą logowania przez Google.62af2f225e790ef6.png
  2. (Opcjonalnie) Zezwól na domeny dla Uwierzytelniania Firebase za pomocą konsoli Firebase (np. http://127.0.0.1).
    1. W ustawieniach Uwierzytelnianie otwórz Autoryzowane domeny.
    2. Kliknij „Dodaj domenę” i dodaj do listy swoją domenę lokalną.

c255098f12549886.png

Wdrażanie za pomocą wiersza poleceń Firebase

  1. dataconnect/dataconnect.yaml sprawdź, czy identyfikator instancji, baza danych i identyfikator usługi pasują do Twojego projektu:
    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"]
    
  2. Sprawdź, czy wiersz poleceń Firebase jest skonfigurowany w projekcie:
    npm i -g firebase-tools
    firebase login --reauth
    firebase use --add
    
  3. Aby wdrożyć aplikację, w terminalu uruchom to polecenie:
    firebase deploy --only dataconnect,hosting
    
  4. Aby porównać zmiany w schemacie, uruchom to polecenie:
    firebase dataconnect:sql:diff
    
  5. Jeśli zmiany są akceptowalne, zastosuj je za pomocą tego polecenia:
    firebase dataconnect:sql:migrate
    

Instancja Cloud SQL for PostgreSQL zostanie zaktualizowana o ostateczną wdrożoną strukturę i dane. Stan możesz śledzić w konsoli Firebase.

Aplikacja powinna być teraz widoczna na stronie your-project.web.app/. Możesz też kliknąć Uruchom (wersja produkcyjna) w panelu Firebase Data Connect, tak jak w przypadku lokalnych emulatorów, aby dodać dane do środowiska produkcyjnego.

11. Opcjonalnie: wyszukiwanie wektorowe za pomocą Firebase Data Connect (wymaga płatności)

W tej sekcji włączysz wyszukiwanie wektorowe w aplikacji do oceniania filmów za pomocą Firebase Data Connect. Ta funkcja umożliwia wyszukiwanie na podstawie treści, np. znajdowanie filmów o podobnych opisach za pomocą wektorów dystrybucyjnych.

Ten krok wymaga ukończenia ostatniego kroku tego ćwiczenia, czyli wdrożenia w Google Cloud.

4b5aca5a447d2feb.png

Aktualizowanie schematu w celu uwzględnienia osadzania w przypadku pola

dataconnect/schema/schema.gql dodaj pole descriptionEmbedding do tabeli Movie:

type Movie
  # The below parameter values are generated by default with @table, and can be edited manually.
  @table {
  # implicitly calls @col to generates a column name. ex: @col(name: "movie_id")
  id: UUID! @default(expr: "uuidV4()")
  title: String!
  imageUrl: String!
  releaseYear: Int
  genre: String
  rating: Float
  description: String
  tags: [String]
  descriptionEmbedding: Vector @col(size:768) # Enables vector search
}

Podsumowanie:

  • descriptionEmbedding: Vector @col(size:768): to pole przechowuje semantyczne osadzanie opisów filmów, co umożliwia wyszukiwanie treści w aplikacji na podstawie wektorów.

Aktywowanie Vertex AI

  1. Aby skonfigurować interfejsy Vertex AI API w Google Cloud, postępuj zgodnie z przewodnikiem po wymaganiach wstępnych. Ten krok jest niezbędny do obsługi generowania wektorów dystrybucyjnych i funkcji wyszukiwania wektorowego.
  2. Ponownie wdróż schemat, aby aktywować pgvector i wyszukiwanie wektorowe. W tym celu kliknij „Deploy to Production” (Wdróż w środowisku produkcyjnym) w rozszerzeniu Firebase Data Connect do VS Code.

Wypełnianie bazy danych wektorami dystrybucyjnymi

  1. Otwórz folder dataconnect w VS Code.
  2. Kliknij Uruchom(lokalnie)optional_vector_embed.gql, aby wypełnić bazę danych wektorami osadzania filmów.

b858da780f6ec103.png

Dodawanie zapytania w ramach wyszukiwania wektorowego

W polu dataconnect/movie-connector/queries.gql dodaj to zapytanie, aby przeprowadzić wyszukiwanie wektorowe:

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

Podsumowanie:

  • compare_embed: określa model osadzania (textembedding-gecko@003) i tekst wejściowy ($query) do porównania.
  • method: określa metodę podobieństwa (L2), która reprezentuje odległość euklidesową.
  • within: ogranicza wyszukiwanie do filmów o odległości L2 wynoszącej 2 lub mniej, koncentrując się na bliskich dopasowaniach treści.
  • limit: ogranicza liczbę zwracanych wyników do 5.

Wdrażanie funkcji wyszukiwania wektorowego w aplikacji

Po skonfigurowaniu schematu i zapytania zintegruj wyszukiwanie wektorowe z warstwą usług aplikacji. Ten krok umożliwia wywołanie zapytania z aplikacji internetowej.

  1. app/src/lib/ MovieService.ts odkomentuj te importy z pakietów SDK. Będzie to działać jak każde inne zapytanie.
    import {
      searchMovieDescriptionUsingL2similarity,
      SearchMovieDescriptionUsingL2similarityData,
    } from "@movie/dataconnect";
    
  2. Dodaj tę funkcję, aby zintegrować wyszukiwanie oparte na wektorach z aplikacją:
    // 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;
      }
    };
    

Podsumowanie:

  • searchMoviesByDescription: ta funkcja wywołuje zapytanie searchMovieDescriptionUsingL2similarity, przekazując tekst wejściowy w celu przeprowadzenia wyszukiwania treści na podstawie wektorów.

Zobacz, jak to działa

Na pasku nawigacyjnym otwórz sekcję „Wyszukiwanie wektorowe” i wpisz frazy takie jak „romantyczny i nowoczesny”. Zobaczysz listę filmów pasujących do wyszukiwanych treści. Możesz też otworzyć stronę z informacjami o dowolnym filmie i sprawdzić sekcję podobnych filmów u dołu strony.

7b71f1c75633c1be.png

12. Podsumowanie

Gratulujemy, możesz już korzystać z aplikacji internetowej. Jeśli chcesz użyć własnych danych o filmach, nie martw się – wstaw je za pomocą rozszerzenia Firebase Data Connect, naśladując _insert.gql pliki, lub dodaj je w panelu wykonania Data Connect w VS Code.

Więcej informacji