ذخیره کردن داده‌ها با «پایگاه داده بی‌درنگ Firebase» برای C++‎

شروع کنید

اگر هنوز برنامه‌تان را راه‌اندازی نکرده‌اید و به پایگاه داده دسترسی ندارید، ابتدا راهنمای Get Started را ببینید.

دریافت DatabaseReference

برای نوشتن داده‌ها در «پایگاه داده»، به نمونه‌ای از DatabaseReference نیاز دارید:

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

درحال ذخیره کردن داده‌ها

چهار روش برای نوشتن داده‌ها در Firebase Realtime Database وجود دارد:

روش استفاده‌های رایج
SetValue() نوشتن یا جایگزین کردن داده‌ها در مسیر تعریف‌شده، مثل users/<user-id>/<username>.
PushChild() به فهرست داده‌ها اضافه کنید. هر بار که Push() را فراخوانی می‌کنید، Firebase کلید یکتایی تولید می‌کند که می‌تواند به‌عنوان شناسه یکتا نیز استفاده شود، مثل user-scores/<user-id>/<unique-score-id>.
UpdateChildren() برخی‌از کلیدها را برای مسیر تعریف‌شده به‌روز کنید بدون اینکه همه داده‌ها را جایگزین کنید.
RunTransaction() به‌روزرسانی داده‌های پیچیده که ممکن است به‌دلیل به‌روزرسانی‌های هم‌زمان خراب شوند.

نوشتن، به‌روزرسانی، یا حذف داده‌ها در مرجع

عملیات نوشتن پایه

برای عملیات نوشتن پایه، می‌توانید از SetValue() برای ذخیره داده‌ها در یک مرجع مشخص استفاده کنید و هر داده موجود در آن مسیر را جایگزین کنید. می‌توانید از این روش برای ارسال انواع پذیرفته‌شده توسط JSON ازطریق نوع Variant استفاده کنید که از موارد زیر پشتیبانی می‌کند:

  • Null (این مقدار داده‌ها را حذف می‌کند)
  • اعداد صحیح (۶۴ بیتی)
  • اعداد ممیز شناور با دقت مضاعف
  • بولی‌ها
  • رشته‌ها
  • بردارهای متغیرها
  • Maps of strings to Variants

استفاده از 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&ltstd::string, Object&gt ToMap() {
    std::map&ltstring, Variant&gt result = new std::map&ltstring, Variant&gt();
    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&ltstd::string, Variant&gt entryValues = entry.ToMap();

  std::map&ltstring, Variant&gt childUpdates = new std::map&ltstring, Variant&gt();
  childUpdates["/scores/" + key] = entryValues;
  childUpdates["/user-scores/" + userId + "/" + key] = entryValues;

  dbref.UpdateChildren(childUpdates);
}

این مثال از PushChild() برای ایجاد ورودی در گره حاوی ورودی‌های همه کاربران در /scores/$key استفاده می‌کند و هم‌زمان کلید را با key() بازیابی می‌کند. سپس می‌توان از این کلید برای ایجاد ورودی دوم در امتیازهای کاربر در /user-scores/$userid/$key استفاده کرد.

بااستفاده از این مسیرها، می‌توانید به‌روزرسانی‌های هم‌زمان را در چندین مکان در درخت JSON با یک تماس با UpdateChildren() انجام دهید، مانند اینکه چگونه این مثال ورودی جدید را در هر دو مکان ایجاد می‌کند. به‌روزرسانی‌های هم‌زمان که به این روش انجام می‌شوند اتمی هستند: یا همه به‌روزرسانی‌ها موفق می‌شوند یا همه به‌روزرسانی‌ها ناموفق می‌شوند.

حذف کردن داده‌ها

ساده‌ترین راه برای حذف داده‌ها، فراخوانی RemoveValue() در ارجاع به مکان آن داده‌ها است.

همچنین می‌توانید با مشخص کردن null Variant به‌عنوان مقدار برای عملیات نوشتن دیگری مانند SetValue() یا UpdateChildren()، حذف کنید. می‌توانید از این تکنیک با UpdateChildren() برای حذف چندین فرزند در یک فراخوانی API استفاده کنید.

بدانید چه زمانی داده‌هایتان ثبت می‌شود.

برای اینکه بدانید داده‌هایتان چه زمانی به سرور Firebase Realtime Database ارسال می‌شود، نتیجه Future را برای موفقیت بررسی کنید.

ذخیره داده‌ها به‌عنوان تراکنش

هنگام کار با داده‌هایی که ممکن است به‌دلیل اصلاحات هم‌زمان خراب شوند، مثل شمارنده‌های افزایشی، می‌توانید از عملیات تراکنش استفاده کنید. شما به این عملیات یک تابع DoTransaction می‌دهید. این تابع به‌روزرسانی، وضعیت فعلی داده‌ها را به‌عنوان متغیر مستقل می‌گیرد و وضعیت مطلوب جدیدی را که می‌خواهید بنویسید برمی‌گرداند. اگر کارخواه دیگری قبل‌از اینکه مقدار جدید شما با موفقیت نوشته شود در مکان بنویسد، تابع به‌روزرسانی شما دوباره با مقدار فعلی جدید فراخوانی می‌شود و نوشتن دوباره امتحان می‌شود.

برای مثال، در یک بازی می‌توانید به کاربران اجازه دهید تابلو پیشتازان را با پنج امتیاز برتر به‌روز کنند:

void AddScoreToLeaders(std::string email,
                       long score,
                       DatabaseReference leaderBoardRef) {
  leaderBoardRef.RunTransaction([](firebase::database::MutableData* mutableData) {
    if (mutableData.children_count() &gt= MaxScores) {
      long minScore = LONG_MAX;
      MutableData *minVal = null;
      std::vector&ltMutableData&gt children = mutableData.children();
      std::vector&ltMutableData&gt::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 &lt minScore) {
          minScore = childScore;
          minVal = &amp*it;
        }
      }
      if (minScore &gt 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&ltstd::string, Variant&gt newScoreMap =
      new std::map&ltstd::string, Variant&gt();
    newScoreMap["score"] = score;
    newScoreMap["email"] = email;
    children.Add(newScoreMap);
    mutableData->set_value(children);
    return kTransactionResultSuccess;
  });
}

استفاده از تراکنش مانع از نادرست شدن جدول رده‌بندی می‌شود، اگر چند کاربر به‌طور هم‌زمان امتیاز ثبت کنند یا کارخواه داده‌های قدیمی داشته باشد. اگر تراکنش رد شود، سرور مقدار فعلی را به کارخواه برمی‌گرداند و کارخواه تراکنش را دوباره با مقدار به‌روزشده اجرا می‌کند. این روند تا زمانی که تراکنش پذیرفته شود یا تعداد تلاش‌ها بیش‌ازحد شود تکرار می‌شود.

نوشتن داده‌ها به‌صورت آفلاین

اگر اتصال شبکه کارخواه قطع شود، برنامه‌تان همچنان به‌درستی کار خواهد کرد.

هر کارخواه متصل به پایگاه داده Firebase نسخه داخلی خودش را از هر داده فعالی حفظ می‌کند. وقتی داده‌ها نوشته می‌شوند، ابتدا در این نسخه محلی نوشته می‌شوند. سپس کارخواه Firebase آن داده‌ها را با سرورهای پایگاه داده راه دور و با کارخواه‌های دیگر براساس «بهترین تلاش» همگام‌سازی می‌کند.

درنتیجه، همه نوشتن‌ها در پایگاه داده بلافاصله رویدادهای محلی را راه‌اندازی می‌کنند، قبل‌از اینکه داده‌ای در سرور نوشته شود. این یعنی برنامه‌تان بدون توجه به تأخیر شبکه یا اتصال، واکنش‌گرا باقی می‌ماند.

پس‌از برقراری مجدد اتصال، برنامه شما مجموعه مناسبی از رویدادها را دریافت می‌کند تا سرویس‌گیرنده با وضعیت فعلی سرور همگام‌سازی شود، بدون اینکه نیازی به نوشتن کد سفارشی باشد.

مراحل بعدی