UNPKG

objection

Version:
113 lines (92 loc) 3.16 kB
import _ from 'lodash'; import QueryBuilderOperation from './QueryBuilderOperation'; import ReferenceBuilder from '../ReferenceBuilder'; import jsonFieldExpressionParser from '../parsers/jsonFieldExpressionParser'; import {fromJson, toDatabaseJson} from '../../model/modelFactory'; import {afterReturn} from '../../utils/promiseUtils'; export default class UpdateOperation extends QueryBuilderOperation { constructor(name, opt) { super(name, opt); /** * The update model. * * @type {Model} */ this.model = null; /** * Options for the Model.fromJson call. * * @type {ModelOptions} */ this.modelOptions = Object.assign({}, this.opt.modelOptions || {}); /** * Query properties separated from this.model. * * @type {Map} */ this.queryProps = null; /** * @type {boolean} */ this.isWriteOperation = true; } call(builder, args) { const modelClass = builder.modelClass(); let json = args[0]; if (json instanceof modelClass) { this.model = json; } else if (json) { // Convert into model instance and separate query properties like // query builders, knex raw calls etc. const split = fromJson({ modelOptions: this.modelOptions, modelClass: modelClass, deep: false, json }); this.model = split.model; this.queryProps = split.queryProps; } return true; } onBeforeInternal(builder, result) { const maybePromise = this.model.$beforeUpdate(this.modelOptions, builder.context()); return afterReturn(maybePromise, result); } onBuild(knexBuilder, builder) { // Builder options can contain a queryProps map. Use it // if there isn't a local one. const queryProps = this.queryProps || builder.internalOptions().queryProps; const json = toDatabaseJson({ model: this.model, queryProps }); // convert ref syntax to knex.raw // TODO: jsonb attr update implementation for mysql and sqlite.. const knex = builder.knex(); const loweredJson = {}; _.forOwn(json, (val, key) => { // convert ref values to raw let loweredValue = (val instanceof ReferenceBuilder) ? knex.raw(...(val.toRawArgs())) : val; // convert update to jsonb_set format if attr inside jsonb column is set if (key.indexOf(':') > -1) { // e.g. 'col:attr' : ref('other:lol') is transformed to // "col" : raw(`jsonb_set("col", '{attr}', to_jsonb("other"#>'{lol}'), true)`) let parsed = jsonFieldExpressionParser.parse(key); let jsonRefs = '{' + _(parsed.access).map('ref').value().join(',') + '}'; loweredJson[parsed.columnName] = knex.raw( `jsonb_set(??, '${jsonRefs}', to_jsonb(?), true)`, [parsed.columnName, loweredValue] ); } else { loweredJson[key] = loweredValue; } }); knexBuilder.update(loweredJson); } onAfterInternal(builder, numUpdated) { const maybePromise = this.model.$afterUpdate(this.modelOptions, builder.context()); return afterReturn(maybePromise, numUpdated); } }