n1-sql
Version:
199 lines (174 loc) • 5.13 kB
JavaScript
;
var Immutable = require('immutable');
var Fetch = require('fetch-ponyfill')();
var setSingularState = require('./util').setSingularState;
var callJoinBuilder = require('./join').callJoinBuilder;
var callWhereBuilder = require('./where').callWhereBuilder;
var RESULT_CTXT = 'https://schema.n1analytics.com/api/vbase/1/query/finished/result';
var TRY_LATER_CTXT = 'https://schema.n1analytics.com/api/vbase/1/query/tryLater';
var ERROR_CTXT = 'https://schema.n1analytics.com/api/vbase/1/query/finished/error';
var RETRY_INTERVAL = 5000;
function builder(state) {
function setSingular(path, value) {
var s = setSingularState(state, path, value);
return builder(s);
}
function toSql() {
var a = ['SELECT', toSqlSelect(), 'FROM', state.get('join').join(' '), 'WHERE', state.get('where').join(' AND ')];
if (state.get('groupBy')) {
a.push('GROUP BY ' + state.get('groupBy'));
}
return a.join(' ') + ';';
}
function toSqlSelect() {
return state.get('select').map(function (e) {
var a = [];
if (e.aggOp) a.push(e.aggOp + '(');
a.push(e.column);
if (e.aggOp) a.push(')');
if (e.alias) {
a.push(' AS ');
a.push(e.alias);
}
return a.join('');
}).join(', ');
}
function _select(aggOp) {
return function (column, alias) {
return builder(state.updateIn(['select'], function (l) {
return l.push({ aggOp: aggOp, column: column, alias: alias });
}));
};
}
function exec() {
var url = getUrl();
var sql = toSql();
console.debug('Remotely "' + url + '" executing query: ' + sql);
return Fetch.fetch(url, {
method: 'POST',
body: sql,
headers: {
'Content-Type': 'text'
},
mode: 'cors',
credentials: 'include'
}).then(function (resp) {
if (!resp.ok) {
throw new Error(resp.statusText);
}
return resp.json();
}).then(handleServerReply).then(function (result) {
console.debug('Received ' + result.rows.length + ' rows ' + 'requiring ' + Math.round(result.runInfo.runTime) + ' sec for query: ' + sql);
return result; // for debugging only
}).catch(function (e) {
console.warn('Error "' + e + ' while executing query - ' + sql);
throw new Error(e);
});
}
function handleServerReply(j) {
var ctxt = j['@context'];
switch (ctxt) {
case RESULT_CTXT:
return j;
case ERROR_CTXT:
throw new Error(errMsg(j));
case TRY_LATER_CTXT:
var url = j['@id'];
return new Promise(function (fulfill, reject) {
retryAgainLater(url, fulfill, reject);
});
default:
throw Error('Unknown server reply message type "' + ctxt + '".');
}
}
function retryAgainLater(url, fulfill, reject) {
console.debug('Checking for result "' + url + '" again in ' + RETRY_INTERVAL + ' msec.');
setTimeout(function () {
Fetch.fetch(url, {
method: 'GET',
mode: 'cors',
credentials: 'include'
}).then(function (resp) {
if (!resp.ok) {
reject(resp.statusText);
}
return resp.json();
}).then(function (j) {
var ctxt = j['@context'];
if (ctxt === TRY_LATER_CTXT) {
return retryAgainLater(url, fulfill, reject);
}
fulfill(handleServerReply(j));
}).catch(reject);
}, RETRY_INTERVAL);
}
function errMsg(j) {
var msg = 'Unknown error';
if (j.cause && j.cause.message) {
msg = j.cause.message;
}
return msg;
}
function getUrl() {
var url = state.get('url');
var vdbs = state.get('vdbs');
return url + '/v1/vdbs/' + vdbs + '/queries';
}
return {
useTable: function useTable(alias, table) {
if (!table) {
table = alias;
}
var s1 = state.updateIn(['tables'], function (l) {
return l.push({ table: table, alias: alias });
});
var h = {};
h[table] = table;
if (alias) {
h[alias] = table;
}
var s2 = s1.mergeDeep({ table2dp: h });
return builder(s2);
},
select: _select(),
selectCount: function selectCount(alias) {
return _select('COUNT')('*', alias);
},
selectSum: _select('SUM'),
selectAvg: _select('AVG'),
vdbs: function vdbs(name) {
return setSingular('vdbs', name);
},
join: function join(f) {
var s = callJoinBuilder(f, state);
return builder(s);
},
groupBy: function groupBy(column) {
return setSingular('groupBy', column);
},
where: function where(f) {
var s = callWhereBuilder(f, state);
return builder(s);
},
toSql: toSql,
debug: function debug() {
var s = setSingularState(state, 'sql', toSql());
console.debug(s.toJS());
return builder(state);
},
exec: exec
};
}
function intialState(url) {
return Immutable.fromJS({
url: url,
tables: [],
table2dp: {},
join: [],
where: [],
select: []
});
}
module.exports = function (url) {
return builder(intialState(url));
};