You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何Firestore批量操作在arrayRemove()执行失败时仍能完成?

问题:Firestore批量操作中arrayRemove未匹配到对象时为何不触发批量失败?

场景与实现代码

需求是更新Firestore数组中的嵌套对象:通过arrayRemove()移除原对象,再用arrayUnion()添加更新后的对象,将这两个操作放在批量操作中,预期任一操作失败则整个批量操作回滚。

Swift实现代码如下:

let batch = db.batch()
         
let docRef = db.collection("events").document("20220715")
let encodedRemove = try! Firestore.Encoder().encode(originalEvent)
let encodedUpdate = try! Firestore.Encoder().encode(updatedEvent)

batch.updateData(["dayEvent": FieldValue.arrayRemove([encodedRemove])], forDocument: docRef)
batch.updateData(["dayEvent": FieldValue.arrayUnion([encodedUpdate])], forDocument: docRef)

batch.commit() { err in
    if let err = err {
        print("Error writing batch \(err)")
    } else {
        print("Batch write succeeded.")
    }
}

异常现象

正常流程下操作符合预期:移除原对象并添加更新后的对象。但如果本地保存原对象后,服务器上的该对象被修改,此时批量操作依然会显示成功:

  • arrayRemove()因找不到匹配对象,未执行移除动作
  • arrayUnion()成功添加了更新后的对象
    最终数组中会同时存在原对象(已被服务器修改的版本)和更新后的对象,出现数据不一致的问题。

核心疑问

根据Firestore官方文档,批量操作的特性是“要么全部操作成功,要么都不执行”,为何上述场景中批量操作仍能完成?

原因说明

Firestore的arrayRemove()操作本身不会因找不到匹配对象而抛出错误,它只会静默地不执行任何变更。而批量操作的“原子性”仅保证所有合法操作要么全部执行,要么全部不执行——这里的“合法”指操作本身符合Firestore的规则(比如权限合规、数据格式正确、文档存在等),而非操作必须产生预期的变更效果。

因为arrayRemove()找不到匹配对象属于“合法但无变更”的情况,而非操作失败,所以整个批量操作会正常完成,不会触发回滚。

如果要实现“原对象不存在则终止操作”的逻辑,不能使用批量操作,而是需要用事务:在事务中先读取文档,确认原对象的状态符合预期后,再执行移除和添加操作;若读取时发现原对象已被修改,事务会自动重试或抛出错误,确保数据一致性。

事务实现示例:

db.runTransaction({ (transaction, errorPointer) -> Any? in
    let docRef = self.db.collection("events").document("20220715")
    // 读取当前文档数据
    guard let snapshot = try? transaction.getDocument(docRef) else {
        errorPointer?.pointee = NSError(domain: "AppError", code: -1, userInfo: [NSLocalizedDescriptionKey: "读取文档失败"])
        return nil
    }
    // 获取数组数据
    guard var dayEvents = snapshot.data()?["dayEvent"] as? [[String: Any]] else {
        errorPointer?.pointee = NSError(domain: "AppError", code: -2, userInfo: [NSLocalizedDescriptionKey: "未找到dayEvent数组"])
        return nil
    }
    // 编码原对象并检查是否存在
    let encodedRemove = try! Firestore.Encoder().encode(originalEvent)
    guard dayEvents.contains(where: { $0 == encodedRemove }) else {
        errorPointer?.pointee = NSError(domain: "AppError", code: -3, userInfo: [NSLocalizedDescriptionKey: "原对象不存在"])
        return nil
    }
    // 执行移除和添加操作
    dayEvents.removeAll(where: { $0 == encodedRemove })
    let encodedUpdate = try! Firestore.Encoder().encode(updatedEvent)
    dayEvents.append(encodedUpdate)
    transaction.setData(["dayEvent": dayEvents], forDocument: docRef)
    return nil
}) { (_, error) in
    if let error = error {
        print("事务执行失败: \(error)")
    } else {
        print("事务执行成功!")
    }
}

内容的提问来源于stack exchange,提问作者ericboles

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 20:45:55