rutile
Version:
Factory automation for Mobile Enterprise.
378 lines (335 loc) • 10.6 kB
JavaScript
// {{entity}}SQLMaker
// usage:
//
// * search logic
//
// (request)
//
// context.request = {
// "SegmentA/EntityA.search" : {
// constraint : {
// "segment/entity.fieldA(key)" : { values : [valueA,valueB], logic : 'OR' },
// "segment/entity.fieldB(num)" : { min : min_value, max : max_value, limit : max }
// },
// logic : 'AND',
// limit : max_to_be_selected,
// orderby : {
// field : 'desc', // | asc
// },
// scope : {
// constraint : {
// "segment/entity.fieldC(key)" : { values : [shouldbe1,shouldbe2], logic : 'AND' }
// },
// logic : 'AND'
// }
// },
// "SegmentB/EntityB.search" : {
// constraint : {
// "segment/entity.fieldA(key)" : { values : [valueA] },
// "segment/entity.fieldC(timestamp)" : { from : 'ISO8601' to : 'ISO8601' }
// }
// }
// };
//
// (response)
//
// context.response = {
// "Segment/EntityA.search" : [
// instance1, instance2,
// ],
// "Segment/EntityB.search" : [
// instance1, instance2,
// ]
// };
//
// idiom:
//
// var EntitySQLMaker = require('Segment/EntitySQLMaker');
// var entity_sqlmaker = new EntitySQLMaker();
//
// entity_sqlmaker.setup({
// constarint : {
// "segment/entity.entityID(key)" : { values : [53243,34948], logic : 'OR' },
// "segment/entity.entityName(like)" : { values : ["Apple","Orange"], logic : 'AND' },
// "segment/entity.numbers(num)" : { min : 100, max : 200 },
// },
// logic : 'AND',
// orderby : {
// numbers : 'desc'
// }
// });
//
// var constraint = entity_sqlmaker.makeConstraint();
// var sql = constraint.sql;
// var params = constraint.params;
//
//
var {{APP_NAME}}Impl = require('{{APP_NAME}}Impl');
var SelectbyImplFactory = {{APP_NAME}}Impl.getSelectbyImplFactory();
var OrderbyImplFactory = {{APP_NAME}}Impl.getOrderbyImplFactory();
// static variables
var UNION = ' union ';
var INTERSECT = ' intersect ';
// manifest
var Manifest = require('./{{entity}}Manifest');
var SelectbyManifest = Manifest["selectby"];
var OrderbyManifest = Manifest["orderby"];
// * * * * * * * * * * * * * * * * * * * *
// selectby modules
var selectby_modules = {};
var getSelectbyModule = function(entity_field_type){
if( selectby_modules[entity_field_type] ){
return selectby_modules[entity_field_type];
}
var impl = SelectbyImplFactory.getImplementation('{{segment}}/{{entity}}',entity_field_type);
if( impl ){
selectby_modules[entity_field_type] = impl;
return impl;
}
var module = SelectbyManifest[entity_field_type];
if( module ){
selectby_modules[entity_field_type] = require(module);
return selectby_modules[entity_field_type];
}else{
return false;
}
};
// orderby modules
var orderby_modules = {};
var getOrderbyModule = function(field){
if( orderby_modules[field] ){
return orderby_modules[field];
}
var impl = OrderbyImplFactory.getImplementation('{{segment}}/{{entity}}',field);
if( impl ){
orderby_modules[field] = impl;
return impl;
}
var module = OrderbyManifest[field];
if( module ){
orderby_modules[field] = require(module);
return orderby_modules[field];
}else{
return false;
}
};
// limiter
var getLimitModule = function(){
var module = Manifest["limit"];
if( module ){
this.limiter = require(module);
return this.limiter;
}else{
return false;
}
};
// * * * * * * * * * * * * * * * * * * * *
// cunstructor
function Constructor(){
this.user_constraints = [];
this.orderbys = [];
this.has_orderby = false;
this.scope_constraints = [];
this.user_logic = '';
this.scope_logic = '';
this.limit = ''; // max to select
this.limiter = ''; // its object expression (without orderby wrapper)
this.parameterizable = true;
}
module.exports = Constructor;
var SQLMaker = Constructor.prototype;
// * * * * * * * * * * * * * * * * * * * *
SQLMaker.setup = function(query)
{
// limiter
if( query.limit ){
this.limit = query.limit;
var limiter = getLimitModule();
this.limiter = new limiter(this.limit);
}
// selectby
if( query.constraint ){
var raw_constraints = Object.keys(query.constraint);
for( var i=0; i<raw_constraints.length; i++ ){
var entity_field_type = raw_constraints[i];
var SelectbyModule = getSelectbyModule(entity_field_type);
var selectby = new SelectbyModule(query.constraint[entity_field_type]);
if( selectby.responsible() ){
this.user_constraints.push(selectby);
}
}
}
// user constraint logic
if( query.logic && query.logic.toLowerCase() == 'or' ){
this.user_logic = UNION;
}else{
this.user_logic = INTERSECT;
}
// orderby
if( query.orderby ){
var raw_orderbys = Object.keys(query.orderby);
for( var i=0; i<raw_orderbys.length; i++ ){
var field = raw_orderbys[i];
var modulename = field;
var orderby = '';
if( this.limit ){
modulename = modulename + 'WithLimit';
var OrderbyModule = getOrderbyModule(modulename);
orderby = new OrderbyModule(query.orderby[field],this.limit);
}else{
var OrderbyModule = getOrderbyModule(modulename);
orderby = new OrderbyModule(query.orderby[field]);
}
if( orderby.responsible() ){
this.orderbys.push(orderby);
}
}
this.has_orderby = true;
}
// scope
if( query.scope ){
var scopes = Object.keys(query.scope.constraint);
for( var i=0; i<scopes.length; i++ ){
var field = scopes[i];
var SelectbyModule = getSelectbyModule(entity_field_type);
var selectby = new SelectbyModule(query.scope[entity_field_type]);
if( selectby.responsible() ){
this.scope_constraints.push(selectby);
}
}
// scope constraint logic
if( query.scope.logic && query.scope.logic.toLowerCase() == 'or' ){
this.scope_logic = UNION;
}else{
this.scope_logic = INTERSECT;
}
}
// select all, without any constraint
if( (this.user_constraints.length == 0) && (this.scope_constraints.length == 0) ){
var SelectAllModule = getSelectbyModule("selectAll");
var selectall = new SelectAllModule();
this.user_constraints.push(selectall);
}
};
SQLMaker.makeConstraint = function()
{
// user query
var user_query_sqls = [];
var user_query_params = [];
for( var i=0; i<this.user_constraints.length; i++ ){
var user_constraint = this.user_constraints[i];
var constraint = user_constraint.makeConstraint();
var sql = constraint.sql;
var params = constraint.params;
user_query_sqls.push(sql);
if( params.length > 0 ){
for( var j=0; j<params.length; j++ ){
user_query_params.push(params[j]);
}
}
if( !constraint.parameterizable ){ this.parameterizable = false; }
}
var user_query_sql = user_query_sqls.join(this.user_logic);
// scope query
var scope_query_sqls = [];
var scope_query_params = [];
for( var i=0; i<this.scope_constraints.length; i++ ){
var scope_constraint = this.scope_constraints[i];
var constraint = scope_constraint.makeConstraint();
var sql = constraint.sql;
var params = constraint.params;
scope_query_sqls.push(sql);
if( params.length > 0 ){
for( var j=0; j<params.length; j++ ){
scope_query_params.push(params[j]);
}
}
if( !constraint.parameterizable ){ this.parameterizable = false; }
}
var scope_query_sql = scope_query_sqls.join(this.scope_logic);
// make sql and params
var sql = '';
var params = [];
if( scope_query_sql && user_query_sql ){
sql = "( ( " + scope_query_sql + " ) INTERSECT ( " + user_query_sql + " ) )"; // scope represent INTERSECT
if( scope_query_params.length > 0 ){
for( var i=0; i<scope_query_params.length; i++ ){
params.push(scope_query_params[i]);
}
}
if( user_query_params.length > 0 ){
for( var i=0; i<user_query_params.length; i++ ){
params.push(user_query_params[i]);
}
}
}
else if( !scope_query_sql && user_query_sql ){
sql = "( " + user_query_sql + " )";
if( user_query_params.length > 0 ){
for( var i=0; i<user_query_params.length; i++ ){
params.push(user_query_params[i]);
}
}
}
// orderby
for( var i=0; i<this.orderbys.length; i++ ){
sql = this.orderbys[i].wrap(sql);
}
// limiter
if( this.limit && !this.has_orderby ){
sql = this.limiter.wrap(sql);
}
// convert place holders for libpq
var holder_pattern = /\?/g;
var result = [];
var first = 0;
var last = 0;
var pqsql = '';
var i = 1;
while( (result = holder_pattern.exec(sql)) != null ){
var holder = '$' + i;
last = result.index;
pqsql += sql.substring(first,last);
pqsql += holder;
first = result.index+1;
i++;
}
pqsql += sql.substring(first,sql.length);
return { "sql" : pqsql, "params" : params, "parameterizable" : this.parameterizable };
};
SQLMaker.stringify = function()
{
// user query
var user_query_strs = [];
var user_constriant_keys = Object.keys(this.user_constraints).sort();
for( var i=0; i<user_constriant_keys.length; i++ ){
var key = user_constriant_keys[i];
var module = this.user_constraints[key];
user_query_strs.push( module.stringify() );
}
var user_query_str = user_query_sqls.join(',');
// orderby
var orderby_strs = [];
var orderby_keys = Object.keys(this.orderbys).sort();
for( var i=0; i<orderby_keys.length; i++ ){
var key = orderby_keys[i];
var module = this.orderbys[key];
orderby_strs.push( module.stringify() );
}
var orderby_str = user_query_sqls.join(',');
// scope
var scope_strs = [];
var scope_constriant_keys = Object.keys(this.scope_constraints).sort();
for( var i=0; i<scope_constriant_keys.length; i++ ){
var key = scope_constriant_keys[i];
var module = this.scope_constraints[key];
scope_strs.push( module.stringify() );
}
var scope_str = scope_strs.join(',');
// limit
var limit_str;
if( this.limiter ){
limit_str = this.limiter.stringify();
}
return (user_query_str,orderby_str,scope_str,limit_str).join();
};