Catch up on everthing we announced at this year's Firebase Summit. Learn more

การบันทึกข้อมูลด้วยฐานข้อมูลเรียลไทม์ของ Firebase สำหรับ 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 ยอมรับผ่านประเภท Variant ที่รองรับ:

  • Null (จะเป็นการลบข้อมูล)
  • จำนวนเต็ม (64 บิต)
  • ตัวเลขทศนิยมความแม่นยำสองเท่า
  • บูลีน
  • เครื่องสาย
  • เวกเตอร์ของ Variants
  • แผนที่ของสตริงไปยัง Variants

ใช้ SetValue() ในลักษณะนี้จะเขียนทับข้อมูลในตำแหน่งที่ระบุรวมทั้งต่อมน้ำเด็ก ๆ อย่างไรก็ตาม คุณยังคงสามารถอัปเดตรายการย่อยได้โดยไม่ต้องเขียนใหม่ทั้งวัตถุ หากคุณต้องการอนุญาตให้ผู้ใช้อัปเดตโปรไฟล์ของพวกเขา คุณสามารถอัปเดตชื่อผู้ใช้ได้ดังนี้:

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

ผนวกเข้ากับรายการข้อมูล

ใช้ PushChild() วิธีการเพื่อให้ข้อมูลที่ผนวกเข้าไปในรายการในการใช้งานแบบ multiuser 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() ในการอ้างอิงถึงตำแหน่งของข้อมูลว่า

นอกจากนี้คุณยังสามารถลบโดยระบุ null Variant เป็นค่าสำหรับการดำเนินการเขียนอีกเช่น SetValue() หรือ UpdateChildren() คุณสามารถใช้เทคนิคนี้กับ UpdateChildren() ลบเด็กหลายในการเรียก API เดียว

รู้ว่าข้อมูลของคุณมีความมุ่งมั่นเมื่อใด

หากต้องการทราบข้อมูลของคุณเมื่อมีความมุ่งมั่นไปยังเซิร์ฟเวอร์ฐานข้อมูลเรียลไทม์ Firebase ตรวจสอบ ในอนาคต ผลให้ประสบความสำเร็จ

บันทึกข้อมูลเป็นธุรกรรม

เมื่อทำงานกับข้อมูลที่อาจจะเสียหายจากการปรับเปลี่ยนพร้อมกันเช่นเคาน์เตอร์เพิ่มขึ้นคุณสามารถใช้ การดำเนินการทำธุรกรรม คุณจะให้การดำเนินการนี้ 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 จะซิงโครไนซ์ข้อมูลนั้นกับเซิร์ฟเวอร์ฐานข้อมูลระยะไกลและกับไคลเอนต์อื่น ๆ ด้วย "ความพยายามอย่างดีที่สุด"

ด้วยเหตุนี้ การเขียนทั้งหมดไปยังฐานข้อมูลจะทริกเกอร์เหตุการณ์ในเครื่องทันที ก่อนที่ข้อมูลใดๆ จะถูกเขียนไปยังเซิร์ฟเวอร์ ซึ่งหมายความว่าแอปของคุณยังคงตอบสนองโดยไม่คำนึงถึงเวลาแฝงของเครือข่ายหรือการเชื่อมต่อ

เมื่อสร้างการเชื่อมต่อขึ้นใหม่แล้ว แอปของคุณจะได้รับชุดเหตุการณ์ที่เหมาะสม เพื่อให้ไคลเอ็นต์ซิงค์กับสถานะเซิร์ฟเวอร์ปัจจุบัน โดยไม่ต้องเขียนโค้ดที่กำหนดเองใดๆ

ขั้นตอนถัดไป