ذخیره داده ها با پایگاه داده بیدرنگ Firebase برای C++

شروع کنید

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

دریافت مرجع پایگاه داده

برای نوشتن داده‌ها در پایگاه داده، به یک نمونه از 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 (این داده را حذف می‌کند)
  • اعداد صحیح (۶۴ بیتی)
  • اعداد ممیز شناور با دقت مضاعف
  • بولی‌ها
  • رشته‌ها
  • بردارهای متغیرها
  • نگاشت رشته‌ها به انواع مختلف

استفاده از 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() روی ارجاعی به محل آن داده‌ها است.

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

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

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

مراحل بعدی