UNPKG

@ddn/json-sql

Version:

node.js json to sql queries mapper

442 lines (339 loc) 11 kB
'use strict'; var _ = require('underscore'); var scheme = require('./scheme.js'); function removeTopBrackets(condition) { if (condition.length && condition[0] === '(' && condition[condition.length - 1] === ')') { condition = condition.slice(1, condition.length - 1); } return condition; } module.exports = function(dialect) { dialect.blocks.add('distinct', function() { return 'distinct'; }); dialect.blocks.add('field', function(params) { var field = params.field || params.$field; var expression = params.expression || params.$expression; if (!field && !expression) { throw new Error('Neither `field` nor `expression` properties aren\'t set'); } if (field) { field = this.dialect._wrapIdentifier(field); var table = params.table || params.$table; if (table) { field = this.buildBlock('table', {table: table}) + '.' + field; } if (expression) { if (!_.isArray(expression)) expression = [expression]; var exprSuffix = (new Array(expression.length + 1)).join(')'); field = expression.join('(') + '(' + field + exprSuffix; } } else { field = expression; } var cast = params.cast || params.$cast; if (cast) { field = 'cast(' + field + ' as ' + cast + ')'; } var alias = params.alias || params.$alias; if (alias) { field += ' ' + this.buildBlock('alias', {alias: alias}); } return field; }); dialect.blocks.add('fields', function(params) { var self = this; var fields = params.fields || {}; var result = ''; if (!_.isObject(fields) && !_.isString(fields)) { throw new Error('Invalid `fields` property type "' + (typeof fields) + '"'); } if (_.isObject(fields)) { if (_.isEmpty(fields)) { result = '*'; } else { // If fields is array: ['a', {b: 'c'}, {field: '', table: 't', alias: 'r'}] if (_.isArray(fields)) { result = _(fields).map(function(item) { if (_.isObject(item)) { // if field is field object: {field: '', table: 't', alias: 'r'} if (item.field || item.expression) { return self.buildBlock('field', item); // if field is non-field object: {b: 'c'} } else { return self.buildBlock('fields', {fields: item}); } // if field is string: 'a' } else if (_.isString(item)) { return self.buildBlock('field', {field: item}); } }); // If fields is object: {a: 'u', b: {table: 't', alias: 'c'}} } else { // use keys as field names result = _(fields).map(function(item, field) { // b: {table: 't', alias: 'c'} if (_.isObject(item)) { if (!item.field) { item = _.clone(item); item.field = field; } return self.buildBlock('field', item); // a: 'u' } else if (_.isString(item)) { return self.buildBlock('field', { field: field, alias: item }); } return ''; }); } result = result.join(', '); } } else { result = fields; } return result; }); dialect.blocks.add('table', function(params) { return this.dialect._wrapIdentifier(params.table); }); dialect.blocks.add('expression', function(params) { return params.expression; }); dialect.blocks.add('name', function(params) { return this.dialect._wrapIdentifier(params.name); }); dialect.blocks.add('alias', function(params) { var self = this; var result; if (_.isObject(params.alias)) { if (!params.alias.name) { throw new Error('Alias `name` property is required'); } result = this.dialect._wrapIdentifier(params.alias.name); if (_.isArray(params.alias.columns)) { result += '(' + _(params.alias.columns).map(function(column) { return self.dialect._wrapIdentifier(column); }).join(', ') + ')'; } } else { result = this.dialect._wrapIdentifier(params.alias); } return 'as ' + result; }); dialect.blocks.add('condition', function(params) { var condition = params.condition; var result = ''; if (_.isObject(condition)) { result = this.buildCondition({ condition: condition, operator: '$eq' }); } else if (_.isString(condition)) { result = condition; } if (result) { result = 'where ' + removeTopBrackets(result); } return result; }); dialect.blocks.add('modifier', function(params) { var self = this; var modifier = params.modifier; var result = ''; // if modifier is object -> call method for each operator if (_.isObject(modifier)) { result = _(modifier).map(function(values, field) { var modifierFn = self.dialect.modifiers.get(field); var methodParams = values; if (!modifierFn) { modifierFn = self.dialect.modifiers.get(self.dialect.config.defaultModifier); methodParams = {}; methodParams[field] = values; } return modifierFn.call(self, methodParams); }).join(', '); // if modifier is string -> not process it } else if (_.isString(modifier)) { result = modifier; } if (result) { result = 'set ' + result; } return result; }); dialect.blocks.add('join', function(params) { var self = this; var join = params.join; var result = ''; // if join is array -> make each joinItem if (_.isArray(join)) { result = _(join).map(function(joinItem) { return self.buildTemplate('joinItem', joinItem); }).join(' '); // if join is object -> set table name from key and make each joinItem } else if (_.isObject(join)) { result = _(join).map(function(joinItem, table) { if (!joinItem.table && !joinItem.query && !joinItem.select) { joinItem = _.clone(joinItem); joinItem.table = table; } return self.buildTemplate('joinItem', joinItem); }).join(' '); // if join is string -> not process } else if (_.isString(join)) { result = join; } return result; }); dialect.blocks.add('type', function(params) { return params.type.toLowerCase(); }); dialect.blocks.add('on', function(params) { var on = params.on; var result = ''; // `on` block is use `$field` as default compare operator // because it most used case if (_.isObject(on)) { result = this.buildCondition({ condition: on, operator: '$field' }); } else if (_.isString(on)) { result = on; } if (result) { result = 'on ' + removeTopBrackets(result); } return result; }); dialect.blocks.add('group', function(params) { var result = ''; var group = params.group; if (_.isArray(group)) { var self = this; result = _(group).map(function(field) { return self.dialect._wrapIdentifier(field); }).join(', '); } else if (_.isString(group)) { result = this.dialect._wrapIdentifier(group); } else if (_.isObject(group)) { result = group.expression + (group.having ? " having " + this.buildCondition({ condition: group.having }) : ""); } if (result) { result = 'group by ' + result; } return result; }); dialect.blocks.add('sort', function(params) { var result = ''; var sort = params.sort; // if sort is array -> field1, field2, ... var self = this; if (_.isArray(sort)) { result = _(sort).map(function(sortField) { return self.dialect._wrapIdentifier(sortField); }).join(', '); // if sort is object -> field1 asc, field2 desc, ... } else if (_.isObject(sort)) { result = _(sort).map(function(direction, field) { return self.dialect._wrapIdentifier(field) + ' ' + (direction > 0 ? 'asc' : 'desc'); }).join(', '); // if sort is string -> not process } else if (_.isString(sort)) { result = this.dialect._wrapIdentifier(sort); } if (result) { result = 'order by ' + result; } return result; }); dialect.blocks.add('limit', function(params) { return 'limit ' + this._pushValue(params.limit); }); dialect.blocks.add('offset', function(params) { return 'offset ' + this._pushValue(params.offset); }); dialect.blocks.add('or', function(params) { return 'or ' + params.or; }); dialect.blocks.add('values', function(params) { var self = this; var fieldValues = params.values; if (!_.isArray(fieldValues)) { fieldValues = [fieldValues]; } var fields = params.fields || _(fieldValues).chain().map(function(values) { return _(values).keys(); }).flatten().uniq().value(); fieldValues = _(fieldValues).map(function(values) { return _(fields).map(function(field) { return self._pushValue(values[field]); }); }); return this.buildTemplate('insertValues', { fields: fields, fieldValues: fieldValues }); }); dialect.blocks.add('fieldValues', function(params) { return _(params.fieldValues).map(function(values) { return '(' + values.join(', ') + ')'; }).join(', '); }); dialect.blocks.add('queryBody', function(params) { var query = params.queryBody || {}; return this.buildTemplate(query.type || 'select', query); }); dialect.blocks.add('query', function(params) { return this.buildTemplate('subQuery', {queryBody: params.query}); }); dialect.blocks.add('select', function(params) { return this.buildTemplate('subQuery', {queryBody: params.select}); }); dialect.blocks.add('tableFields', function (params) { return scheme.parse.call(this, params.tableFields, params.foreignKeys); }); dialect.blocks.add('queries', function(params) { var self = this; return _(params.queries).map(function(query) { return self.buildTemplate('subQuery', {queryBody: query}); }).join(' ' + params.type + (params.all ? ' all' : '') + ' '); }); dialect.blocks.add('indexOn', function (params) { return params.indexOn; }); dialect.blocks.add('with', function(params) { var self = this; var withList = params['with']; var result = ''; // if with clause is array -> make each withItem if (_.isArray(withList)) { result = _(withList).map(function(withItem) { return self.buildTemplate('withItem', withItem); }).join(', '); // if with clause is object -> set name from key and make each withItem } else if (_.isObject(withList)) { result = _(withList).map(function(withItem, name) { if (!withItem.name) { withItem = _.clone(withItem); withItem.name = name; } return self.buildTemplate('withItem', withItem); }).join(', '); // if with clause is string -> not process } else if (_.isString(withList)) { result = withList; } return 'with ' + result; }); dialect.blocks.add('returning', function(params) { return 'returning ' + this.buildBlock('fields', {fields: params.returning}); }); };