Google is committed to advancing racial equity for Black communities. See how.
本頁面由 Cloud Translation API 翻譯而成。
Switch to English

使用Firebase Realtime Database for C ++構建數據

結構化數據

本指南涵蓋了數據體系結構中的一些關鍵概念以及在Firebase Realtime數據庫中構造JSON數據的最佳實踐。

構建結構合理的數據庫需要大量的前瞻性工作。最重要的是,您需要計劃如何保存數據並在以後檢索數據,以使該過程盡可能簡單。

數據的結構方式:這是一棵JSON樹

所有Firebase實時數據庫數據都存儲為JSON對象。您可以將數據庫視為雲託管的JSON樹。與SQL數據庫不同,沒有表或記錄。將數據添加到JSON樹時,它成為現有JSON結構中具有關聯密鑰的節點。您可以提供自己的密鑰,例如用戶ID或語義名稱,也可以使用Push()方法為您提供它們。

如果創建自己的密鑰,則它們必須是UTF-8編碼的,最大長度為768個字節,並且不能包含.$#[]/或ASCII控製字符0-31或127。您也不能在值本身中使用ASCII控製字符。

例如,考慮一個允許用戶存儲基本個人資料和聯繫人列表的聊天應用程序。典型的用戶配置文件位於路徑中,例如/users/$uid 。用戶alovelace可能具有一個類似於以下內容的數據庫條目:

{
  "users": {
    "alovelace": {
      "name": "Ada Lovelace",
      "contacts": { "ghopper": true },
    },
    "ghopper": { ... },
    "eclarke": { ... }
  }
}

儘管數據庫使用JSON樹,但是可以將數據庫中存儲的數據表示為與可用JSON類型相對應的某些本機類型,以幫助您編寫更多可維護的代碼。

數據結構的最佳做法

避免嵌套數據

由於Firebase實時數據庫最多可嵌套32級數據,因此您可能會以為這應該是默認結構。但是,當您在數據庫中的某個位置獲取數據時,也會檢索其所有子節點。此外,當您授予某人在數據庫節點上的讀取或寫入訪問權限時,您還授予他們對該節點下所有數據的訪問權限。因此,在實踐中,最好保持數據結構盡可能平坦。

有關為什麼嵌套數據不好的示例,請考慮以下多重嵌套結構:

{
  // This is a poorly nested data architecture, because iterating the children
  // of the "chats" node to get a list of conversation titles requires
  // potentially downloading hundreds of megabytes of messages
  "chats": {
    "one": {
      "title": "Historical Tech Pioneers",
      "messages": {
        "m1": { "sender": "ghopper", "message": "Relay malfunction found. Cause: moth." },
        "m2": { ... },
        // a very long list of messages
      }
    },
    "two": { ... }
  }
}

通過這種嵌套設計,遍歷數據變得很成問題。例如,列出聊天對話的標題要求將整個chats樹(包括所有成員和消息)下載到客戶端。

扁平化數據結構

如果將數據拆分為單獨的路徑(也稱為非規範化),則可以根據需要在單獨的調用中有效地下載數據。考慮以下扁平化結構:

{
  // Chats contains only meta info about each conversation
  // stored under the chats's unique ID
  "chats": {
    "one": {
      "title": "Historical Tech Pioneers",
      "lastMessage": "ghopper: Relay malfunction found. Cause: moth.",
      "timestamp": 1459361875666
    },
    "two": { ... },
    "three": { ... }
  },

  // Conversation members are easily accessible
  // and stored by chat conversation ID
  "members": {
    // we'll talk about indices like this below
    "one": {
      "ghopper": true,
      "alovelace": true,
      "eclarke": true
    },
    "two": { ... },
    "three": { ... }
  },

  // Messages are separate from data we may want to iterate quickly
  // but still easily paginated and queried, and organized by chat
  // conversation ID
  "messages": {
    "one": {
      "m1": {
        "name": "eclarke",
        "message": "The relay seems to be malfunctioning.",
        "timestamp": 1459361875337
      },
      "m2": { ... },
      "m3": { ... }
    },
    "two": { ... },
    "three": { ... }
  }
}

現在,可以通過每次對話僅下載幾個字節來遍歷房間列表,快速獲取元數據以在UI中列出或顯示房間。可以分別提取消息並在消息到達時顯示它們,從而使UI保持快速響應。

創建可擴展的數據

在構建應用程序時,通常最好下載列表的子集。如果列表包含成千上萬條記錄,則這種情況尤其常見。如果此關係是靜態的且是單向的,則可以簡單地將子對象嵌套在父對像下。

有時,這種關係是動態的,或者可能有必要對數據進行非規範化。您可以多次使用查詢來檢索數據的子集來對數據進行非規範化,如“ 檢索數據”中所述

但是,這可能還不夠。例如,考慮用戶和組之間的雙向關係。用戶可以屬於一個組,並且組包括用戶列表。當需要確定用戶屬於哪個組時,事情就變得複雜了。

需要的是一種優雅的方式來列出用戶所屬的組並僅獲取這些組的數據。組索引可以在這里大有幫助:

// An index to track Ada's memberships
{
  "users": {
    "alovelace": {
      "name": "Ada Lovelace",
      // Index Ada's groups in her profile
      "groups": {
         // the value here doesn't matter, just that the key exists
         "techpioneers": true,
         "womentechmakers": true
      }
    },
    ...
  },
  "groups": {
    "techpioneers": {
      "name": "Historical Tech Pioneers",
      "members": {
        "alovelace": true,
        "ghopper": true,
        "eclarke": true
      }
    },
    ...
  }
}

您可能會注意到,通過將關係存儲在Ada的記錄和組下,這會重複一些數據。現在, alovelace已在一個組下被索引,並且技術techpioneers已列在Ada的個人資料中。因此,要從組中刪除Ada,必須在兩個位置進行更新。

這是雙向關係的必要冗餘。它使您可以快速有效地獲取Ada的成員身份,即使用戶或組的列表擴展到數以百萬計,或者當實時數據庫安全規則阻止訪問某些記錄時。

這種方法通過將ID列為鍵並將其值設置為true來反轉數據,從而使得檢查鍵的過程非常簡單,如讀取/users/$uid/groups/$group_id並檢查其是否為null 。與查詢或掃描數據相比,索引更快且效率更高。

下一步