在嵌套字段上应用delta值

时间:2017-11-18 13:34:52

标签: rethinkdb rethinkdb-javascript

假设我有这样的记录:

{
    id: 1,
    statistics: {
        stat1: 1,
        global: {
            stat2: 3
        },
        stat111: 99
    }
}

我想用对象进行记录更新:

{
    statistics: {
        stat1: 8,
        global: {
            stat2: 6
        },
        stat4: 3
    }
}

它应该作为delta添加到当前记录中。因此,结果记录应如下所示:

{
    id: 1,
    statistics: {
        stat1: 9,
        global: {
            stat2: 9
        },
        stat4: 3,
        stat111: 99
    }
}

是否可以通过一个查询进行此操作?

1 个答案:

答案 0 :(得分:1)

你想要一些通用的或特定的东西吗? 具体很容易,这是一般情况:

const updateValExpr = r.expr(updateVal);
const updateStats = (stats, val) => val
    .keys()
    .map(key => r.branch(
        stats.hasFields(key), 
        [key, stats(key).add(val(key))], 
        [key, val(key)]
    ))
    .coerceTo('object')

r.table(...)
 .update(stats => 
      updateStats(stats.without('global'), updateValExpr.without('global'))
          .merge({ global: updateStats(stats('global'), updateValExpr('global'))
 )

这里可能存在一些错误,因为它未经测试,但解决方案的关键点是updateStats函数,事实上您可以使用.keys()获取所有密钥并且coerceTo('object')转换为此数组:[['a',1],['b',2]]到此对象:{ a: 1, b: 2 }

编辑: 你可以递归地执行它,虽然堆栈有限(因为你不能直接发送递归堆栈,它们会在查询实际构建时解析:

function updateStats(stats, val, stack = 10) { 
    return stack === 0 
        ? {} 
        : val
           .keys()
           .map(key => r.branch(
                stats.hasFields(key).not(), 
                [key, val(key)],
                stats(key).typeOf().eq('OBJECT'),
                [key, updateStats(stats(key), val(key), stack - 1)], 
                [key, stats(key).add(val(key))]
            )).coerceTo('object')
}

r.table(...).update(row => updateStats(row, r(updateVal)).run(conn)

// test in admin panel
updateStats(r({
    id: 1,
    statistics: {
        stat1: 1,
        global: {
            stat2: 3
        },
        stat111: 99
    }
}), r({
    statistics: {
        stat1: 8,
        global: {
            stat2: 6
        },
        stat4: 3
    }
}))