【问题标题】:How to make a rethinkdb atomic update if document exists, insert otherwise?如果文档存在,如何进行 rethinkdb 原子更新,否则插入?
【发布时间】:2014-06-19 12:43:23
【问题描述】:

如果文档存在,如何进行 rethinkdb 原子更新,否则插入?

我想做类似的事情:

var tab = r.db('agflow').table('test');
r.expr([{id: 1, n: 0, x: 11}, {id: 2, n: 0, x: 12}]).forEach(function(row){
  var _id = row('id');
  return r.branch(
    tab.get(_id).eq(null),  // 1
    tab.insert(row),        // 2 
    tab.get(_id).update(function(row2){return {n: row2('n').add(row('n'))}}) // 3
  )})

然而,这并不是完全原子的,因为在我们检查文档是否存在 (1) 和插入它 (2) 之间,其他线程可能会插入它。

如何使这个查询原子化?

【问题讨论】:

    标签: rethinkdb


    【解决方案1】:

    我认为解决方案正在通过

    conflict="update"
    

    到插入方法。

    另见RethinkDB documentation on insert

    【讨论】:

    【解决方案2】:

    要使内部原子化,可以使用replace

    tab.get(_id).replace(function(row2){
      return r.expr({id: _id, n: row2('n').add(row('n'))})
              .default(row)
    })
    

    使用replace 是目前使用非平凡更新操作执行完全原子更新插入的唯一方法。

    【讨论】:

    • 如何更新字段或插入文档?是否可以像计数器、数量字段或 last_visit 时间戳一样以原子方式实际更新字段?我不想用更新的数据创建一个新文档,我想更新一个计数器字段。对于具有计数器 x=1 的用户 A,我希望计数器为 x++ (x=2) 我找到了 inc 指令,但无法进行适当的原子 upsert,类似于 Redis 中的 zincrby 所做的
    【解决方案3】:

    这是一种称为“upsert”的标准数据库操作。

    RethinkDB 是否支持 upserts?

    塞尔吉奥的另一个问题 图连采夫。第一个公开版本不包括支持 upserts,但现在可以通过向插入传递额外的标志来完成:

    r.table('marvel').insert({ 
          superhero: 'Iron Man', 
          superpower: 'Arc Reactor' 
      }, {upsert: true}).run() 
    

    当设置为 true 时,新文档 from insert 将覆盖现有的。

    引用:Answers to common questions about RethinkDB

    【讨论】:

    • 我知道更新。情况是我不想覆盖。我想更新现有文档。
    • 奇怪,它没有实现传统的upsert
    • 我在 node.js 客户端 2.3.2 中使用 upsert 时出错 - ReqlServerCompileError: Unrecognized optional argument upsert in: r.db("myDB").table("mytable").insert ({"userId": something, "lastUpdate": timestamp }, {"upsert": true})
    • Unrecognized optional argument upsert.
    • 这可能已经改变了。
    【解决方案4】:

    好的,我想出了办法。

    解决方法是先尝试insert,如果失败我们就做update

    var tab = r.db('agflow').table('test');
    tab.delete();
    r.expr([{id: 1, n: 0, x: 11}, {id: 2, n: 0, x: 12}]).forEach(function(row){
      var _id = row('id');
      var res = tab.insert(row);
      return r.branch(
        res('inserted').eq(1),
        res,
        tab.get(_id).update(function(row2){return {n: row2('n').add(1)}})
      )})
    

    【讨论】:

    • 你确定这会自动执行吗?
    • 所以您可以使用它来增加/更新字段或插入文档以防二级索引不存在?类似于你可以用 redis.io/commands/zincrby 做什么?假设我有一个用户名和该用户的分数,每次用户单击按钮时,他的分数都会增加 +1,有没有简单的方法可以做到这一点?
    • 这不是原子的。相反,您需要将冲突解决参数传递给插入方法。
    猜你喜欢
    • 2014-02-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-17
    • 2014-10-23
    • 2013-04-19
    相关资源
    最近更新 更多