Tworzenie z użyciem Firebase Data Connect

1. Zanim zaczniesz

Aplikacja FriendlyMovies

W tym laboratorium programistycznym zintegrujesz Firebase Data Connect z bazą danych Cloud SQL, aby utworzyć aplikację internetową do przeglądania filmów. Ukończona aplikacja pokazuje, jak Firebase Data Connect upraszcza proces tworzenia aplikacji opartych na SQL. Obejmuje ona te funkcje:

  • Uwierzytelnianie: zaimplementuj niestandardowe uwierzytelnianie w przypadku zapytań i mutacji w aplikacji, aby mieć pewność, że tylko upoważnione osoby będą mieć dostęp do Twoich danych.
  • Schemat GraphQL: twórz struktury danych i zarządzaj nimi za pomocą elastycznego schematu GraphQL dostosowywanego do potrzeb aplikacji internetowej z recenzjami 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 uwzględnieniem częściowego dopasowania ciągu znaków: użyj filtrów i opcji wyszukiwania, aby znaleźć filmy na podstawie pól takich jak tytuł, opis czy tagi.
  • (Opcjonalnie) Integracja z wyszukiwaniem wektorów: dodaj funkcję wyszukiwania treści za pomocą wyszukiwania wektorów w Firebase Data Connect, aby zapewnić użytkownikom bogatsze wrażenia na podstawie ich preferencji i preferencji.

Wymagania wstępne

Musisz mieć podstawową znajomość języka JavaScript.

Czego się nauczysz

  • Konfigurowanie Firebase Data Connect za pomocą emulatorów lokalnych.
  • Zaprojektuj schemat danych za pomocą Data Connect i GraphQL.
  • Napisz i przetestuj różne zapytania i mutacje dla aplikacji z opiniami o filmach.
  • Dowiedz się, jak Firebase Data Connect generuje pakiet SDK i z niego korzysta w aplikacji.
  • Wdróż 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 nie masz jeszcze projektu Firebase, utwórz go w konsoli Firebase.
  • (Opcjonalnie) W przypadku wyszukiwania wektorowego przenieś projekt na abonament Blaze z taryfą „Płać według wykorzystania”.

2. Konfigurowanie środowiska programistycznego

Na tym etapie tego samouczka dowiesz się, jak skonfigurować środowisko, aby zacząć tworzyć aplikację do tworzenia recenzji 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 wykonaniu tych poleceń otwórz w przeglądarce adres http://localhost:5173, aby wyświetlić aplikację internetową uruchomioną lokalnie. Jest to interfejs do tworzenia aplikacji do recenzowania filmów i interakcji z jej funkcjami.93f6648a2532c606.png
  3. Otwórz sklonowany folder codelab-dataconnect-web za pomocą Visual Studio Code. Tutaj zdefiniujesz schemat, napiszesz zapytania i przetestujesz funkcje aplikacji.
  4. Aby korzystać z funkcji Data Connect, zainstaluj rozszerzenie Visual Studio dla Data Connect w Firebase.
    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 w VSCode. W rozszerzeniu wykonaj te czynności:
    1. Kliknij przycisk Zaloguj się.
    2. Kliknij Połącz z projektem Firebase i wybierz projekt Firebase.
    4bb2fbf8f9fac29b.png
  7. Uruchom emulatory Firebase za pomocą rozszerzenia Firebase Data Connect w VS Code:
    Kliknij Uruchom emulatory, a potem sprawdź, czy emulatory działają w terminalu.6d3d95f4cb708db1.png

3. Sprawdzanie kodu źródłowego startowego

W tej sekcji poznasz kluczowe obszary kodu źródłowego aplikacji startowej. Aplikacja nie zawiera niektórych funkcji, ale warto zapoznać się z jej ogólną strukturą.

Struktura folderów i plików

W następnych podsekcjach znajdziesz omówienie struktury folderów i plików aplikacji.

Katalog dataconnect/

Zawiera konfiguracje Firebase Data Connect, łączniki (określające zapytania i mutacje) oraz pliki schematu.

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

Katalog app/src/

Zawiera logikę aplikacji i interakcje z Firebase Data Connect.

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

4. Definiowanie schematu dla recenzji filmów

W tej sekcji określisz strukturę i relacje między kluczowymi elementami w aplikacji dotyczącej filmów w schemacie. Elementy takie jak Movie, User, ActorReview są mapowane na tabele baz danych, a relacje są ustanawiane za pomocą dyrektyw schematu Firebase Data Connect i GraphQL. Gdy to zrobisz, aplikacja będzie mogła obsługiwać wszystkie funkcje, od wyszukiwania filmów z najlepszymi ocenami i filtrowania według gatunku po umożliwianie użytkownikom publikowania recenzji, oznaczania ulubionych, przeglądania podobnych filmów czy znajdowania polecanych filmów na podstawie tekstu wpisanego w wyszukiwarce wektorowej.

Podstawowe elementy i powiązania

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

Relacje między filmami, aktorami i użytkownikami sprawiają, że aplikacja jest bardziej dynamiczna. Tabela złączenia MovieActor ułatwia wyświetlanie szczegółów obsady i filmografii aktorów. Typ FavoriteMovie pozwala użytkownikom dodawać filmy do ulubionych, dzięki czemu aplikacja może wyświetlać spersonalizowaną listę ulubionych i wyróżniać popularne pozycje.

Konfigurowanie tabeli Movie

Typ Movie definiuje główną strukturę elementu filmu, w tym pola 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 dla każdego filmu wygenerowany za pomocą funkcji @default(expr: "uuidV4()").

Konfigurowanie tabeli MovieMetadata

Typ MovieMetadata tworzy relację jeden-do-jednego z typem Movie. Zawiera ono dodatkowe dane, takie jak reżyser 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, ustanawiają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 danych filmów, gdzie każdy aktor może występować w wielu filmach, 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, domyślnie generuje klucz obcy movieId: UUID!.
  • actor: odwołuje się do typu Użytkownik, który wykonał czynność, i po cichu generuje klucz obcy actorId: UUID!.
  • role: określa rolę aktora w filmie (np. „główny” lub „dodatkowy”).

Konfigurowanie tabeli User

Typ User definiuje użytkownika, który wchodzi w interakcję z filmami, pozostawiając opinie lub dodając je do ulubionych.

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

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

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 element User z elementem Movie.

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, domyślnie generuje klucz obcy movieId: UUID!.
  • user: odwołuje się do typu użytkownika, domyślnie generuje klucz obcy userId: UUID!.

Konfigurowanie tabeli Review

Typ Review reprezentuje element opinii i łączy typy User oraz Movie w relacji wiele do wielu (jeden użytkownik może pozostawić 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 zostawił opinię.
  • movie: odnosi się do filmu, który jest sprawdzany.
  • reviewDate: wartość ustawiana automatycznie na czas utworzenia opinii za pomocą elementu @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 MovieReview jest automatycznie wypełniane identyfikatorem UUID podczas tworzenia nowego rekordu.

Teraz, gdy schemat jest zdefiniowany, aplikacja filmowa ma solidną podstawę dla struktury danych i ich relacji.

5. Pobieranie najpopularniejszych i najnowszych filmów

Aplikacja FriendlyMovies

W tej sekcji wstawisz do lokalnych emulatorów dane testowe dotyczące filmów, a potem zaimplementujesz łączniki (zapytania) i kod TypeScript, aby wywołać te łączniki w aplikacji internetowej. Ostatecznie Twoja aplikacja będzie mogła dynamicznie pobierać i wyświetlać najnowsze filmy z najlepszymi ocenami bezpośrednio z bazy danych.

Wstawianie danych dotyczących fikcyjnego filmu, aktora i recenzji

  1. W VSCode otwórz dataconnect/moviedata_insert.gql. Upewnij się, że emulatory w rozszerzeniu Firebase Data Connect są uruchomione.
  2. U góry pliku powinien być widoczny przycisk Uruchom (lokalnie). Kliknij, aby wstawić do bazy danych dane testowe filmu.
    e424f75e63bf2e10.png
  3. Sprawdź terminal Wykonanie Data Connect, aby potwierdzić, że dane zostały dodane.
    e0943d7704fb84ea.png

Wdrażanie 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 powoduje jednak sortowania filmów.
  2. Zastąp istniejące zapytanie ListMovies tym zapytaniem, aby dodać opcje sortowania i ograniczenia:
    # 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. Kliknij przycisk Uruchom (lokalnie), aby wykonać zapytanie w bazie danych lokalnej. Zanim uruchomisz zapytanie, możesz też wprowadzić zmienne zapytania w panelu konfiguracji.
    c4d947115bb11b16.png

Podsumowanie:

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

Integracja zapytań w aplikacji internetowej

W tej części tego ćwiczenia będziesz używać w aplikacji internetowej zapytań zdefiniowanych w poprzedniej sekcji. Emulaty Firebase Data Connect generują pakiety SDK na podstawie informacji w plikach .gql (szczególnie schema.gql, queries.gql, mutations.gql) i pliku connector.yaml. Te zestawy SDK można wywoływać bezpośrednio w aplikacji.

  1. W pliku MovieService (app/src/lib/MovieService.tsx) odkomentuj instrukcję importu u góry:
    import { listMovies, ListMoviesData, OrderDirection } from "@movie/dataconnect";
    
    Funkcja listMovies, typ odpowiedzi ListMoviesData i typ zbioru OrderDirection to pakiety SDK wygenerowane przez emulatory Firebase Data Connect na podstawie wcześniej zdefiniowanego schematu i 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 generowana funkcja, która wywołuje zapytanie listMovies, aby pobrać listę filmów. Zawiera ona opcje sortowania według oceny lub roku wydania oraz opcję ograniczania liczby wyników.
  • ListMoviesData: typ wyniku używany do wyświetlania 10 najpopularniejszych i najnowszych filmów na stronie głównej aplikacji.

Jak to działa

Załaduj ponownie aplikację internetową, aby zobaczyć działanie zapytania. Strona główna wyświetla teraz dynamicznie listę filmów, pobierając dane bezpośrednio z lokalnej bazy danych. Najwyżej oceniane i najnowsze filmy będą się wyświetlać automatycznie, zgodnie z niedawno ustawionymi przez Ciebie danymi.

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

W tej sekcji zaimplementujesz funkcję umożliwiającą pobieranie szczegółowych informacji o filmie lub aktorze na podstawie ich unikalnych identyfikatorów. Polega to nie tylko na pobieraniu danych z odpowiednich tabel, ale też na złączaniu powiązanych tabel w celu wyświetlania szczegółowych informacji, takich jak recenzje filmów czy filmografia aktora.

ac7fefa7ff779231.png

Wdrażanie 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 powiązanego typu, który ma relację obcego klucza. Na przykład reviews_on_movie pobiera wszystkie recenzje powiązane z danym filmem.
  • _via_: służy do przechodzenia między relacjami „wiele do wielu” za pomocą tabeli złączeń. Na przykład actors_via_MovieActor uzyskuje dostęp do typu Actor za pomocą tabeli złączenia MovieActor, a warunek where filtruje aktorów na podstawie ich roli (np. „główny” lub „pomocniczy”).

Testowanie zapytania przez wprowadzenie danych testowych

  1. W panelu wykonania usługi Data Connectors możesz przetestować zapytanie, podając fikcyjne identyfikatory, takie jak:
    {"id": "550e8400-e29b-41d4-a716-446655440000"}
    
  2. Kliknij Uruchom (lokalnie) obok GetMovieById, aby pobrać szczegóły dotyczące „Quantum Paradox” (filmu symulacyjnego, do którego odnosi się powyższy identyfikator).

1b08961891e44da2.png

Integracja zapytań w aplikacji internetowej

  1. W pliku MovieService (app/src/lib/MovieService.tsx) odkomentuj te importy:
    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: to automatycznie generowane 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, które służą do wyświetlania w aplikacji szczegółów filmów i aktorów.

Jak to działa

Teraz otwórz stronę główną swojej aplikacji internetowej. Kliknij film, aby wyświetlić wszystkie jego szczegóły, w tym informacje o aktorach i recenzjach, które zostały pobrane z powiązanych tabel. Podobnie, kliknięcie aktora spowoduje wyświetlenie filmów, w których zagrał.

7. Obsługa uwierzytelniania użytkowników

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

9890838045d5a00e.png

Wdrażanie 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": ta opcja używa auth.uid, który jest dostarczany bezpośrednio przez Uwierzytelnianie Firebase, a nie przez użytkownika ani aplikację. Zapewnia to dodatkowy poziom zabezpieczeń, ponieważ identyfikator użytkownika jest przetwarzany w sposób bezpieczny i automatyczny.

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: wartość ta jest pobierana bezpośrednio z Uwierzytelniania Firebase, co zapewnia bezpieczny dostęp do danych dotyczących użytkownika.
  • _on_ pola: te pola reprezentują tabele złączenia:
    • reviews_on_user: pobieranie wszystkich opinii powiązanych z użytkownikiem, w tym idtitle filmu.
    • favorite_movies_on_user: pobiera wszystkie filmy oznaczone przez użytkownika jako ulubione, w tym szczegółowe informacje, takie jak genre, releaseYear, rating i metadata.

Integrowanie zapytań w aplikacji internetowej

  1. W pliku MovieService (app/src/lib/MovieService.tsx) odkomentuj te importy:
    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 sprawdza zmiany 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 pobiera opinie i ulubione filmy użytkownika.

Jak to działa

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

8. Implementowanie interakcji użytkownika

W tej sekcji tego CodeLab zaimplementujesz interakcje użytkowników w aplikacji dodawania opinii o filmach, umożliwiając im m.in. zarządzanie ulubionymi filmami oraz dodawanie i usuwanie opinii.

b3d0ac1e181c9de9.png

Zezwalanie użytkownikowi na dodanie filmu do ulubionych

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

Wdrażanie złączy

  1. Otwórz mutations.gql w dataconnect/movie-connector/.
  2. Aby obsługiwać dodawanie filmów do ulubionych, dodaj te mutacje:
    # 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": Używa auth.uid, który jest dostarczany bezpośrednio przez Uwierzytelnianie Firebase, co zapewnia, że dostęp do danych i ich modyfikacja są możliwe tylko dla uwierzytelnionego użytkownika.

Sprawdzanie, czy film jest oznaczony jako ulubiony

  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 użytkownika za pomocą Uwierzytelniania Firebase.
  • favorite_movie: sprawdza tabelę złączenia favorite_movies, aby sprawdzić, czy dany film jest oznaczony jako ulubiony przez bieżącego użytkownika.

Integracja zapytań w aplikacji internetowej

  1. W pliku MovieService (app/src/lib/MovieService.tsx) odkomentuj te importy:
    import { addFavoritedMovie, deleteFavoritedMovie, getIfFavoritedMovie } from "@movie/dataconnect";
    
  2. Zamień funkcje handleAddFavoritedMovie, handleDeleteFavoritedMoviehandleGetIfFavoritedMovie na ten kod:
    // 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ć film do ulubionych lub usunąć go z ulubionych.
  • handleGetIfFavoritedMovie: korzysta z zapytania getIfFavoritedMovie, aby sprawdzić, czy film został oznaczony przez użytkownika jako ulubiony.

Jak to działa

Teraz możesz dodawać filmy do ulubionych lub usuwać je z listy ulubionych, klikając ikonę serca na karcie filmu i na stronie z informacjami o filmie. Dodatkowo na stronie profilu możesz wyświetlić ulubione filmy.

Zezwalanie użytkownikom na dodawanie opinii i ich usuwanie

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

Wdrażanie złączy

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, że opinie są powiązane z uwierzytelnionym użytkownikiem.
  • reviewDate_date: { today: true }: automatycznie generuje bieżącą datę opinii za pomocą DataConnect, co eliminuje konieczność ręcznego wprowadzania danych.

Integracja zapytań w aplikacji internetowej

  1. W pliku MovieService (app/src/lib/MovieService.tsx) odkomentuj te importy:
    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 zapytania deleteReview do usunięcia recenzji filmu przez uwierzytelnionego użytkownika.

Jak to działa

Użytkownicy mogą teraz zamieszczać 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 zaimplementujesz zaawansowane funkcje wyszukiwania, które pozwolą użytkownikom wyszukiwać filmy na podstawie różnych ocen i lat wydania, filtrować według gatunków i tagów, przeprowadzać częściowe dopasowanie tekstu w tytułach lub opisach, a nawet łączyć kilka filtrów, aby uzyskać dokładniejsze wyniki.

ece70ee0ab964e28.png

Wdrażanie złączy

  1. Otwórz queries.gql w 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: umożliwia połączenie wielu warunków w jednym zapytaniu, co pozwala filtrować wyniki wyszukiwania według kilku pól, np. releaseYear, ratinggenre.
  • Operator contains: wyszukiwanie częściowych dopasowań tekstowych w polach. W tym zapytaniu szuka dopasowań w strumieniach title, description, name lub reviewText.
  • Klauzula where: określa warunki filtrowania danych. Każda sekcja (filmy, aktorzy, recenzje) używa klauzuli where, aby określić konkretne kryteria wyszukiwania.

Integracja zapytań w aplikacji internetowej

  1. W pliku MovieService (app/src/lib/MovieService.tsx) odkomentuj te importy:
    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 przeszukiwania na podstawie danych wejściowych użytkownika, filtrując wyniki według takich parametrów jak rok, ocena, gatunek i częściowe dopasowanie tekstu.

Jak to działa

Otwórz stronę „Szukanie zaawansowane” z poziomu paska nawigacyjnego w aplikacji internetowej. Teraz możesz 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 jest rozliczenie)

Po przeprowadzeniu iteracji w lokalnym środowisku programistycznym nadszedł czas na wdrożenie schematu, danych i zapytań na serwer. Możesz to zrobić za pomocą rozszerzenia Firebase Data Connect w VS Code lub wiersza poleceń Firebase.

Uaktualnianie abonamentu Firebase

Aby zintegrować Firebase Data Connect z Cloud SQL for PostgreSQL, projekt Firebase musi być w taryfie „pay-as-you-go” (Blaze), co oznacza, że jest połączony z kontem rozliczeniowym Cloud.

Aby przenieść projekt na abonament Blaze:

  1. W konsoli Firebase wybierz uaktualnienie abonamentu.
  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 poziom musiałeś/musiałaś utworzyć konto rozliczeniowe Cloud, konieczne może być powrót do procesu przejścia w konsoli Firebase, aby dokończyć przejście.

Połącz aplikację internetową z projektem Firebase

  1. Zarejestruj aplikację internetową w projekcie Firebase, korzystając z konsoli Firebase:
    1. Otwórz projekt i kliknij Dodaj aplikację.
    2. Pomiń na razie konfigurowanie i instalowanie pakietu SDK, ale skopiuj wygenerowany obiekt firebaseConfig.
    7030822793e4d75b.png
  2. Zastąp istniejące firebaseConfigapp/src/lib/firebase.tsx konfiguracją, którą właśnie skopiowałeś/skopiowałaś 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ą: w VS Code w folderze app użyj Vite do utworzenia aplikacji internetowej na potrzeby wdrożenia na serwerze:
    cd app
    npm run build
    

Konfigurowanie uwierzytelniania Firebase w projekcie Firebase

  1. Skonfiguruj Uwierzytelnianie Firebase za pomocą logowania Google.62af2f225e790ef6.png
  2. (Opcjonalnie) Zezwól na domeny w Uwierzytelnianiu Firebase za pomocą konsoli Firebase (na przykład http://127.0.0.1).
    1. W ustawieniach Uwierzytelnianie otwórz Zatwierdzone domeny.
    2. Kliknij „Dodaj domenę” i dodaj do listy swoją lokalną domenę.

c255098f12549886.png

Wdróż za pomocą wiersza poleceń Firebase

  1. dataconnect/dataconnect.yaml sprawdź, czy identyfikator instancji, bazy danych i usługi są zgodne z Twoim projektem:
    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. Upewnij się, że w Twoim projekcie skonfigurowano wiersz poleceń Firebase:
    npm i -g firebase-tools
    firebase login --reauth
    firebase use --add
    
  3. Aby wdrożyć, uruchom w terminalu 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ą:
    firebase dataconnect:sql:migrate
    

Twoja instancja Cloud SQL for PostgreSQL zostanie zaktualizowana o ostateczny wdrożony schemat i dane. Stan możesz sprawdzać w konsoli Firebase.

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

11. Opcjonalnie: wyszukiwanie wektorów za pomocą Firebase Data Connect (wymagane ustawienie płatności)

W tej sekcji włączysz wyszukiwanie wektorów w aplikacji z opiniami o filmach za pomocą Firebase Data Connect. Ta funkcja umożliwia wyszukiwanie treści, np. znajdowanie filmów z podobnymi opisami za pomocą wektorów osadzonych.

Aby wykonać ten krok, musisz najpierw wykonać ostatni krok tego ćwiczenia, czyli wdrożyć aplikację w Google Cloud.

4b5aca5a447d2feb.png

Zaktualizuj schemat, aby uwzględnić w nim uczenie maszynowe dla pola

W 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 zanurzone opisy filmów, co umożliwia wyszukiwanie treści w aplikacji na podstawie wektorów.

Aktywowanie Vertex AI

  1. Aby skonfigurować interfejsy Vertex AI API z Google Cloud, postępuj zgodnie z przewodnikiem po wymaganiach wstępnych. Ten krok jest niezbędny do obsługi funkcji generowania wektorów dystrybucyjnych i wyszukiwania wektorowego.
  2. Ponownie wdrożyć schemat, aby aktywować pgvector i wyszukiwanie wektorów, klikając „Wdróż w wersji produkcyjnej” za pomocą rozszerzenia Firebase Data Connect w VS Code.

Wpełnianie bazy danych wektorami zastępczymi

  1. Otwórz folder dataconnect w VS Code.
  2. Aby wypełnić bazę danych wektorami wbudowanymi dla filmów, w optional_vector_embed.gql kliknij Uruchom(lokalnie).

b858da780f6ec103.png

Dodawanie zapytania w ramach wyszukiwania wektorowego

Aby przeprowadzić wyszukiwanie wektorowe, w dataconnect/movie-connector/queries.gql dodaj to zapytanie:

# 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 zanurzania (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 2 lub mniejszej, skupiając się na treściach o zbliżonych do siebie atrybutach.
  • limit: ogranicza liczbę zwracanych wyników do 5.

Wdrażanie funkcji wyszukiwania wektorowego w aplikacji

Po skonfigurowaniu schematu i zapytania zintegruj wyszukiwanie wektorów z poziomem usług aplikacji. Dzięki temu możesz wywołać zapytanie wyszukiwania z aplikacji internetowej.

  1. W pliku app/src/lib/ MovieService.ts odkomentuj te importy z pakietów SDK. Będą one działać jak zwykłe zapytania.
    import {
      searchMovieDescriptionUsingL2similarity,
      SearchMovieDescriptionUsingL2similarityData,
    } from "@movie/dataconnect";
    
  2. Aby zintegrować wyszukiwanie wektorów z aplikacją, dodaj tę funkcję:
    // 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.

Jak to działa

Na pasku nawigacyjnym otwórz sekcję „Wyszukiwarka wektorów” 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 przejrzeć sekcję podobnych filmów na dole strony.

7b71f1c75633c1be.png

12. Podsumowanie

Gratulacje! Powinieneś mieć teraz dostęp do aplikacji internetowej. Jeśli chcesz pracować z własnymi danymi filmu, wstaw je za pomocą rozszerzenia Firebase Data Connect, naśladując pliki _insert.gql, lub dodaj je w panelu wykonywania Data Connect w VS Code.

Więcej informacji