使用firestore.MergeAll清空Sessions失败,咨询与Merge的差异及解决方案
我有一个使用Firestore存储数据的Go应用,更新文档时,传入数据映射后,使用firestore.MergeAll和firestore.Merge(...)的表现不同。
定义的结构体如下:
type User struct { Tokens Tokens } type Tokens struct { Sessions map[string]Token } type Token struct { Token string Expires int }
需要清空Sessions属性时,用以下代码会触发Firestore错误:firestore: missing WriteResult
dataMap := map[string]interface{}{ "Tokens": map[string]interface{}{ "Sessions": map[string]interface{}{}, }, } _, err = c.ref.Doc(id).Set(ctx, dataMap, firestore.MergeAll) // dataMap结构为: map[Tokens:map[Sessions:map[]]]
但改用firestore.Merge(FieldPath)的方式却能正常执行,Firestore中的Sessions会变为空映射:
dataMap := map[string]interface{}{ "Tokens": map[string]interface{}{ "Sessions": map[string]interface{}{}, }, } _, err = c.ref.Doc(id).Set(ctx, dataMap, firestore.Merge(firevault.FieldPath{"Tokens", "Sessions"})) // dataMap结构为: map[Tokens:map[Sessions:map[]]]
想知道二者的差异是什么?是否可以用firestore.MergeAll实现相同功能,无需每次传入FieldPath?
二者的核心差异
firestore.MergeAll的行为逻辑:
使用MergeAll时,SDK会将传入的嵌套映射解析为要合并的字段路径集合。当你传入原生空映射map[string]interface{}{}作为Sessions的值时,SDK会判定这个空映射代表“没有可合并的字段”,相当于没有提供有效更新内容,Firestore服务端会因无实际写入操作返回firestore: missing WriteResult错误。firestore.Merge(FieldPath)的行为逻辑:
当明确指定要合并的字段路径{"Tokens", "Sessions"}时,SDK会直接将该路径对应的空映射值写入Firestore。此时服务端会将Sessions字段设置为空映射——因为你已经明确指定了要更新的字段路径,哪怕值是空的,这也是一个有效的写入指令,不会被忽略。
用firestore.MergeAll实现相同功能的方法
可以通过将空映射包装为firestore.Map类型,让SDK将其识别为明确的设置值,而非“无字段可合并”的信号。修改后的代码如下:
import "cloud.google.com/go/firestore" dataMap := map[string]interface{}{ "Tokens": map[string]interface{}{ "Sessions": firestore.Map{}, // 使用firestore.Map替代原生空映射 }, } _, err = c.ref.Doc(id).Set(ctx, dataMap, firestore.MergeAll)
这样MergeAll会正确识别Sessions字段需要被设置为空映射,不会触发错误,最终效果和使用Merge(FieldPath)一致。
内容的提问来源于stack exchange,提问作者CentoKili53

