ذخیره داده ها با پایگاه داده بیدرنگ 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 استفاده کنید که پشتیبانی می کند:

  • تهی (با این کار داده ها حذف می شوند)
  • اعداد صحیح (64 بیتی)
  • اعداد ممیز شناور با دقت دو برابر
  • بولین ها
  • رشته ها
  • بردارهای انواع
  • نقشه رشته ها به 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<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 متعهد شده است، نتیجه Future را برای موفقیت بررسی کنید.

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

هنگام کار با داده هایی که ممکن است توسط تغییرات همزمان خراب شوند، مانند شمارنده های افزایشی، می توانید از عملیات تراکنش استفاده کنید. شما به این عملیات یک تابع 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 آن داده ها را با سرورهای پایگاه داده راه دور و با سایر مشتریان بر اساس "بهترین تلاش" همگام سازی می کند.

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

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

مراحل بعدی