Firebase 心得(Realtime Database)

官方文件

此文使用版本 : 3.1.0,官方文件更新日期 : 2016/06/22

Firebase Doc

介紹

Firebase Realtime Database 是一個雲端 NoSQL 資料庫

資料使用 JSON 來儲存,當各裝置連上資料庫時可進行同步

主要功能

  • Realtime
  • Offline
  • Accessible from Client Devices

Realtime

1
2
每當資料更新時,任何連上資料庫的裝置都會收到更新資訊
已完成資料同步,不用考慮網路的程式碼

Offline

1
2
當離線狀態時資料也可以使用,因為資料儲存在你的儲存裝置中
一旦裝置連線,資料會立刻和雲端資料同步

Accessible from Client Devices

1
2
裝置可以直接存取雲端資料庫的資料,不需要透過額外的伺服器
資料的加密以及驗證都幫你做好了

流程

Realtime Database 提供一種彈性的規則語言,稱為 Firebase Realtime Database Security Rules

用來定義資料儲存的架構、讀寫等規則

此外也可以整合使用者登入權限,限制那些人可以對資料庫進行讀寫

實作流程

1.使用 Realtime Database

1
2
3
Android : Gradle
IOS : CococaPods
Web : Script include

2.建立 DB 參考

1
2
使用 JSON 格式,例如
users/user:1234/phone_number

3.設定資料或檢查資料是否變動

4.開啟 Offline Persistence

1
允許資料寫到使用者上的儲存裝置,以便離線使用

5.資料加密

其他儲存方法

除了 Realtime Database 還有其他 3 種儲存方法

你可以根據你要的需求來選擇使用哪一種資料庫

Firebase Remote Config

1
key-value pairs(鍵值對),不需要使用者下載或上傳

Firebase Hosting

Firebase Storage

1
儲存檔案、圖片、影片

Web 建置

一樣先初始化 Firebase,接下來要設定 DB 的存取規則

存取規則定義你的資料如何儲存或如何排序

也定義了讀寫資料的權限,預設狀態下需要使用者驗證後才能讀寫資料

但是你也可以把規則改為公開讀寫,這樣沒驗證的使用者也可以進行讀寫

下面連結說明規則如何撰寫,如果你要公開讀寫就參考 PUBLIC 的寫法

規則寫法

規則決定好前往這裡修改,Firebase console > Project > Database > 規則

現在先設為公開狀態方便測試

2016-07-21_115633.png

初始化 DB

在初始化程式碼下面加入,就可以使用 Firebase Realtime Database!

1
2
// Get a reference to the database service
var database = firebase.database();

建構 DB

這裡我們要建構 DB,建構 DB 時你要先預想未來的使用情境

資料如何儲存或如何排序等,這都會引響未來存取資料的便利性

Firebase Realtime DB 是以 JSON Tree 來存資料

不像 SQL DB 是用 Table 來紀錄資料,舉例:

1
2
3
4
5
6
7
8
9
10
11
12
下面是舉 user 的例子,你可以看到 user 欄位下面有 3 個 user id
alovelace、ghopper、eclarke,每個 user id 下面再儲存姓名,通訊錄等
{
"users": {
"alovelace": {
"name": "Ada Lovelace",
"contacts": { "ghopper": true },
},
"ghopper": { ... },
"eclarke": { ... }
}
}

避免多巢狀的資料型態

Firebase Realtime DB 只允許最多 32 層的資料儲存

以下例子為錯誤示範

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
// 如果你要取得 chats/one 裡面的 message 資料
// 你會下載幾百個訊息,這樣會耗時且耗流量
"chats": {
"one": {
"title": "Historical Tech Pioneers",
"messages": {
"m1": { "sender": "ghopper", "message": "Relay malfunction found. Cause: moth." },
"m2": { ... },
// a very long list of messages
}
},
"two": { ... }
}
}

扁平式的資料架構

把上述錯誤例子分割,變成以下資料架構

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
{
// Chats 只包含一些 meta 訊息,如 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": { ... }
}
}

下面是另一個例子,這例子是說如果要顯示某個 group 中有那些人要如何做

雖然有些資料會重複,但這種作法可以更快取得資料

所以某些情況下,重複儲存資料是允許的

但要注意如果更新資料時,重複的地方也要一併更新

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 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
}
},
...
}
}

儲存資料

下面有 4 種方法將資料寫入資料庫

  • set()
  • push()
  • update()
  • transaction()

set()

1
給指定的路徑,直接寫或覆蓋資料。例如: users/<user-id>/<username>

push()

1
2
加入 list Data,每一次呼叫 push(),都會產生  unique ID
例如: user-posts/<user-id>/<unique-post-id>

update()

1
給一個 key,並更新此 key 的資料,不會覆蓋全部的資料

transaction()

1
更新複雜的資料,同時更新會造成錯誤

Set

範例

如果成功匯出下以下畫面,Firebase console > Database > 資料

2016-07-25_153108.png

set 可以直接修改,覆蓋舊的檔案

Push

使用 push 時會產生一個 unique id,這個 unique id 是基於 timestamp 的

所以多個 client 可以使用 push 而不會造成衝突

你可以在 push 後面加上 .key 來獲得產生出來的 unique id

範例

2016-07-25_160107.png

Update

範例

2016-07-25_160952.png 2016-07-25_161030.png

Delete

刪除資料可以呼叫 remove() 或是使用 set() 或 update() 並將值設為 null

範例

使用 transactions

使用 transactions 的情況下在於多個使用者同時儲存資料到 DB

這時就要處理多使用者存取的問題,以下是解決的範例

你可以對某篇文章按讚,其他人也可以,這時就要處理計算按讚次數的問題

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function toggleStar(postRef, uid) {
postRef.transaction(function(post) {
if (post.stars && post.stars[uid]) {
post.starCount--;
post.stars[uid] = null;
} else {
post.starCount++;
if (!post.stars) {
post.stars = {};
}
post.stars[uid] = true;
}
return post;
});
}

取得資料

參考網址

我們藉由一個 listener 去取得資料,這是非同步的處理

監聽事件

你可以監聽以下事件,要增加事件監聽使用 on() 方法

1
2
3
4
5
value
child_added
child_changed
child_removed
child_moved

你可以把 on() 換成 once(),他就只會讀取一次 DB

範例

其他函示庫

目前我們都是使用官方的 javascript 函示庫

以下是將官方函示庫包成不同語言的函示庫介紹

不過由於是非官方的可能有問題

非官方函示庫列表