Check out what’s new from Firebase at Google I/O 2022. Learn more

שמירת נתונים עם Firebase Realtime Database עבור C++

להתחיל

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

קבל הפניית מסד נתונים

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

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

שמירת נתונים

ישנן ארבע שיטות לכתיבת נתונים למסד הנתונים בזמן אמת של Firebase:

שיטה שימושים נפוצים
SetValue() כתוב או החלף נתונים לנתיב מוגדר, כגון users/<user-id>/<username> .
PushChild() הוסף לרשימת נתונים. בכל פעם שאתה קורא ל- Push() , Firebase מייצר מפתח ייחודי שיכול לשמש גם כמזהה ייחודי, כגון user-scores/<user-id>/<unique-score-id> .
UpdateChildren() עדכן חלק מהמפתחות עבור נתיב מוגדר מבלי להחליף את כל הנתונים.
RunTransaction() עדכן נתונים מורכבים שעלולים להיפגם על ידי עדכונים במקביל.

כתוב, עדכן או מחק נתונים בהפניה

פעולות כתיבה בסיסיות

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

  • Null (זה מוחק את הנתונים)
  • מספרים שלמים (64 סיביות)
  • מספרי נקודה צפה דיוק כפול
  • בוליאנים
  • מחרוזות
  • וקטורים של גרסאות
  • מפות של מחרוזות לוריאנטים

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

dbref.Child("users").Child(userId).Child("username").SetValue(name);

הוסף לרשימת נתונים

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

אתה יכול להשתמש בהפניה לנתונים החדשים המוחזרים על ידי שיטת PushChild() כדי לקבל את הערך של המפתח שנוצר אוטומטית של הילד או להגדיר נתונים עבור הילד. קריאה GetKey() PushChild() מחזירה את הערך של המפתח שנוצר אוטומטית.

עדכן שדות ספציפיים

כדי לכתוב בו-זמנית לילדים ספציפיים של צומת מבלי לדרוס צמתים צאצאים אחרים, השתמש בשיטת UpdateChildren() .

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

class LeaderboardEntry {
  std::string uid;
  int score = 0;

 public:
  LeaderboardEntry() {
  }

  LeaderboardEntry(std::string uid, int score) {
    this->uid = uid;
    this->score = score;
  }

  std::map<std::string, Object> ToMap() {
    std::map<string, Variant> result = new std::map<string, Variant>();
    result["uid"] = Variant(uid);
    result["score"] = Variant(score);

    return result;
  }
}

כדי ליצור LeaderboardEntry ולעדכן אותו בו-זמנית לעדכון הניקוד האחרון ולרשימת הניקוד של המשתמש עצמו, המשחק משתמש בקוד הבא:

void WriteNewScore(std::string userId, int score) {
  // Create new entry at /user-scores/$userid/$scoreid and at
  // /leaderboard/$scoreid simultaneously
  std::string key = dbref.Child("scores").PushChild().GetKey();
  LeaderBoardEntry entry = new LeaderBoardEntry(userId, score);
  std::map<std::string, Variant> entryValues = entry.ToMap();

  std::map<string, Variant> childUpdates = new std::map<string, Variant>();
  childUpdates["/scores/" + key] = entryValues;
  childUpdates["/user-scores/" + userId + "/" + key] = entryValues;

  dbref.UpdateChildren(childUpdates);
}

דוגמה זו משתמשת ב- PushChild() כדי ליצור ערך בצומת המכיל ערכים עבור כל המשתמשים ב- /scores/$key ובו-זמנית לאחזר את המפתח עם key() . לאחר מכן ניתן להשתמש במפתח ליצירת ערך שני בציוני המשתמש ב- /user-scores/$userid/$key .

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

מחק נתונים

הדרך הפשוטה ביותר למחוק נתונים היא לקרוא ל- RemoveValue() בהפניה למיקום הנתונים הללו.

אתה יכול גם למחוק על ידי ציון Variant null כערך עבור פעולת כתיבה אחרת כגון SetValue() או UpdateChildren() . אתה יכול להשתמש בטכניקה זו עם UpdateChildren() כדי למחוק ילדים מרובים בקריאה אחת ל-API.

דע מתי הנתונים שלך מחויבים.

כדי לדעת מתי הנתונים שלך מחויבים לשרת Firebase Realtime Database, בדוק את התוצאה העתידית להצלחה.

שמור נתונים כעסקאות

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

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

void AddScoreToLeaders(std::string email,
                       long score,
                       DatabaseReference leaderBoardRef) {
  leaderBoardRef.RunTransaction([](firebase::database::MutableData* mutableData) {
    if (mutableData.children_count() >= MaxScores) {
      long minScore = LONG_MAX;
      MutableData *minVal = null;
      std::vector<MutableData> children = mutableData.children();
      std::vector<MutableData>::iterator it;
      for (it = children.begin(); it != children.end(); ++it) {
        if (!it->value().is_map())
          continue;
        long childScore = (long)it->Child("score").value().int64_value();
        if (childScore < minScore) {
          minScore = childScore;
          minVal = &*it;
        }
      }
      if (minScore > score) {
        // The new score is lower than the existing 5 scores, abort.
        return kTransactionResultAbort;
      }

      // Remove the lowest score.
      children.Remove(minVal);
    }

    // Add the new high score.
    std::map<std::string, Variant> newScoreMap =
      new std::map<std::string, Variant>();
    newScoreMap["score"] = score;
    newScoreMap["email"] = email;
    children.Add(newScoreMap);
    mutableData->set_value(children);
    return kTransactionResultSuccess;
  });
}

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

כתוב נתונים במצב לא מקוון

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

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

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

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

הצעדים הבאים