בניית מסד הנתונים שלך

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

כדי להשתמש ב-Realtime Database, צריך:

  • רושמים את הפרויקט ב-Unity ומגדירים אותו לשימוש ב-Firebase.

    • אם כבר משתמשים ב-Firebase בפרויקט שלכם ב-Unity, הוא כבר רשום ב-Firebase ועבר הגדרה.

    • אם אין לכם פרויקט ב-Unity, תוכלו להוריד אפליקציה לדוגמה.

  • מוסיפים את Firebase Unity SDK (במיוחד FirebaseDatabase.unitypackage) לפרויקט ב-Unity.

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

מבנה הנתונים

במדריך הזה נסביר על כמה מהמושגים המרכזיים בארכיטקטורת נתונים ועל השיטות המומלצות ליצירת מבנה לנתוני ה-JSON ב-Firebase Realtime Database.

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

איך הנתונים בנויים: זה עץ JSON

כל הנתונים של Firebase Realtime Database מאוחסנים כאובייקטים של JSON. אפשר לחשוב על מסד הנתונים כעץ JSON שמתארח בענן. בניגוד למסד נתונים של SQL, אין בו טבלאות או רשומות. כשמוסיפים נתונים לעץ ה-JSON, הוא הופך לצומת במבנה JSON הקיים עם מפתח משויך. אתם יכולים לספק מפתחות משלכם, כמו מזהי משתמשים או שמות סמנטיים, או שתקבלו אותם באמצעות השיטה Push().

אם אתם יוצרים מפתחות משלכם, הם חייבים להיות בקידוד UTF-8, באורך של עד 768 בייטים ולא יכולים להכיל את התווים .,‏ $,‏ #,‏ [,‏ ],‏ / או תווים רגולטוריים של ASCII בטווח 0-31 או 127. אי אפשר להשתמש גם בתווי בקרה מסוג ASCII בערכים עצמם.

לדוגמה, אפליקציית צ'אט שמאפשרת למשתמשים לשמור פרופיל בסיסי ורשימת אנשי קשר. פרופיל משתמש טיפוסי נמצא בנתיב, למשל /users/$uid. יכול להיות שלמשתמש alovelace תהיה רשומה במסד הנתונים שנראית בערך כך:

{
  "users": {
    "alovelace": {
      "name": "Ada Lovelace",
      "contacts": { "ghopper": true },
    },
    "ghopper": { ... },
    "eclarke": { ... }
  }
}

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

שיטות מומלצות למבנה הנתונים

הימנעות מהטמעת נתונים

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

כדי להבין למה נתונים בתצוגת עץ הם לא טובים, נבחן את המבנה הבא עם כמה רמות עץ:

{
  // This is a poorly nested data architecture, because iterating the children
  // of the "chats" node to get a list of conversation titles requires
  // potentially downloading hundreds of megabytes of messages
  "chats": {
    "one": {
      "title": "Historical Tech Pioneers",
      "messages": {
        "m1": { "sender": "ghopper", "message": "Relay malfunction found. Cause: moth." },
        "m2": { ... },
        // a very long list of messages
      }
    },
    "two": { ... }
  }
}

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

יישור מבני נתונים

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

{
  // Chats contains only meta info about each conversation
  // stored under the chats's unique ID
  "chats": {
    "one": {
      "title": "Historical Tech Pioneers",
      "lastMessage": "ghopper: Relay malfunction found. Cause: moth.",
      "timestamp": 1459361875666
    },
    "two": { ... },
    "three": { ... }
  },

  // Conversation members are easily accessible
  // and stored by chat conversation ID
  "members": {
    // we'll talk about indices like this below
    "one": {
      "ghopper": true,
      "alovelace": true,
      "eclarke": true
    },
    "two": { ... },
    "three": { ... }
  },

  // Messages are separate from data we may want to iterate quickly
  // but still easily paginated and queried, and organized by chat
  // conversation ID
  "messages": {
    "one": {
      "m1": {
        "name": "eclarke",
        "message": "The relay seems to be malfunctioning.",
        "timestamp": 1459361875337
      },
      "m2": { ... },
      "m3": { ... }
    },
    "two": { ... },
    "three": { ... }
  }
}

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

יצירת נתונים בקנה מידה נרחב

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

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

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

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

// An index to track Ada's memberships
{
  "users": {
    "alovelace": {
      "name": "Ada Lovelace",
      // Index Ada's groups in her profile
      "groups": {
         // the value here doesn't matter, just that the key exists
         "techpioneers": true,
         "womentechmakers": true
      }
    },
    ...
  },
  "groups": {
    "techpioneers": {
      "name": "Historical Tech Pioneers",
      "members": {
        "alovelace": true,
        "ghopper": true,
        "eclarke": true
      }
    },
    ...
  }
}

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

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

הגישה הזו, שבה הנתונים הופכים על ידי ציון המזהים כמפתחות והגדרת הערך כ-true, מאפשרת לבדוק מפתח באותה קלות שבה קוראים את הערך /users/$uid/groups/$group_id ובודקים אם הוא null. האינדקס מהיר יותר ויעיל הרבה יותר מאשר שליחת שאילתות או סריקה של הנתונים.

השלבים הבאים