ذخیره کردن داده‌ها با «پایگاه داده بی‌درنگ 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 ازطریق نوع «متغیر» استفاده کنید که از موارد زیر پشتیبانی می‌کند:

  • تهی (این کار داده‌ها را حذف می‌کند)
  • اعداد صحیح (۶۴ بیتی)
  • اعداد نقطه شناور با دقت دوبرابر
  • مقادیر منطقی
  • رشته‌ها
  • بردارهای متغیرها
  • نقشه‌های رشته‌ها به «گونه‌ها»

استفاده از 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 استفاده کرد.

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

حذف داده‌ها

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

همچنین می‌توانید با مشخص کردن null Variant به‌عنوان مقدار برای عملیات نوشتاری دیگر مثل 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() &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 آن داده‌ها را با سرورهای پایگاه داده از دور و با کارخواه‌های دیگر براساس «بهترین تلاش» همگام‌سازی می‌کند.

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

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

مراحل بعدی