@vulcan-sql/core
Version:
Core package of VulcanSQL
635 lines • 22.9 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.DataQueryBuilder = exports.Direction = exports.WherePredicate = exports.JoinCommandType = exports.AggregateFuncType = exports.SelectCommandType = void 0;
const tslib_1 = require("tslib");
const models_1 = require("../../../models/index");
const uuid = require("uuid");
const lodash_1 = require("lodash");
const commonTypes_1 = require("./commonTypes");
const joinOnClause_1 = require("./joinOnClause");
const errors_1 = require("../../utils/errors");
var SelectCommandType;
(function (SelectCommandType) {
SelectCommandType["SELECT"] = "SELECT";
SelectCommandType["SELECT_DISTINCT"] = "SELECT_DISTINCT";
})(SelectCommandType = exports.SelectCommandType || (exports.SelectCommandType = {}));
var AggregateFuncType;
(function (AggregateFuncType) {
AggregateFuncType["COUNT"] = "COUNT";
AggregateFuncType["SUM"] = "SUM";
AggregateFuncType["AVG"] = "AVG";
AggregateFuncType["MIN"] = "MIN";
AggregateFuncType["MAX"] = "MAX";
})(AggregateFuncType = exports.AggregateFuncType || (exports.AggregateFuncType = {}));
var JoinCommandType;
(function (JoinCommandType) {
JoinCommandType["INNER_JOIN"] = "INNER_JOIN";
JoinCommandType["LEFT_JOIN"] = "LEFT_JOIN";
JoinCommandType["RIGHT_JOIN"] = "RIGHT_JOIN";
JoinCommandType["FULL_JOIN"] = "FULL_JOIN";
})(JoinCommandType = exports.JoinCommandType || (exports.JoinCommandType = {}));
var WherePredicate;
(function (WherePredicate) {
WherePredicate["WRAPPED"] = "WRAPPED";
WherePredicate["LIKE"] = "LIKE";
})(WherePredicate = exports.WherePredicate || (exports.WherePredicate = {}));
// Order by clause operation
var Direction;
(function (Direction) {
Direction["ASC"] = "ASCENDING";
Direction["DESC"] = "DESCENDING";
})(Direction = exports.Direction || (exports.Direction = {}));
class DataQueryBuilder {
constructor({ statement, operations, parameterizer, dataSource, profileName, headers, }) {
this.identifier = uuid.v4();
this.statement = statement;
this.dataSource = dataSource;
this.parameterizer = parameterizer;
this.operations = operations || {
select: null,
where: [],
join: [],
groupBy: [],
having: [],
orderBy: [],
limit: null,
offset: null,
};
this.headers = headers;
this.profileName = profileName;
}
// Select clause methods
select(...columns) {
// if columns is empty, set to select all
if ((0, lodash_1.isEmpty)(columns))
columns = ['*'];
// skip current select if select only '*' value and has existed select '*' in record.
const isSelectAllExist = (column) => column.name === '*';
const isAllColumns = (column) => ['*', ''].includes(column) ||
[{ name: '*' }, { name: '' }].includes(column);
if (columns.length === 1 &&
isAllColumns(columns[0]) &&
this.operations.select &&
(0, lodash_1.find)(this.operations.select.columns, isSelectAllExist)) {
return this;
}
this.recordSelect({ command: SelectCommandType.SELECT, columns });
return this;
}
distinct(...columns) {
this.recordSelect({ command: SelectCommandType.SELECT_DISTINCT, columns });
return this;
}
// alias name method for select
column(...columns) {
return this.select(...columns);
}
// select and limit 1
first(...columns) {
this.select(...columns);
this.limit(1);
return this;
}
count(column = '*') {
const normalized = typeof column === 'string'
? {
name: '*',
aggregateType: AggregateFuncType.COUNT,
}
: Object.assign(Object.assign({}, column), { aggregateType: AggregateFuncType.COUNT });
this.select(normalized);
return this;
}
min(column) {
const normalized = typeof column === 'string'
? {
name: column,
aggregateType: AggregateFuncType.MIN,
}
: Object.assign(Object.assign({}, column), { aggregateType: AggregateFuncType.MIN });
this.select(normalized);
return this;
}
max(column) {
const normalized = typeof column === 'string'
? {
name: column,
aggregateType: AggregateFuncType.MAX,
}
: Object.assign(Object.assign({}, column), { aggregateType: AggregateFuncType.MAX });
this.select(normalized);
return this;
}
avg(column) {
const normalized = typeof column === 'string'
? {
name: column,
aggregateType: AggregateFuncType.AVG,
}
: Object.assign(Object.assign({}, column), { aggregateType: AggregateFuncType.AVG });
this.select(normalized);
return this;
}
sum(column) {
const normalized = typeof column === 'string'
? {
name: column,
aggregateType: AggregateFuncType.SUM,
}
: Object.assign(Object.assign({}, column), { aggregateType: AggregateFuncType.SUM });
this.select(normalized);
return this;
}
// Join clause methods
innerJoin(builder, joinCallback) {
this.recordJoin({
command: JoinCommandType.INNER_JOIN,
builder,
joinCallback,
});
return this;
}
leftJoin(builder, joinCallback) {
this.recordJoin({
command: JoinCommandType.LEFT_JOIN,
builder,
joinCallback,
});
return this;
}
rightJoin(builder, joinCallback) {
this.recordJoin({
command: JoinCommandType.RIGHT_JOIN,
builder,
joinCallback,
});
return this;
}
fullJoin(builder, joinCallback) {
this.recordJoin({
command: JoinCommandType.FULL_JOIN,
builder,
joinCallback,
});
return this;
}
// Where clause methods
where(column, operator, value) {
if (!(0, commonTypes_1.isOfComparisonOperator)(operator))
throw new errors_1.TemplateError(`'There is no ${operator} operator.`);
this.recordWhere({
command: null,
data: {
column,
operator,
value,
},
});
return this;
}
whereNot(column, operator, value) {
this.recordWhere({ command: commonTypes_1.LogicalOperator.NOT });
return this.where(column, operator, value);
}
whereWrapped(builderCallback) {
const wrappedBuilder = new DataQueryBuilder({
statement: '',
dataSource: this.dataSource,
parameterizer: this.parameterizer,
profileName: this.profileName,
headers: this.headers,
});
builderCallback(wrappedBuilder);
this.recordWhere({
command: WherePredicate.WRAPPED,
data: wrappedBuilder.operations.where,
});
return this;
}
whereNotWrapped(builderCallback) {
this.recordWhere({ command: commonTypes_1.LogicalOperator.NOT });
return this.whereWrapped(builderCallback);
}
whereBetween(column, min, max) {
this.recordWhere({
command: commonTypes_1.ComparisonPredicate.BETWEEN,
data: { column, min, max },
});
return this;
}
whereNotBetween(column, min, max) {
this.recordWhere({ command: commonTypes_1.LogicalOperator.NOT });
return this.whereBetween(column, min, max);
}
whereIn(column, values) {
this.recordWhere({
command: commonTypes_1.ComparisonPredicate.IN,
data: { column, values: values },
});
return this;
}
whereNotIn(column, values) {
this.recordWhere({ command: commonTypes_1.LogicalOperator.NOT });
return this.whereIn(column, values);
}
whereNull(column) {
this.recordWhere({
command: commonTypes_1.ComparisonPredicate.IS_NULL,
data: { column },
});
return this;
}
whereNotNull(column) {
this.recordWhere({ command: commonTypes_1.LogicalOperator.NOT });
return this.whereNull(column);
}
whereLike(column, searchValue) {
this.recordWhere({
command: WherePredicate.LIKE,
data: { column, searchValue },
});
return this;
}
whereExists(subQueryBuilder) {
this.recordWhere({
command: commonTypes_1.ComparisonPredicate.EXISTS,
data: subQueryBuilder,
});
return this;
}
whereNotExists(subQueryBuilder) {
this.recordWhere({ command: commonTypes_1.LogicalOperator.NOT });
return this.whereExists(subQueryBuilder);
}
// and
andWhere(column, operator, value) {
this.recordWhere({ command: commonTypes_1.LogicalOperator.AND });
return this.where(column, operator, value);
}
andWhereNot(column, operator, value) {
this.recordWhere({ command: commonTypes_1.LogicalOperator.AND });
return this.whereNot(column, operator, value);
}
andWhereWrapped(builderCallback) {
this.recordWhere({ command: commonTypes_1.LogicalOperator.AND });
return this.whereWrapped(builderCallback);
}
andWhereNotWrapped(builderCallback) {
this.recordWhere({ command: commonTypes_1.LogicalOperator.AND });
return this.whereNotWrapped(builderCallback);
}
andWhereBetween(column, min, max) {
this.recordWhere({ command: commonTypes_1.LogicalOperator.AND });
return this.whereBetween(column, min, max);
}
andWhereNotBetween(column, min, max) {
this.recordWhere({ command: commonTypes_1.LogicalOperator.AND });
return this.whereNotBetween(column, min, max);
}
andWhereIn(column, values) {
this.recordWhere({ command: commonTypes_1.LogicalOperator.AND });
return this.whereIn(column, values);
}
andWhereNotIn(column, values) {
this.recordWhere({ command: commonTypes_1.LogicalOperator.AND });
return this.whereNotIn(column, values);
}
andWhereNull(column) {
this.recordWhere({ command: commonTypes_1.LogicalOperator.AND });
return this.whereNull(column);
}
andWhereNotNull(column) {
this.recordWhere({ command: commonTypes_1.LogicalOperator.AND });
return this.whereNotNull(column);
}
andWhereLike(column, searchValue) {
this.recordWhere({ command: commonTypes_1.LogicalOperator.AND });
return this.whereLike(column, searchValue);
}
andWhereExists(subQueryBuilder) {
this.recordWhere({ command: commonTypes_1.LogicalOperator.AND });
return this.whereExists(subQueryBuilder);
}
andWhereNotExists(subQueryBuilder) {
this.recordWhere({ command: commonTypes_1.LogicalOperator.AND });
return this.whereNotExists(subQueryBuilder);
}
// or
orWhere(column, operator, value) {
this.recordWhere({ command: commonTypes_1.LogicalOperator.OR });
return this.where(column, operator, value);
}
orWhereNot(column, operator, value) {
this.recordWhere({ command: commonTypes_1.LogicalOperator.OR });
return this.whereNot(column, operator, value);
}
orWhereWrapped(builderCallback) {
this.recordWhere({ command: commonTypes_1.LogicalOperator.OR });
return this.whereWrapped(builderCallback);
}
orWhereNotWrapped(builderCallback) {
this.recordWhere({ command: commonTypes_1.LogicalOperator.OR });
return this.whereNotWrapped(builderCallback);
}
orWhereBetween(column, min, max) {
this.recordWhere({ command: commonTypes_1.LogicalOperator.OR });
return this.whereBetween(column, min, max);
}
orWhereNotBetween(column, min, max) {
this.recordWhere({ command: commonTypes_1.LogicalOperator.OR });
return this.whereNotBetween(column, min, max);
}
orWhereIn(column, values) {
this.recordWhere({ command: commonTypes_1.LogicalOperator.OR });
return this.whereIn(column, values);
}
orWhereNotIn(column, values) {
this.recordWhere({ command: commonTypes_1.LogicalOperator.OR });
return this.whereNotIn(column, values);
}
orWhereNull(column) {
this.recordWhere({ command: commonTypes_1.LogicalOperator.OR });
return this.whereNull(column);
}
orWhereNotNull(column) {
this.recordWhere({ command: commonTypes_1.LogicalOperator.OR });
return this.whereNotNull(column);
}
orWhereLike(column, searchValue) {
this.recordWhere({ command: commonTypes_1.LogicalOperator.OR });
return this.whereLike(column, searchValue);
}
orWhereExists(subQueryBuilder) {
this.recordWhere({ command: commonTypes_1.LogicalOperator.OR });
return this.whereExists(subQueryBuilder);
}
orWhereNotExists(subQueryBuilder) {
this.recordWhere({ command: commonTypes_1.LogicalOperator.OR });
return this.whereNotExists(subQueryBuilder);
}
// Group by clause method
groupBy(...columns) {
this.operations.groupBy = this.operations.groupBy.concat(columns);
return this;
}
having(column, operator, value) {
const normalized = typeof column === 'string' ? { name: column } : column;
if (!(0, commonTypes_1.isOfComparisonOperator)(operator))
throw new errors_1.TemplateError(`'There is no ${operator} operator.`);
this.recordHaving({
command: null,
data: {
column: normalized,
operator,
value,
},
});
return this;
}
havingIn(column, values) {
const normalized = typeof column === 'string' ? { name: column } : column;
this.recordHaving({
command: commonTypes_1.ComparisonPredicate.IN,
data: {
column: normalized,
values,
},
});
return this;
}
havingNotIn(column, values) {
this.recordHaving({ command: commonTypes_1.LogicalOperator.NOT });
return this.havingIn(column, values);
}
havingBetween(column, min, max) {
const normalized = typeof column === 'string' ? { name: column } : column;
this.recordHaving({
command: commonTypes_1.ComparisonPredicate.BETWEEN,
data: {
column: normalized,
min,
max,
},
});
return this;
}
havingNotBetween(column, min, max) {
this.recordHaving({ command: commonTypes_1.LogicalOperator.NOT });
return this.havingBetween(column, min, max);
}
havingNull(column) {
this.recordHaving({
command: commonTypes_1.ComparisonPredicate.IS_NULL,
data: { column },
});
return this;
}
havingNotNull(column) {
this.recordHaving({ command: commonTypes_1.LogicalOperator.NOT });
return this.havingNull(column);
}
havingExists(subQueryBuilder) {
this.recordHaving({
command: commonTypes_1.ComparisonPredicate.EXISTS,
data: subQueryBuilder,
});
return this;
}
havingNotExists(subQueryBuilder) {
this.recordHaving({ command: commonTypes_1.LogicalOperator.NOT });
return this.havingExists(subQueryBuilder);
}
// And Having clause
andHaving(column, operator, value) {
this.recordHaving({ command: commonTypes_1.LogicalOperator.AND });
return this.having(column, operator, value);
}
andHavingIn(column, values) {
this.recordHaving({ command: commonTypes_1.LogicalOperator.AND });
return this.havingIn(column, values);
}
andHavingNotIn(column, values) {
this.recordHaving({ command: commonTypes_1.LogicalOperator.AND });
return this.havingNotIn(column, values);
}
andHavingBetween(column, min, max) {
this.recordHaving({ command: commonTypes_1.LogicalOperator.AND });
return this.havingBetween(column, min, max);
}
andHavingNotBetween(column, min, max) {
this.recordHaving({ command: commonTypes_1.LogicalOperator.AND });
return this.havingNotBetween(column, min, max);
}
andHavingNull(column) {
this.recordHaving({ command: commonTypes_1.LogicalOperator.AND });
return this.havingNull(column);
}
andHavingNotNull(column) {
this.recordHaving({ command: commonTypes_1.LogicalOperator.AND });
return this.havingNotNull(column);
}
andHavingExists(subQueryBuilder) {
this.recordHaving({ command: commonTypes_1.LogicalOperator.AND });
return this.havingExists(subQueryBuilder);
}
andHavingNotExists(subQueryBuilder) {
this.recordHaving({ command: commonTypes_1.LogicalOperator.AND });
return this.havingNotExists(subQueryBuilder);
}
// Or Having clause
orHaving(column, operator, value) {
this.recordHaving({ command: commonTypes_1.LogicalOperator.OR });
return this.having(column, operator, value);
}
orHavingIn(column, values) {
this.recordHaving({ command: commonTypes_1.LogicalOperator.OR });
return this.havingIn(column, values);
}
orHavingNotIn(column, values) {
this.recordHaving({ command: commonTypes_1.LogicalOperator.OR });
return this.havingNotIn(column, values);
}
orHavingBetween(column, min, max) {
this.recordHaving({ command: commonTypes_1.LogicalOperator.OR });
return this.havingBetween(column, min, max);
}
orHavingNotBetween(column, min, max) {
this.recordHaving({ command: commonTypes_1.LogicalOperator.OR });
return this.havingNotBetween(column, min, max);
}
orHavingNull(column) {
this.recordHaving({ command: commonTypes_1.LogicalOperator.OR });
return this.havingNull(column);
}
orHavingNotNull(column) {
this.recordHaving({ command: commonTypes_1.LogicalOperator.OR });
return this.havingNotNull(column);
}
orHavingExists(subQueryBuilder) {
this.recordHaving({ command: commonTypes_1.LogicalOperator.OR });
return this.havingExists(subQueryBuilder);
}
orHavingNotExists(subQueryBuilder) {
this.recordHaving({ command: commonTypes_1.LogicalOperator.OR });
return this.havingNotExists(subQueryBuilder);
}
// Order by clause method
orderBy(column, direction = Direction.ASC) {
this.operations.orderBy.push({
column,
direction,
});
return this;
}
// Limit and Offset clause
limit(size) {
this.operations.limit = size;
return this;
}
offset(move) {
this.operations.offset = move;
return this;
}
take(size, move) {
this.operations.limit = size;
this.operations.offset = move;
return this;
}
clone() {
return new DataQueryBuilder({
statement: this.statement,
dataSource: this.dataSource,
operations: this.operations,
parameterizer: this.parameterizer.clone(),
profileName: this.profileName,
headers: this.headers,
});
}
// setup pagination if would like to do paginate
paginate(pagination) {
if (!(0, models_1.isOffsetPagination)(pagination)) {
throw new errors_1.InternalError(`Only offset pagination is supported`);
}
this.take(pagination.limit, pagination.offset);
}
setHeaders(headers) {
if (!headers)
return;
this.headers = headers;
}
parameterizeOperations() {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
// We only support offset and limit now.
const parameterizedOperations = {
offset: yield this.parameterizeValue(this.operations.offset),
limit: yield this.parameterizeValue(this.operations.limit),
};
this.parameterizer.reset();
return parameterizedOperations;
});
}
value() {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
// call data source
const result = yield this.dataSource.execute({
statement: this.statement,
operations: yield this.parameterizeOperations(),
bindParams: this.parameterizer.getBinding(),
profileName: this.profileName,
headers: this.headers,
});
return result;
});
}
// record Select-On related operations
recordSelect({ command, columns, }) {
const normalized = columns.map((column) => {
if (typeof column === 'string')
return { name: column };
return column;
});
if (this.operations.select === null) {
this.operations.select = {
command,
columns: normalized,
};
return;
}
// distinct will replace previous select command,
// even builder call select method in later of the distinct method, it not influence distinct.
if (command === SelectCommandType.SELECT_DISTINCT)
this.operations.select.command = command;
this.operations.select.columns =
this.operations.select.columns.concat(normalized);
}
recordJoin({ command, builder, joinCallback, }) {
const joinOnClause = new joinOnClause_1.JoinOnClause();
joinCallback(joinOnClause);
this.operations.join.push({
command,
onClauses: joinOnClause.operations,
joinBuilder: builder,
});
}
recordWhere({ command, data, }) {
this.operations.where.push({
command,
data,
});
}
recordHaving({ command, data, }) {
this.operations.having.push({
command,
data,
});
}
parameterizeValue(value) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
if ((0, lodash_1.isUndefined)(value) || (0, lodash_1.isNull)(value))
return null;
return yield this.parameterizer.generateIdentifier(value);
});
}
}
exports.DataQueryBuilder = DataQueryBuilder;
//# sourceMappingURL=dataQueryBuilder.js.map