EP02 | Firebase 中的資料庫 - 如何在 Cloud Firestore 中新增、刪除資料

本篇文章主要是來紀錄如何使用、更新及添加文檔,會做一個 TodoList 來說明。

使用 Firestore 的前提

上一篇文章有提到如何在 Firebase 創建 Firestore 資料庫,但仔細觀察一下 Firestore 文件說明中,針對初始化 Firestore 的寫法:

1
2
3
4
5
6
7
8
9
10
11
12
<script src="https://www.gstatic.com/firebasejs/8.6.8/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.6.8/firebase-firestore.js"></script>
<script>
// Initialize Cloud Firestore through Firebase
firebase.initializeApp({
apiKey: '### FIREBASE API KEY ###',
authDomain: '### FIREBASE AUTH DOMAIN ###',
projectId: '### CLOUD FIRESTORE PROJECT ID ###'
});

var db = firebase.firestore();
</script>

但在我們得到的 SDK 中是這樣的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!-- The core Firebase JS SDK is always required and must be listed first -->
<script src="https://www.gstatic.com/firebasejs/8.6.8/firebase-app.js"></>

<!-- TODO: Add SDKs for Firebase products that you want to use https://firebase.google.com/docs/web/setup#available-libraries -->
<script src="https://www.gstatic.com/firebasejs/8.6.8/firebase-analytics.js"></script>

<script>
// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
var firebaseConfig = {
apiKey: "AIzaSyA3VJK2kf6oPs8fPLr4Oc0cigbWaOjEMIs",
authDomain: "my-first-firestore-b8b26.firebaseapp.com",
projectId: "my-first-firestore-b8b26",
storageBucket: "my-first-firestore-b8b26.appspot.com",
messagingSenderId: "1028819130660",
appId: "1:1028819130660:web:421f7eaea330f894be7bb1",
measurementId: "G-N71RBCWPK7"
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
firebase.analytics();
</script>

對比之後會發現,除了要在自己加入 Firestore 的 Library 之外,還必須使用 var db = firebase.firestore(); 將 Firestore 初始化。

設置文檔

自訂文檔ID

如果想要在本地專案裡直接創建文檔,並且**自訂文檔名(ID)**的話,可以使用 set() ,接下來來說說 set() 的使用方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 指定集合 todos 中的 JUN 文檔
// 如果文檔不存在,則會創建它。如果文檔確實存在,就選取該文檔
// 可以透過一個變數儲存文檔位置
const todosCollection = db.collection('todos').doc('JUN');

//透過 set() 在該文檔中寫入資料
// set({資料}, {merge: 合併與否})
// 透過 {merge: true} 來讓新資料與文檔其他資料合併
todosCollection.set({undo: ['date with parents']}, {merge: true})
.then(()=>{
// 寫入成功
console.log("Document successfully written!")
})
.catch(err=>{
// 寫入失敗
console.log(err)
})

在 TodoList 中新增一筆資料,並且該筆資料送出後,會被儲存在 Firestore 裡。

自動生成文檔名(ID)

set()

doc() 中部寫入任何參數,即為隨機生成文檔 ID。

1
2
3
4
5
6
7
8
9
10
11
// 不指定文檔名,firestore會自動升成一組隨機的 ID 作為文檔名
const todosCollection = db.collection('todos').doc();
todosCollection.set({undo: ['date with parents']})
.then(()=>{
// 寫入成功
console.log("Document successfully written!")
})
.catch(err=>{
// 寫入失敗
console.log(err)
})

add()

等同於使用 doc().set(...) ,但更簡潔。

1
2
3
4
5
6
7
8
9
db.collection('todos').add({undo: ['date with parents']})
.then(()=>{
// 寫入成功
console.log("Document successfully written!")
})
.catch(err=>{
// 寫入失敗
console.log(err)
})

更新文檔

如果只想更改文檔中某一項屬性值的話,要怎麼辦呢?
set() 可以嗎?
經過測試的確可以,但有一個前提是一定要加入 {merge: true} 這個參數,否則是覆蓋文檔原本的資料。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const todosCollection = db.collection('todos').doc('JUN');

// 原本資料為 disappear: false
todosCollection.set({
disappear: 'No'
},{
merge: true
})
.then(()=>{
// 寫入成功
console.log("Updated ? No.")
})
.catch(err=>{
// 寫入失敗
console.log(err)
})

有沒有更簡潔的寫法?
有,就是使用 update()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const todosCollection = db.collection('todos').doc('JUN');

// 原本資料為 undo: ['date with parents']
todosCollection.undate({
disappear: 'No'
})
.then(()=>{
// 寫入成功
console.log("Document successfully written!")
})
.catch(err=>{
// 寫入失敗
console.log(err)
})

上面 2 種方法結果都是相同的。

這時候又有一個疑問了,那就是要如何新增陣列值,這時候就要來了解一下 firebase. firestore. FieldValue 這個構造函數了,在 firebase. firestore. FieldValue 包含了幾種方法,除了新增、移除陣列值外,還可以追蹤伺服器何時收到更新以及在資料庫的數值中新增或減少指定數值。

  • isEqual(val):資料庫的該屬性資料與指定 val 是否相同。

  • arrayUnion(val):從資料庫的該陣列值中新增指定 val,但只添加不存在的 val 。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    todosCollection.undate({
    undo: firebase.firestore.FieldValue.arrayUnion('writting')
    })
    .then(()=>{
    // 寫入成功
    console.log("Document successfully update!")
    })
    .catch(err=>{
    // 寫入失敗
    console.log(err)
    })
  • arrayRemove(val):從資料庫的該陣列值中刪除指定 val 。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    todosCollection.undate({
    undo: firebase.firestore.FieldValue.arrayRemove('reading')
    })
    .then(()=>{
    // 寫入成功
    console.log("Document successfully written!")
    })
    .catch(err=>{
    // 寫入失敗
    console.log(err)
    })
  • increment(number):對資料庫的當前數值進行新增或減少指定 number 。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    todosCollection.undate({
    undo: firebase.firestore.FieldValue.arrayRemove('saving'),
    undoNum: firebase.firestore.FieldValue.increment(1)
    })
    .then(()=>{
    // 寫入成功
    console.log("Document successfully written!")
    })
    .catch(err=>{
    // 寫入失敗
    console.log(err)
    })
  • serverTimestamp():跟踪服務器何時收到更新。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    todosCollection.undate({
    undo: firebase.firestore.FieldValue.arrayRemove('drinking'),
    undoNum: firebase.firestore.FieldValue.increment(1),
    updateTime: firebase.firestore.FieldValue.serverTimestamp()
    })
    .then(()=>{
    // 寫入成功
    console.log("Document successfully written!")
    })
    .catch(err=>{
    // 寫入失敗
    console.log(err)
    })

除陣列更新外,要如何指定物件中某項值的更新,這時可以直接使用點表示法來引用文檔中的嵌套字段,並賦予新值。

1
2
3
4
5
6
7
8
9
10
11
todosCollection.undate({
'author.name': 'Celeste'
})
.then(()=>{
// 寫入成功
console.log("Document successfully written!")
})
.catch(err=>{
// 寫入失敗
console.log(err)
})

特別提醒:指定物件屬性名時,一定要使用字串型式。

刪除文檔或文檔中指定屬性

不論是刪除文檔或文檔中指定屬性都是使用 delete() ,只是使用的方法即位置不一樣。

刪除文檔中指定屬性
在文檔使用 update() ,在欲刪除的屬性上使用 firebase.firestore.FieldValue.delete()

1
2
3
4
5
6
7
8
9
10
11
db.collection('todos').doc('JUN').update({
undo: firebase.firestore.FieldValue.delete()
})
.then(()=>{
// 寫入成功
console.log("Document successfully deleted!")
})
.catch(err=>{
// 寫入失敗
console.log(err)
})

刪除文檔
在文檔使用 delete() 即可直接刪除文檔。

1
2
3
4
5
6
7
8
9
db.collection('todos').doc('JUN').delete()
.then(()=>{
// 寫入成功
console.log("Document successfully deleted!")
})
.catch(err=>{
// 寫入失敗
console.log(err)
})

參考資料

將數據添加到 Cloud Firestore

firebase. firestore. FieldValue