Firebase 教學 - Firestore 安裝、寫入和讀取
Cloud Firestore 是 Google 所提供的新一代即時資料庫,延續 RealTime Database 的高效率以及低延遲性,可以即時監聽資料庫的變化,並同步有使用到資料庫的各個應用,此外,在安全性的定義上也更加彈性和直覺,對於「純前端」工程師來說,是相當好用的資料庫。
延伸閱讀:
建立資料庫
首先我們先用 Google 帳號登入 ( https://firebase.google.com/ ),接著進入 firebase 的控制台,點選「新增專案」,伺服器所在的位置選擇台灣,Cloud Firestore 選擇 asia-east2 ( 單純只是選擇距離台灣比較近的 ),相關條款打勾後即可新增專案。( 如果名稱和別人重複,自訂名稱後方多出一串亂碼 )
專案建立後,會自動進入專案的管理畫面,點選左側選單的「Database」,點選右側 Cloud Firestore 建立資料庫。
一開始建立先使用「以測試模式啟動」,方便一開始寫入或讀取資料,後續篇幅會再介紹安全性規則。
完成後就會看見類似下圖的畫面,表示資料庫建立完成,可以開始使用囉!
初始化
首先載入對應的 Javascript 函式庫 ( 我使用 Google 所提供的連結 )
<script src="https://www.gstatic.com/firebasejs/4.12.1/firebase.js"></script>
<script src="https://www.gstatic.com/firebasejs/4.12.1/firebase-firestore.js"></script>
在 Javascript 寫入下面這些程式碼,Project ID 填入自己的 Project ID,並宣告 db 變數為 Firestore 資料庫,執行後就可完成 Firestore 初始化。
firebase.initializeApp({
projectId: '### CLOUD FIRESTORE PROJECT ID ###'
});
var db = firebase.firestore();
雖然有別於 Realtime Database 是使用 json 節點的形式,Firestore 採用集合 ( collection ) 與文件 ( doc ) 的概念,集合內會包含文件,文件內也可以包含集合,資料只能存在文件內,整體來說,本質上仍然是 json 格式,但又更靈活有彈性。( 可以把集合想像成 A4 資料夾,文件就是 A4 紙張,資料是寫在 A4 紙上而不是寫在資料夾上 )
寫入資料
Firestore 寫入資料的方式有設定 ( set
)、更新 ( update
)、添加 ( add
) 和刪除 ( delete
),四種方式都支援 Promise,可以串接 then 監聽是否完成。
1. 設定 set
透過set
的方法,可以設定某個資料夾的內容,下面的程式範例執行後,會在 fruit 水果的集合裡,添加 apple 蘋果的文件,並在文件內寫入資料。
var db = firebase.firestore();
var ref = db.collection('fruit').doc('apple');
ref.set({
total: 500,
good: 480,
sale: 330
}).then(() => {
console.log('set data successful');
});
有別於 Realtime Database 的set
,firestroe 更提供了merge
的方法,幫助我們在寫入資料時不會完全覆蓋,而是進行合併整理,merge
的用法只需在 set 的參數後方,新增{merge:true}
即可,下方的程式碼,會將 bad 和 eat 的屬性,merge 到原本的文件內。
var db = firebase.firestore();
var ref = db.collection('fruit').doc('apple');
ref.set({
bad:15,
eat:5
},{merge: true}).then(() => {
console.log('set data successful');
});
如果沒有指定文件 doc 的名稱,執行後會自動產生一個亂數代碼作為文件名稱。
var db = firebase.firestore();
var ref = db.collection('fruit').doc(); // doc() 沒有指定名稱
ref.set({
total:200,
good:200
}).then(() => {
console.log('set data successful');
});
2. 更新 update
update
方法可以指定文件內某個屬性進行更新,避免覆寫整個文件內容 ( 跟上述 merge 有異曲同工之妙 ),下方程式碼執行後,會針對 total 和 good 的屬性做更新。
var db = firebase.firestore();
var ref = db.collection('fruit').doc('apple');
ref.update({
total:450,
good:400
}).then(() => {
console.log('update data successful');
});
3. 添加 add
add
主要針對「集合」而非「文件」,使用後可以自動添加文件,文件名稱為系統自動產生的亂碼,下方的程式碼在每次執行時,都會自動在 fruit 的集合內添加文件。( 和上述 set 資料夾不指定名稱的用法有異曲同工之妙 )
var db = firebase.firestore();
var ref = db.collection('fruit'); // add() 是針對集合使用
ref.add({
total:450,
good:400
}).then(() => {
console.log('add data successful');
});
4. 刪除 delete
delete
方法可以用於刪除集合或是文件,但如果集合裡有文件,則無法刪除集合,下面的程式碼執行後,會刪除 fruit 集合裡的 apple 文件。
var db = firebase.firestore();
var ref = db.collection('fruit').doc('apple');
ref.delete().then(() => {
console.log('delete data successful');
});
如果不是想要刪除整個文件,只是想刪除文件內的某個屬性,則需要透過update
的方式來實現,下方程式碼執行後會刪除 apple 文件內的 eat 屬性。
var db = firebase.firestore();
var ref = db.collection('fruit').doc('apple');
ref.update({
eat:firebase.firestore.FieldValue.delete()
}).then(() => {
console.log('set data successful');
});
讀取資料
Firestore 讀取資料的方式有一次性讀取 ( get
) 和即時監聽變化 ( onSnapshot
) 兩種方法,兩種方法可以搭配篩選 ( where
) 和排序 ( orderBy
) 兩種進一步的篩選,進行更精準的資料取得和比對的前置動作。
在讀取資料前,我先在 firestore 裡建立了一個名為 fruit 的集合,裡面放入 apple、mango、banana 和 orange 四個文件,每個文件都有 total 和 good 的屬性,有些則有 recommend 或 eat 的屬性,待會就會用這份資料來操作。
1. 一次性讀取 get
get
的方法可以取得某個集合裡,所有文件的資料 ( 子集合只能在後端讀取,無法在前端或 App 段實現 ),下面的程式碼,執行後可以取得 fruit 裡頭四種水果的名稱和屬性,如果get
的對象為集合 collection,取得資料後可使用forEach
個別取出文件內容,詳細 API 可以參考:firebase. firestore. QuerySnapshot
var db = firebase.firestore();
var ref = db.collection('fruit');
ref.get().then(querySnapshot => {
querySnapshot.forEach(doc => {
console.log(doc.id, doc.data());
});
});
如果直接指定文件的名稱,就可直接取得文件的內容。
var db = firebase.firestore();
var ref = db.collection('fruit').doc('apple');
ref.get().then(doc => {
console.log(doc.data());
});
2. 即時監聽變化 onSnapshot
onSnapshot
方法可以即時監聽資料庫的變化,有別於get
方法,onSnapshot
處在隨時監聽的狀態,只要有變化就會立刻傳送指定的資料,下面的程式執行後,可以取得 fruit 裡頭四種水果的名稱和屬性,這時如果再新增一種水果 grape,就會看到即時出現變化。( 對象為集合 collection,取得資料後可使用forEach
個別取出文件內容 )
var db = firebase.firestore();
var ref = db.collection('fruit');
ref.onSnapshot(querySnapshot => {
querySnapshot.forEach(doc => {
console.log(doc.id, doc.data());
});
});
如果直接指定文件的名稱,就可直接取得文件的內容。
var db = firebase.firestore();
var ref = db.collection('fruit').doc('apple');
ref.onSnapshot(doc => {
console.log(doc.data());
});
3. 篩選 where
讀取資料的下一步就是要處理資料,處理時不免會用到許多邏輯判斷,然而 firestore 提供的where
方法,能幫助我們進行第一步的資料篩選,如此一來就能減少並較為精準的讀入的資料,後續處理起來也就更簡單。
where
包含三個參數,第一個是屬性,第二個是邏輯運算子 ( 大於、小於...等 ),第三個是參數,會寫在get
或onSnapshot
的前方,下面的程式執行後,會篩選出 fruit 集合裡具有 recommend 為 true 的 doc 文件。
var db = firebase.firestore();
var ref = db.collection('fruit');
ref.where('recommend','==',true).get().then(querySnapshot => {
querySnapshot.forEach(doc => {
console.log(doc.id, doc.data());
});
});
where
可以一個串一個的進行複合式的邏輯篩選,如果是==
的形式,可以篩選不同的屬性,如果是>
、>=
、<=
、<
之類的邏輯範圍,則只能針對單一屬性,下方的程式會篩選出屬性 total 介於 200 到 400 之間的 doc 文件。
使用
where
做判斷要注意型別,如果資料庫中的數字為 string,則無法使用 number 的邏輯判斷。
var db = firebase.firestore();
var ref = db.collection('fruit');
ref.where('total', '>=', 200).where('total', '<=', 400).get().then(querySnapshot => {
querySnapshot.forEach(doc => {
console.log(doc.id, doc.data());
});
});
4. 排序 orderBy
orderBy
通常會和limit
搭配,主要作為排序後篩選特定數量的資料,orderBy
有兩個參數,第一個是要進行排序的屬性名稱,第二個是遞增 ( asc ) 或遞減 ( desc ),下方程式執行後會篩選出 total 屬性最高的前三名。
var db = firebase.firestore();
var ref = db.collection('fruit');
ref.orderBy('total','desc').limit(3).get().then(querySnapshot => {
querySnapshot.forEach(doc => {
console.log(doc.id, doc.data());
});
});
小結
其實 Cloud Firestore 使用上就跟 Realtime Database 一樣簡單,而且功能還更多,後續篇幅會再繼續介紹 Cloud Firestore 的安全性規則。