如何在RethinkDB中基于旧值原子化更新文档?

问题描述 投票:0回答:1

假设我有一个文档模式如下。

{
  users: [{userid: 123, username: "foo"}, {userid: 234, username: "bar"}]
}

我想添加一个项目到 users 的用户名等于给定用户名的 "唯一化 "版本。例如,如果我尝试添加 {userid: 456, username: "baz"} 到上面的用户列表中,应该可以成功,但如果我尝试将 {userid: 456, username: "foo"} 至此 {userid: 456, username: "foo (1)"} 应该被添加。

在RethinkDB中,有什么方法可以用原子更新来实现吗?这是一个确定性的操作,所以理论上应该是可以的吧?如果没有,有没有什么方法可以让我至少在插入过程中检测到用户名冲突,然后直接拒绝更新?

我知道我可以使用子查询,但似乎结果不会是原子操作?我在文档中看到的所有子查询的例子都是在一个单独的表上显示子查询。

nosql rethinkdb
1个回答
0
投票

根据 一致性保证,你可以结合 get (不是 getAllfilter 虽然)与 update 并有一个原子链。然后,在里面 update,它是 描述 如果你使用子查询或其他非确定性的东西,那么你就不是原子的,必须明确地声明 nonAtomic 标志。

查询中最啰嗦的部分就变成了递增计数的方式,因为你不希望最后出现多个 bar (1).

假设你已经提供了以下信息,那么下面的行为应该是原子式的。

  • 文件的id,这里是 did = '3a297bc8-9fda-4c57-8bcf-510f51158f7f'
  • 用户名,这里 uname = 'bar'
  • 的用户ID,这里 uid = 345
var uname = 'baz';
var did = "3a297bc8-9fda-4c57-8bcf-510f51158f7f";
var uid = 345;
// not sure readMode is necessary here, it's described in consistency guarantees
//  but not shown in the example with get/update/branch
r.db('db').table('table', { readMode: 'majority' })
// use only get to ensure atomicity between get and update
.get(did)
// update will throw if you're not deterministic, i.e. it can't behave atomically
//  here we never address anything but the current document so it's OK
.update(function(doc) {
  // this retrieves the highest index in parentheses if any
  //  put in a var because 1/ we use the result twice 2/ it's kind of verbose...
  var matched = doc('users').map(function(user) {
    // regex is /^bar(?: \(([0-9]+)\))?$/
    //  with the only capturing group on the index itself
    return user('username').match(r.expr('').add('^', uname, '(?: \\(([0-9]+)\\))?$'))
  }).filter(function(match) {
    // remove all user items that didn't match (i.e. they're null)
    return match.typeOf().ne('NULL');
  }).map(function(match) {
    // check whether we are processing 'bar' or 'bar (N)'
    //  (no captured group = no '(N)' = pure 'bar' = set index at zero)
    return r.branch(
      match('groups').filter(function(group) {
        return group.typeOf().ne('NULL');
      }).count().gt(0),
      // wrap in { index } for the following orderBy
      { index: match('groups').nth(0)('str').coerceTo('number') },
      { index: 0 }
    );
  })
  // ensure the first item in the list is the highest index
  .orderBy(r.desc('index'));
  // now we can decide on what to add
  return r.branch(
    // if there were some matches, 'bar' exists already
    //  and we now have the highest index in the list
    matched.count().gt(0),
    // add 'bar' appended with ' (N)', having N = highest index + 1
    {
      users: doc('users').add([{
        userid: uid,
        username: r.expr(uname).add(
          ' (',
          matched.nth(0)('index').add(1).coerceTo('string'),
          ')'
        )
      }])
    },
    // else, just add the user as is
    { users: doc('users').add([{ userid: uid, username: uname }]) }
  );
});

希望对大家有所帮助!

© www.soinside.com 2019 - 2024. All rights reserved.