昨天有提到以子集合的架構與同街集合的架構,去查詢聊天室的訊息,在速度上是差不多的,但只簡單提到和索引(Index)有關,而沒詳細解釋,所以今天就來研究索引吧。
資料庫的查詢效率,通常取決於索引的建立。若是沒有建立索引,那麼資料庫系統就只能將整份清單的資料都逐一比對,這會在清單的資料筆數越來越多時,讓查詢效率越來越差。所以針對時常查詢的欄位建立索引,是在規劃資料庫必須做的事情之一。
而 Cloud Firestore 之所以查詢速度會如此快速,有一個很大的原因就在於它為文件的每個欄位都自動建立了「單欄索引」(Single-field indexes)。也就是為每個文件欄位,都建立兩個最基本的索引——升序和降序,讓我們只需要針對這個欄位去查詢、或是排序時,都能有最佳的查詢速度。
以前面的例子來說明:
users: {
"ruoshi": {
first : "Ruoshi"
last : "Lin"
born : 1900
},
"ironman": {
nick : "IR"
}
}
在 users 這個集合裡面,就有八個索引,分別是 first、last、born、nick 欄位的升降序索引。這都是 Firestore 會自動建立的,我們不用刻意去管理,就可以直接使用 <
, <=
, ==
, >=
, >
做查詢,像是這樣:
usersRef.where("first", "==", "Ruoshi")
usersRef.where("born", "<", 1950)
usersRef.where("nick", ">=", "IR")
而這個自動建立索引的範圍也包括 map 資料型別欄位裡的任何欄位,以下面的例子為例:
users: {
"ruoshi": {
name: {
first : "Ruoshi"
last : "Lin"
}
born : 1900
}
}
我們可以透過這樣的方式去查詢而不用另外建立索引:
usersRef.where("name.first", "==", "Ruoshi")
但自動建立索引的對象並不是所有資料型別的欄位都通用,像是 map 型別就只會針對其底下的非 map 型別欄位建立索引,map 型別欄位本身是沒有索引的。另一個比較特殊的是 array 型別的欄位,該欄位無法像是一般欄位可以透過 <
, <=
, ==
, >=
, >
做查詢,也不會建立升降序兩個索引。但相對的,Cloud Firestore 會為它建立一個稱作 array-contains
的索引,讓我們可快速查詢該 array 有沒有包含什麼值。一樣舉個例子:
users: {
"ruoshi": {
tags: ["abc", "xyz"]
}
}
可以這樣查詢:
usersRef.where("tags", "array_contains", "abc")
講解到這邊,就有另一個疑惑了,假設我要多重條件查詢時,那該怎麼辦?而 Cloud Firestore 其實還有另一種索引,叫做「複合索引」(Composite indexes),也就是讓我們能夠客製化多個條件下的索引,這部分就需要我們自己手動建立,至於為什麼不像單欄索引一樣幫忙建立呢?那是因為複合索引的組合實在太多了,而會用到的組合少之又少,效益就顯得低落。
那我們該如何建立複合索引呢?有兩種方式可以進行,第一種就是自己到 Cloud Firestore 資料庫頁面的索引分頁去建立,另一種方式就是先編寫程式進行多重條件查詢,當資料庫沒有該複合索引時,程式就會出錯,而錯誤訊息中就包含了建立該索引的懶人連結,只要點選該連結就可以到已經填好的複合索引建立表單囉。
今天大致就針對研究索引的部分簡單記錄到這邊,其實還有一些小細節沒有提到,就等到之後有時間再另外說明吧!