Как сделать атомное обновление rethinkdb, если документ существует, вставить иначе?

Как сделать атомное обновление 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), какой-либо другой поток может вставить его.

Как сделать этот запрос атомарным?

Ответ 1

Хорошо, я понял, как это сделать.

Решение состоит, во-первых, попробовать 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)}})
  )})

Ответ 3

Чтобы сделать внутреннюю часть атомой, вы можете использовать replace:

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

Использование replace - единственный способ в настоящий момент выполнить полностью атомные upserts с нетривиальными операциями обновления.

Ответ 4

Это стандартная операция базы данных, называемая "upsert".

Поддерживает ли RethinkDB поддержку?

Еще один вопрос от Серхио Tulentsev. Первый публичный выпуск не включал поддержку upserts, но теперь это можно сделать, передав дополнительный флаг для вставки:

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

Если установлено значение true, новый документ из вставки будет перезаписывать существующий.

cite: Ответы на распространенные вопросы о RethinkDB