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

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

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

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

כל נתוני מסד הנתונים בזמן אמת של Firebase נשמרים כאובייקטים של 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 מאפשר קינון נתונים בעומק של עד 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
      }
    },
    ...
  }
}

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

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

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

הצעדים הבאים