Creare con Firebase Data Connect (web)

1. Prima di iniziare

App FriendlyMovies

In questo codelab, integrerai Firebase Data Connect con un database Cloud SQL per creare un'app web di recensioni di film. L'app completata mostra come Firebase Data Connect semplifica il processo di creazione di applicazioni basate su SQL. Include le seguenti funzionalità:

  • Autenticazione:implementa l'autenticazione personalizzata per le query e le mutazioni della tua app, assicurandoti che solo gli utenti autorizzati possano interagire con i tuoi dati.
  • Schema GraphQL:crea e gestisci le tue strutture di dati utilizzando uno schema GraphQL flessibile e personalizzato in base alle esigenze di un'app web di recensioni di film.
  • Query e mutazioni SQL: recupera, aggiorna e gestisci i dati in Cloud SQL utilizzando query e mutazioni basate su GraphQL.
  • Ricerca avanzata con corrispondenza parziale della stringa:utilizza i filtri e le opzioni di ricerca per trovare film in base a campi come titolo, descrizione o tag.
  • (Facoltativo) Integrazione della ricerca vettoriale:aggiungi la funzionalità di ricerca dei contenuti utilizzando la ricerca vettoriale di Firebase Data Connect per offrire un'esperienza utente avanzata basata sull'input e sulle preferenze.

Prerequisiti

Avrai bisogno di una conoscenza di base di JavaScript.

Cosa imparerai a fare

  • Configura Firebase Data Connect con gli emulatori locali.
  • Progetta uno schema dei dati utilizzando Data Connect e GraphQL.
  • Scrivi e testa varie query e mutazioni per un'app di recensioni di film.
  • Scopri come Firebase Data Connect genera e utilizza l'SDK nell'app.
  • Esegui il deployment dello schema e gestisci il database in modo efficiente.

Che cosa ti serve

  • Git
  • Visual Studio Code
  • Installa Node.js utilizzando nvm-windows (Windows) o nvm (macOS/Linux)
  • Se non l'hai ancora fatto, crea un progetto Firebase nella console Firebase.
  • (Facoltativo) Per la ricerca vettoriale, esegui l'upgrade del progetto al piano tariffario Blaze con pagamento a consumo

2. Configurazione dell'ambiente di sviluppo

Questa fase del codelab ti guiderà nella configurazione dell'ambiente per iniziare a creare la tua app di recensioni di film utilizzando Firebase Data Connect.

  1. Clona il repository del progetto e installa le dipendenze richieste:
    git clone https://github.com/firebaseextended/codelab-dataconnect-web
    cd codelab-dataconnect-web
    cd ./app && npm i
    npm run dev
    
  2. Dopo aver eseguito questi comandi, apri http://localhost:5173 nel browser per visualizzare l'app web in esecuzione in locale. Questo serve come front-end per creare l'app di recensioni di film e interagire con le sue funzionalità.93f6648a2532c606.png
  3. Apri la cartella codelab-dataconnect-web clonata utilizzando Visual Studio Code. Qui definirai lo schema, scriverai le query e testerai la funzionalità dell'app.
  4. Per utilizzare le funzionalità di Data Connect, installa l'estensione Visual Studio di Firebase Data Connect.
    In alternativa, puoi installare l'estensione da Visual Studio Code Marketplace o cercarla in VS Code.b03ee38c9a81b648.png
  5. Apri o crea un nuovo progetto Firebase nella console Firebase.
  6. Collega il tuo progetto Firebase all'estensione VSCode Firebase Data Connect. Nell'estensione, procedi nel seguente modo:
    1. Fai clic sul pulsante Accedi.
    2. Fai clic su Collega un progetto Firebase e seleziona il progetto Firebase.
    4bb2fbf8f9fac29b.png
  7. Avvia gli emulatori Firebase utilizzando l'estensione Firebase Data Connect VS Code:
    fai clic su Avvia emulatori e poi verifica che gli emulatori siano in esecuzione nel terminale.6d3d95f4cb708db1.png

3. Esamina la codebase iniziale

In questa sezione esplorerai le aree chiave del codebase iniziale dell'app. Sebbene l'app non disponga di alcune funzionalità, è utile per comprendere la struttura generale.

Struttura di cartelle e file

Le seguenti sottosezioni forniscono una panoramica della struttura di cartelle e file dell'app.

La directory dataconnect/

Contiene configurazioni, connettori (che definiscono query e mutazioni) e file di schema di Firebase Data Connect.

  • schema/schema.gql: definisce lo schema GraphQL
  • connector/queries.gql: Query necessarie nella tua app
  • connector/mutations.gql: Mutazioni necessarie nella tua app
  • connector/connector.yaml: file di configurazione per la generazione dell'SDK

La directory app/src/

Contiene la logica dell'applicazione e l'interazione con Firebase Data Connect.

  • firebase.ts: configurazione per connettersi a un'app Firebase nel tuo progetto Firebase.
  • lib/dataconnect-sdk/: contiene l'SDK generato. Puoi modificare la posizione della generazione dell'SDK nel file connector/connector.yaml e gli SDK verranno generati automaticamente ogni volta che definisci una query o una mutazione.

4. Definisci uno schema per le recensioni di film

In questa sezione, definirai la struttura e le relazioni tra le entità chiave nell'applicazione per film in uno schema. Entità come Movie, User, Actor e Review vengono mappate alle tabelle del database, con relazioni stabilite utilizzando le direttive dello schema Firebase Data Connect e GraphQL. Una volta implementata, l'app sarà pronta a gestire qualsiasi attività, dalla ricerca di film con le valutazioni più alte al filtraggio per genere, fino a consentire agli utenti di lasciare recensioni, contrassegnare i preferiti, esplorare film simili o trovare film consigliati in base all'input di testo tramite la ricerca vettoriale.

Entità e relazioni principali

Il tipo Movie contiene dettagli chiave come titolo, genere e tag, che l'app utilizza per le ricerche e i profili dei film. Il tipo User monitora le interazioni degli utenti, come recensioni e preferiti. Reviews collega gli utenti ai film, consentendo all'app di mostrare valutazioni e feedback generati dagli utenti.

Le relazioni tra film, attori e utenti rendono l'app più dinamica. La tabella di unione MovieActor consente di visualizzare i dettagli del cast e le filmografie degli attori. Il tipo FavoriteMovie consente agli utenti di aggiungere film ai preferiti, in modo che l'app possa mostrare un elenco personalizzato di preferiti ed evidenziare le scelte più popolari.

Configurare la tabella Movie

Il tipo Movie definisce la struttura principale di un'entità film, inclusi campi come title, genre, releaseYear e rating.

Copia e incolla lo snippet di codice nel file 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]
}

Concetti chiave:

  • id:un UUID univoco per ogni film, generato utilizzando @default(expr: "uuidV4()").

Configurare la tabella MovieMetadata

Il tipo MovieMetadata stabilisce una relazione uno a uno con il tipo Movie. Include dati aggiuntivi, come il regista del film.

Copia e incolla lo snippet di codice nel file 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
}

Concetti chiave:

  • Film! @ref: fa riferimento al tipo Movie, stabilendo una relazione di chiave esterna.

Configurare la tabella Actor

Copia e incolla lo snippet di codice nel file dataconnect/schema/schema.gql:

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

Il tipo Actor rappresenta un attore nel database dei film, dove ogni attore può far parte di più film, formando una relazione molti-a-molti.

Configurare la tabella MovieActor

Copia e incolla lo snippet di codice nel file 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"
}

Concetti chiave:

  • movie: fa riferimento al tipo Movie e genera implicitamente una chiave esterna movieId: UUID.
  • actor: fa riferimento al tipo Actor e genera implicitamente una chiave esterna actorId: UUID!.
  • role: definisce il ruolo dell'attore nel film (ad es. "principale" o "secondario").

Configurare la tabella User

Il tipo User definisce un'entità utente che interagisce con i film lasciando recensioni o aggiungendoli ai preferiti.

Copia e incolla lo snippet di codice nel file 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
}

Configurare la tabella FavoriteMovie

Il tipo FavoriteMovie è una tabella di join che gestisce le relazioni many-to-many tra gli utenti e i loro film preferiti. Ogni tabella collega un User a un Movie.

Copia e incolla lo snippet di codice nel file 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!
}

Concetti chiave:

  • movie:fa riferimento al tipo Movie e genera implicitamente una chiave esterna movieId: UUID!.
  • user:fa riferimento al tipo di utente e genera implicitamente una chiave esterna userId: UUID!.

Configurare la tabella Review

Il tipo Review rappresenta l'entità recensione e collega i tipi User e Movie in una relazione molti-a-molti (un utente può lasciare molte recensioni e ogni film può avere molte recensioni).

Copia e incolla lo snippet di codice nel file 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")
}

Concetti chiave:

  • user: fa riferimento all'utente che ha lasciato la recensione.
  • movie:fa riferimento al film recensito.
  • reviewDate: impostato automaticamente sull'ora in cui viene creata la recensione utilizzando @default(expr: "request.time").

Campi e valori predefiniti generati automaticamente

Lo schema utilizza espressioni come @default(expr: "uuidV4()") per generare automaticamente ID e timestamp univoci. Ad esempio, il campo id nei tipi Movie e Review viene compilato automaticamente con un UUID quando viene creato un nuovo record.

Ora che lo schema è definito, la tua app per film ha una base solida per la struttura e le relazioni dei dati.

5. Recuperare i film più popolari e recenti

App FriendlyMovies

In questa sezione inserirai dati cinematografici simulati negli emulatori locali, quindi implementerai i connettori (query) e il codice TypeScript per chiamarli nell'applicazione web. Al termine, la tua app sarà in grado di recuperare e visualizzare dinamicamente i film più recenti e con le valutazioni più alte direttamente dal database.

Inserire dati simulati di film, attori e recensioni

  1. In VSCode, apri dataconnect/moviedata_insert.gql. Assicurati che gli emulatori nell'estensione Firebase Data Connect siano in esecuzione.
  2. Dovresti vedere un pulsante Esegui (locale) nella parte superiore del file. Fai clic qui per inserire i dati simulati dei film nel database.
    e424f75e63bf2e10.png
  3. Controlla il terminale Data Connect Execution per verificare che i dati siano stati aggiunti correttamente.
    e0943d7704fb84ea.png

Implementare il connettore

  1. Apri dataconnect/movie-connector/queries.gql. Nei commenti troverai una query ListMovies di base:
    query ListMovies @auth(level: PUBLIC) {
      movies {
        id
        title
        imageUrl
        releaseYear
        genre
        rating
        tags
        description
      }
    }
    
    Questa query recupera tutti i film e i relativi dettagli (ad esempio id, title, releaseYear). Tuttavia, non ordina i film.
  2. Sostituisci la query ListMovies esistente con la seguente query per aggiungere opzioni di ordinamento e limite:
    # 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. Fai clic sul pulsante Esegui (locale) per eseguire la query sul database locale. Puoi anche inserire le variabili della query nel riquadro di configurazione prima dell'esecuzione.
    c4d947115bb11b16.png

Concetti chiave:

  • movies(): campo della query GraphQL per recuperare i dati dei film dal database.
  • orderByRating: parametro per ordinare i film in base alla valutazione (in ordine crescente/decrescente).
  • orderByReleaseYear: parametro per ordinare i film in base all'anno di uscita (in ordine crescente/decrescente).
  • limit: limita il numero di film restituiti.

Integrare le query nell'app web

In questa parte del codelab, utilizzerai le query definite nella sezione precedente nella tua app web. Gli emulatori Firebase Data Connect generano SDK in base alle informazioni contenute nei file .gql (in particolare schema.gql, queries.gql, mutations.gql) e nel file connector.yaml. Questi SDK possono essere chiamati direttamente nella tua applicazione.

  1. In MovieService (app/src/lib/MovieService.tsx), rimuovi il commento dall'istruzione di importazione in alto:
    import { listMovies, ListMoviesData, OrderDirection } from "@movie/dataconnect";
    
    La funzione listMovies, il tipo di risposta ListMoviesData e l'enumerazione OrderDirection sono tutti SDK generati dagli emulatori Firebase Data Connect in base allo schema e alle query che hai definito in precedenza .
  2. Sostituisci le funzioni handleGetTopMovies e handleGetLatestMovies con il seguente codice:
    // 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;
      }
    };
    

Concetti chiave:

  • listMovies: una funzione generata automaticamente che chiama la query listMovies per recuperare un elenco di film. Include opzioni per l'ordinamento in base alla valutazione o all'anno di uscita e per limitare il numero di risultati.
  • ListMoviesData: il tipo di risultato utilizzato per visualizzare i 10 film più recenti e più popolari nella home page dell'app.

Guarda come funziona

Ricarica l'app web per vedere la query in azione. La home page ora mostra dinamicamente l'elenco dei film, recuperando i dati direttamente dal database locale. Vedrai i film più recenti e con le valutazioni più alte apparire senza problemi, riflettendo i dati che hai appena configurato.

6. Visualizzare i dettagli di film e attori

In questa sezione, implementerai la funzionalità per recuperare informazioni dettagliate su un film o un attore utilizzando i relativi ID univoci. Ciò comporta non solo il recupero dei dati dalle rispettive tabelle, ma anche l'unione di tabelle correlate per visualizzare dettagli completi, come le recensioni dei film e le filmografie degli attori.

ac7fefa7ff779231.png

Implementare i connettori

  1. Apri dataconnect/movie-connector/queries.gql nel tuo progetto.
  2. Aggiungi le seguenti query per recuperare i dettagli di film e attori:
    # 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. Salva le modifiche e rivedi le query.

Concetti chiave:

  • movie()/actor(): campi di query GraphQL per recuperare un singolo film o attore dalla tabella Movies o Actors.
  • _on_: consente l'accesso diretto ai campi di un tipo associato che ha una relazione di chiave esterna. Ad esempio, reviews_on_movie recupera tutte le recensioni relative a un film specifico.
  • _via_: utilizzato per navigare nelle relazioni molti-a-molti tramite una tabella di unione. Ad esempio, actors_via_MovieActor accede al tipo Actor tramite la tabella di unione MovieActor e la condizione where filtra gli attori in base al loro ruolo (ad esempio "principale" o "secondario").

Testare la query inserendo dati simulati

  1. Nel riquadro di esecuzione di Data Connect, puoi testare la query inserendo ID simulati, ad esempio:
    {"id": "550e8400-e29b-41d4-a716-446655440000"}
    
  2. Fai clic su Esegui (locale) per GetMovieById per recuperare i dettagli di "Quantum Paradox" (il film fittizio a cui si riferisce l'ID precedente).

1b08961891e44da2.png

Integrare le query nell'app web

  1. In MovieService (app/src/lib/MovieService.tsx), rimuovi il commento dai seguenti import:
    import { getMovieById, GetMovieByIdData } from "@movie/dataconnect";
    import { GetActorByIdData, getActorById } from "@movie/dataconnect";
    
  2. Sostituisci le funzioni handleGetMovieById e handleGetActorById con il seguente codice:
    // 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;
      }
    };
    

Concetti chiave:

  • getMovieById / getActorById: si tratta di funzioni generate automaticamente che chiamano le query che hai definito, recuperando informazioni dettagliate su un film o un attore specifico.
  • GetMovieByIdData / GetActorByIdData: questi sono i tipi di risultati utilizzati per visualizzare i dettagli di film e attori nell'app.

Guarda come funziona

Ora vai alla home page della tua app web. Fai clic su un film per visualizzarne tutti i dettagli, inclusi attori e recensioni, informazioni estratte dalle tabelle correlate. Allo stesso modo, se fai clic su un attore, vengono visualizzati i film a cui ha partecipato.

7. Gestire l'autenticazione utente

In questa sezione implementerai le funzionalità di accesso e disconnessione degli utenti utilizzando Firebase Authentication. Utilizzerai anche i dati di Firebase Authentication per recuperare o inserire direttamente i dati utente in Firebase Data Connect, garantendo una gestione sicura degli utenti all'interno della tua app.

9890838045d5a00e.png

Implementare i connettori

  1. Apri mutations.gql in dataconnect/movie-connector/.
  2. Aggiungi la seguente mutazione per creare o aggiornare l'utente autenticato corrente:
    # Create or update the current authenticated user
    mutation UpsertUser($username: String!) @auth(level: USER) {
      user_upsert(
        data: {
          id_expr: "auth.uid"
          username: $username
        }
      )
    }
    

Concetti chiave:

  • id_expr: "auth.uid": utilizza auth.uid, fornito direttamente da Firebase Authentication, non dall'utente o dall'app, aggiungendo un ulteriore livello di sicurezza garantendo che l'ID utente venga gestito in modo sicuro e automatico.

Recuperare l'utente corrente

  1. Apri queries.gql in dataconnect/movie-connector/.
  2. Aggiungi la seguente query per recuperare l'utente corrente:
    # 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
            }
          }
        }
      }
    }
    

Concetti chiave:

  • auth.uid: questo valore viene recuperato direttamente da Firebase Authentication, garantendo l'accesso sicuro ai dati specifici dell'utente.
  • Campi _on_: questi campi rappresentano le tabelle di join:
    • reviews_on_user: recupera tutte le recensioni relative all'utente, inclusi id e title del film.
    • favorite_movies_on_user: recupera tutti i film contrassegnati come preferiti dall'utente, incluse informazioni dettagliate come genre, releaseYear, rating e metadata.

Integrare le query nell'app web

  1. In MovieService (app/src/lib/MovieService.tsx), rimuovi il commento dai seguenti import:
    import { upsertUser } from "@movie/dataconnect";
    import { getCurrentUser, GetCurrentUserData } from "@movie/dataconnect";
    
  2. Sostituisci le funzioni handleAuthStateChange e handleGetCurrentUser con il seguente codice:
    // 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;
      }
    };
    

Concetti chiave:

  • handleAuthStateChange: questa funzione rileva le modifiche dello stato di autenticazione. Quando un utente esegue l'accesso, imposta i dati dell'utente e chiama la mutazione upsertUser per creare o aggiornare le informazioni dell'utente nel database.
  • handleGetCurrentUser: recupera il profilo dell'utente corrente utilizzando la query getCurrentUser, che recupera le recensioni e i film preferiti dell'utente.

Guarda come funziona

Ora, fai clic sul pulsante "Accedi con Google" nella barra di navigazione. Puoi accedere utilizzando l'emulatore Firebase Authentication. Dopo aver eseguito l'accesso, fai clic su "Il mio profilo". Per ora sarà vuoto, ma hai creato le basi per la gestione dei dati specifici per gli utenti nella tua app.

8. Implementare le interazioni dell'utente

In questa sezione del codelab, implementerai le interazioni degli utenti nell'app di recensioni di film, in particolare consentendo agli utenti di gestire i propri film preferiti e lasciare o eliminare le recensioni.

b3d0ac1e181c9de9.png

Consentire a un utente di aggiungere un film ai preferiti

In questa sezione configurerai il database per consentire agli utenti di aggiungere un film ai preferiti.

Implementare i connettori

  1. Apri mutations.gql in dataconnect/movie-connector/.
  2. Aggiungi le seguenti mutazioni per gestire l'aggiunta di film ai preferiti:
    # 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 })
    }
    
    

Concetti chiave:

  • userId_expr: "auth.uid": utilizza auth.uid, fornito direttamente da Firebase Authentication, garantendo che vengano modificati o a cui si acceda solo ai dati dell'utente autenticato.

Controllare se un film è stato aggiunto ai preferiti

  1. Apri queries.gql in dataconnect/movie-connector/.
  2. Aggiungi la seguente query per verificare se un film è stato aggiunto ai preferiti:
    query GetIfFavoritedMovie($movieId: UUID!) @auth(level: USER) {
      favorite_movie(key: { userId_expr: "auth.uid", movieId: $movieId }) {
        movieId
      }
    }
    

Concetti chiave:

  • auth.uid: garantisce l'accesso sicuro ai dati specifici dell'utente utilizzando Firebase Authentication.
  • favorite_movie: controlla la tabella di unione favorite_movies per verificare se un film specifico è contrassegnato come preferito dall'utente corrente.

Integrare le query nell'app web

  1. In MovieService (app/src/lib/MovieService.tsx), rimuovi il commento dai seguenti import:
    import { addFavoritedMovie, deleteFavoritedMovie, getIfFavoritedMovie } from "@movie/dataconnect";
    
  2. Sostituisci le funzioni handleAddFavoritedMovie, handleDeleteFavoritedMovie e handleGetIfFavoritedMovie con il seguente codice:
    // 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;
      }
    };
    

Concetti chiave:

  • handleAddFavoritedMovie e handleDeleteFavoritedMovie: utilizza le mutazioni per aggiungere o rimuovere in modo sicuro un film dai preferiti dell'utente.
  • handleGetIfFavoritedMovie: utilizza la query getIfFavoritedMovie per verificare se un film è contrassegnato come preferito dall'utente.

Guarda come funziona

Ora puoi aggiungere o rimuovere i film dai preferiti facendo clic sull'icona a forma di cuore nelle schede dei film e nella pagina dei dettagli del film. Inoltre, puoi visualizzare i tuoi film preferiti nella pagina del profilo.

Consentire agli utenti di lasciare o eliminare recensioni

Successivamente, implementerai la sezione per la gestione delle recensioni degli utenti nell'app.

Implementare i connettori

In mutations.gql (dataconnect/movie-connector/mutations.gql), aggiungi le seguenti mutazioni:

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

Concetti chiave:

  • userId_expr: "auth.uid": garantisce che le recensioni siano associate all'utente autenticato.
  • reviewDate_date: { today: true }: genera automaticamente la data corrente per la recensione utilizzando DataConnect, eliminando la necessità di inserimento manuale.

Integrare le query nell'app web

  1. In MovieService (app/src/lib/MovieService.tsx), rimuovi il commento dai seguenti import:
    import { addReview, deleteReview } from "@movie/dataconnect";
    
  2. Sostituisci le funzioni handleAddReview e handleDeleteReview con il seguente codice:
    // 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;
      }
    };
    

Concetti chiave:

  • handleAddReview: chiama la mutazione addReview per aggiungere una recensione per il film specificato, collegandola in modo sicuro all'utente autenticato.
  • handleDeleteReview: utilizza la mutazione deleteReview per rimuovere una recensione di un film da parte dell'utente autenticato.

Guarda come funziona

Ora gli utenti possono lasciare recensioni per i film nella pagina dei dettagli del film. Possono anche visualizzare ed eliminare le proprie recensioni nella pagina del profilo, il che consente loro di avere il pieno controllo delle interazioni con l'app.

9. Filtri avanzati e corrispondenza parziale del testo

In questa sezione implementerai funzionalità di ricerca avanzate, che consentono agli utenti di cercare film in base a una serie di valutazioni e anni di uscita, filtrare per generi e tag, eseguire la corrispondenza parziale del testo nei titoli o nelle descrizioni e persino combinare più filtri per risultati più precisi.

ece70ee0ab964e28.png

Implementare i connettori

  1. Apri queries.gql in dataconnect/movie-connector/.
  2. Aggiungi la seguente query per supportare varie funzionalità di ricerca:
    # 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
        }
      }
    }
    

Concetti chiave:

  • Operatore _and: combina più condizioni in una singola query, consentendo di filtrare la ricerca in base a diversi campi come releaseYear, rating e genre.
  • Operatore contains: cerca corrispondenze parziali del testo all'interno dei campi. In questa query, cerca corrispondenze all'interno di title, description, name o reviewText.
  • Clausola where: specifica le condizioni per il filtraggio dei dati. Ogni sezione (film, attori, recensioni) utilizza una clausola where per definire i criteri specifici per la ricerca.

Integrare le query nell'app web

  1. In MovieService (app/src/lib/MovieService.tsx), rimuovi il commento dai seguenti import:
    import { searchAll, SearchAllData } from "@movie/dataconnect";
    
  2. Sostituisci la funzione handleSearchAll con il seguente codice:
    // 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;
      }
    };
    

Concetti chiave:

  • handleSearchAll: questa funzione utilizza la query searchAll per eseguire una ricerca in base all'input dell'utente, filtrando i risultati in base a parametri quali anno, valutazione, genere e corrispondenze parziali del testo.

Guarda come funziona

Vai alla pagina "Ricerca avanzata" dalla barra di navigazione dell'app web. Ora puoi cercare film, attori e recensioni utilizzando vari filtri e input, ottenendo risultati di ricerca dettagliati e personalizzati.

10. (Facoltativo) Esegui il deployment su Cloud (è richiesta la fatturazione)

Ora che hai completato l'iterazione di sviluppo locale, è il momento di eseguire il deployment dello schema, dei dati e delle query sul server. Puoi farlo utilizzando l'estensione VS Code di Firebase Data Connect o la CLI Firebase.

Esegui l'upgrade del piano tariffario Firebase

Per integrare Firebase Data Connect con Cloud SQL per PostgreSQL, il tuo progetto Firebase deve utilizzare il piano tariffario con pagamento a consumo (Blaze), il che significa che è collegato a un account di fatturazione Cloud.

  • Un account di fatturazione Cloud richiede un metodo di pagamento, ad esempio una carta di credito.
  • Se non hai mai utilizzato Firebase e Google Cloud, verifica se hai diritto a un credito di 300$e a un account Cloud Billing di prova senza costi.
  • Se stai svolgendo questo codelab nell'ambito di un evento, chiedi all'organizzatore se sono disponibili crediti Cloud.

Per eseguire l'upgrade del progetto al piano Blaze:

  1. Nella console Firebase, seleziona l'opzione per eseguire l'upgrade del piano.
  2. Seleziona il piano Blaze. Segui le istruzioni sullo schermo per collegare un account di fatturazione Cloud al tuo progetto.
    Se hai dovuto creare un account di fatturazione Cloud nell'ambito di questo upgrade, potresti dover tornare al flusso di upgrade nella console Firebase per completarlo.

Collega la tua app web al tuo progetto Firebase

  1. Registra la tua app web nel progetto Firebase utilizzando la Console Firebase:
    1. Apri il progetto e fai clic su Aggiungi app.
    2. Per ora ignora la configurazione dell'SDK, ma assicurati di copiare l'oggetto firebaseConfig generato.
    7030822793e4d75b.png
  2. Sostituisci il valore firebaseConfig esistente in app/src/lib/firebase.tsx con la configurazione che hai appena copiato dalla console 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. Crea l'app web:torna a VS Code e, nella cartella app, utilizza Vite per creare l'app web per il deployment dell'hosting:
    cd app
    npm run build
    

Configura Firebase Authentication nel tuo progetto Firebase

  1. Configura Firebase Authentication con l'accesso con Google.62af2f225e790ef6.png
  2. (Facoltativo) Consenti domini per Firebase Authentication utilizzando la console Firebase (ad esempio, http://127.0.0.1).
    1. Nelle impostazioni Autenticazione, vai a Domini autorizzati.
    2. Fai clic su "Aggiungi dominio" e includi il tuo dominio locale nell'elenco.

c255098f12549886.png

Esegui il deployment con la CLI Firebase

  1. In dataconnect/dataconnect.yaml, assicurati che l'ID istanza, il database e l'ID servizio corrispondano al tuo progetto:
    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. Assicurati di aver configurato l'interfaccia a riga di comando di Firebase con il tuo progetto:
    npm i -g firebase-tools
    firebase login --reauth
    firebase use --add
    
  3. Nel terminale, esegui questo comando per il deployment:
    firebase deploy --only dataconnect,hosting
    
  4. Esegui questo comando per confrontare le modifiche allo schema:
    firebase dataconnect:sql:diff
    
  5. Se le modifiche sono accettabili, applicale con:
    firebase dataconnect:sql:migrate
    

L'istanza Cloud SQL per PostgreSQL verrà aggiornata con lo schema e i dati di cui è stato eseguito il deployment finale. Puoi monitorare lo stato nella console Firebase.

Ora dovresti essere in grado di vedere la tua app in diretta all'indirizzo your-project.web.app/. Inoltre, puoi fare clic su Esegui (produzione) nel riquadro Firebase Data Connect, proprio come hai fatto con gli emulatori locali, per aggiungere dati all'ambiente di produzione.

11. (Facoltativo) Ricerca vettoriale con Firebase Data Connect (è richiesta la fatturazione)

In questa sezione, attiverai la ricerca vettoriale nella tua app di recensioni di film utilizzando Firebase Data Connect. Questa funzionalità consente ricerche basate sui contenuti, ad esempio la ricerca di film con descrizioni simili utilizzando gli embedding vettoriali.

Questo passaggio richiede di aver completato l'ultimo passaggio di questo codelab per il deployment su Google Cloud.

4b5aca5a447d2feb.png

Aggiorna lo schema in modo da includere gli incorporamenti per un campo

In dataconnect/schema/schema.gql, aggiungi il campo descriptionEmbedding alla tabella 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
}

Concetti chiave:

  • descriptionEmbedding: Vector @col(size:768): questo campo memorizza gli incorporamenti semantici delle descrizioni dei film, consentendo la ricerca di contenuti basata su vettori nella tua app.

Attiva Vertex AI

  1. Segui la guida ai prerequisiti per configurare le API Vertex AI da Google Cloud. Questo passaggio è essenziale per supportare la generazione di incorporamenti e la funzionalità di ricerca vettoriale.
  2. Esegui nuovamente il deployment dello schema per attivare pgvector e la ricerca vettoriale facendo clic su "Esegui il deployment in produzione" utilizzando l'estensione VS Code di Firebase Data Connect.

Compilare il database con gli embedding

  1. Apri la cartella dataconnect in VS Code.
  2. Fai clic su Esegui(locale) in optional_vector_embed.gql per compilare il database con gli incorporamenti dei film.

b858da780f6ec103.png

Aggiungere una query di ricerca vettoriale

In dataconnect/movie-connector/queries.gql, aggiungi la seguente query per eseguire ricerche vettoriali:

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

Concetti chiave:

  • compare_embed: specifica il modello di embedding (textembedding-gecko@003) e il testo di input ($query) per il confronto.
  • method: specifica il metodo di similarità (L2), che rappresenta la distanza euclidea.
  • within: limita la ricerca ai film con una distanza L2 pari o inferiore a 2, concentrandosi sulle corrispondenze di contenuti vicini.
  • limit: limita a 5 il numero di risultati restituiti.

Implementare la funzione di ricerca vettoriale nella tua app

Ora che lo schema e la query sono configurati, integra la ricerca vettoriale nel livello di servizio della tua app. Questo passaggio ti consente di chiamare la query di ricerca dalla tua app web.

  1. In app/src/lib/ MovieService.ts, rimuovi il commento dai seguenti import dagli SDK, in questo modo funzionerà come qualsiasi altra query.
    import {
      searchMovieDescriptionUsingL2similarity,
      SearchMovieDescriptionUsingL2similarityData,
    } from "@movie/dataconnect";
    
  2. Aggiungi la seguente funzione per integrare la ricerca basata su vettori nell'app:
    // 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;
      }
    };
    

Concetti chiave:

  • searchMoviesByDescription: questa funzione chiama la query searchMovieDescriptionUsingL2similarity, passando il testo di input per eseguire una ricerca di contenuti basata su vettori.

Guarda come funziona

Vai alla sezione "Ricerca vettoriale" nella barra di navigazione e digita frasi come "romantico e moderno". Vedrai un elenco di film che corrispondono ai contenuti che stai cercando oppure puoi andare alla pagina dei dettagli di un film e consultare la sezione dei film simili nella parte inferiore della pagina.

7b71f1c75633c1be.png

12. Conclusione

Congratulazioni, dovresti riuscire a utilizzare l'app web. Se vuoi utilizzare i tuoi dati sui film, non preoccuparti, inseriscili utilizzando l'estensione Firebase Data Connect imitando i file _insert.gql o aggiungili tramite il riquadro di esecuzione di Data Connect in VS Code.

Scopri di più