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

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

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

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

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

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

  • מוסיפים את ה-SDK Firebase Unity (באופן ספציפי, 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, גם כשרשימת המשתמשים או הקבוצות מונה מיליוני רשומות או כשכללי האבטחה של Realtime Database מונעים גישה לחלק מהרשומות.

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

השלבים הבאים