const { ref } = require('../../queryBuilder/ReferenceBuilder'); const { afterReturn } = require('../../utils/promiseUtils'); const { isKnexRaw, isKnexQueryBuilder } = require('../../utils/knexUtils'); const QueryBuilderOperation = require('./QueryBuilderOperation'); class UpdateOperation extends QueryBuilderOperation { constructor(name, opt) { super(name, opt); this.model = null; this.modelOptions = Object.assign({}, this.opt.modelOptions || {}); } onAdd(builder, args) { const json = args[0]; const modelClass = builder.modelClass(); this.model = modelClass.ensureModel(json, this.modelOptions); return true; } onBefore2(builder, result) { const maybePromise = this.model.$beforeUpdate(this.modelOptions, builder.context()); return afterReturn(maybePromise, result); } onBuildKnex(knexBuilder, builder) { const json = this.model.$toDatabaseJson(builder.knex()); const convertedJson = this.convertFieldExpressionsToRaw(builder, json); knexBuilder.update(convertedJson); } onAfter2(builder, numUpdated) { const maybePromise = this.model.$afterUpdate(this.modelOptions, builder.context()); return afterReturn(maybePromise, numUpdated); } convertFieldExpressionsToRaw(builder, json) { const knex = builder.knex(); const convertedJson = {}; const keys = Object.keys(json); for (let i = 0, l = keys.length; i < l; ++i) { let key = keys[i]; let val = json[key]; if (key.indexOf(':') > -1) { // 'col:attr' : ref('other:lol') is transformed to // "col" : raw(`jsonb_set("col", '{attr}', to_jsonb("other"#>'{lol}'), true)`) let parsed = ref(key); let jsonRefs = '{' + parsed.reference.access.map(it => it.ref).join(',') + '}'; let valuePlaceholder = '?'; if (isKnexQueryBuilder(val) || isKnexRaw(val)) { valuePlaceholder = 'to_jsonb(?)'; } else { val = JSON.stringify(val); } convertedJson[parsed.column] = knex.raw( `jsonb_set(??, '${jsonRefs}', ${valuePlaceholder}, true)`, [convertedJson[parsed.column] || parsed.column, val] ); } else { convertedJson[key] = val; } } return convertedJson; } } module.exports = UpdateOperation;