UNPKG

n1-sql

Version:
199 lines (174 loc) 5.13 kB
'use strict'; 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)); };