Firebase Firestore 如何检查用户ticketSale对象数组中是否存在指定uidTicket
为什么原有arrayUnion方法无法适配当前场景
FieldValue.arrayUnion()的判重规则是全对象匹配:当数组元素是对象类型时,只有待加入的对象和数组内已存在的某一项的所有键值完全一致,才会被判定为重复、不执行写入。
它完全不支持“按对象内某个指定字段(比如这里的uidTicket)匹配判重”的逻辑——哪怕两个对象的uidTicket完全相同,只要use字段的取值有差异,arrayUnion就会把它当成两个不同的元素直接新增,没法满足需求。
方案一:本地读取校验+全量数组回写
这是最贴合现有数据结构的实现方式,流程非常直接:
- 拉取当前用户的文档,读取全量
ticketSale数组 - 本地遍历数组,判断是否存在
item.uidTicket === 目标待添加ticket UID的记录 - 分支处理:如果已存在就直接返回提示;如果不存在,把新的票务对象拼接到数组末尾,将更新后的完整数组写回数据库
对应实现代码:
const targetTicketUid = "abcxyz" // 待添加的目标ticket UID const userRef = firebase.firestore().collection("user").doc(Fire.shared.uid) // 读取用户文档 const userSnap = await userRef.get() const existTicketList = userSnap.data()?.ticketSale || [] // 本地校验是否重复 const hasAdded = existTicketList.some(item => item.uidTicket === targetTicketUid) if (hasAdded) { alert("该票务已添加到你的列表") return } // 构造新数组并写回 const newTicketItem = { uidTicket: targetTicketUid, use: false } const updatedList = [...existTicketList, newTicketItem] await userRef.update({ ticketSale: updatedList }) alert("票务添加成功")
方案二:调整存储结构为Map键值结构(适合大数组场景)
如果后续用户的ticketSale数组规模会到上百甚至上千条,每次拉取全量数组做遍历的性能会随数组长度上升,这时候可以调整存储结构规避这个问题:
- 把原来的数组结构,改成以
uidTicket为键的Map结构,调整后的文档结构示例:User/{uid} ticketSale: { "abcxyz": { use: true }, "defghi": { use: false } } - 这种结构下,判断某个UID是否存在不需要遍历全量内容,直接读取对应键的取值即可;写入时可以用
merge模式做增量更新,不会覆盖其他已存在的票务记录 - 对应写入代码:
await userRef.set({ ticketSale: { [targetTicketUid]: { use: false } } }, { merge: true })
注意:如果存在高并发提交的场景,仅靠客户端校验会出现竞态问题(多个端同时提交同一个票务的添加请求,都完成“不存在”的校验后同时写入,最终出现重复记录)。这种场景需要配合Firestore安全规则,在服务端层面增加uidTicket的唯一性校验,从链路层面阻断重复写入。
内容的提问来源于stack exchange,提问作者Thắng Trương Khắc
相关产品推荐
相关产品推荐

