使用 update(...) 和 delete(...) 数据操纵语言 (DML) 阶段构建可查询文档然后删除或修改数据的数据流水线。
版本要求
本页介绍的操作需要 Firestore 企业版。
准备工作
您应该熟悉如何使用流水线操作查询数据库。
更新文档
使用 update(...) DML 阶段构建可查询文档并添加或修改数据的数据流水线。
所有 DML 阶段都必须位于流水线的末尾。
进入此阶段的文档必须包含 __name__ 字段,以标识要更新的文档。如果您尝试更新的任何文档不存在,则操作会失败。
例如,以下操作会将数据模型更改回填到集合组中的所有文档。流水线会向 users 集合组中缺少 preferences.color 字段的所有文档添加该字段。
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 企业版事务限制的约束。
非事务性执行
非事务性 DML 专为跨大量文档进行批量修改而设计,不会遇到事务大小限制。通过在文档级(而非整个操作)锁定,它允许其他并发操作在批量更新文档时继续进行。
当 DML 阶段在显式事务之外运行时:
- 独立更新和部分成功:每个文档都是独立更新的,并且操作在文档之间不具有原子性。流水线会在出现第一个错误时停止,并且之前修改的文档仍会写入数据库,这可能会导致部分成功。
- 阶段支持:
- 在事务之外的 DML 之前,不支持高级和多查询阶段(例如
aggregate(...)、distinct(...)、unnest(...)、find_nearest(...)、union(...)、联接和子查询)。如果您的流水线需要这些阶段,请使用事务。
- 在事务之外的 DML 之前,不支持高级和多查询阶段(例如
- 安全规则 20 次查找限制:每批文档都会在自己的事务中执行,并重新评估安全规则。如果您的安全规则使用
get()或exists()查找,则每个批次都会执行一次查找,在写入大约 200 个文档后,就会达到每个请求 20 次查找的上限,并失败并显示PERMISSION_DENIED。请使用自定义声明(而非get()或exists()查找)来避免此限制。
限制
请注意 DML 阶段的以下限制:
- 在调用
.execute()之前,DML 阶段必须是流水线定义中的最后一个阶段。 - 在事务之外运行时,每个文档都会独立于其他文档进行更新,并且允许部分成功。
- 由于存在 20 次查找限制,受具有
get()或exists()查找的安全规则约束的非事务性 DML 操作最多只能处理大约 200 个文档。 - 事务性 DML 操作受 Firestore 企业版事务限制的约束。由于 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错误。
- 对于