Применить дельта-значения к вложенным полям

Предположим, у меня есть такая запись:

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

Я хочу сделать обновление по записи с объектом:

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

И это должно быть добавлено к текущей записи как дельта. Итак, запись результата должна выглядеть так:

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

Можно ли сделать это одним запросом?

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
    }
}))
Другие вопросы по тегам