Search code examples
rethinkdbrethinkdb-javascript

Proper Upsert (Atomic Update Counter Field or Insert Document) with RethinkDB


After looking at some SO questions and issues on RethinkDB github, I failed to come to a clear conclusion if atomic Upsert is possible?

Essentially I would like to perform the same operation as ZINCRBY using Redis.

If member does not exist in the sorted set, it is added with increment as its score (as if its previous score was 0.0). If key does not exist, a new sorted set with the specified member as its sole member is created.

The current implementation appears to differ from almost all databases that I have used. With the data being replaced or inserted not updated. This is a simple use case, like update the last visit, update the number of clicks, update a product quantity. So I must be missing something very obvious, because I cannot see a simple way to do this.


Solution

  • Yes, it is possible. After get on the key, perform an atomic replace. Something like this might work:

    function set_or_increment_score(player, points){
      return r.table('scores').get(player).replace(
        row =>
          { id: player,
            score: r.branch(
                     row.eq(null),
                     points,
                     row('score').add(points))
          });
    }
    

    It has the following behaviour:

    > set_or_increment_score("alice", 1).run(conn)
    { inserted: 1 }
    > set_or_increment_score("alice", 2).run(conn)
    { replaced: 1 }
    

    It works because get returns null when the document doesn't exist, and a replace on a non-existing document tuns into an insert. See the documentation for replace