透過管道作業修改資料

使用 update(...) 和 delete(...) 資料操縱語言 (DML) 階段,建構可查詢文件,然後刪除或修改資料的資料管道。

版本需求

本頁所述作業需要 Firestore Enterprise 版。

事前準備

您應熟悉如何透過管道作業查詢資料庫。

更新文件

使用 update(...) DML 階段建構資料管道,以便查詢文件,然後新增或修改資料。

所有 DML 階段都必須位於管道末端。進入這個階段的文件必須包含 __name__ 欄位,以識別要更新的文件。如果嘗試更新的文件不存在,作業就會失敗。

舉例來說,下列作業會將資料模型變更回填至集合群組中的所有文件。管道會將 preferences.color 欄位新增至 users 集合群組中缺少該欄位的所有文件。

Node.js
const snapshot = await db.pipeline()
   .collectionGroup("users")
   .where(not(exists(field("preferences.color"))))
   .addFields(constant(null).as("preferences.color"))
   .removeFields("color")
   .update()
   .execute();
Python
from google.cloud.firestore_v1.pipeline_expressions import Constant, Field, Not

snapshot = (
    client.pipeline()
    .collection_group("users")
    .where(Not(Field.of("preferences.color").exists()))
    .add_fields(Constant.of(None).as_("preferences.color"))
    .remove_fields("color")
    .update()
    .execute()
)
Java
Pipeline.Snapshot snapshot = firestore.pipeline()
   .collectionGroup("users")
   .where(not(exists(field("preferences.color"))))
   .addFields(constant((String) null).as("preferences.color"))
   .removeFields("color")
   .update()
   .execute().get();
Go
snapshot := client.Pipeline().
	CollectionGroup("users").
	Where(firestore.Not(firestore.FieldExists(firestore.FieldOf("preferences.color")))).
	AddFields(firestore.Selectables(
		firestore.ConstantOfNull().As("preferences.color"),
	)).
	RemoveFields(firestore.Fields("color")).
	Update().
	Execute(ctx)

刪除文件

使用 delete(...) DML 階段建構資料管道,以便查詢文件並刪除資料。為避免意外大量刪除,以 delete(...) 結尾的管道應至少包含一個 where(...) 階段。所有 DML 階段都必須位於管道結尾。

舉例來說,下列管道會刪除所有 address.users 設為 USA 且 __create_time__ 小於 10 天的 users 文件:

Node.js
const pipeline = db.pipeline()
  .collectionGroup("users")
  .where(field("address.country").equal("USA"))
  .where(field("__create_time__").timestampAdd("day", 10).lessThan(currentTimestamp()))
  .delete();
await pipeline.execute();
Python
from google.cloud.firestore_v1.pipeline_expressions import CurrentTimestamp, Field

snapshot = (
    client.pipeline()
    .collection_group("users")
    .where(Field.of("address.country").equal("USA"))
    .where(
        Field.of("__create_time__")
        .timestamp_add("day", 10)
        .less_than(CurrentTimestamp())
    )
    .delete()
    .execute()
)
Java
Pipeline.Snapshot deleteResults = firestore.pipeline()
  .collectionGroup("users")
  .where(field("address.country").equal("USA"))
  .where(field("__create_time__").add(constant(10)).lessThan(currentTimestamp()))
  .delete()
  .execute().get();
Go
snapshot := client.Pipeline().
	CollectionGroup("users").
	Where(firestore.FieldOf("address.country").Equal("USA")).
	Where(firestore.FieldOf("__create_time__").Add(firestore.ConstantOf(10)).LessThan(firestore.CurrentTimestamp())).
	Delete().
	Execute(ctx)

執行模式和一致性

DML 階段可在明確交易內或交易外執行:

交易執行

交易式 DML 可在文件之間提供不可分割、全有或全無的一致性,並支援整套管道查詢階段。因此,如果工作負載需要嚴格的一致性保證 (不接受部分更新),或仰賴複雜的查詢階段,只要修改內容符合交易大小限制,就很適合使用這項功能。

在明確交易中執行 DML 管道 (使用 runTransaction/transaction.execute(pipeline) 或 atomic: true 選項) 時,會發生下列行為:

  • 所有異動都會一併提交,或在失敗時完全復原。
  • DML 階段之前支援所有管道查詢階段 (包括 where、add_fields、union、find_nearest 和 aggregate 子查詢)。
  • 在交易中,所有讀取作業都必須在寫入作業之前執行。由於 DML 管道會執行寫入作業,因此在同一筆交易中執行 DML 管道後,您無法執行讀取作業。例如,您無法在單一交易中多次呼叫 DML 管道,也無法在執行 DML 管道後讀取文件。在 DML 管道執行後嘗試執行讀取作業,會失敗並顯示 FAILED_PRECONDITION 錯誤:Cannot have reads after the first write in a Firestore transaction.
  • 這項作業受 Firestore Enterprise 版交易限制。

非交易執行

非交易 DML 專為大量修改大量文件而設計,不會受到交易大小限制。鎖定文件層級而非整個作業,可讓其他並行作業在大量更新文件時繼續進行。

在明確交易以外執行 DML 階段時:

  • 獨立更新和部分成功:每份文件都會獨立更新,且作業不會跨文件進行。管道會在發生第一個錯誤時停止,先前修改的文件仍會寫入資料庫,因此可能會部分成功。
  • 階段支援:
    • 在交易外的 DML 之前,不支援進階和多查詢階段 (例如 aggregate(...)、distinct(...)、unnest(...)、find_nearest(...)、union(...)、join 和子查詢)。如果管道需要這些階段,請使用交易。
  • 安全性規則 20 次查詢限制:每批文件都會在各自的交易中執行,並重新評估安全性規則。如果安全性規則使用 get() 或 exists() 查詢,每個批次都會執行查詢,在寫入約 200 份文件後,就會達到每個要求 20 次查詢的限制,並失敗並顯示 PERMISSION_DENIED。請使用自訂宣告,而非 get() 或 exists() 查詢,以免超出這項限制。

限制

請注意 DML 階段的下列限制:

  • 在呼叫 .execute() 之前,DML 階段必須是管道定義中的最後一個階段。
  • 在交易外執行時,每份文件都會獨立更新,且允許部分成功。
  • 非交易 DML 作業受限於安全規則,其中 get() 或 exists() 查詢上限約為 200 個文件,因為查詢上限為 20 次。
  • 交易 DML 作業受 Firestore Enterprise 版交易限制。Firestore 交易不允許在首次寫入後讀取,因此您無法在單一交易中多次呼叫 DML 管道,也無法在執行 DML 管道後讀取文件。在 DML 寫入失敗後嘗試執行讀取作業,會發生 FAILED_PRECONDITION 錯誤:Cannot have reads after the first write in a Firestore transaction.
  • 如果 DML 階段之前的階段產生多個具有相同 __name__ 的文件,或評估為相同的目的地文件 ID,則會依序處理每個異動:
    • upsert(...) 和 update(...):針對同一份文件進行多項異動時,系統會遵循「最後一項異動優先」語意,也就是說,最後處理的異動會決定文件的最終狀態。
    • delete(...):第一次嘗試會刪除文件,後續對同一文件的嘗試則不會執行任何操作。
    • insert(...):第一次嘗試會建立文件,後續嘗試指定相同文件 ID 時,則會失敗並傳回 ALREADY_EXISTS 錯誤。