פיתוח באמצעות Firebase Data Connect

1. לפני שמתחילים

אפליקציית FriendlyMovies

בקודלאב הזה תלמדו לשלב את Firebase Data Connect עם מסד נתונים של Cloud SQL כדי ליצור אפליקציית אינטרנט של ביקורות על סרטים. האפליקציה המושלמת תמחיש איך Firebase Data Connect מפשט את תהליך היצירה של אפליקציות שמבוססות על SQL. התכונות האלה כלולות בו:

  • אימות: מטמיעים אימות מותאם אישית לשאילתות ולמוטציות של האפליקציה, כדי לוודא שרק משתמשים מורשים יכולים ליצור אינטראקציה עם הנתונים.
  • סכימה של GraphQL: יצירת וניהול של מבני הנתונים באמצעות סכימה גמישה של GraphQL שמותאמת לצרכים של אפליקציית אינטרנט לביקורות על סרטים.
  • שאילתות וטרנספורמציות של SQL: אחזור, עדכון וניהול של נתונים ב-Cloud SQL באמצעות שאילתות וטרנספורמציות שמבוססות על GraphQL.
  • חיפוש מתקדם עם התאמה חלקית למחרוזת: אפשר להשתמש במסננים ובאפשרויות החיפוש כדי למצוא סרטים על סמך שדות כמו שם, תיאור או תגים.
  • (אופציונלי) שילוב חיפוש וקטורים: מוסיפים פונקציונליות של חיפוש תוכן באמצעות חיפוש וקטורים של Firebase Data Connect, כדי לספק חוויית משתמש עשירה על סמך קלט והעדפות.

דרישות מוקדמות

נדרשת הבנה בסיסית של JavaScript.

מה תלמדו

  • הגדרת Firebase Data Connect באמצעות אמולטורים מקומיים.
  • עיצוב סכימת נתונים באמצעות Data Connect ו-GraphQL.
  • כתיבת בדיקה של שאילתות ומוטציות שונות לאפליקציה של ביקורות על סרטים.
  • איך Firebase Data Connect יוצר את ה-SDK ומשתמש בו באפליקציה
  • פריסת הסכימה וניהול יעיל של מסד הנתונים.

מה צריך להכין

  • Git
  • Visual Studio Code
  • התקנה של Node.js באמצעות nvm-windows (Windows) או nvm (macOS/‏Linux)
  • אם עדיין לא עשיתם זאת, יוצרים פרויקט ב-Firebase במסוף Firebase.
  • (אופציונלי) לחיפוש וקטורים, משדרגים את הפרויקט לתוכנית התמחור Blaze בתשלום לפי שימוש

2. הגדרת סביבת הפיתוח

בשלב הזה של הקודלאב נסביר איך מגדירים את הסביבה כדי להתחיל לפתח את אפליקציית ביקורות הסרטים באמצעות Firebase Data Connect.

  1. משכפלים את מאגר הפרויקט ומתקינים את יחסי התלות הנדרשים:
    git clone https://github.com/firebaseextended/codelab-dataconnect-web
    cd codelab-dataconnect-web
    cd ./app && npm i
    npm run dev
    
  2. אחרי שמריצים את הפקודות האלה, פותחים את http://localhost:5173 בדפדפן כדי לראות את אפליקציית האינטרנט פועלת באופן מקומי. זהו החלק הקדמי של האפליקציה לסקירות סרטים, שבו אפשר ליצור את האפליקציה ולנהל אינטראקציה עם התכונות שלה.93f6648a2532c606.png
  3. פותחים את התיקייה codelab-dataconnect-web שהועתק באמצעות Visual Studio Code. כאן תגדירו את הסכימה, תכתבו שאילתות ותבדקו את הפונקציונליות של האפליקציה.
  4. כדי להשתמש בתכונות של Data Connect, צריך להתקין את תוסף Visual Studio של Firebase Data Connect.
    לחלופין, אפשר להתקין את התוסף מ-Visual Studio Code Marketplace או לחפש אותו ב-VS Code.b03ee38c9a81b648.png
  5. פותחים או יוצרים פרויקט Firebase חדש במסוף Firebase.
  6. מקשרים את פרויקט Firebase לתוסף VSCode של Firebase Data Connect. בתוסף, מבצעים את הפעולות הבאות:
    1. לוחצים על הלחצן Sign in.
    2. לוחצים על קישור פרויקט Firebase ובוחרים את פרויקט Firebase.
    4bb2fbf8f9fac29b.png
  7. מפעילים את המהדמנים של Firebase באמצעות התוסף Firebase Data Connect ל-VS Code:
    לוחצים על Start Emulators (הפעלת המהדמנים) ומוודאים שהמהדמנים פועלים במסוף.6d3d95f4cb708db1.png

3. בדיקת קוד המקור של ה-starter

בקטע הזה נסקור תחומים מרכזיים בקוד המקור של ערכת ההתחלה של האפליקציה. באפליקציה חסרות פונקציות מסוימות, אבל כדאי להבין את המבנה הכללי שלה.

מבנה התיקיות והקבצים

בקטעים המשניים הבאים מוצגת סקירה כללית של מבנה התיקיות והקבצים של האפליקציה.

הספרייה dataconnect/

מכיל הגדרות של Firebase Data Connect, מחברים (שמגדירים שאילתות ומוטציות) וקובצי סכימה.

  • schema/schema.gql: הגדרת הסכימה של GraphQL
  • connector/queries.gql: שאילתות שנדרשות באפליקציה
  • connector/mutations.gql: המוטציות הנדרשות באפליקציה
  • connector/connector.yaml: קובץ תצורה ליצירת SDK

הספרייה app/src/

מכיל את הלוגיקה של האפליקציה ואת האינטראקציה עם Firebase Data Connect.

  • firebase.ts: הגדרה להתחברות לאפליקציה ב-Firebase בפרויקט Firebase.
  • lib/dataconnect-sdk/: מכיל את ה-SDK שנוצר. אפשר לערוך את המיקום של יצירת ה-SDK בקובץ connector/connector.yaml, ו-SDKs ייווצרו באופן אוטומטי בכל פעם שתגדירו שאילתה או מוטציה.

4. הגדרת סכימה לביקורות על סרטים

בקטע הזה מגדירים את המבנה ואת היחסים בין ישויות המפתח באפליקציית הסרטים בסכימה. ישויות כמו Movie,‏ User,‏ Actor ו-Review ממפות לטבלאות של מסדי נתונים, והיחסים נוצרים באמצעות Firebase Data Connect והוראות הסכימה של GraphQL. אחרי שתגדירו את הרכיב, האפליקציה תהיה מוכנה לטפל בכל דבר, החל מחיפוש סרטים עם הדירוג הגבוה ביותר וסינון לפי ז'אנר, ועד לאפשרות של המשתמשים לכתוב ביקורות, לסמן פריטים כ'מועדפים', לבחון סרטים דומים או למצוא סרטים מומלצים על סמך קלט טקסט באמצעות חיפוש וקטורים.

ישויות ויחסים מרכזיים

הסוג Movie מכיל פרטים חשובים כמו כותרת, ז'אנר ותגים, שהאפליקציה משתמשת בהם בחיפושים ובפרופילים של סרטים. הסוג User עוקב אחרי אינטראקציות של משתמשים, כמו ביקורות ומועדפים. Reviews לקשר משתמשים לסרטים, ולאפשר לאפליקציה להציג דירוגים ומשוב שנוצרו על ידי משתמשים.

הקשרים בין סרטים, שחקנים ומשתמשים הופכים את האפליקציה לדינמית יותר. טבלת המיזוג MovieActor עוזרת להציג את פרטי השחקנים ואת הפילמוגרפיות שלהם. הסוג FavoriteMovie מאפשר למשתמשים להוסיף סרטים למועדפים, כך שהאפליקציה יכולה להציג רשימת מועדפים מותאמת אישית ולהדגיש בחירות פופולריות.

הגדרת הטבלה Movie

הסוג Movie מגדיר את המבנה הראשי של ישות סרט, כולל שדות כמו title,‏ genre,‏ releaseYear ו-rating.

מעתיקים ומדביקים את קטע הקוד בקובץ 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]
}

מסקנות עיקריות:

  • id: מזהה UUID ייחודי לכל סרט, שנוצר באמצעות @default(expr: "uuidV4()").

הגדרת הטבלה MovieMetadata

הסוג MovieMetadata יוצר קשר אחד-ל-אחד עם הסוג Movie. הוא כולל נתונים נוספים, כמו הבמאי של הסרט.

מעתיקים ומדביקים את קטע הקוד בקובץ 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
}

מסקנות עיקריות:

  • סרט! @ref: הפניה לסוג Movie, שמגדירה קשר של מפתח זר.

הגדרת הטבלה Actor

מעתיקים ומדביקים את קטע הקוד בקובץ dataconnect/schema/schema.gql:

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

הסוג Actor מייצג שחקן במסד הנתונים של הסרטים, שבו כל שחקן יכול להשתתף בכמה סרטים, ויוצר קשר של 'הרבה לרבים'.

הגדרת הטבלה MovieActor

מעתיקים ומדביקים את קטע הקוד בקובץ 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"
}

מסקנות עיקריות:

  • movie: הפניה לסוג Movie, יוצרת באופן משתמע מפתח זר movieId: UUID!.
  • actor: הפניה לסוג Actor, יוצרת באופן משתמע מפתח זר actorId: UUID!.
  • role: הגדרת התפקיד של השחקן בסרט (למשל, 'עיקרי' או 'תומך').

הגדרת הטבלה User

הסוג User מגדיר ישות משתמש שמקיימת אינטראקציה עם סרטים על ידי פרסום ביקורות או הוספת סרטים למועדפים.

מעתיקים ומדביקים את קטע הקוד בקובץ 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
}

הגדרת הטבלה FavoriteMovie

הטיפוס FavoriteMovie הוא טבלת צירוף שמטפלת ביחסי 'הרבה לרבים' בין משתמשים לבין הסרטים המועדפים עליהם. כל טבלה מקשרת User ל-Movie.

מעתיקים ומדביקים את קטע הקוד בקובץ 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!
}

מסקנות עיקריות:

  • movie: הפניה לסוג Movie, יוצרת באופן משתמע מפתח זר movieId: UUID!.
  • user: הפניה לסוג המשתמש, יוצרת באופן משתמע מפתח זר userId: UUID!.

הגדרת הטבלה Review

הסוג Review מייצג את הישות של הביקורת ומקשר בין הסוגים User ו-Movie ביחסות 'הרבה לרבים' (משתמש אחד יכול לכתוב הרבה ביקורות, וכל סרט יכול לכלול הרבה ביקורות).

מעתיקים ומדביקים את קטע הקוד בקובץ 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")
}

מסקנות עיקריות:

  • user: הפניה למשתמש שהשאיר את הביקורת.
  • movie: הפניה לסרט שעליו נכתבת הביקורת.
  • reviewDate: מוגדר באופן אוטומטי לזמן שבו הבדיקה נוצרה באמצעות @default(expr: "request.time").

שדות שמוגדרים כברירת מחדל ושדות שנוצרים באופן אוטומטי

בסכימה נעשה שימוש בביטויים כמו @default(expr: "uuidV4()") כדי ליצור באופן אוטומטי מזהים ייחודיים חותמות זמן. לדוגמה, השדה id בסוגי Movie ו-Review מאוכלס באופן אוטומטי ב-UUID כשיוצרים רשומה חדשה.

עכשיו, אחרי שהגדרתם את הסכימה, לאפליקציית הסרטים יש בסיס יציב למבנה הנתונים וליחסים שלה.

5. אחזור הסרטים המובילים והחדשים ביותר

אפליקציית FriendlyMovies

בקטע הזה תוסיפו נתוני סרטים מדומים למהדמרים המקומיים, ולאחר מכן תטמיעו את המחברים (שאילתות) ואת קוד TypeScript כדי לבצע קריאה למחברים האלה באפליקציית האינטרנט. בסיום, האפליקציה תוכל לאחזר באופן דינמי ולהציג את הסרטים החדשים והמובילים בדירוג ישירות ממסד הנתונים.

הוספת נתונים מדומים של סרטים, שחקנים וביקורות

  1. ב-VSCode, פותחים את dataconnect/moviedata_insert.gql. מוודאים שהמעבדים בסימולטורים של התוסף Firebase Data Connect פועלים.
  2. בחלק העליון של הקובץ אמור להופיע הלחצן הפעלה (מקומית). לוחצים עליו כדי להוסיף את נתוני הסרט המדומה למסד הנתונים.
    e424f75e63bf2e10.png
  3. בודקים את מסוף Data Connect Execution כדי לוודא שהנתונים נוספו בהצלחה.
    e0943d7704fb84ea.png

הטמעת המחבר

  1. פתיחת dataconnect/movie-connector/queries.gql. בתגובות תמצאו שאילתה בסיסית של ListMovies:
    query ListMovies @auth(level: PUBLIC) {
      movies {
        id
        title
        imageUrl
        releaseYear
        genre
        rating
        tags
        description
      }
    }
    
    השאילתה הזו מאחזרת את כל הסרטים ואת הפרטים שלהם (לדוגמה, id, ‏ title, ‏ releaseYear). עם זאת, היא לא ממיינת את הסרטים.
  2. מחליפים את השאילתה הקיימת ListMovies בשאילתה הבאה כדי להוסיף אפשרויות מיון ומגבלות:
    # 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. לוחצים על הלחצן Run (local) כדי להריץ את השאילתה במסד הנתונים המקומי. אפשר גם להזין את משתני השאילתה בחלונית ההגדרות לפני ההרצה.
    c4d947115bb11b16.png

מסקנות עיקריות:

  • movies(): שדה שאילתת GraphQL לאחזור נתוני סרטים מהמסד נתונים.
  • orderByRating: פרמטר למיון סרטים לפי דירוג (עולה/יורד).
  • orderByReleaseYear: פרמטר למיון סרטים לפי שנת הפרסום (לפי סדר עולה/יורד).
  • limit: הגבלת מספר הסרטים שיתקבלו בתוצאה.

שילוב שאילתות באפליקציית האינטרנט

בקטע הזה של הקודלאב, תשתמשו בשאילתות שהוגדרו בקטע הקודם באפליקציית האינטרנט. המהדמנים של Firebase Data Connect יוצרים ערכות SDK על סמך המידע שבקבצים .gql (במיוחד schema.gql,‏ queries.gql ו-mutations.gql) ובקובץ connector.yaml. אפשר להפעיל את ערכות ה-SDK האלה ישירות באפליקציה.

  1. בקובץ MovieService (app/src/lib/MovieService.tsx), מסירים את ההערה של הצהרת הייבוא בחלק העליון:
    import { listMovies, ListMoviesData, OrderDirection } from "@movie/dataconnect";
    
    הפונקציה listMovies, סוג התגובה ListMoviesData וה-enum OrderDirection הן כולן ערכות SDK שנוצרו על ידי אמוללטורים של Firebase Data Connect על סמך הסכימה והשאילתות שהגדרתם קודם .
  2. מחליפים את הפונקציות handleGetTopMovies ו-handleGetLatestMovies בקוד הבא:
    // 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;
      }
    };
    

מסקנות עיקריות:

  • listMovies: פונקציה שנוצרת באופן אוטומטי שמפעילה את השאילתה listMovies כדי לאחזר רשימה של סרטים. הוא כולל אפשרויות למיון לפי דירוג או שנת פרסום ולהגבלת מספר התוצאות.
  • ListMoviesData: סוג התוצאה שמשמש להצגת 10 הסרטים המובילים והסרטים החדשים ביותר בדף הבית של האפליקציה.

הדגמה

צריך לטעון מחדש את אפליקציית האינטרנט כדי לראות את השאילתה בפעולה. עכשיו רשימת הסרטים מוצגת באופן דינמי בדף הבית, והנתונים מאוחזרים ישירות ממסד הנתונים המקומי. הסרטים עם הדירוג הגבוה ביותר והסרטים החדשים ביותר יופיעו בצורה חלקה, בהתאם לנתונים שהגדרתם.

6. הצגת פרטי הסרט והשחקן

בקטע הזה נטמיע את הפונקציונליות של אחזור מידע מפורט על סרט או שחקן באמצעות המזהים הייחודיים שלהם. התהליך הזה כולל לא רק אחזור נתונים מהטבלאות המתאימות, אלא גם צירוף של טבלאות קשורות כדי להציג פרטים מקיפים, כמו ביקורות על סרטים ופילוגרפיות של שחקנים.

ac7fefa7ff779231.png

הטמעת מחברים

  1. פותחים את dataconnect/movie-connector/queries.gql בפרויקט.
  2. מוסיפים את השאילתות הבאות כדי לאחזר פרטים של סרטים ושחקנים:
    # 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. שומרים את השינויים ובודקים את השאילתות.

מסקנות עיקריות:

  • movie()/actor(): שדות של שאילתות GraphQL לאחזור סרט או שחקן יחיד מהטבלה Movies או Actors.
  • _on_: האפשרות הזו מאפשרת גישה ישירה לשדות מסוג משויך שיש לו קשר למפתח זר. לדוגמה, reviews_on_movie מאחזרת את כל הביקורות שקשורות לסרט מסוים.
  • _via_: משמש לניווט ביחסים 'רבים לרבים' באמצעות טבלת איחוד. לדוגמה, actors_via_MovieActor ניגשת לסוג Actor דרך טבלת המיזוג MovieActor, והתנאי where מסנן שחקנים על סמך התפקיד שלהם (לדוגמה, 'ראשי' או 'תומך').

בדיקת השאילתה באמצעות הזנת נתוני דמה

  1. בחלונית ההפעלה של Data Connect, אפשר לבדוק את השאילתה על ידי הזנת מזהי מדגם, כמו:
    {"id": "550e8400-e29b-41d4-a716-446655440000"}
    
  2. לוחצים על הפעלה (מקומית) עבור GetMovieById כדי לאחזר את הפרטים על 'פרדוקס קוונטי' (סרט ההדמיה שהמזהה שלמעלה קשור אליו).

1b08961891e44da2.png

שילוב שאילתות באפליקציית האינטרנט

  1. בקובץ MovieService (app/src/lib/MovieService.tsx), מסירים את ההערות מהייבוא הבא:
    import { getMovieById, GetMovieByIdData } from "@movie/dataconnect";
    import { GetActorByIdData, getActorById } from "@movie/dataconnect";
    
  2. מחליפים את הפונקציות handleGetMovieById ו-handleGetActorById בקוד הבא:
    // 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;
      }
    };
    

מסקנות עיקריות:

  • getMovieById / getActorById: אלה פונקציות שנוצרות באופן אוטומטי ומפעילות את השאילתות שהגדרתם, כדי לאחזר מידע מפורט על סרט או שחקן ספציפיים.
  • GetMovieByIdData / GetActorByIdData: אלה סוגי התוצאות שמשמשים להצגת פרטי הסרטים והשחקנים באפליקציה.

הדגמה

עכשיו עוברים לדף הבית של אפליקציית האינטרנט. לוחצים על סרט כדי לראות את כל הפרטים שלו, כולל השחקנים והביקורות – מידע שנשלף מטבלאות קשורות. באופן דומה, לחיצה על שחקן תציג את הסרטים שבהם הוא השתתף.

7. טיפול באימות משתמשים

בקטע הזה נטמיע את הפונקציונליות של כניסת משתמשים לחשבון ויציאה ממנו באמצעות אימות ב-Firebase. תוכלו גם להשתמש בנתוני האימות של Firebase כדי לאחזר או להוסיף נתוני משתמשים ישירות ב-Firebase DataConnect, וכך להבטיח ניהול מאובטח של משתמשים באפליקציה.

9890838045d5a00e.png

הטמעת מחברים

  1. פותחים את mutations.gql ב-dataconnect/movie-connector/.
  2. מוסיפים את המוטציה הבאה כדי ליצור או לעדכן את המשתמש המאומת הנוכחי:
    # Create or update the current authenticated user
    mutation UpsertUser($username: String!) @auth(level: USER) {
      user_upsert(
        data: {
          id_expr: "auth.uid"
          username: $username
        }
      )
    }
    

מסקנות עיקריות:

  • id_expr: "auth.uid": האפשרות הזו משתמשת ב-auth.uid, שמסופק ישירות על ידי אימות ב-Firebase ולא על ידי המשתמש או האפליקציה. כך מתווספת שכבת אבטחה נוספת, כי המערכת מוודאת שהטיפול במזהה המשתמש מתבצע באופן מאובטח ואוטומטי.

אחזור המשתמש הנוכחי

  1. פותחים את queries.gql ב-dataconnect/movie-connector/.
  2. מוסיפים את השאילתה הבאה כדי לאחזר את המשתמש הנוכחי:
    # 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
            }
          }
        }
      }
    }
    

מסקנות עיקריות:

  • auth.uid: השדה הזה מאוחזר ישירות מאימות ב-Firebase, וכך מובטחת גישה מאובטחת לנתונים ספציפיים למשתמש.
  • שדות _on_: השדות האלה מייצגים את טבלאות המיזוג:
    • reviews_on_user: אחזור כל הביקורות שקשורות למשתמש, כולל id ו-title של הסרט.
    • favorite_movies_on_user: אחזור של כל הסרטים שהמשתמש סימן כ'מועדפים', כולל מידע מפורט כמו genre,‏ releaseYear,‏ rating ו-metadata.

שילוב שאילתות באפליקציית האינטרנט

  1. ב-MovieService (app/src/lib/MovieService.tsx), מסירים את ההערות מהייבוא הבא:
    import { upsertUser } from "@movie/dataconnect";
    import { getCurrentUser, GetCurrentUserData } from "@movie/dataconnect";
    
  2. מחליפים את הפונקציות handleAuthStateChange ו-handleGetCurrentUser בקוד הבא:
    // 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;
      }
    };
    

מסקנות עיקריות:

  • handleAuthStateChange: הפונקציה הזו מקשיבה לשינויים במצב האימות. כשמשתמש נכנס לחשבון, המערכת מגדירה את הנתונים של המשתמש ומפעילה את המוטציה upsertUser כדי ליצור או לעדכן את פרטי המשתמש במסד הנתונים.
  • handleGetCurrentUser: אחזור הפרופיל של המשתמש הנוכחי באמצעות השאילתה getCurrentUser, שמאחזרת את הביקורות והסרטים האהובים על המשתמש.

הדגמה

עכשיו לוחצים על הלחצן 'כניסה באמצעות חשבון Google' בסרגל הניווט. אפשר להיכנס באמצעות אמולטור האימות של Firebase. אחרי הכניסה לחשבון, לוחצים על 'הפרופיל שלי'. בשלב הזה הוא יהיה ריק, אבל עכשיו כבר הגדרתם את הבסיס לטיפול בנתונים ספציפיים למשתמש באפליקציה.

8. הטמעת אינטראקציות של משתמשים

בקטע הזה של הקודלה, נתכנן אינטראקציות של משתמשים באפליקציית ביקורות הסרטים, במיוחד כדי לאפשר למשתמשים לנהל את הסרטים האהובים עליהם ולכתוב או למחוק ביקורות.

b3d0ac1e181c9de9.png

איך מאפשרים למשתמש להוסיף סרט למועדפים

בקטע הזה מגדירים את מסד הנתונים כך שמשתמשים יוכלו להוסיף סרט למועדפים.

הטמעת מחברים

  1. פותחים את mutations.gql ב-dataconnect/movie-connector/.
  2. מוסיפים את המוטציות הבאות כדי לטפל בהוספת סרטים למועדפים:
    # 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 })
    }
    
    

מסקנות עיקריות:

  • userId_expr: "auth.uid": משתמשים ב-auth.uid, שמסופק ישירות על ידי אימות ב-Firebase, כדי להבטיח שרק לנתונים של המשתמש המאומת תהיה גישה או שינוי.

איך בודקים אם סרט מסוים מסומן כ'מועדף'

  1. פותחים את queries.gql ב-dataconnect/movie-connector/.
  2. מוסיפים את השאילתה הבאה כדי לבדוק אם סרט מסוים נכלל ברשימת המועדפים:
    query GetIfFavoritedMovie($movieId: UUID!) @auth(level: USER) {
      favorite_movie(key: { userId_expr: "auth.uid", movieId: $movieId }) {
        movieId
      }
    }
    

מסקנות עיקריות:

  • auth.uid: מבטיחה גישה מאובטחת לנתונים ספציפיים למשתמש באמצעות אימות ב-Firebase.
  • favorite_movie: הפונקציה בודקת את טבלת המיזוג favorite_movies כדי לראות אם סרט ספציפי מסומן כמועדף על ידי המשתמש הנוכחי.

שילוב שאילתות באפליקציית האינטרנט

  1. בקובץ MovieService (app/src/lib/MovieService.tsx), מסירים את ההערות מהייבוא הבא:
    import { addFavoritedMovie, deleteFavoritedMovie, getIfFavoritedMovie } from "@movie/dataconnect";
    
  2. מחליפים את הפונקציות handleAddFavoritedMovie,‏ handleDeleteFavoritedMovie ו-handleGetIfFavoritedMovie בקוד הבא:
    // 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;
      }
    };
    

מסקנות עיקריות:

  • handleAddFavoritedMovie ו-handleDeleteFavoritedMovie: אפשר להשתמש במונטיזציות כדי להוסיף או להסיר סרט מהמועדפים של המשתמש באופן מאובטח.
  • handleGetIfFavoritedMovie: השאילתה getIfFavoritedMovie משמשת לבדיקה אם המשתמש סימן סרט כ'מועדף'.

הדגמה

עכשיו אפשר להוסיף סרטים למועדפים או לבטל את ההוספה שלהם על ידי לחיצה על סמל הלב בכרטיסי הסרטים ובדף הפרטים של הסרט. בנוסף, אתם יכולים לראות את הסרטים האהובים עליכם בדף הפרופיל.

איך משתמשים יכולים לכתוב או למחוק ביקורות

בשלב הבא, תאמצו את הקטע לניהול ביקורות המשתמשים באפליקציה.

הטמעת מחברים

בקובץ mutations.gql (dataconnect/movie-connector/mutations.gql): מוסיפים את המוטציות הבאות:

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

מסקנות עיקריות:

  • userId_expr: "auth.uid": מוודא שהביקורות משויכות למשתמש המאומת.
  • reviewDate_date: { today: true }: המערכת יוצרת באופן אוטומטי את התאריך הנוכחי של הבדיקה באמצעות DataConnect, כך שאין צורך להזין נתונים באופן ידני.

שילוב שאילתות באפליקציית האינטרנט

  1. בקובץ MovieService (app/src/lib/MovieService.tsx), מסירים את ההערות מהייבוא הבא:
    import { addReview, deleteReview } from "@movie/dataconnect";
    
  2. מחליפים את הפונקציות handleAddReview ו-handleDeleteReview בקוד הבא:
    // 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;
      }
    };
    

מסקנות עיקריות:

  • handleAddReview: קריאה למוטציה addReview כדי להוסיף ביקורת על הסרט שצוין, ולקשר אותה בצורה מאובטחת למשתמש המאומת.
  • handleDeleteReview: משתמשים בטרנספורמציה deleteReview כדי להסיר ביקורת על סרט שהמשתמש המאומת פרסם.

הדגמה

עכשיו המשתמשים יכולים להשאיר ביקורות על סרטים בדף הפרטים של הסרט. הם יכולים גם לראות ולמחוק את הביקורות שלהם בדף הפרופיל שלהם, כך שיש להם שליטה מלאה על האינטראקציות שלהם עם האפליקציה.

9. מסננים מתקדמים והתאמת טקסט חלקית

בקטע הזה נסביר איך מטמיעים יכולות חיפוש מתקדמות שמאפשרות למשתמשים לחפש סרטים לפי מגוון דירוגים ושנות הפקה, לסנן לפי ז'אנרים ותגים, לבצע התאמה חלקית של טקסט בשמות או בתיאורים ואפילו לשלב כמה מסננים כדי לקבל תוצאות מדויקות יותר.

ece70ee0ab964e28.png

הטמעת מחברים

  1. פתיחת queries.gql ב-dataconnect/movie-connector/.
  2. מוסיפים את השאילתה הבאה כדי לתמוך ביכולות חיפוש שונות:
    # 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
        }
      }
    }
    

מסקנות עיקריות:

  • אופרטור _and: שילוב של כמה תנאים בשאילתה אחת, שמאפשר לסנן את החיפוש לפי כמה שדות כמו releaseYear, ‏rating ו-genre.
  • אופרטור contains: חיפוש התאמות טקסט חלקיות בתוך שדות. בשאילתה הזו, היא מחפשת התאמות ב-title, ב-description, ב-name או ב-reviewText.
  • תנאי where: מציין את התנאים לסינון הנתונים. בכל קטע (סרטים, שחקנים, ביקורות) נעשה שימוש בפסקה where כדי להגדיר את הקריטריונים הספציפיים לחיפוש.

שילוב שאילתות באפליקציית האינטרנט

  1. בקובץ MovieService (app/src/lib/MovieService.tsx), מסירים את ההערות מהייבוא הבא:
    import { searchAll, SearchAllData } from "@movie/dataconnect";
    
  2. מחליפים את הפונקציה handleSearchAll בקוד הבא:
    // 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;
      }
    };
    

מסקנות עיקריות:

  • handleSearchAll: הפונקציה הזו משתמשת בשאילתה searchAll כדי לבצע חיפוש על סמך הקלט של המשתמש, ומסננת את התוצאות לפי פרמטרים כמו שנה, דירוג, ז'אנר והתאמות חלקיות של טקסט.

הדגמה

עוברים לדף 'חיפוש מתקדם' בסרגל הניווט באפליקציית האינטרנט. עכשיו אפשר לחפש סרטים, שחקנים וביקורות באמצעות מסננים ומקורות קלט שונים, ולקבל תוצאות חיפוש מפורטות ומותאמות אישית.

10. אופציונלי: פריסה ב-Cloud (נדרש חיוב)

עכשיו, אחרי שסיימתם את מחזור הפיתוח המקומי, הגיע הזמן לפרוס את הסכימה, הנתונים והשאילתות בשרת. אפשר לעשות זאת באמצעות התוסף של Firebase Data Connect ל-VS Code או באמצעות ה-CLI של Firebase.

שדרוג של תוכנית התמחור ב-Firebase

כדי לשלב את Firebase Data Connect עם Cloud SQL for PostgreSQL, פרויקט Firebase צריך להיות בתוכנית התמחור 'תשלום לפי שימוש' (Blaze), כלומר הוא מקושר לחשבון לחיוב ב-Cloud.

  • בחשבון לחיוב ב-Cloud נדרש אמצעי תשלום, כמו כרטיס אשראי.
  • אם אתם משתמשים חדשים ב-Firebase וב-Google Cloud, כדאי לבדוק אם אתם זכאים לקרדיט בסך 300$ולחשבון לחיוב ב-Cloud בתקופת ניסיון בחינם.
  • אם אתם מבצעים את הקודלאב הזה כחלק מאירוע, כדאי לשאול את המארגן אם יש זיכויים ב-Cloud שזמינים.

כדי לשדרג את הפרויקט לתוכנית Blaze:

  1. במסוף Firebase, בוחרים באפשרות שדרוג התוכנית.
  2. בוחרים את תוכנית Blaze. פועלים לפי ההוראות במסך כדי לקשר חשבון לחיוב ב-Cloud לפרויקט.
    אם נדרשת יצירת חשבון לחיוב ב-Cloud כחלק מהשדרוג, יכול להיות שתצטרכו לחזור לתהליך השדרוג במסוף Firebase כדי להשלים את השדרוג.

קישור אפליקציית האינטרנט לפרויקט Firebase

  1. רושמים את אפליקציית האינטרנט בפרויקט Firebase באמצעות מסוף Firebase:
    1. פותחים את הפרויקט ולוחצים על Add App.
    2. בשלב הזה, אפשר להתעלם מהגדרת ה-SDK וההגדרות, אבל חשוב להעתיק את אובייקט firebaseConfig שנוצר.
    7030822793e4d75b.png
  2. מחליפים את firebaseConfig הקיים ב-app/src/lib/firebase.tsx בתצורה שהעתקתם זה עתה ממסוף 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. יצירת אפליקציית האינטרנט: חזרה ל-VS Code, בתיקייה app, משתמשים ב-Vite כדי ליצור את אפליקציית האינטרנט לצורך פריסה באירוח:
    cd app
    npm run build
    

הגדרת אימות Firebase בפרויקט Firebase

  1. מגדירים אימות ב-Firebase באמצעות כניסה באמצעות חשבון Google.62af2f225e790ef6.png
  2. (אופציונלי) מאפשרים דומיינים לאימות ב-Firebase באמצעות מסוף Firebase (לדוגמה, http://127.0.0.1).
    1. בהגדרות של Authentication, עוברים אל Authorized Domains.
    2. לוחצים על 'הוספת דומיין' וכוללים את הדומיין המקומי ברשימה.

c255098f12549886.png

פריסה באמצעות Firebase CLI

  1. בקובץ dataconnect/dataconnect.yaml, מוודאים שמזהה המכונה, מסד הנתונים ומזהה השירות תואמים לפרויקט:
    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. מוודאים שהגדרתם את Firebase CLI בפרויקט:
    npm i -g firebase-tools
    firebase login --reauth
    firebase use --add
    
  3. מריצים את הפקודה הבאה בטרמינל כדי לפרוס:
    firebase deploy --only dataconnect,hosting
    
  4. מריצים את הפקודה הבאה כדי להשוות בין השינויים בסכימה:
    firebase dataconnect:sql:diff
    
  5. אם השינויים מקובלים, מחילים אותם באמצעות:
    firebase dataconnect:sql:migrate
    

המכונה של Cloud SQL for PostgreSQL תתעדכן בנתונים ובסכימה הסופיים שנפרסו. אפשר לעקוב אחרי הסטטוס במסוף Firebase.

עכשיו אמורה להופיע האפליקציה שלך ב-your-project.web.app/. בנוסף, אפשר ללחוץ על הפעלה (ייצור) בחלונית של Firebase Data Connect, בדיוק כמו שעשיתם עם המהדמנים המקומיים, כדי להוסיף נתונים לסביבת הייצור.

11. אופציונלי: חיפוש וקטורים באמצעות Firebase Data Connect (נדרש חיוב)

בקטע הזה מפעילים חיפוש וקטורים באפליקציה של ביקורות על סרטים באמצעות Firebase Data Connect. התכונה הזו מאפשרת לבצע חיפושים מבוססי-תוכן, כמו חיפוש סרטים עם תיאורים דומים באמצעות הטמעת וקטורים.

כדי לבצע את השלב הזה, צריך להשלים את השלב האחרון של הקודלאב הזה לפריסה ב-Google Cloud.

4b5aca5a447d2feb.png

עדכון הסכימה כך שתכלול הטמעות (embeddings) לשדה

ב-dataconnect/schema/schema.gql, מוסיפים את השדה descriptionEmbedding לטבלה 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
}

מסקנות עיקריות:

  • descriptionEmbedding: Vector @col(size:768): השדה הזה מאחסן את הטמעות הסמנטיקה של תיאורי הסרטים, ומאפשר חיפוש תוכן מבוסס-וקטור באפליקציה.

הפעלת Vertex AI

  1. פועלים לפי המדריך לדרישות מוקדמות כדי להגדיר את ממשקי Vertex AI API מ-Google Cloud. השלב הזה חיוני כדי לתמוך ביצירת הטמעה ובפונקציונליות של חיפוש וקטורים.
  2. כדי להפעיל את pgvector ואת החיפוש לפי וקטור, פורסים מחדש את הסכימה בלחיצה על 'פריסה בסביבת הייצור' באמצעות התוסף של Firebase Data Connect ל-VS Code.

איכלוס מסד הנתונים ב-embeddings

  1. פותחים את התיקייה dataconnect ב-VS Code.
  2. לוחצים על Run(local) ב-optional_vector_embed.gql כדי לאכלס את מסד הנתונים ב-embeddings של הסרטים.

b858da780f6ec103.png

הוספת שאילתה לחיפוש וקטור

כדי לבצע חיפושים של וקטורים, מוסיפים את השאילתה הבאה לקובץ dataconnect/movie-connector/queries.gql:

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

מסקנות עיקריות:

  • compare_embed: מציין את מודל הטמעת הטקסט (textembedding-gecko@003) ואת טקסט הקלט ($query) לצורך השוואה.
  • method: מציין את שיטת הדמיון (L2), שמייצגת את המרחק האוקלידי.
  • within: הגבלת החיפוש לסרטים עם מרחק L2 של 2 או פחות, תוך התמקדות בהתאמות תוכן דומות.
  • limit: הגבלת מספר התוצאות שהוחזרו ל-5.

הטמעת פונקציית החיפוש לפי וקטור באפליקציה

עכשיו, אחרי שהסכימת והגדרת את השאילתה, אפשר לשלב את חיפוש הווקטורים בשכבת השירות של האפליקציה. השלב הזה מאפשר לכם להפעיל את שאילתת החיפוש מאפליקציית האינטרנט.

  1. בקובץ app/src/lib/ MovieService.ts, מסירים את ההערות של הייבוא הבא מערכות ה-SDK. זה יפעל כמו כל שאילתה אחרת.
    import {
      searchMovieDescriptionUsingL2similarity,
      SearchMovieDescriptionUsingL2similarityData,
    } from "@movie/dataconnect";
    
  2. מוסיפים את הפונקציה הבאה כדי לשלב חיפוש מבוסס-וקטורים באפליקציה:
    // 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;
      }
    };
    

מסקנות עיקריות:

  • searchMoviesByDescription: הפונקציה הזו קוראת לשאילתה searchMovieDescriptionUsingL2similarity ומעבירה את טקסט הקלט כדי לבצע חיפוש תוכן מבוסס-וקטור.

הדגמה

עוברים לקטע 'חיפוש וקטור' בסרגל הניווט ומקלידים ביטויים כמו 'רומנטי ומודרני'. תוצג רשימה של סרטים שתואמים לתוכן שאתם מחפשים. לחלופין, תוכלו להיכנס לדף הפרטים של סרט כלשהו ולבדוק את הקטע'סרטים דומים' שבתחתית הדף.

7b71f1c75633c1be.png

12. סיכום

מעולה, עכשיו אמורה להיות לך אפשרות להשתמש באפליקציית האינטרנט. אם אתם רוצים לשחק עם נתוני סרטים משלכם, אל דאגה, תוכלו להוסיף נתונים משלכם באמצעות התוסף Firebase Data Connect, על ידי חיקוי של קובצי _insert.gql, או להוסיף אותם באמצעות חלונית הביצוע של Data Connect ב-VS Code.

מידע נוסף