Cloud Firestore 中的索引類型

索引是資料庫效能的重要因素。資料庫索引就像是將書籍主題對應至頁碼的書籍索引一樣,資料庫索引會將資料庫中的項目對應至資料庫中的位置。傳送查詢時,資料庫可使用索引快速查詢要求項目的位置。

本頁面說明 Cloud Firestore 使用的兩種索引類型:單一欄位索引複合式索引

索引定義和結構

索引的定義是在指定文件的欄位清單中,且每個欄位都會有對應的索引模式

索引包含的項目,適用於索引定義中提及的每個欄位。這個索引包括所有文件,這些文件是根據索引而顯示的結果。只有在索引的每個欄位中,文件都設有已編入索引的值時,該文件才會加入索引;如果索引定義參照了該文件未設定任何值的欄位,該文件便不會出現在索引中,因此,因此不會在任何基於該索引的查詢結果中傳回。

複合式索引會按照欄位值,按照索引定義中指定的順序排序。

每次查詢背後的索引

如果查詢沒有索引,大多數資料庫都會依項目逐一檢索其內容項目,如果處理速度較慢,隨著資料庫增長,速度也會變慢。Cloud Firestore 會為「所有」查詢使用索引,保證高效能的查詢效能。因此,查詢效能取決於結果集大小,而非資料庫中的項目數量。

減少索引管理,更多應用程式開發

Cloud Firestore 提供的功能可減少管理索引所需的時間。系統會自動建立大部分基本查詢所需的索引。您在使用及測試應用程式時,Cloud Firestore 可協助您識別及建立其他索引,讓應用程式需要額外的索引。

索引類型

Cloud Firestore 使用兩種類型的索引:單一欄位複合型。除了已建立索引的欄位數量之外,單一欄位和複合式索引的管理方式也有所不同。

單一欄位索引

單一欄位索引會儲存含有特定欄位的集合中所有文件的排序對應。單一欄位索引中的每個項目都會記錄特定欄位的文件值,以及文件在資料庫中的位置。Cloud Firestore 使用這些索引來執行許多基本查詢。您可以設定資料庫的自動索引設定和索引豁免,藉此管理單一欄位索引。

自動建立索引

根據預設,Cloud Firestore 會自動維護文件內每個欄位的單一欄位索引,以及地圖中每個子欄位的單一欄位索引。Cloud Firestore 會針對單一欄位索引使用下列預設設定:

  • 針對每個非陣列和非對應欄位,Cloud Firestore 會為每個欄位定義兩個「集合範圍」單一欄位索引,一個採用遞增模式,另一個則採用遞減模式。

  • Cloud Firestore 會為每個對應欄位建立下列項目:

    • 每個非陣列的非對應子欄位各有一個集合範圍遞增索引。
    • 每個非陣列的非對應子欄位建立一個集合範圍遞減索引。
    • 一個集合範圍陣列包含每個陣列子欄位的索引。
    • Cloud Firestore 會以遞迴方式為每個地圖子欄位建立索引。
  • 對於文件中的每個陣列欄位,Cloud Firestore 會建立並維護集合範圍陣列的陣列包含索引。

  • 預設不維護包含集合群組範圍的單一欄位索引。

單一欄位索引豁免設定

您可以建立單一欄位索引豁免項目,從自動索引設定中排除特定欄位。索引豁免設定會覆寫整個資料庫的自動索引設定。豁免項目可以啟用單一欄位索引,否則自動索引設定會停用或停用自動索引功能啟用的單一欄位索引。如要瞭解豁免情況可能相當實用,請參閱建立索引的最佳做法

使用 * 欄位路徑值,在集合群組的所有欄位中新增集合層級的索引豁免設定。例如,針對集合群組 comments,請將欄位路徑設為 *,以符合 comments 集合群組中的所有欄位,並停用集合群組下所有欄位的索引功能。然後您可以新增豁免項目,僅將豁免項目新增至查詢所需的欄位索引。減少已建立索引的欄位數量可降低儲存空間成本,同時改善寫入效能。

如果您為地圖欄位建立了單一欄位索引豁免設定,地圖的子欄位會沿用這些設定。不過,您可以為特定子欄位定義單一欄位的索引豁免條件。如果您刪除子欄位的豁免項目,子欄位將會繼承父項的豁免設定 (如果有),或是整個資料庫的豁免設定 (如果沒有父項豁免項目)。

如要建立及管理單一欄位索引豁免,請參閱管理 Cloud Firestore 中的索引

複合式索引

複合式索引會根據要建立索引的已排序欄位清單,儲存集合中的所有文件經過排序的對應關係。

Cloud Firestore 使用複合式索引支援單一欄位索引尚未支援的查詢。

Cloud Firestore 不會像單一欄位索引一樣自動建立複合式索引,因為有大量的可能欄位組合。相反地,Cloud Firestore 可在建構應用程式時協助您識別及建立必要的複合式索引

當您嘗試執行索引不支援的查詢時,Cloud Firestore 會傳回錯誤訊息,並提供連結建立遺漏的索引。

您也可以使用主控台或 Firebase CLI,手動定義及管理複合式索引。如要進一步瞭解如何建立及管理複合式索引,請參閱管理索引

索引模式與查詢範圍

單一欄位和複合式索引的設定方式不同,但兩者都必須需要為索引設定索引模式和查詢範圍。

索引模式

定義索引時,您必須為每個已建立索引的欄位選取索引模式。每個欄位的索引模式都可支援該欄位的特定查詢子句。您可以選取下列索引模式:

索引模式 說明
遞增 支援針對欄位的 <<===>=>!=innot-in 查詢子句,並支援根據這個欄位值遞增排序結果。
遞減 支援欄位的 <<===>=>!=innot-in 查詢子句,並支援根據這個欄位值以遞減方式排序結果。
陣列-包含 支援欄位的 array-containsarray-contains-any 查詢子句。
向量 支援欄位的 FindNearest 查詢子句。

查詢範圍

每個索引的範圍都是一個集合或集合群組。這稱為索引的查詢範圍:

集合範圍
Cloud Firestore 預設會建立含有集合範圍的索引。這些索引支援只傳回單一集合結果的查詢。

集合群組範圍
集合群組包含 ID 相同的所有集合。如要執行集合群組查詢,以傳回來自集合群組的篩選或排序結果,您必須使用集合群組範圍建立對應的索引。

預設順序和 __name__ 欄位

除了按照各欄位指定的索引模式排序文件 (遞增或遞減) 之外,索引還會依照每個文件的 __name__ 欄位套用最終排序。__name__ 欄位的值會設為完整的文件路徑。這表示在結果集中,具有相同欄位值的文件會按文件路徑排序。

根據預設,__name__ 欄位的排序方向與索引定義中最後排序的欄位相同。例如:

集合 已建立索引的欄位 查詢範圍
城市 姓名,__name__ () 集合
城市 個州/省,__name__ () 集合
城市 國家/地區,人口, __name__ 集合

如要按非預設 __name__ 方向排序結果,您必須建立該索引。

完美指數

查詢的完美索引能讓查詢功能發揮最高執行效率,依照順序在下列屬性中定義:

  1. 等式篩選器中使用的欄位
  2. 用於排序順序的欄位
  3. 用於「範圍」和「不等式」篩選器的欄位 (尚未包含在排序順序中)
  4. 匯總作業中使用的欄位 (尚未納入排序順序、範圍和不等式篩選器)

Firestore 計算查詢結果的結果如下:

  1. 找出查詢集合、篩選器屬性、篩選器運算子和排序順序所對應的索引
  2. 識別索引位置,從使用查詢的相等篩選器、範圍和不等式篩選器,依欄位的第一順序開始掃描
  3. 開始掃描索引,並傳回符合所有篩選器的所有文件,直到:
    1. 遇到不符合篩選條件的文件,並確認後續的文件一律不會完全符合篩選條件
    2. 掃描到索引結尾處,或
    3. 已收集到查詢所要求的結果數上限

索引建立範例

Cloud Firestore 會自動為您建立單一欄位索引,可讓應用程式快速支援最基本的資料庫查詢。單一欄位索引可讓您根據欄位值和比較子 <<===>=>in,執行簡易查詢。若是陣列欄位,則可讓您執行 array-containsarray-contains-any 查詢。

為進一步說明,請從索引建立的角度檢視下列範例。下列程式碼片段會在 cities 集合中建立少數 city 文件,並為每個文件設定 namestatecountrycapitalpopulationtags 欄位:

網站
var citiesRef = db.collection("cities");

citiesRef.doc("SF").set({
    name: "San Francisco", state: "CA", country: "USA",
    capital: false, population: 860000,
    regions: ["west_coast", "norcal"] });
citiesRef.doc("LA").set({
    name: "Los Angeles", state: "CA", country: "USA",
    capital: false, population: 3900000,
    regions: ["west_coast", "socal"] });
citiesRef.doc("DC").set({
    name: "Washington, D.C.", state: null, country: "USA",
    capital: true, population: 680000,
    regions: ["east_coast"] });
citiesRef.doc("TOK").set({
    name: "Tokyo", state: null, country: "Japan",
    capital: true, population: 9000000,
    regions: ["kanto", "honshu"] });
citiesRef.doc("BJ").set({
    name: "Beijing", state: null, country: "China",
    capital: true, population: 21500000,
    regions: ["jingjinji", "hebei"] });

假設預設的自動索引設定,Cloud Firestore 會更新每個非陣列欄位的單一遞增單一欄位索引、每個非陣列欄位一個遞減的單一欄位索引,以及一個陣列包含陣列欄位的單一欄位索引。下表中的每一列都代表單一欄位索引中的項目:

集合 已建立索引的欄位 查詢範圍
城市 名稱 集合
城市 個州 集合
城市 個國家/地區 集合
城市 首都 集合
城市 人口 集合
城市 名稱 集合
城市 個州 集合
城市 個國家/地區 集合
城市 首都 集合
城市 人口 集合
城市 array-contains 個區域 集合

單一欄位索引支援的查詢

使用這些自動建立的單一欄位索引,您可以執行簡單的查詢,如下所示:

網站
const stateQuery = citiesRef.where("state", "==", "CA");
const populationQuery = citiesRef.where("population", "<", 100000);
const nameQuery = citiesRef.where("name", ">=", "San Francisco");

您也可以建立 in 和複合等式 (==) 查詢:

網站
citiesRef.where('country', 'in', ["USA", "Japan", "China"])

// Compound equality queries
citiesRef.where("state", "==", "CO").where("name", "==", "Denver")
citiesRef.where("country", "==", "USA")
         .where("capital", "==", false)
         .where("state", "==", "CA")
         .where("population", "==", 860000)

如果您需要執行採用範圍比較 (<<=>>=) 的複合查詢,或是需要依其他欄位排序,必須為該查詢建立複合式索引

array-contains 索引可讓您查詢 regions 陣列欄位:

網站
citiesRef.where("regions", "array-contains", "west_coast")
// array-contains-any and array-contains use the same indexes
citiesRef.where("regions", "array-contains-any", ["west_coast", "east_coast"])

複合式索引支援的查詢

Cloud Firestore 使用複合式索引支援單一欄位索引尚未支援的複合查詢。例如,您需要複合式索引進行下列查詢:

網站
citiesRef.where("country", "==", "USA").orderBy("population", "asc")
citiesRef.where("country", "==", "USA").where("population", "<", 3800000)
citiesRef.where("country", "==", "USA").where("population", ">", 690000)
// in and == clauses use the same index
citiesRef.where("country", "in", ["USA", "Japan", "China"])
         .where("population", ">", 690000)

這些查詢需要下列複合式索引。由於查詢對 country 欄位使用等式 (==in),因此您可以針對這個欄位使用遞增或遞減的索引模式。根據預設,不等式子句會根據不等子子句中的欄位,套用遞增排序順序。

集合 已建立索引的欄位 查詢範圍
城市 (或 ) 個國家/地區, 人口 集合

如要執行相同的查詢,但以遞減順序排列,則 population 需要額外的複合式索引 (遞減方向):

網站
citiesRef.where("country", "==", "USA").orderBy("population", "desc")

citiesRef.where("country", "==", "USA")
         .where("population", "<", 3800000)
         .orderBy("population", "desc")

citiesRef.where("country", "==", "USA")
         .where("population", ">", 690000)
         .orderBy("population", "desc")

citiesRef.where("country", "in", ["USA", "Japan", "China"])
         .where("population", ">", 690000)
         .orderBy("population", "desc")
集合 已建立索引的欄位 查詢範圍
城市 個國家/地區, 人口 集合
城市 國家/地區 人口 集合

您也需要建立複合式索引,才能將 array-containsarray-contains-any 查詢與其他子句結合。

網站
citiesRef.where("regions", "array-contains", "east_coast")
         .where("capital", "==", true)

// array-contains-any and array-contains use the same index
citiesRef.where("regions", "array-contains-any", ["west_coast", "east_coast"])
         .where("capital", "==", true)
集合 已建立索引的欄位 查詢範圍
城市 array-contains 標記, (或 ) 大寫 集合

集合群組索引支援的查詢

如要示範含有集合群組範圍的索引,請假設您在部分 city 文件中新增 landmarks 子集合:

網站
var citiesRef = db.collection("cities");

citiesRef.doc("SF").collection("landmarks").doc().set({
    name: "Golden Gate Bridge",
    category : "bridge" });
citiesRef.doc("SF").collection("landmarks").doc().set({
    name: "Golden Gate Park",
    category : "park" });

citiesRef.doc("DC").collection("landmarks").doc().set({
    name: "National Gallery of Art",
    category : "museum" });
citiesRef.doc("DC").collection("landmarks").doc().set({
    name: "National Mall",
    category : "park" });

使用下列單一欄位索引搭配集合範圍,即可根據 category 欄位查詢單一城市的 landmarks 集合:

集合 已建立索引的欄位 查詢範圍
地標 (或 ) 類別 集合
網站
citiesRef.doc("SF").collection("landmarks").where("category", "==", "park")
citiesRef.doc("SF").collection("landmarks").where("category", "in", ["park", "museum"])

現在,假設您想查詢所有城市的地標。如要對由所有 landmarks 集合組成的集合群組執行這項查詢,必須啟用具有集合群組範圍的 landmarks 單一欄位索引:

集合 已建立索引的欄位 查詢範圍
地標 (或 ) 類別 集合群組

啟用這個索引後,您可以查詢 landmarks 集合群組:

網站
var landmarksGroupRef = db.collectionGroup("landmarks");

landmarksGroupRef.where("category", "==", "park")
landmarksGroupRef.where("category", "in", ["park", "museum"])

如要執行會傳回篩選或排序結果的集合群組查詢,您必須啟用設有集合群組範圍的對應單一欄位或複合式索引。然而,不會篩選或排序結果的集合群組查詢,不需要任何其他索引定義。

舉例來說,您可以在不啟用額外索引的情況下執行以下集合群組查詢:

網站
db.collectionGroup("landmarks").get()

索引項目

文件的索引項目數量取決於您專案設定的索引和文件結構。索引項目會計入索引項目數量限制

以下範例示範文件的索引項目。

文件

/cities/SF

city_name : "San Francisco"
temperatures : {summer: 67, winter: 55}
neighborhoods : ["Mission", "Downtown", "Marina"]

單一欄位索引

  • 城市名稱 ASC
  • 城市名稱 DESC
  • 隨機性參數 (ASC)
  • 溫度 s.summer DESC
  • Winters.winter ASC
  • results.winter DESC
  • Communitys Array Contains (ASC 和 DESC)

複合式索引

  • City_name ASC,底座 ARRAY
  • City_name DESC,鄰近地區 ARRAY

索引項目

這項索引設定會產生文件下列 18 個索引項目:

索引 已建立索引的資料
單一欄位索引項目
城市名稱 ASC City_name:「San Francisco」
城市名稱 DESC City_name:「San Francisco」
隨機性參數 (ASC) results.summer:67
溫度 s.summer DESC results.summer:67
Winters.winter ASC results.winter:55
results.winter DESC results.winter:55
Communitys Array 包含 ASC 社區:「使命」
Communitys Array 包含 DESC 社區:「使命」
Communitys Array 包含 ASC 社區:「市中心」
Communitys Array 包含 DESC 社區:「市中心」
Communitys Array 包含 ASC 社區:「小艇碼頭」
Communitys Array 包含 DESC 社區:「小艇碼頭」
複合式索引項目
City_name ASC,底座 ARRAY City_name:「舊金山」、社區:「使命」
City_name ASC,底座 ARRAY City_name:「舊金山」、社區:「市區」
City_name ASC,底座 ARRAY City_name:「舊金山」、社區:「小艇碼頭」
City_name DESC,鄰近地區 ARRAY City_name:「舊金山」、社區:「使命」
City_name DESC,鄰近地區 ARRAY City_name:「舊金山」、社區:「市區」
City_name DESC,鄰近地區 ARRAY City_name:「舊金山」、社區:「小艇碼頭」

索引與定價

索引會計入應用程式的儲存空間費用。如要進一步瞭解索引的儲存空間大小,請參閱索引項目大小一文。

利用索引合併功能

雖然 Cloud Firestore 每個查詢都會使用索引,但每項查詢並不需要使用索引。針對包含多個等式 (==) 子句的查詢,以及 (選用) orderBy 子句的查詢,Cloud Firestore 可以重複使用現有的索引。Cloud Firestore 可以合併索引來提供簡易的相等性篩選器,藉此建構大型等式查詢所需的複合式索引。

找出能利用索引合併作業的情況,即可降低索引成本。舉例來說,假設餐廳評分應用程式的 restaurants 集合如下:

  • 間餐廳

    • 漢堡

      name : "Burger Thyme"
      category : "burgers"
      city : "San Francisco"
      editors_pick : true
      star_rating : 4

現在,請想像這個應用程式使用以下查詢。請注意,應用程式會使用 categorycityeditors_pick 的等式子句組合,同時一律依遞增 star_rating 排序:

網站
db.collection("restaurants").where("category", "==", "burgers")
                            .orderBy("star_rating")

db.collection("restaurants").where("city", "==", "San Francisco")
                            .orderBy("star_rating")

db.collection("restaurants").where("category", "==", "burgers")
                            .where("city", "==", "San Francisco")
                            .orderBy("star_rating")

db.collection("restaurants").where("category", "==", "burgers")
                            .where("city", "==" "San Francisco")
                            .where("editors_pick", "==", true )
                            .orderBy("star_rating")

您可以為每個查詢建立索引:

集合 已建立索引的欄位 查詢範圍
餐廳 類別, 星級評等 集合
餐廳 座城市, 星級評等 集合
餐廳 類別, 城市, 星級評等 集合
餐廳 類別, 城市, 編輯器_pick, 顆星 集合

Cloud Firestore 可為等式子句合併索引,是更好的解決方案:

集合 已建立索引的欄位 查詢範圍
餐廳 類別, 星級評等 集合
餐廳 座城市, 星級評等 集合
餐廳 編輯精選,評分: 顆星 集合

不僅如此,這組索引不僅較小,還支援其他查詢:

網站
db.collection("restaurants").where("editors_pick", "==", true)
                            .orderBy("star_rating")

索引限制

以下限制適用於索引。如要查看所有配額和限制,請參閱配額與限制一文。

限制 說明
資料庫的複合式索引數量上限
資料庫的單一欄位設定數量上限

一個欄位層級設定可以包含相同欄位的多項設定。舉例來說,如果單一欄位的索引豁免項目和同一個欄位的存留時間政策,就會視為一項欄位設定計入上限。

每個文件的索引項目數量上限

40,000 個

索引項目數量是文件中下列兩種項目的總和:

  • 單一欄位索引項目的數量
  • 複合式索引項目的數量

如要瞭解 Cloud Firestore 如何將文件和一組索引轉換為索引項目,請參閱這個索引項目計數範例

複合式索引中的欄位數量上限 100
索引項目大小上限

7.5 KiB

如要瞭解 Cloud Firestore 計算索引項目大小的方式,請參閱索引項目大小一節。

文件的索引項目大小總計上限

8 MiB

大小總計是文件中下列兩種項目的總和:

  • 文件的單一欄位索引項目大小總和
  • 文件的複合式索引項目大小總和
  • 已建立索引的欄位值大小上限

    1500 個位元組

    超過 1500 個位元組的欄位值會遭到截斷。如果查詢中有欄位值遭截斷,系統可能會傳回不一致的結果。

    建立索引的最佳做法

    對於大多數應用程式,您可以仰賴自動索引和錯誤訊息連結來管理索引。但在下列情況下,您可能會想要新增單一欄位豁免:

    案件 說明
    大型字串欄位

    如果有字串欄位經常包含不用於查詢的長字串值,則可將該欄位排除在索引範圍之外,藉此節省儲存空間費用。

    集合寫入具有序列值的文件的寫入速度偏高

    如果您建立索引的欄位會在集合中的文件之間依序增加或減少 (例如時間戳記),則集合的最大寫入速率為每秒 500 次。如果您不根據含有序列值的欄位執行查詢,可以將該欄位排除在索引之外,略過這項限制。

    在寫入速率較高的 IoT 用途中,例如,含有時間戳記欄位的文件集合,可能達到每秒 500 次寫入的上限。

    存留時間欄位

    請注意,如果您使用存留時間 (存留時間) 政策,則「TTL」欄位必須為時間戳記。存留時間欄位的索引功能預設為啟用,可能會在流量費率提高的情況下影響效能。最佳做法是為存留時間欄位新增單一欄位豁免項目。

    大型陣列或對應欄位

    大型陣列或對應欄位可能會達到每份文件 40,000 個索引項目的上限。如果您不是根據大型陣列或對應欄位進行查詢,請讓索引不必進行索引。

    如果您在多個欄位使用範圍和不等式運算子的查詢,請參閱索引考量事項一文,您應考慮考量最佳化 Cloud Firestore 查詢的效能與成本

    如要進一步瞭解如何解決索引問題 (索引擴散問題、INVALID_ARGUMENT 錯誤),請參閱疑難排解頁面