Firebase初探(2)-基礎用法與排序

Sean Yeh
Web Design Zone
Published in
13 min readMar 27, 2020

--

上一篇我們說到Firebase的安裝與啟用,這裏接著說明Firebase的基礎用法與資料的排序方式。本篇使用的是Firebase的Realtime Database。

基礎用法

Firebase的用法中,常用的有下面幾項:

  • ref 路徑
  • set 新增
  • once, on 讀取資料
  • push 新增資料,與set不同點
  • child子路徑
  • remove移除

排序方式

Firebase的資料排序方式中,下面幾個方法比較常用到。

  • orderByChild 排序的操作。
  • startAt, endAt, equalTo 等與過濾條件有關之操作。
  • limitToFirst, limitToLast 等與限制筆數有關之操作。

我們就對這些部分分別進行說明。

ref()與set()

假設大家已經連接了資料庫(可以參考上一篇),現在就來測試看看,該如何操控資料庫。為了這個目的,我們需要暸解下面的指令:

ref() : 尋找資料庫路徑(預設:根目錄)

這個指令可以尋找資料庫的路徑位置。其預設值為根目錄,因此如果在 ref()括弧中間保留空白的話(如下),表示從根目錄開始。

db.ref()

set(): 新增資料(寫入方式:覆蓋)

在基礎的操作中,使用set可以將資料庫中原來存在的值全部取代掉,換上目前指定的內容。因此,可以說非常具有破壞性。

繼續上一篇的程式碼,我們可以使用set()來新增資料到資料庫中。例如:我們想要加入『Hi』到資料庫中。

db.ref().set('Hi')

發佈看看:結果出現錯誤訊息。

因為資料庫的權限不夠。我們可以回到firebase的介面看看。在規則的地方,我們發現讀取的權限與寫入的權限都是「false」。這表示我們無法讀寫這個資料庫。

為了測試一下資料庫的讀寫,我們「暫時」將權限改成true。之所以是「暫時」,是因為這樣子門戶洞開狀態在實務上是很危險的,在此只為了方便說明的便宜措施。

在重新整理index.html測試一下這次是否可以寫入資料庫?

成功了,資料庫中出現「Hi」。

Firebase可以儲存物件

在set()裡面除了放字串外,也可以存入物件。存入物件的方式如下。大家應該都知道,物件的格式為「鍵、值」格式。假設我們要存入一個「鍵」為home而「值」為Taiwan資料,方法如下:

key為home

value為Taiwan

db.ref().set({  "home": 'Taiwan'})

資料庫儲存的結果:

如果有兩個會員的資料想要存入,我們可以先設定資料為物件data。細節如下:

var data = null;    data = {      user1: {        name: 'Jonny',        num: 1       },      user2: {        name: 'Mary',        num: 2     }}

將上面的資料存入firebase裡面。

db.ref().set(data)

資料庫儲存的結果:

如果想要將第一個使用者的名稱改為Tom,可以用下面的方式:

firebase.database().ref('user1/name').set('Tom')

資料庫儲存的結果:

要注意的是, set() 在每次寫入的時後,會覆蓋掉目前資料庫該位置的內容,所以要注意ref() 是否有指定到正確的位置,不然有可能會發生慘劇。

Firebase不可以儲存陣列格式

下面的式子可以執行嗎?我們想要儲存一組陣列到資料庫中。

firebase.database().ref().set([1,2,3,4,5])

資料庫儲存的結果:

可以存入資料庫,但是存入的資料並不是陣列格式,而是物件格式。所以,在設計資料庫的時候,要以物件方式來思考才行。

設計多層次的資料結構

假設我們賣兩項產品(products):筆(pen)與筆記本(note)。這兩項產品各有各的價格以及它的存貨量。

產品:

訂單:

資料結構可以設計如下:

var data = null;data = {    products: {        pen: {            price: 15,            num: 10        },        note: {            price: 30,            num: 20        }    },    orders: {        1: {            pen: 2        },        2: {            pen: 5,            note: 2        }    }}

存入資料庫:

db.ref().set(data)

資料庫儲存的結果:

once讀取資料

使用once可以讀取一次資料庫的資料。假設目前資料庫的結構如下圖所示,我們要使用once來讀取他,並且顯示在console中:

首先,指定一個變數myData,讓這個變數對應到資料庫myData的路徑。

var myData = firebase.database().ref('myData');

接著就可以使用once來讀取它。我們使用myData.once(a,b)的方式來讀取。

myData.once(a,b)

其中a為value而b為一個無名函數。這個無名函數中需要一個參數,我們一般習慣將這個參數取名為snapshot。

因為使用once讀取的行為,就像是拿一個相機對著資料庫照一張快照一樣,把那一霎那的狀況忠實的顯示出來。

我們可以用snapshot.val()取出快照的內容。由於是非同步事件,想要對取得的資料進行後續操作時,要寫在 callback function 中。

myData.once('value', function(snapshot){var data = snapshot.val()console.log(data)})

因此,顯示在console中的結果如下:

on讀取資料

使用on可以即時監聽並且讀取資料庫的資料,其他在用法上和 once 幾乎一樣。

如下程式碼,只是把once改成on,就產生不一樣的效果。

myData.on('value', function(snapshot){var data = snapshot.val()console.log(data)})

使用on的話,程式會在背景待命,一旦資料變動的時候,就會重新印出新的結果。由於這種特性,on常常被用在需要即時更新訊息的聊天室應用程式的開發上。

child子路徑

我們可以用child找到子路徑。

// 1.
var todos = db.ref('todos');

例如使用上面1這樣的撰寫方式與下面2的方式是一樣的。

// 2.
var todos = db.ref().child('todos');

使用下面2的程式碼,會事先指向到根目錄(ref()),再從根目錄移到子路徑todos(child('todos'))與上面1的程式碼,直接指向todo路徑(ref('todos'))是一樣的。

新增資料

新增資料進入Realtime Database有幾個方式:

  1. set
  2. push
  3. update

這三種的用法大致上都一樣,但是要注意的是三者間的不同。

set 會直接取代當前路徑中的所有資料,而update 只修改相關資料,不會直接取代路徑中的所有資料,對於只需要修改局部資料來說,這個方式比較適合。

如果要修改的資料在資料庫中找不到該路徑的話,update就會自動新增一筆資料。

至於push ,一樣是新增資料,但它既不會取代也不會更新任何資料,並且會自己帶入一個隨機產生的 key。

push可以增加一個物件進資料庫。下面我們用這段程式碼示範一下:

var db = firebase.database()var todos = db.ref('todos');todos.push({content:'買菜'});

結果,資料庫增加了一個物件,並且產生了一組隨機的key值。

todos.push({content:'逛街'});

再寫一次,就會再加上一筆。你可以發現,原來的資料還在。

刪除資料

如果想要刪除資料,該如何做?在Firebase中要刪除資料可以使用remove來處理。

繼續上面的例子,我們執行remove()的時候,就可以刪除該筆資料。

todos.child('-M3QA3JL55SqRpzd3AuF').remove()

排序方式

如果您曾經學過SQL指令的話,應該對於order by之類的語法不陌生吧。在資料庫的存取與控制上,這類的這類的指令十分常見。那麼在Firebase中是否也有相似的操作方式?

orderBy排序

是的,在Firebase中我們可以看到orderBy這個方法的使用。如字面意思,orderBy是用來排序的。

在Firebase Realtime Database中,你可以透過「Key」、「Value」或者是「Child的值」來獲取資料。也可以對資料庫進行篩選。因此orderBy還可以分成:

要注意個是,你一次只能用『一個』orderBy來排序。如果不慎在同ㄧ個擷取時用上兩個以上的orderBy會導致錯誤發生。

orderByChild 排序

orderByChild()依據資料某一個特定子節點來做排序,產生新的結果。orderByChild()不可以配重複使用,否則會導致錯誤發生。

範例:假設我們有下面的資料。

範例程式碼:

var db = firebase.database()var userRef = db.ref('user');userRef.orderByChild('weight').once('value',function(snapshot){    snapshot.forEach(function(item){    console.log(item.val());    })    console.log(snapshot.val());})

結果:

第一個console顯示的結果是透過orderByChild排序後的資料,我們針對weight值來進行排序。

第二個console顯示的結果是沒有透過排序的資料,你可以清楚地發現順序上的差異。

orderByKey 排序

orderByKey()中不需要帶參數,依據資料的Key來做排序,產生新的結果。排序的方式依照資料Key的升冪來排序,也就是資料少的排前面資料大的排後面。

orderByValue 排序

orderByValue中不需要帶參數,依據資料的value來做排序,產生新的結果。如果查詢的子級屬於字串、數字或布林值,則可以按其升冪值進行排序。

過濾條件

區間限制

startAt:大於

比如說我們想要過濾掉,體重70公斤以下的人,可以使用startAt(70)。

userRef.orderByChild("weight").startAt(70).once("value", function (snapshot) {    snapshot.forEach(function (item) {        console.log(item.val());    });});

結果顯示大於70公斤的會員。

endAt:小於

比如說我們想要過濾掉,體重50公斤以上的人,可以使用startAt(50)。

userRef.orderByChild("weight").endAt(50).once("value", function (snapshot) {    snapshot.forEach(function (item) {        console.log(item.val());    });});

結果顯示:裡面包含50公斤者。

equalTo:等於

比如說我們想要體重等於50公斤的人時,就要使用equalTo(50)。

userRef.orderByChild("weight").equalTo(50).once("value", function (snapshot) {    snapshot.forEach(function (item) {        console.log(item.val());    });});

結果只顯示體重為50公斤者。

如果你想要知道是誰的話,可以加上key (item.key)

userRef.orderByChild("weight").equalTo(50).once("value", function (snapshot) {    snapshot.forEach(function (item) {        console.log(item.val(), item.key);    });});

結果會顯示這筆被篩選出資料的key值。

筆數限制

如果資料庫的資料很多,而我們只需要其中的一部分資料的話,該如何處理?

userRef.orderByChild("weight").startAt(70).once("value", function (snapshot) {    snapshot.forEach(function (item) {        console.log(item.val());    });});

limitToFirst從頭開始的前幾筆

如前例體重70大於公斤的會員,如果我只需要其中第一筆資料的話,該如何處理?

userRef.orderByChild("weight").startAt(70).limitToFirst(1).once("value", function (snapshot) {    snapshot.forEach(function (item) {        console.log(item.val());    });});

可以使用 .limitToFirst(1),找到第一筆資料。

limitToLast 從最尾端起算的幾筆

想要最後一筆資料,可以使用.limitToLast(1),如果是最後5筆資料,可以使用.limitToLast(5)。

--

--

Sean Yeh
Web Design Zone

# Taipei, Internet Digital Advertising,透過寫作讓我們回想過去、理解現在並思考未來。並樂於分享,這才是最大贏家。