Cloud Firestore 中的索引類型

索引是影響資料庫效能的重要因素。如同書籍索引會將書中主題對應至頁碼,資料庫索引也會將資料庫中的項目對應至資料庫中的位置。查詢資料庫時,資料庫可使用索引快速找出您要求的項目位置。

本頁說明 Cloud Firestore 使用的兩種索引:自動索引手動索引

索引定義和結構

索引的定義是以指定文件的欄位清單為準,每個欄位各有其對應的索引模式

索引定義中指定的每個欄位,索引都會包含一個項目。索引包含所有文件,這些文件可能是以索引為基礎的查詢結果。只有在文件中,索引中使用的每個欄位都設有索引值,該文件才會納入索引。如果索引定義參照的文件欄位未設定任何值,該文件就不會出現在索引中。在這種情況下,根據索引進行的任何查詢都不會傳回該文件。

索引會依欄位值排序,順序則取決於索引定義。

每個查詢背後的索引

如果查詢沒有索引,大多數資料庫會逐一檢索內容,這個過程很緩慢,而且資料庫越大,速度就越慢。Cloud Firestore 會為所有查詢使用索引,確保查詢效能良好。因此查詢效能取決於結果集的大小,而非資料庫中的項目數量。

減少索引管理工作,專注於應用程式開發

Cloud Firestore 內建多項功能,可減少您管理索引所需的時間。系統會自動為您建立最基本查詢所需的索引。使用及測試應用程式時,Cloud Firestore 可協助您找出並建立應用程式所需的其他索引

索引類型

Cloud Firestore 使用兩種索引:自動手動。手動和自動索引的管理方式不同。

自動索引

根據預設,Cloud Firestore 會自動為集合中文件內的所有欄位建立索引。這些單一欄位索引可讓您執行許多基本查詢。您可以設定資料庫的自動索引設定和索引豁免項目,藉此管理自動索引。

自動索引預設值

Cloud Firestore 會使用下列自動索引的預設設定:

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

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

    • 每個非陣列、非對應子欄位各有一個集合範圍的遞增索引。
    • 每個非陣列/非對應子欄位各有一個集合範圍的遞減索引。
    • 整個對應值的一個集合範圍升序索引
    • 整個對應值的單一集合範圍遞減索引
    • 每個陣列子欄位各有一個集合範圍的 array-contains 索引。
    • Cloud Firestore 會以遞迴方式為每個對應子欄位建立索引。
  • 針對文件中的每個陣列欄位,Cloud Firestore 會建立下列項目:

    • 整個陣列值的一個集合範圍遞增索引
    • 整個陣列值的一個集合範圍遞減索引
    • 一個集合範圍的「陣列包含」索引。
  • 系統預設不會維護產品素材資源集合群組範圍的自動索引。

自動索引豁免設定

您可以建立索引豁免設定,將欄位從自動索引設定中排除。索引豁免設定會覆寫整個資料庫的自動索引設定。如果自動索引設定會停用某個索引,您可以透過豁免設定啟用該索引;如果自動索引設定會啟用某個索引,則可透過豁免設定停用該索引。如要瞭解豁免的適用情況,請參閱索引最佳做法

使用 * 欄位路徑值,在產品素材資源集合群組的所有欄位中新增集合層級的索引豁免。舉例來說,如要為產品素材資源集合群組 comments 設定欄位路徑,請將路徑設為 *,以比對 comments 產品素材資源集合群組中的所有欄位,並停用產品素材資源集合群組中所有欄位的索引。然後新增豁免項目,只為查詢所需的欄位建立索引。減少索引欄位數量可降低儲存費用,並提升寫入效能。

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

如要建立及管理自動索引豁免,請參閱「管理索引」。

手動索引

手動索引會儲存集合內所有文件的對應關係,並按照已排序的待建立索引欄位清單整理對應關係。

Cloud Firestore 使用手動索引來支援自動索引尚未支援的查詢。

根據預設,Cloud Firestore 會自動為集合中的每個欄位建立單一欄位索引。由於可能的欄位組合數量龐大,Cloud Firestore 不會自動為欄位組合建立索引。而是協助您在建構應用程式時Cloud Firestore識別及建立必要索引

每當您嘗試執行現有索引不支援的查詢時,Cloud Firestore會傳回錯誤訊息,並附上連結,方便您建立缺少的索引。

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

索引模式和查詢範圍

自動和手動索引的設定方式不同,但兩者都需要為索引設定索引模式和查詢範圍。

索引模式

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

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

查詢範圍

每個索引的範圍都是集合或產品素材資源集合群組。這就是索引的查詢範圍:

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

集合群組範圍
產品素材資源集合群組包含集合 ID 相同的所有集合。如要執行產品素材資源集合群組查詢,從產品素材資源集合群組傳回經過篩選或排序的結果,您必須建立相應的產品素材資源集合群組範圍索引。

預設排序方式和 __name__ 欄位

除了依據為每個欄位指定的索引模式 (遞增或遞減) 排序文件外,索引還會依據每份文件的 __name__ 欄位進行最終排序。__name__ 欄位的值會設為完整的文件路徑。也就是說,結果集中的文件若有相同的欄位值,會依文件路徑排序。

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

集合 已建立索引的欄位 查詢範圍
城市 name, __name__ 集合
城市 state, __name__ 集合
城市 國家/地區、 人口、 __name__ 集合

如要依非預設的 __name__ 方向排序結果,您需要建立該索引。

索引屬性

可讓查詢以最高效率執行的索引是由下列屬性定義:

  • 等式篩選器使用的欄位
  • 排序順序使用的欄位
  • 範圍和不等式篩選器中使用的欄位 (尚未納入排序順序)
  • 用於彙整的欄位 (不包含已納入排序順序、範圍和不等式篩選器的欄位)

Cloud Firestore 會依下列方式計算查詢結果:

  1. 找出查詢的集合、篩選屬性、篩選運算子和排序順序所對應的索引。
  2. 識別掃描開始的索引位置。起始位置會加上查詢的等式篩選條件,並以第一個 orderBy 欄位的範圍和不等式篩選條件結尾。
  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 單一欄位索引。下表中的每一列都代表單一欄位索引中的項目:

集合 已建立索引的欄位 查詢範圍
城市 名稱 集合
城市 個州 集合
城市 個國家/地區 集合
城市 capital 集合
城市 人口 集合
城市 個區域 集合
城市 名稱 集合
城市 個州 集合
城市 個國家/地區 集合
城市 capital 集合
城市 人口 集合
城市 個區域 集合
城市 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"])

手動索引支援的查詢

建立手動索引,支援自動單一欄位索引尚未支援的複合查詢。舉例來說,下列查詢需要手動建立索引:

網頁
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")
集合 已建立索引的欄位 查詢範圍
城市 國家/地區、人口 集合
城市 country, population 集合

為避免索引合併導致效能降低,建議您建立索引,將 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 標記、 (或 ) 大寫 集合

產品素材資源集合群組索引支援的查詢

如要示範具有產品素材資源集合群組範圍的索引,請將 landmarks 子集合新增至部分 city 文件:

網頁
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"]

自動索引

  • city_name ASC
  • city_name DESC
  • 鄰里社區 ASC
  • 鄰近地區 DESC
  • temperatures ASC
  • temperatures DESC
  • temperatures.summer ASC
  • temperatures.summer DESC
  • temperatures.winter ASC
  • temperatures.winter DESC
  • neighborhoods 陣列包含

手動索引

  • city_name ASC, neighborhoods ARRAY
  • city_name DESC, neighborhoods ARRAY

索引項目

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

索引 已編入索引的資料
自動建立索引項目
city_name ASC city_name: "San Francisco"
city_name DESC city_name: "San Francisco"
鄰里社區 ASC neighborhoods: ["Mission", "Downtown", "Marina"]
鄰近地區 DESC neighborhoods: ["Mission", "Downtown", "Marina"]
temperatures ASC temperatures: {summer: 67, winter: 55}
temperatures DESC temperatures: {summer: 67, winter: 55}
temperatures.summer ASC temperatures.summer: 67
temperatures.summer DESC temperatures.summer: 67
temperatures.winter ASC temperatures.winter: 55
temperatures.winter DESC temperatures.winter: 55
neighborhoods 陣列包含 鄰近地區:「Mission」
neighborhoods 陣列包含 neighborhoods: "Downtown"
neighborhoods 陣列包含 neighborhoods: "Marina"
手動索引項目
city_name ASC, neighborhoods ARRAY city_name: "San Francisco", neighborhoods: "Mission"
city_name ASC, neighborhoods ARRAY city_name: "San Francisco", neighborhoods: "Downtown"
city_name ASC, neighborhoods ARRAY city_name: "San Francisco", neighborhoods: "Marina"
city_name DESC, neighborhoods ARRAY city_name: "San Francisco", neighborhoods: "Mission"
city_name DESC, neighborhoods ARRAY city_name: "San Francisco", neighborhoods: "Downtown"
city_name DESC, neighborhoods ARRAY city_name: "San Francisco", neighborhoods: "Marina"

索引和價格

索引會增加應用程式的儲存空間費用。如要進一步瞭解如何計算索引的儲存空間大小,請參閱「索引項目大小」一節。

使用索引合併

雖然 Cloud Firestore 會對每個查詢使用索引,但並非每個查詢都需要一個索引。對於具有多個等式 (==) 子句和 (選擇性) orderBy 子句的查詢,Cloud Firestore 可以重複使用現有索引。Cloud Firestore 可以合併簡單等式篩選器的索引,建立較大型等式查詢所需的索引。

找出可使用索引合併的情況,即可降低索引費用。舉例來說,在餐廳評分應用程式的 restaurants 集合中:

  • 間餐廳

    • burgerthyme

      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")

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

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

更好的解決方法是利用 Cloud Firestore 合併等值子句索引的功能,減少索引數量:

集合 已建立索引的欄位 查詢範圍
餐廳 類別、星等 集合
餐廳 個城市、 星級評等 集合
餐廳 editors_pick、 star_rating 集合

這組索引不僅較小,也支援額外查詢:

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

索引限制

索引有下列限制。如要進一步瞭解配額和限制,請參閱「配額和限制」。

限制 說明
資料庫的複合式索引數量上限
資料庫的單一欄位設定數量上限
  • 如果尚未為 Google Cloud 專案啟用計費功能,系統會傳回 200。

    如需更多配額,請Google Cloud 專案啟用計費功能

  • 啟用 Google Cloud 專案的計費功能時,系統會提供 1,000 美元的抵免額。

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

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

40,000 個

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

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

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

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

7.5 KiB

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

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

8 MiB

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

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

    1500 個位元組

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

    索引最佳做法

    對於大多數應用程式,您可以依賴自動建立索引功能,並透過錯誤訊息連結管理索引。不過,在下列情況中,您可能需要新增自動索引豁免:

    案件 說明
    大型字串欄位

    如果字串欄位經常保存您不常用於查詢的長字串值,您可以免除該欄位的索引,藉此降低儲存空間費用。

    對含有序列值的文件集合進行高頻率寫入

    如果您為集合中文件之間依序遞增或遞減的欄位建立索引 (例如時間戳記),則該集合的寫入頻率上限為每秒 500 次寫入。如果不是根據具有連續值的欄位查詢,可以將該欄位從索引中排除,藉此略過這項限制。

    舉例來說,在寫入頻率高的 IoT 用途中,如果集合包含具有時間戳記欄位的文件,可能會接近每秒 500 次寫入的上限。

    存留時間欄位

    如果您使用 TTL (存留時間) 政策,請注意 TTL 欄位必須是時間戳記。系統預設會對存留時間欄位建立索引,但這可能會在高流量時影響效能。最佳做法是為 TTL 欄位新增自動索引豁免項目。

    大型陣列或對應欄位

    大型陣列或對應欄位可能會接近每個文件 40,000 個索引項目的上限。如果不是根據大型陣列或對應欄位查詢,則應將其排除在索引之外。

    如果您在多個欄位使用範圍和不等於運算子查詢,請參閱索引編製注意事項,瞭解如何提升 Cloud Firestore 查詢的效能並降低費用

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