objection
Version:
An SQL-friendly ORM for Node.js
113 lines (92 loc) • 3.16 kB
JavaScript
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);
}
}