אחזור נתונים באמצעות מסד נתונים בזמן אמת ב-Firebase עבור C++

במאמר הזה נסביר איך מאחזרים נתונים ואיך מסדרים ומסננים נתונים ב-Firebase.

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

צריך לוודא שהגדרתם את האפליקציה ויש לכם גישה למסד הנתונים, כמו שמוסבר במדריך Get Started.

אחזור נתונים

הנתונים מ-Firebase מאוחזרים באמצעות קריאה חד-פעמית אל GetValue() או באמצעות צירוף אל ValueListener בהפניה אל FirebaseDatabase. הפונקציה valueListener נקראת פעם אחת עבור המצב הראשוני של הנתונים, ושוב בכל פעם שהנתונים משתנים.

קבלת DatabaseReference

כדי לכתוב נתונים למסד הנתונים, צריך מופע של DatabaseReference:

    // Get the root reference location of the database.
    firebase::database::DatabaseReference dbref = database->GetReference();

קריאת נתונים פעם אחת

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

  firebase::Future&ltfirebase::database::DataSnapshot&gt result =
    dbRef.GetReference("Leaders").GetValue();

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

  // In the game loop that polls for the result...

  if (result.status() != firebase::kFutureStatusPending) {
    if (result.status() != firebase::kFutureStatusComplete) {
      LogMessage("ERROR: GetValue() returned an invalid result.");
      // Handle the error...
    } else if (result.error() != firebase::database::kErrorNone) {
      LogMessage("ERROR: GetValue() returned error %d: %s", result.error(),
                 result.error_message());
      // Handle the error...
    } else {
      firebase::database::DataSnapshot snapshot = result.result();
      // Do something with the snapshot...
    }
  }

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

האזנה לאירועים

אפשר להוסיף מאזינים כדי להירשם לשינויים בנתונים:

ValueListener מחלקה בסיסית

התקשרות חזרה שימוש אופייני
OnValueChanged קריאה והאזנה לשינויים בכל התוכן של נתיב.

OnChildListener מחלקה בסיסית

OnChildAdded אחזור רשימות של פריטים או האזנה להוספות לרשימה של פריטים. מומלץ להשתמש ב-OnChildChanged וב-OnChildRemoved כדי לעקוב אחרי שינויים ברשימות.
OnChildChanged חיפוש שינויים בפריטים ברשימה. אפשר להשתמש ב-OnChildAdded וב-OnChildRemoved כדי לעקוב אחרי שינויים ברשימות.
OnChildRemoved האזנה להסרת פריטים מרשימה. אפשר להשתמש ב-OnChildAdded וב-OnChildChanged כדי לעקוב אחרי שינויים ברשימות.
OnChildMoved האזנה לשינויים בסדר של פריטים ברשימה ממוספרת. OnChildMoved תמיד יתבצעו קריאות חוזרות (callback) של OnChildChanged קריאות חוזרות (callback) בגלל שינוי בסדר הפריטים (בהתאם לשיטת המיון הנוכחית).

המחלקות ValueListener

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

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

  class LeadersValueListener : public firebase::database::ValueListener {
   public:
    void OnValueChanged(
        const firebase::database::DataSnapshot& snapshot) override {
      // Do something with the data in snapshot...
    }
    void OnCancelled(const firebase::database::Error& error_code,
                     const char* error_message) override {
      LogMessage("ERROR: LeadersValueListener canceled: %d: %s", error_code,
                 error_message);
    }
  };

  // Elsewhere in the code...

  LeadersValueListener* listener = new LeadersValueListener();
  firebase::Future&ltfirebase::database::DataSnapshot&gt result =
    dbRef.GetReference("Leaders").AddValueListener(listener);

התוצאה Future&ltDataSnapshot&gt מכילה את הנתונים במיקום שצוין במסד הנתונים בזמן האירוע. הפעלת הפונקציה value() על תמונת מצב מחזירה Variant שמייצג את הנתונים.

בדוגמה הזו, השיטה OnCancelled גם מוחלפת כדי לבדוק אם הקריאה בוטלה. לדוגמה, אפשר לבטל קריאה אם ללקוח אין הרשאה לקרוא ממיקום במסד נתונים של Firebase. הסיבה לכישלון תופיע בdatabase::Error.

המחלקות ChildListener

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

  class SessionCommentsChildListener : public firebase::database::ChildListener {
   public:
    void OnChildAdded(const firebase::database::DataSnapshot& snapshot,
                      const char* previous_sibling) override {
      // Do something with the data in snapshot ...
    }
    void OnChildChanged(const firebase::database::DataSnapshot& snapshot,
                        const char* previous_sibling) override {
      // Do something with the data in snapshot ...
    }
    void OnChildRemoved(
        const firebase::database::DataSnapshot& snapshot) override {
      // Do something with the data in snapshot ...
    }
    void OnChildMoved(const firebase::database::DataSnapshot& snapshot,
                      const char* previous_sibling) override {
      // Do something with the data in snapshot ...
    }
    void OnCancelled(const firebase::database::Error& error_code,
                     const char* error_message) override {
      LogMessage("ERROR: SessionCommentsChildListener canceled: %d: %s",
                 error_code, error_message);
    }
  };

  // elsewhere ....

  SessionCommentsChildListener* listener = new SessionCommentsChildListener();
  firebase::Future&ltfirebase::database::DataSnapshot&gt result =
    dbRef.GetReference("GameSessionComments").AddChildListener(listener);

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

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

הקריאה החוזרת OnChildRemoved מופעלת כשמסירים צאצא מיידי. בדרך כלל משתמשים בה בשילוב עם קריאות חוזרות (callback) של OnChildAdded ו-OnChildChanged. התמונה שמועברת לקריאה החוזרת מכילה את הנתונים של הילד שהוסר.

הקריאה החוזרת OnChildMoved מופעלת בכל פעם שמתקבלת קריאה ל-OnChildChanged בעקבות עדכון שגורם לשינוי הסדר של הילד. הוא משמש עם נתונים שמסודרים לפי OrderByChild או OrderByValue.

מיון וסינון נתונים

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

מיון נתונים

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

שיטה שימוש
OrderByChild() ממיינים את התוצאות לפי הערך של מפתח צאצא ספציפי.
OrderByKey() מיון התוצאות לפי מפתחות צאצא.
OrderByValue() מיון התוצאות לפי ערכי צאצא.

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

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

  firebase::database::Query query =
    dbRef.GetReference("Leaders").OrderByChild("score");

  // To get the resulting DataSnapshot either use query.GetValue() and poll the
  // future, or use query.AddValueListener() and register to handle the
  // OnValueChanged callback.

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

הקריאה ל-method‏ OrderByChild() מציינת את מפתח הילד שבאמצעותו ימוינו התוצאות. במקרה הזה, התוצאות ממוינות לפי הערך של "score" value בכל רכיב צאצא. מידע נוסף על הסדר של סוגי נתונים אחרים זמין במאמר איך מסודרים הנתונים בשאילתות.

סינון נתונים

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

שיטה שימוש
LimitToFirst() מגדיר את המספר המקסימלי של פריטים שיוחזרו מתחילת רשימת התוצאות הממוינת.
LimitToLast() מגדיר את המספר המקסימלי של פריטים שיוחזרו מסוף הרשימה הממוינת של התוצאות.
StartAt() הפונקציה מחזירה פריטים שגדולים מהמפתח או מהערך שצוינו או שווים להם, בהתאם לשיטת המיון שנבחרה.
EndAt() הפונקציה מחזירה פריטים שקטנים מהמפתח או מהערך שצוינו או שווים להם, בהתאם לשיטת המיון שנבחרה.
EqualTo() הפונקציה מחזירה פריטים ששווים למפתח או לערך שצוינו, בהתאם לשיטת המיון שנבחרה.

בניגוד לשיטות order-by, אפשר לשלב כמה פונקציות limit או range. לדוגמה, אפשר לשלב בין השיטות StartAt() ו-EndAt() כדי להגביל את התוצאות לטווח ערכים מסוים.

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

הגבלת מספר התוצאות

אפשר להשתמש בשיטות LimitToFirst() ו-LimitToLast() כדי להגדיר מספר מקסימלי של ילדים שיסונכרנו עבור קריאה חוזרת נתונה. לדוגמה, אם משתמשים ב-LimitToFirst() כדי להגדיר מגבלה של 100, מקבלים בהתחלה רק עד 100 קריאות חוזרות של OnChildAdded. אם יש לכם פחות מ-100 פריטים שמאוחסנים במסד הנתונים של Firebase, מתבצעת קריאה חוזרת (callback) של OnChildAdded לכל פריט.

כשהפריטים משתנים, אתם מקבלים OnChildAdded קריאות חוזרות (callback) לגבי פריטים שנכנסים לשאילתה וOnChildRemoved קריאות חוזרות לגבי פריטים שיוצאים ממנה, כך שהמספר הכולל נשאר 100.

לדוגמה, הקוד הבא מחזיר את הציון הכי גבוה מטבלת המובילים:

  firebase::database::Query query =
    dbRef.GetReference("Leaders").OrderByChild("score").LimitToLast(1);

  // To get the resulting DataSnapshot either use query.GetValue() and poll the
  // future, or use query.AddValueListener() and register to handle the
  // OnValueChanged callback.

סינון לפי מפתח או ערך

אפשר להשתמש בסימנים StartAt(), EndAt() ו-EqualTo() כדי לבחור נקודות התחלה, סיום ושקילות שרירותיות לשאילתות. האפשרות הזו שימושית כשרוצים להוסיף מספור לדפים של נתונים או למצוא פריטים עם פריטי צאצא שיש להם ערך ספציפי.

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

בקטע הזה מוסבר איך הנתונים ממוינים לפי כל אחת מהשיטות של order-by במחלקה Query.

OrderByChild

כשמשתמשים ב-OrderByChild(), הנתונים שמכילים את מפתח הצאצא שצוין מסודרים באופן הבא:

  1. ילדים עם ערך null למפתח הילד שצוין מופיעים ראשונים.
  2. אחריהם מופיעים ילדים עם הערך false למפתח הצאצא שצוין. אם לכמה צאצאים יש ערך של false, הם ממוינים לקסיקוגרפית לפי מפתח.
  3. אחריהם מופיעים ילדים עם הערך true למפתח הצאצא שצוין. אם לכמה צאצאים יש ערך של true, הם ממוינים לפי מפתח בסדר לקסיקוגרפי.
  4. אחריהם מופיעים ילדים עם ערך מספרי, ממוינים בסדר עולה. אם לכמה צאצאים יש את אותו ערך מספרי עבור צומת הצאצא שצוין, הם ממוינים לפי מפתח.
  5. מחרוזות מופיעות אחרי מספרים וממוינות בסדר עולה לפי סדר מילוני. אם לכמה צאצאים יש את אותו ערך לצומת הצאצא שצוין, הם מסודרים לפי מפתח בסדר לקסיקוגרפי.
  6. האובייקטים מופיעים בסוף וממוינים בסדר עולה לפי מפתח, בסדר לקסיקוגרפי.

OrderByKey

כשמשתמשים ב-OrderByKey() כדי למיין את הנתונים, הנתונים מוחזרים בסדר עולה לפי מפתח.

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

OrderByValue

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

השלבים הבאים