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

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

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

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

אחזור נתונים

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

אחזור של DatabaseReference

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

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

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

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

  firebase::Future<firebase::database::DataSnapshot> result =
    dbRef.GetReference("Leaders").GetValue();

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

  // 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 האזנה לשינויים בסדר הפריטים ברשימה מסודרת. קריאות החזרה (callbacks) של OnChildMoved תמיד מגיעות אחרי קריאות החזרה של OnChildChanged, כי סדר הפריטים משתנה (על סמך שיטת הסדר הנוכחית).

הכיתה ValueListener

אפשר להשתמש בקריאות החזרה (callbacks) של OnValueChanged כדי להירשם לשינויים בתוכן בנתיב נתון. הקריאה החוזרת הזו מופעלת פעם אחת כשהמאזין מצורף, ופעם נוספת בכל פעם שהנתונים, כולל הצאצאים, משתנים. בקריאה החוזרת מועברת קובץ snapshot שמכיל את כל הנתונים במיקום הזה, כולל נתוני הצאצאים. אם אין נתונים, קובץ ה-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<firebase::database::DataSnapshot> 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<firebase::database::DataSnapshot> result =
    dbRef.GetReference("GameSessionComments").AddChildListener(listener);

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

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

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

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

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

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

מיון נתונים

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

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

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

הדוגמה הבאה מראה איך אפשר להירשם למצעד מנהיגים של ציונים שממוינים לפי ציון.

  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" בכל הצאצאים. מידע נוסף על סדר של סוגים אחרים של נתונים זמין במאמר איך מתבצע הסדר של נתוני השאילתות.

סינון נתונים

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

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

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

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

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

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

כשהפריטים משתנים, אתם מקבלים OnChildAdded קריאות חזרה (callbacks) על פריטים שמתווספים לשאילתה ו-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() כדי לבחור נקודות התחלה, סיום ודומות שרירותיות לשאילתות. האפשרות הזו יכולה להיות שימושית כדי לפלח את הנתונים או למצוא פריטים עם צאצאים שיש להם ערך ספציפי.

איך מתבצע הסדר של נתוני השאילתה

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

OrderByChild

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

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

OrderByKey

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

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

OrderByValue

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

השלבים הבאים