2021-06-17 16:17:15 +02:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
const _ = require('lodash/fp');
|
|
|
|
|
|
|
|
const helpers = require('./helpers');
|
|
|
|
|
|
|
|
const createQueryBuilder = (uid, db) => {
|
|
|
|
const meta = db.metadata.get(uid);
|
|
|
|
const { tableName } = meta;
|
|
|
|
|
2021-07-01 14:32:50 +02:00
|
|
|
const state = {
|
2021-06-17 16:17:15 +02:00
|
|
|
type: 'select',
|
|
|
|
select: [],
|
|
|
|
count: null,
|
|
|
|
first: false,
|
|
|
|
data: null,
|
|
|
|
where: [],
|
|
|
|
joins: [],
|
|
|
|
populate: null,
|
|
|
|
limit: null,
|
|
|
|
offset: null,
|
|
|
|
orderBy: [],
|
|
|
|
groupBy: [],
|
|
|
|
};
|
|
|
|
|
|
|
|
let counter = 0;
|
|
|
|
const getAlias = () => `t${counter++}`;
|
|
|
|
|
|
|
|
return {
|
|
|
|
alias: getAlias(),
|
|
|
|
getAlias,
|
|
|
|
|
2021-08-06 10:51:34 +02:00
|
|
|
select(args) {
|
|
|
|
state.type = 'select';
|
2021-08-11 09:34:55 +02:00
|
|
|
state.select = _.uniq(_.castArray(args)).map(col => this.aliasColumn(col));
|
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
addSelect(args) {
|
|
|
|
_.uniq(_.castArray(args))
|
|
|
|
.map(col => this.aliasColumn(col))
|
|
|
|
.forEach(toSelect => {
|
|
|
|
if (!state.select.includes(toSelect)) {
|
|
|
|
state.select.push(toSelect);
|
|
|
|
}
|
|
|
|
});
|
2021-08-06 10:51:34 +02:00
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
2021-06-17 16:17:15 +02:00
|
|
|
insert(data) {
|
|
|
|
state.type = 'insert';
|
|
|
|
state.data = data;
|
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
delete() {
|
|
|
|
state.type = 'delete';
|
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
2021-09-13 12:03:12 +02:00
|
|
|
ref(name) {
|
|
|
|
// TODO: name to column ?
|
|
|
|
return db.connection.ref(name);
|
|
|
|
},
|
|
|
|
|
2021-06-17 16:17:15 +02:00
|
|
|
update(data) {
|
|
|
|
state.type = 'update';
|
|
|
|
state.data = data;
|
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
count(count = '*') {
|
|
|
|
state.type = 'count';
|
|
|
|
state.count = count;
|
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
2021-06-22 17:13:11 +02:00
|
|
|
where(where = {}) {
|
2021-06-17 16:17:15 +02:00
|
|
|
const processedWhere = helpers.processWhere(where, { qb: this, uid, db });
|
|
|
|
|
|
|
|
state.where.push(processedWhere);
|
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
limit(limit) {
|
|
|
|
state.limit = limit;
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
offset(offset) {
|
|
|
|
state.offset = offset;
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
orderBy(orderBy) {
|
|
|
|
state.orderBy = helpers.processOrderBy(orderBy, { qb: this, uid, db });
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
groupBy(groupBy) {
|
|
|
|
state.groupBy = groupBy;
|
2021-06-24 18:28:36 +02:00
|
|
|
return this;
|
2021-06-17 16:17:15 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
populate(populate) {
|
|
|
|
state.populate = helpers.processPopulate(populate, { qb: this, uid, db });
|
2021-07-28 21:03:32 +02:00
|
|
|
return this;
|
|
|
|
},
|
2021-06-17 16:17:15 +02:00
|
|
|
|
2021-07-28 21:03:32 +02:00
|
|
|
search(query) {
|
|
|
|
state.search = query;
|
2021-06-17 16:17:15 +02:00
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
init(params = {}) {
|
2021-07-28 21:03:32 +02:00
|
|
|
const { _q, where, select, limit, offset, orderBy, groupBy, populate } = params;
|
2021-06-17 16:17:15 +02:00
|
|
|
|
2021-08-09 18:20:27 +02:00
|
|
|
if (!_.isNil(where)) {
|
2021-06-17 16:17:15 +02:00
|
|
|
this.where(where);
|
|
|
|
}
|
|
|
|
|
2021-08-09 18:20:27 +02:00
|
|
|
if (!_.isNil(_q)) {
|
2021-07-28 21:03:32 +02:00
|
|
|
this.search(_q);
|
|
|
|
}
|
|
|
|
|
2021-08-09 18:20:27 +02:00
|
|
|
if (!_.isNil(select)) {
|
2021-06-17 16:17:15 +02:00
|
|
|
this.select(select);
|
|
|
|
} else {
|
|
|
|
this.select('*');
|
|
|
|
}
|
|
|
|
|
2021-08-09 18:20:27 +02:00
|
|
|
if (!_.isNil(limit)) {
|
2021-06-17 16:17:15 +02:00
|
|
|
this.limit(limit);
|
|
|
|
}
|
|
|
|
|
2021-08-09 18:20:27 +02:00
|
|
|
if (!_.isNil(offset)) {
|
2021-06-17 16:17:15 +02:00
|
|
|
this.offset(offset);
|
|
|
|
}
|
|
|
|
|
2021-08-09 18:20:27 +02:00
|
|
|
if (!_.isNil(orderBy)) {
|
2021-06-17 16:17:15 +02:00
|
|
|
this.orderBy(orderBy);
|
|
|
|
}
|
|
|
|
|
2021-08-09 18:20:27 +02:00
|
|
|
if (!_.isNil(groupBy)) {
|
2021-06-17 16:17:15 +02:00
|
|
|
this.groupBy(groupBy);
|
|
|
|
}
|
|
|
|
|
2021-08-09 18:20:27 +02:00
|
|
|
if (!_.isNil(populate)) {
|
2021-06-17 16:17:15 +02:00
|
|
|
this.populate(populate);
|
|
|
|
}
|
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
first() {
|
|
|
|
state.first = true;
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
join(join) {
|
|
|
|
state.joins.push(join);
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
aliasColumn(columnName) {
|
2021-07-08 18:15:32 +02:00
|
|
|
if (typeof columnName !== 'string') {
|
|
|
|
return columnName;
|
|
|
|
}
|
|
|
|
|
2021-09-16 23:29:25 +02:00
|
|
|
if (columnName.indexOf('.') >= 0) {
|
|
|
|
return columnName;
|
|
|
|
}
|
|
|
|
|
2021-06-17 16:17:15 +02:00
|
|
|
return this.alias + '.' + columnName;
|
|
|
|
},
|
|
|
|
|
2021-07-08 18:15:32 +02:00
|
|
|
raw(...args) {
|
|
|
|
return db.connection.raw(...args);
|
|
|
|
},
|
|
|
|
|
2021-07-05 18:35:16 +02:00
|
|
|
getKnexQuery() {
|
2021-06-24 18:28:36 +02:00
|
|
|
const aliasedTableName = state.type === 'insert' ? tableName : { [this.alias]: tableName };
|
|
|
|
|
2021-07-05 18:35:16 +02:00
|
|
|
const qb = db.connection(aliasedTableName);
|
2021-06-17 16:17:15 +02:00
|
|
|
|
2021-07-08 18:15:32 +02:00
|
|
|
if (!state.type) {
|
|
|
|
this.select('*');
|
|
|
|
}
|
|
|
|
|
2021-09-20 19:29:06 +02:00
|
|
|
if (
|
|
|
|
['delete', 'update'].includes(state.type) &&
|
|
|
|
(db.dialect.client === 'mysql' || state.joins.length > 0)
|
|
|
|
) {
|
2021-09-16 22:18:13 +02:00
|
|
|
this.select('id');
|
|
|
|
const subQB = this.getKnexQuery();
|
|
|
|
|
|
|
|
const nestedSubQuery = db.connection.select('id').from(subQB.as('subQuery'));
|
|
|
|
|
|
|
|
return db
|
|
|
|
.connection(tableName)
|
|
|
|
[state.type]()
|
|
|
|
.whereIn('id', nestedSubQuery);
|
|
|
|
}
|
|
|
|
|
2021-07-05 18:35:16 +02:00
|
|
|
switch (state.type) {
|
|
|
|
case 'select': {
|
|
|
|
if (state.select.length === 0) {
|
|
|
|
state.select = [this.aliasColumn('*')];
|
2021-06-17 16:17:15 +02:00
|
|
|
}
|
|
|
|
|
2021-08-11 11:02:17 +02:00
|
|
|
if (state.joins.length > 0 && !state.groupBy) {
|
|
|
|
// add a discting when making joins and if we don't have a groupBy
|
2021-07-05 18:35:16 +02:00
|
|
|
// TODO: make sure we return the right data
|
|
|
|
qb.distinct(`${this.alias}.id`);
|
|
|
|
// TODO: add column if they aren't there already
|
|
|
|
state.select.unshift(...state.orderBy.map(({ column }) => column));
|
2021-06-17 16:17:15 +02:00
|
|
|
}
|
|
|
|
|
2021-07-05 18:35:16 +02:00
|
|
|
qb.select(state.select);
|
|
|
|
break;
|
2021-06-24 18:28:36 +02:00
|
|
|
}
|
2021-07-05 18:35:16 +02:00
|
|
|
case 'count': {
|
|
|
|
qb.count({ count: state.count });
|
|
|
|
break;
|
2021-06-17 16:17:15 +02:00
|
|
|
}
|
2021-07-05 18:35:16 +02:00
|
|
|
case 'insert': {
|
|
|
|
qb.insert(state.data);
|
2021-06-17 16:17:15 +02:00
|
|
|
|
2021-07-05 18:35:16 +02:00
|
|
|
if (db.dialect.useReturning() && _.has('id', meta.attributes)) {
|
|
|
|
qb.returning('id');
|
|
|
|
}
|
2021-06-17 16:17:15 +02:00
|
|
|
|
2021-07-05 18:35:16 +02:00
|
|
|
break;
|
2021-06-24 18:28:36 +02:00
|
|
|
}
|
2021-07-05 18:35:16 +02:00
|
|
|
case 'update': {
|
|
|
|
qb.update(state.data);
|
|
|
|
break;
|
2021-06-24 18:28:36 +02:00
|
|
|
}
|
2021-07-05 18:35:16 +02:00
|
|
|
case 'delete': {
|
2021-09-16 23:29:25 +02:00
|
|
|
qb.delete();
|
2021-09-16 22:18:13 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'truncate': {
|
|
|
|
db.truncate();
|
2021-07-05 18:35:16 +02:00
|
|
|
break;
|
2021-06-24 18:28:36 +02:00
|
|
|
}
|
2021-07-05 18:35:16 +02:00
|
|
|
}
|
2021-06-17 16:17:15 +02:00
|
|
|
|
2021-07-05 18:35:16 +02:00
|
|
|
if (state.limit) {
|
|
|
|
qb.limit(state.limit);
|
|
|
|
}
|
2021-06-17 16:17:15 +02:00
|
|
|
|
2021-07-05 18:35:16 +02:00
|
|
|
if (state.offset) {
|
|
|
|
qb.offset(state.offset);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (state.orderBy.length > 0) {
|
|
|
|
qb.orderBy(state.orderBy);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (state.first) {
|
|
|
|
qb.first();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (state.groupBy.length > 0) {
|
|
|
|
qb.groupBy(state.groupBy);
|
|
|
|
}
|
|
|
|
|
2021-09-16 15:32:27 +02:00
|
|
|
// if there are joins and it is a delete or update use a sub query
|
2021-07-05 18:35:16 +02:00
|
|
|
if (state.where) {
|
|
|
|
helpers.applyWhere(qb, state.where);
|
|
|
|
}
|
|
|
|
|
2021-09-16 15:32:27 +02:00
|
|
|
// if there are joins and it is a delete or update use a sub query
|
2021-07-28 21:03:32 +02:00
|
|
|
if (state.search) {
|
|
|
|
qb.where(subQb => {
|
|
|
|
helpers.applySearch(subQb, state.search, { alias: this.alias, db, uid });
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-07-05 18:35:16 +02:00
|
|
|
if (state.joins.length > 0) {
|
|
|
|
helpers.applyJoins(qb, state.joins);
|
|
|
|
}
|
|
|
|
|
|
|
|
return qb;
|
|
|
|
},
|
|
|
|
|
|
|
|
async execute({ mapResults = true } = {}) {
|
|
|
|
try {
|
|
|
|
const qb = this.getKnexQuery();
|
2021-06-17 16:17:15 +02:00
|
|
|
|
2021-06-28 21:37:44 +02:00
|
|
|
const rows = await qb;
|
2021-06-17 16:17:15 +02:00
|
|
|
|
2021-06-30 20:00:03 +02:00
|
|
|
if (state.populate && !_.isNil(rows)) {
|
|
|
|
await helpers.applyPopulate(_.castArray(rows), state.populate, { qb: this, uid, db });
|
2021-06-28 21:37:44 +02:00
|
|
|
}
|
2021-06-17 16:17:15 +02:00
|
|
|
|
2021-06-30 20:00:03 +02:00
|
|
|
let results = rows;
|
|
|
|
if (mapResults && state.type === 'select') {
|
2021-06-30 21:17:32 +02:00
|
|
|
results = helpers.fromRow(meta, rows);
|
2021-06-24 18:28:36 +02:00
|
|
|
}
|
2021-06-17 16:17:15 +02:00
|
|
|
|
2021-06-24 18:28:36 +02:00
|
|
|
return results;
|
|
|
|
} catch (error) {
|
|
|
|
db.dialect.transformErrors(error);
|
|
|
|
}
|
2021-06-17 16:17:15 +02:00
|
|
|
},
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
module.exports = createQueryBuilder;
|