透過管道作業修改資料
使用 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 和子查詢)。如果管道需要這些階段,請使用交易。
- 在交易外的 DML 之前,不支援進階和多查詢階段 (例如
- 安全性規則 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錯誤。