n1-sql
Version:
212 lines (185 loc) • 5.24 kB
JavaScript
// n1-sql Copyright 2018 Data61
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory() :
typeof define === 'function' && define.amd ? define(factory) :
(factory());
}(this, (function () { 'use strict';
const Immutable = require('immutable');
const Fetch = require('fetch-ponyfill')();
const setSingularState = require('./util').setSingularState;
const callJoinBuilder = require('./join').callJoinBuilder;
const callWhereBuilder = require('./where').callWhereBuilder;
const RESULT_CTXT = 'https://schema.n1analytics.com/api/vbase/1/query/finished/result';
const TRY_LATER_CTXT = 'https://schema.n1analytics.com/api/vbase/1/query/tryLater';
const ERROR_CTXT = 'https://schema.n1analytics.com/api/vbase/1/query/finished/error';
const RETRY_INTERVAL = 5000;
function builder(state) {
function setSingular(path, value) {
const s = setSingularState(state, path, value);
return builder(s);
}
function toSql() {
const 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(e => {
const 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 (column, alias) => {
return builder(state.updateIn(['select'], l => {
return l.push({aggOp, column, alias});
}));
};
}
function exec() {
const url = getUrl();
const 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(resp => {
if (!resp.ok) {
throw new Error(resp.statusText);
}
return resp.json();
}).then(handleServerReply)
.then(result => {
console.debug('Received ' + result.rows.length + ' rows '
+ 'requiring ' + Math.round(result.runInfo.runTime) + ' sec for query: ' + sql);
return result; // for debugging only
})
.catch(e => {
console.warn('Error "' + e + ' while executing query - ' + sql);
throw new Error(e);
});
}
function handleServerReply(j) {
const ctxt = j['@context'];
switch (ctxt) {
case RESULT_CTXT:
return j;
case ERROR_CTXT:
throw new Error(errMsg(j));
case TRY_LATER_CTXT:
const url = j['@id'];
return new Promise((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(() => {
Fetch.fetch(url, {
method: 'GET',
mode: 'cors',
credentials: 'include'
}).then(resp => {
if (!resp.ok) {
reject(resp.statusText);
}
return resp.json();
}).then(j => {
const 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() {
const url = state.get('url');
const vdbs = state.get('vdbs');
return url + '/v1/vdbs/' + vdbs + '/queries';
}
return {
useTable: (alias, table) => {
if (!table) {
table = alias;
}
const s1 = state.updateIn(['tables'], l => {
return l.push({table, alias});
});
const h = {};
h[table] = table;
if (alias) {
h[alias] = table;
}
const s2 = s1.mergeDeep({table2dp: h});
return builder(s2);
},
select: _select(),
selectCount: (alias) => _select('COUNT')('*', alias),
selectSum: _select('SUM'),
selectAvg: _select('AVG'),
vdbs: name => {
return setSingular('vdbs', name);
},
join: f => {
const s = callJoinBuilder(f, state);
return builder(s);
},
groupBy: column => {
return setSingular('groupBy', column);
},
where: f => {
const s = callWhereBuilder(f, state);
return builder(s);
},
toSql,
debug: () => {
const s = setSingularState(state, 'sql', toSql());
console.debug(s.toJS());
return builder(state);
},
exec
};
}
function intialState(url) {
return Immutable.fromJS({
url,
tables: [],
table2dp: {},
join: [],
where: [],
select: []
});
}
module.exports = (url) => builder(intialState(url));
})));
//# sourceMappingURL=n1-sql.umd.js.map