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

GraphQL如何实现新分数更高时才执行排行榜条目条件upsert?

结论

这个逻辑完全可以实现,你当前写的原生upsert满足不了需求——现有代码只要匹配到对应id的条目,就会无条件覆盖score字段,根本不会判断新分数和历史分数的高低。

推荐实现方案(无竞态问题)

不要用单次upsert逻辑,GraphCMS本身支持数值比较类的过滤运算符,你可以把更新和创建逻辑放在同一个mutation里提交,所有判断都在服务端执行,从根源避免并发覆盖问题:

mutation submitRankEntry($id: String!, $newScore: Int!) {
  # 仅当条目存在、且历史分数低于新分数时,才执行更新
  updateManyRankentries(
    where: { id: $id, score_lt: $newScore }
    data: { score: $newScore }
  ) {
    count
  }
  # 仅当条目不存在时,创建新条目
  createRankentry(data: { id: $id, score: $newScore }) {
    id
  }
}

对应的前端fetch逻辑里,只需要忽略「id唯一约束冲突」的报错即可——触发这个报错只有一种场景:对应id的条目已经存在,且历史分数大于等于你提交的新分数,这时候更新操作匹配不到条目(count返回0),创建操作因为主键重复失败,刚好符合「新分数不更高就不改动数据」的要求。
参考JS调用代码:

const submitScore = async (entryId, score) => {
  const res = await fetch('你的GraphCMS接口地址', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer 你的API鉴权token',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      query: `
        mutation submitRankEntry($id: String!, $newScore: Int!) {
          updateManyRankentries(
            where: { id: $id, score_lt: $newScore }
            data: { score: $newScore }
          ) { count }
          createRankentry(data: { id: $id, score: $newScore }) { id }
        }
      `,
      variables: { id: entryId, newScore: score }
    })
  })

  const result = await res.json()
  // 过滤掉主键重复的预期内错误,其余错误正常抛出
  if (result.errors?.length && !result.errors.every(err => err.message.includes('Unique constraint'))) {
    throw new Error('分数提交失败')
  }
  return result
}
不推荐方案:前端先查后改

你也可以先发起一次query查询对应id的历史分数,判断新分数更高之后再调用upsert提交:

query getEntryScore($id: String!) {
  rankentry(where: {id: $id}) {
    score
  }
}

但这个方案存在竞态漏洞:如果查询完成后、提交更新前,有其他用户提交了更高的分数,你的请求依然会把更高的分数覆盖掉,只适合并发量极低的测试场景,正式环境不要用。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 09:15:36