Android-Firestore基本查詢

Vincent Zheng
新手工程師的程式教室
7 min readJan 1, 2019

Firestore資料庫的類型屬於NoSQL,其中的集合(collection)類似關聯式資料庫的資料表,裡面儲存一筆一筆的文件(document)。進行查詢時,同樣能像SQL那樣指定某欄位的大小範圍或相等。

本文將介紹查詢Firestore資料庫的基本做法,包括單一欄位和「AND」的邏輯,另外也會模擬「OR」邏輯。最後簡介索引功能,以實現多條件查詢。使用的是Kotlin語言。

一、查詢的基本觀念

在查詢時會用到兩種「參照(reference)」,分別為CollectionReference和DocumentReference。參照是Firestore資料結構的化身,使用它們時就像在資料庫中依循自己的路徑(path)逐步走向需要的資料。

若要在某個集合,利用文件的ID來查詢,程式會像這樣撰寫:

db.collection("Students").document("SVC4IxAwDSd...")
.get()
.addOnSuccessListener { documentSnapshot: DocumentSnapshot ->

}

透過資料庫物件,在名為Students的集合中,指定ID為「SVC4IxAwDSd…」的文件。接著呼叫「get」方法,開始連線到Firestore查詢。接著再配置callback程式,用以在程式中取得結果。

查詢結果是以DocumentSnapshot物件的形式出現。Snapshot的字面翻譯是「快照」,可以想像成到資料庫照一張照片帶回來。縱使之後資料庫的文件可能會異動,但目前在程式中我們仍握有這張文件當時的資料(照片)。

關於查詢結果的使用方式,請繼續閱讀下一節。

二、將文件轉為類別物件

在程式中得到的查詢結果,是一種DocumentSnapshot物件。它能透過特殊的方法轉換為指定的類別物件。舉例來說,假設當初在Firestore新增一個學生的類別物件,在查詢後也可以轉換回學生物件。

接續上一節,若要將DocumentSnapshot轉為類別物件,程式會這樣寫:

db.collection("Students").document("SVC4IxAwDSd...")
.get()
.addOnSuccessListener { documentSnapshot: DocumentSnapshot ->
val student = documentSnapshot.toObject(Student::class.java)
}

使用「toObject」方法能夠將文件的快照轉換成指定的類別物件,需要傳入類別作為參數。如此一來就便可用我們原先熟悉的物件進行操作。

附帶一提,這項轉換機制牽涉到類別的set方法,而Kotlin的特色是本來就不需要特別定義set方法,因此能無後顧之憂地配對到正確的類別屬性。

但對於Java開發者,請確保類別具備無參數的空白建構子。且每個需要對應的屬性都有對應名稱的set方法,如name屬性會有一個setName方法。

三、單一條件查詢

接下來要介紹如何以欄位作為條件來進行查詢。基本上就是大於、小於和等於。相關的操作方法包括以下幾種:

whereEqualTo("欄位", 值)whereGreaterThan("欄位", 值)whereLessThan("欄位", 值)whereGreaterThanOrEqualTo("欄位", 值)whereLessThanOrEqualTo("欄位", 值)

從字面上便能得知這些where方法的用途分別為:等於、大於、小於、大於等於及小於等於。它們是作用在CollectionReference上的,代表要在這個集合查詢符合條件的文件,並且需傳入欄位名稱和比較值最為參數。

舉例來說,若要在名為「Students」的集合查詢年齡未滿18歲的學生,程式會類似這麼寫:

db.collection("Students")
.whereLessThan("age", 18)
.get()
.addOnSuccessListener { querySnapshot: QuerySnapshot ->
val students = querySnapshot.toObjects(Student::class.java)
}

若要查詢電子郵件為「vincent999@gmail.com」的學生,會這麼寫:

db.collection("Students")
.whereEqualTo("email", "vincent999@gmail.com")
.get()
.addOnSuccessListener { querySnapshot: QuerySnapshot ->
val students: List<Student> = querySnapshot.toObjects(Student::class.java)
}

這裡要注意,條件查詢的結果已經不是DocumentSnapshot物件了,而是「QuerySnapshot」物件。它除了能透過「documents」方法得到DocumentSnapshot的清單(List)之外,也可使用「toObjects」方法直接轉換為指定的類別物件。

四、多重條件查詢

若想添加更多查詢條件,只要多多利用上一節介紹的「where」方法就好,例如想找出「18歲的女性」,語法會是這樣子:

db.collection("Students")
.whereEqualTo("age", 18)
.whereEqualTo("gender", "female")
.get()
(以下省略)

連續使用where方法,屬於「AND」邏輯。

但有一點要特別注意,若使用多個where,且有牽涉到大、小於(也就是並非全部都是whereEqualTo),這時查詢可能會失敗,請參考下一節。

回到正題,要是想使用「OR」邏輯,比方說查詢「未滿18歲的男性」和「滿20歲的女性」,可以像這樣子撰寫:

val collection: CollectionReference = db.collection("Students")collection.whereLessThan("age", 18)
.whereEqualTo("gender", "male")
collection.whereGreaterThanEqualTo("age", 20)
.whereEqualTo("gender", "female")

collection
.get()
(以下省略)

做法是取得CollectionReference物件,再透過「分開使用」where方法的技巧來達成。
第一部份是未滿18歲的男性。連續使用where,如同前面所說,是屬於AND邏輯。
第二部份是滿20歲的女性。由於程式碼「斷開」了,因此會與上面的條件形成OR的邏輯關係。條件定義好後,照常呼叫get方法進行查詢即可。

同理,若未特別進行設定,執行牽涉到大小於的多重條件查詢可能會失敗,這時請參考下一節。

五、索引簡介

如果想查詢「滿20歲的會計系學生」,程式應該會這麼寫:

db.collection("Students")
.whereGreaterThanOrEqualTo("age", 20)
.whereEqualTo("department", "Accounting")
.get()
(以下省略)

寫法雖然沒錯,但執行時卻在Console收到需要建立索引的訊息,並附上一個超連結:

The query requires an index. You can create it here: https://console.firebase.google.com/project/.../database/firestore/indexes?create_index=...

點入超連結後,會來到建立索引的畫面

透過超連結建立索引

按下「建立索引」,等待Firestore建立完成後,程式碼中的查詢就可以使用了。如果要自行在網頁建立索引也是可以,但透過點擊超連結的方式能避免操作錯誤。筆者撰寫此文時,應該是沒有打錯字,卻總是不成功,後來點超連結就成功了。因此還是推薦點連結。

--

--

Vincent Zheng
新手工程師的程式教室

我是Vincent,是個來自資管系的後端軟體工程師。當初因為學校作業,才踏出寫部落格的第一步。這裡提供程式教學文章,包含自學和工作上用到的經驗,希望能讓讀者學到東西。我的部落已搬家至 https://chikuwa-tech-study.blogspot.com/