UNPKG

@wmfs/pg-model

Version:

Takes a relational database structure and returns model objects for noSQL-like abilities.

161 lines (131 loc) 5.16 kB
'use strict' const _ = require('lodash') const getPreStatementHookFunction = require('./get-pre-statement-hook-function') function generateValuePlaceholders (l) { const parts = [] for (let i = 1; i <= l; i++) { parts.push('$' + i) } return parts.join(',') } class Creator { constructor (model) { this.model = model this.fullTableName = model.fullTableName this.prefix = 'INSERT INTO ' + this.fullTableName + ' AS a' this.primaryKeyColumns = model.pkColumnNames this.returning = ` RETURNING ${this.primaryKeyColumns.join(', ')};` } makeStatements (jsonData, options, preStatementHook) { const script = [] if (Array.isArray(jsonData)) { jsonData.forEach(jD => this.addInsertStatementToScript(script, jD, options, preStatementHook)) } else { this.addInsertStatementToScript(script, jsonData, options, preStatementHook) } return script } // addStatements addInsertStatementToScript (script, doc, options, preStatementHook) { const parsedDoc = this.model.parseDoc(doc, { includeNullFks: true }) const statement = this.insertStatement(parsedDoc) + this.upsertStatement(parsedDoc, options) + this.returningStatement() const scriptEntry = { sql: statement, params: parsedDoc.keyAndAttributeValues, columnNames: parsedDoc.keyAndAttributeColumns, postStatementHook: this.postCreateStatementHook.bind(this) } if (preStatementHook) { scriptEntry.preStatementHook = preStatementHook.bind(this) } script.push(scriptEntry) for (const propertyId of Object.keys(parsedDoc.subDocs)) { const subModel = this.model.subModels[propertyId] const subScript = subModel.model.creator.makeStatements( doc[propertyId], options, getPreStatementHookFunction(this.fullTableName, subModel.columnJoin) ) script.push(...subScript) } } // addInsertStatementsToScript insertStatement (parsedDoc) { this.pushCreatedBy(parsedDoc) return `${this.prefix} (${parsedDoc.keyAndAttributeColumns.join(',')}) VALUES (${generateValuePlaceholders(parsedDoc.keyAndAttributeColumns.length)})` } // insertStatement upsertStatement (parsedDoc, options) { if (!(Object.prototype.hasOwnProperty.call(options, 'upsert') && options.upsert)) { return '' } const updateColumns = [] const updatePlaceholders = [] const whereParts = [] let i = -1 for (const columnName of parsedDoc.keyAndAttributeColumns) { i++ if (this.model.createdByField === columnName) { continue } if (this.primaryKeyColumns.indexOf(columnName) === -1) { updateColumns.push(columnName) updatePlaceholders.push('$' + (i + 1)) } else { whereParts.push('a.' + columnName + '=$' + (parsedDoc.keyAndAttributeColumns.indexOf(columnName) + 1)) } } // for ... if (updateColumns.length > 0 && whereParts.length > 0) { if (this.model.modifiedByField) { updateColumns.push(this.model.modifiedByField) parsedDoc.keyAndAttributeValues.push(this.model.currentUserFn()) updatePlaceholders.push('$' + (parsedDoc.keyAndAttributeValues.length)) } if (this.model.modifiedField) { updateColumns.push(this.model.modifiedField) parsedDoc.keyAndAttributeValues.push(new Date()) updatePlaceholders.push('$' + (parsedDoc.keyAndAttributeValues.length)) } } const setMissingPropertiesToNull = (Object.prototype.hasOwnProperty.call(options, 'setMissingPropertiesToNull')) ? options.setMissingPropertiesToNull : true if (setMissingPropertiesToNull) { parsedDoc.missingAttributeColumnNames.forEach(column => { updateColumns.push(column) updatePlaceholders.push('null') }) } const conflictClause = (updateColumns.length > 0 && whereParts.length > 0) ? ` ON CONFLICT (${this.primaryKeyColumns.join(', ')}) DO UPDATE SET (${updateColumns.join(',')})=(${updatePlaceholders.join(',')}) WHERE ${whereParts.join(' AND ')}` : ' ON CONFLICT DO NOTHING' return conflictClause } // upsertStatement returningStatement () { return this.returning } // returningStatement pushCreatedBy (parsedDoc) { if (this.model.createdByField) { parsedDoc.keyAndAttributeColumns.push(this.model.createdByField) parsedDoc.keyAndAttributeValues.push(this.model.currentUserFn()) } } // pushCreatedBy postCreateStatementHook (result, ctx) { if (!Object.prototype.hasOwnProperty.call(ctx, 'returnValue')) { const returnValue = {} if (result.rows.length > 0) { for (const [columnName, value] of Object.entries(result.rows[0])) { returnValue[_.camelCase(columnName)] = value } } ctx.returnValue = { idProperties: returnValue } } if (!Object.prototype.hasOwnProperty.call(ctx, 'lastCreatedPrimaryKey')) { ctx.lastCreatedPrimaryKey = {} } ctx.lastCreatedPrimaryKey[this.fullTableName] = result.rows[0] } // postCreateStatementHook } // class Creator module.exports = Creator