Tiết kiệm dữ liệu với Cơ sở dữ liệu thời gian thực của Firebase cho C ++

Sử dụng bộ sưu tập để sắp xếp ngăn nắp các trang Lưu và phân loại nội dung dựa trên lựa chọn ưu tiên của bạn.

Bắt đầu

Trước tiên, hãy xem hướng dẫn Get Started đầu nếu bạn chưa thiết lập ứng dụng của mình và truy cập vào cơ sở dữ liệu.

Nhận cơ sở dữ liệu

Để ghi dữ liệu vào Cơ sở dữ liệu, bạn cần một phiên bản của DatabaseReference :

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

Tiết kiệm dữ liệu

Có bốn phương pháp để ghi dữ liệu vào Cơ sở dữ liệu thời gian thực của Firebase:

Phương pháp Sử dụng phổ biến
SetValue() Ghi hoặc thay thế dữ liệu vào một đường dẫn xác định, chẳng hạn như users/<user-id>/<username> .
PushChild() Thêm vào danh sách dữ liệu. Mỗi khi bạn gọi Push() , Firebase sẽ tạo một khóa duy nhất cũng có thể được sử dụng làm mã định danh duy nhất, chẳng hạn như user-scores/<user-id>/<unique-score-id> .
UpdateChildren() Cập nhật một số khóa cho một đường dẫn xác định mà không cần thay thế tất cả dữ liệu.
RunTransaction() Cập nhật dữ liệu phức tạp có thể bị hỏng bởi các bản cập nhật đồng thời.

Viết, cập nhật hoặc xóa dữ liệu tại một tham chiếu

Các thao tác viết cơ bản

Đối với các thao tác ghi cơ bản, bạn có thể sử dụng SetValue() để lưu dữ liệu vào một tham chiếu được chỉ định, thay thế bất kỳ dữ liệu hiện có nào tại đường dẫn đó. Bạn có thể sử dụng phương pháp này để chuyển các loại được JSON chấp nhận thông qua loại Biến thể hỗ trợ:

  • Null (điều này sẽ xóa dữ liệu)
  • Số nguyên (64-bit)
  • Số dấu phẩy động chính xác gấp đôi
  • Booleans
  • Dây
  • Vectơ của các biến thể
  • Bản đồ của chuỗi thành các biến thể

Sử dụng SetValue() theo cách này sẽ ghi đè dữ liệu tại vị trí được chỉ định, bao gồm bất kỳ nút con nào. Tuy nhiên, bạn vẫn có thể cập nhật một phần tử con mà không cần viết lại toàn bộ đối tượng. Nếu bạn muốn cho phép người dùng cập nhật hồ sơ của họ, bạn có thể cập nhật tên người dùng như sau:

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

Nối vào danh sách dữ liệu

Sử dụng phương thức PushChild() để nối dữ liệu vào danh sách trong các ứng dụng nhiều người dùng. Phương PushChild() tạo một khóa duy nhất mỗi khi một phần tử con mới được thêm vào tham chiếu Firebase đã chỉ định. Bằng cách sử dụng các khóa được tạo tự động này cho mỗi phần tử mới trong danh sách, một số ứng dụng khách có thể thêm phần tử con vào cùng một vị trí cùng một lúc mà không xảy ra xung đột khi ghi. Khóa duy nhất được tạo bởi PushChild() dựa trên dấu thời gian, do đó, các mục trong danh sách được sắp xếp tự động theo thứ tự thời gian.

Bạn có thể sử dụng tham chiếu đến dữ liệu mới được trả về bởi phương thức PushChild() để lấy giá trị của khóa được tạo tự động của đứa trẻ hoặc thiết lập dữ liệu cho đứa trẻ. Gọi GetKey() trên tham chiếu PushChild() trả về giá trị của khóa được tạo tự động.

Cập nhật các trường cụ thể

Để ghi đồng thời cho các nút con cụ thể mà không ghi đè các nút con khác, hãy sử dụng phương thức UpdateChildren() .

Khi gọi UpdateChildren() , bạn có thể cập nhật các giá trị con cấp thấp hơn bằng cách chỉ định một đường dẫn cho khóa. Nếu dữ liệu được lưu trữ ở nhiều vị trí để mở rộng quy mô tốt hơn, bạn có thể cập nhật tất cả các phiên bản của dữ liệu đó bằng cách sử dụng quạt dữ liệu . Ví dụ: một trò chơi có thể có lớp LeaderboardEntry như thế này:

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;
  }
}

Để tạo LeaderboardEntry và đồng thời cập nhật nó vào nguồn cấp dữ liệu điểm gần đây và danh sách điểm của riêng người dùng, trò chơi sử dụng mã sau:

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);
}

Ví dụ này sử dụng PushChild() để tạo một mục nhập trong nút chứa các mục nhập cho tất cả người dùng tại /scores/$key và đồng thời truy xuất khóa bằng key() . Sau đó, khóa có thể được sử dụng để tạo mục nhập thứ hai trong điểm số của người dùng tại /user-scores/$userid/$key .

Sử dụng các đường dẫn này, bạn có thể thực hiện cập nhật đồng thời cho nhiều vị trí trong cây JSON bằng một lệnh gọi tới UpdateChildren() , chẳng hạn như cách ví dụ này tạo mục nhập mới ở cả hai vị trí. Các bản cập nhật đồng thời được thực hiện theo cách này là nguyên tử: tất cả các bản cập nhật đều thành công hoặc tất cả các bản cập nhật không thành công.

Xóa dữ liệu

Cách đơn giản nhất để xóa dữ liệu là gọi RemoveValue() trên một tham chiếu đến vị trí của dữ liệu đó.

Bạn cũng có thể xóa bằng cách chỉ định Variant null làm giá trị cho một thao tác ghi khác như SetValue() hoặc UpdateChildren() . Bạn có thể sử dụng kỹ thuật này với UpdateChildren() để xóa nhiều phần tử con trong một lệnh gọi API.

Biết khi nào dữ liệu của bạn được cam kết.

Để biết khi nào dữ liệu của bạn được cam kết với máy chủ Cơ sở dữ liệu thời gian thực của Firebase, hãy kiểm tra kết quả Tương lai để biết thành công.

Lưu dữ liệu dưới dạng giao dịch

Khi làm việc với dữ liệu có thể bị hỏng do sửa đổi đồng thời, chẳng hạn như bộ đếm tăng dần, bạn có thể sử dụng thao tác giao dịch . Bạn cung cấp cho thao tác này một hàm DoTransaction . Hàm cập nhật này lấy trạng thái hiện tại của dữ liệu làm đối số và trả về trạng thái mong muốn mới mà bạn muốn ghi. Nếu một ứng dụng khách khác ghi vào vị trí trước khi giá trị mới của bạn được ghi thành công, thì hàm cập nhật của bạn sẽ được gọi lại với giá trị hiện tại mới và quá trình ghi sẽ được thử lại.

Ví dụ: trong một trò chơi, bạn có thể cho phép người dùng cập nhật bảng thành tích với năm điểm cao nhất:

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;
  });
}

Việc sử dụng một giao dịch ngăn không cho bảng xếp hạng không chính xác nếu nhiều người dùng ghi điểm cùng một lúc hoặc khách hàng có dữ liệu cũ. Nếu giao dịch bị từ chối, máy chủ sẽ trả về giá trị hiện tại cho máy khách, giá trị này sẽ chạy lại giao dịch với giá trị được cập nhật. Điều này lặp lại cho đến khi giao dịch được chấp nhận hoặc quá nhiều lần thử đã được thực hiện.

Ghi dữ liệu ngoại tuyến

Nếu ứng dụng mất kết nối mạng, ứng dụng của bạn sẽ tiếp tục hoạt động bình thường.

Mọi ứng dụng khách được kết nối với cơ sở dữ liệu Firebase duy trì phiên bản nội bộ của riêng bất kỳ dữ liệu đang hoạt động nào. Khi dữ liệu được ghi, nó sẽ được ghi vào phiên bản cục bộ này trước tiên. Sau đó, ứng dụng khách Firebase sẽ đồng bộ hóa dữ liệu đó với các máy chủ cơ sở dữ liệu từ xa và với các ứng dụng khách khác trên cơ sở "nỗ lực hết sức".

Kết quả là, tất cả các lần ghi vào cơ sở dữ liệu sẽ kích hoạt các sự kiện cục bộ ngay lập tức, trước khi bất kỳ dữ liệu nào được ghi vào máy chủ. Điều này có nghĩa là ứng dụng của bạn vẫn đáp ứng bất kể độ trễ mạng hoặc kết nối.

Khi kết nối được thiết lập lại, ứng dụng của bạn sẽ nhận được tập hợp sự kiện thích hợp để ứng dụng khách đồng bộ hóa với trạng thái máy chủ hiện tại mà không cần phải viết bất kỳ mã tùy chỉnh nào.

Bước tiếp theo