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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.22 11:45:03