Skip to content

Commit

Permalink
feat(select): add more join types (#46)
Browse files Browse the repository at this point in the history
PR-URL: #46
  • Loading branch information
lundibundi authored Sep 29, 2023
1 parent ba67068 commit 9a1cd93
Show file tree
Hide file tree
Showing 7 changed files with 661 additions and 6 deletions.
78 changes: 78 additions & 0 deletions doc/sql.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,16 @@
- [QueryConditionsBuilder.prototype.orWhere](#queryconditionsbuilderprototypeorwherekey-cond-value)
- [QueryConditionsBuilder.prototype.orWhereAny](#queryconditionsbuilderprototypeorwhereanykey-value)
- [QueryConditionsBuilder.prototype.orWhereBetween](#queryconditionsbuilderprototypeorwherebetweenkey-from-to-symmetric)
- [QueryConditionsBuilder.prototype.orWhereEq](#queryconditionsbuilderprototypeorwhereeqkey-value)
- [QueryConditionsBuilder.prototype.orWhereExists](#queryconditionsbuilderprototypeorwhereexistssubquery)
- [QueryConditionsBuilder.prototype.orWhereILike](#queryconditionsbuilderprototypeorwhereilikekey-value)
- [QueryConditionsBuilder.prototype.orWhereIn](#queryconditionsbuilderprototypeorwhereinkey-conds)
- [QueryConditionsBuilder.prototype.orWhereKey](#queryconditionsbuilderprototypeorwherekeyleftkey-cond-rightkey)
- [QueryConditionsBuilder.prototype.orWhereLess](#queryconditionsbuilderprototypeorwherelesskey-value)
- [QueryConditionsBuilder.prototype.orWhereLessEq](#queryconditionsbuilderprototypeorwherelesseqkey-value)
- [QueryConditionsBuilder.prototype.orWhereLike](#queryconditionsbuilderprototypeorwherelikekey-value)
- [QueryConditionsBuilder.prototype.orWhereMore](#queryconditionsbuilderprototypeorwheremorekey-value)
- [QueryConditionsBuilder.prototype.orWhereMoreEq](#queryconditionsbuilderprototypeorwheremoreeqkey-value)
- [QueryConditionsBuilder.prototype.orWhereNot](#queryconditionsbuilderprototypeorwherenotkey-cond-value)
- [QueryConditionsBuilder.prototype.orWhereNotBetween](#queryconditionsbuilderprototypeorwherenotbetweenkey-from-to-symmetric)
- [QueryConditionsBuilder.prototype.orWhereNotILike](#queryconditionsbuilderprototypeorwherenotilikekey-value)
Expand Down Expand Up @@ -59,15 +64,36 @@
- [SelectBuilder.prototype.avg](#selectbuilderprototypeavgfield-alias)
- [SelectBuilder.prototype.build](#selectbuilderprototypebuild)
- [SelectBuilder.prototype.count](#selectbuilderprototypecountfield---alias)
- [SelectBuilder.prototype.crossJoin](#selectbuilderprototypecrossjointablename)
- [SelectBuilder.prototype.crossJoinAs](#selectbuilderprototypecrossjoinastablename-alias)
- [SelectBuilder.prototype.distinct](#selectbuilderprototypedistinct)
- [SelectBuilder.prototype.from](#selectbuilderprototypefromtablename-alias)
- [SelectBuilder.prototype.fullJoin](#selectbuilderprototypefulljointablename-leftkey-rightkey)
- [SelectBuilder.prototype.fullJoinAs](#selectbuilderprototypefulljoinastablename-alias-leftkey-rightkey)
- [SelectBuilder.prototype.fullJoinCond](#selectbuilderprototypefulljoincondtablename-condition)
- [SelectBuilder.prototype.fullJoinCondAs](#selectbuilderprototypefulljoincondastablename-alias-condition)
- [SelectBuilder.prototype.groupBy](#selectbuilderprototypegroupbyfields)
- [SelectBuilder.prototype.innerJoin](#selectbuilderprototypeinnerjointablename-leftkey-rightkey)
- [SelectBuilder.prototype.innerJoinAs](#selectbuilderprototypeinnerjoinastablename-alias-leftkey-rightkey)
- [SelectBuilder.prototype.innerJoinCond](#selectbuilderprototypeinnerjoincondtablename-condition)
- [SelectBuilder.prototype.innerJoinCondAs](#selectbuilderprototypeinnerjoincondastablename-alias-condition)
- [SelectBuilder.prototype.join](#selectbuilderprototypejoinkind-tablename-alias-leftkey-rightkey)
- [SelectBuilder.prototype.joinCond](#selectbuilderprototypejoincondkind-tablename-alias-condition)
- [SelectBuilder.prototype.leftJoin](#selectbuilderprototypeleftjointablename-leftkey-rightkey)
- [SelectBuilder.prototype.leftJoinAs](#selectbuilderprototypeleftjoinastablename-alias-leftkey-rightkey)
- [SelectBuilder.prototype.leftJoinCond](#selectbuilderprototypeleftjoincondtablename-condition)
- [SelectBuilder.prototype.leftJoinCondAs](#selectbuilderprototypeleftjoincondastablename-alias-condition)
- [SelectBuilder.prototype.limit](#selectbuilderprototypelimitlimit)
- [SelectBuilder.prototype.max](#selectbuilderprototypemaxfield-alias)
- [SelectBuilder.prototype.min](#selectbuilderprototypeminfield-alias)
- [SelectBuilder.prototype.naturalJoin](#selectbuilderprototypenaturaljointablename)
- [SelectBuilder.prototype.naturalJoinAs](#selectbuilderprototypenaturaljoinastablename-alias)
- [SelectBuilder.prototype.offset](#selectbuilderprototypeoffsetoffset)
- [SelectBuilder.prototype.orderBy](#selectbuilderprototypeorderbyfield-dir--asc)
- [SelectBuilder.prototype.rightJoin](#selectbuilderprototyperightjointablename-leftkey-rightkey)
- [SelectBuilder.prototype.rightJoinAs](#selectbuilderprototyperightjoinastablename-alias-leftkey-rightkey)
- [SelectBuilder.prototype.rightJoinCond](#selectbuilderprototyperightjoincondtablename-condition)
- [SelectBuilder.prototype.rightJoinCondAs](#selectbuilderprototyperightjoincondastablename-alias-condition)
- [SelectBuilder.prototype.select](#selectbuilderprototypeselectfields)
- [SelectBuilder.prototype.selectAs](#selectbuilderprototypeselectasfield-alias)
- [SelectBuilder.prototype.selectFn](#selectbuilderprototypeselectfnfn-field-alias)
Expand Down Expand Up @@ -204,6 +230,8 @@ Build params for this query

#### QueryConditionsBuilder.prototype.orWhereBetween(key, from, to, symmetric)

#### QueryConditionsBuilder.prototype.orWhereEq(key, value)

#### QueryConditionsBuilder.prototype.orWhereExists(subquery)

#### QueryConditionsBuilder.prototype.orWhereILike(key, value)
Expand All @@ -212,8 +240,16 @@ Build params for this query

#### QueryConditionsBuilder.prototype.orWhereKey(leftKey, cond, rightKey)

#### QueryConditionsBuilder.prototype.orWhereLess(key, value)

#### QueryConditionsBuilder.prototype.orWhereLessEq(key, value)

#### QueryConditionsBuilder.prototype.orWhereLike(key, value)

#### QueryConditionsBuilder.prototype.orWhereMore(key, value)

#### QueryConditionsBuilder.prototype.orWhereMoreEq(key, value)

#### QueryConditionsBuilder.prototype.orWhereNot(key, cond, value)

#### QueryConditionsBuilder.prototype.orWhereNotBetween(key, from, to, symmetric)
Expand Down Expand Up @@ -294,24 +330,66 @@ Build params for this query

#### SelectBuilder.prototype.count(field = '\*', alias)

#### SelectBuilder.prototype.crossJoin(tableName)

#### SelectBuilder.prototype.crossJoinAs(tableName, alias)

#### SelectBuilder.prototype.distinct()

#### SelectBuilder.prototype.from(tableName, alias)

#### SelectBuilder.prototype.fullJoin(tableName, leftKey, rightKey)

#### SelectBuilder.prototype.fullJoinAs(tableName, alias, leftKey, rightKey)

#### SelectBuilder.prototype.fullJoinCond(tableName, condition)

#### SelectBuilder.prototype.fullJoinCondAs(tableName, alias, condition)

#### SelectBuilder.prototype.groupBy(...fields)

#### SelectBuilder.prototype.innerJoin(tableName, leftKey, rightKey)

#### SelectBuilder.prototype.innerJoinAs(tableName, alias, leftKey, rightKey)

#### SelectBuilder.prototype.innerJoinCond(tableName, condition)

#### SelectBuilder.prototype.innerJoinCondAs(tableName, alias, condition)

#### SelectBuilder.prototype.join(kind, tableName, alias, leftKey, rightKey)

#### SelectBuilder.prototype.joinCond(kind, tableName, alias, condition)

#### SelectBuilder.prototype.leftJoin(tableName, leftKey, rightKey)

#### SelectBuilder.prototype.leftJoinAs(tableName, alias, leftKey, rightKey)

#### SelectBuilder.prototype.leftJoinCond(tableName, condition)

#### SelectBuilder.prototype.leftJoinCondAs(tableName, alias, condition)

#### SelectBuilder.prototype.limit(limit)

#### SelectBuilder.prototype.max(field, alias)

#### SelectBuilder.prototype.min(field, alias)

#### SelectBuilder.prototype.naturalJoin(tableName)

#### SelectBuilder.prototype.naturalJoinAs(tableName, alias)

#### SelectBuilder.prototype.offset(offset)

#### SelectBuilder.prototype.orderBy(field, dir = 'ASC')

#### SelectBuilder.prototype.rightJoin(tableName, leftKey, rightKey)

#### SelectBuilder.prototype.rightJoinAs(tableName, alias, leftKey, rightKey)

#### SelectBuilder.prototype.rightJoinCond(tableName, condition)

#### SelectBuilder.prototype.rightJoinCondAs(tableName, alias, condition)

#### SelectBuilder.prototype.select(...fields)

#### SelectBuilder.prototype.selectAs(field, alias)
Expand Down
25 changes: 25 additions & 0 deletions lib/query-conditions-builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,26 +44,51 @@ class QueryConditionsBuilder extends QueryBuilder {
return this;
}

orWhereEq(key, value) {
this.whereConditions.or(key, '=', this._whereValueMapper(value));
return this;
}

whereMore(key, value) {
this.whereConditions.and(key, '>', this._whereValueMapper(value));
return this;
}

orWhereMore(key, value) {
this.whereConditions.or(key, '>', this._whereValueMapper(value));
return this;
}

whereMoreEq(key, value) {
this.whereConditions.and(key, '>=', this._whereValueMapper(value));
return this;
}

orWhereMoreEq(key, value) {
this.whereConditions.or(key, '>=', this._whereValueMapper(value));
return this;
}

whereLess(key, value) {
this.whereConditions.and(key, '<', this._whereValueMapper(value));
return this;
}

orWhereLess(key, value) {
this.whereConditions.or(key, '<', this._whereValueMapper(value));
return this;
}

whereLessEq(key, value) {
this.whereConditions.and(key, '<=', this._whereValueMapper(value));
return this;
}

orWhereLessEq(key, value) {
this.whereConditions.or(key, '<=', this._whereValueMapper(value));
return this;
}

whereNot(key, cond, value) {
this.whereConditions.not(key, cond, this._whereValueMapper(value));
return this;
Expand Down
131 changes: 126 additions & 5 deletions lib/select-builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class SelectBuilder extends QueryConditionsBuilder {
this.operations = {
select: new Set(),
selectDistinct: false,
innerJoin: [],
join: [],
groupBy: new Set(),
orderBy: [],
from: [],
Expand Down Expand Up @@ -59,14 +59,117 @@ class SelectBuilder extends QueryConditionsBuilder {
}

innerJoin(tableName, leftKey, rightKey) {
this.operations.innerJoin.push({
return this.innerJoinAs(tableName, null, leftKey, rightKey);
}

innerJoinAs(tableName, alias, leftKey, rightKey) {
this.join('INNER', tableName, alias, leftKey, rightKey);
return this;
}

innerJoinCond(tableName, condition) {
return this.innerJoinCondAs(tableName, null, condition);
}

innerJoinCondAs(tableName, alias, condition) {
this.joinCond('INNER', tableName, alias, condition);
return this;
}

leftJoin(tableName, leftKey, rightKey) {
return this.leftJoinAs(tableName, null, leftKey, rightKey);
}

leftJoinAs(tableName, alias, leftKey, rightKey) {
this.join('LEFT OUTER', tableName, alias, leftKey, rightKey);
return this;
}

leftJoinCond(tableName, condition) {
return this.leftJoinCondAs(tableName, null, condition);
}

leftJoinCondAs(tableName, alias, condition) {
this.joinCond('LEFT OUTER', tableName, alias, condition);
return this;
}

rightJoin(tableName, leftKey, rightKey) {
return this.rightJoinAs(tableName, null, leftKey, rightKey);
}

rightJoinAs(tableName, alias, leftKey, rightKey) {
this.join('RIGHT OUTER', tableName, alias, leftKey, rightKey);
return this;
}

rightJoinCond(tableName, condition) {
return this.rightJoinCondAs(tableName, null, condition);
}

rightJoinCondAs(tableName, alias, condition) {
this.joinCond('RIGHT OUTER', tableName, alias, condition);
return this;
}

fullJoin(tableName, leftKey, rightKey) {
return this.fullJoinAs(tableName, null, leftKey, rightKey);
}

fullJoinAs(tableName, alias, leftKey, rightKey) {
this.join('FULL OUTER', tableName, alias, leftKey, rightKey);
return this;
}

fullJoinCond(tableName, condition) {
return this.fullJoinCondAs(tableName, null, condition);
}

fullJoinCondAs(tableName, alias, condition) {
this.joinCond('FULL OUTER', tableName, alias, condition);
return this;
}

naturalJoin(tableName) {
return this.naturalJoinAs(tableName, null, null, null);
}

naturalJoinAs(tableName, alias) {
this.join('NATURAL', tableName, alias, null, null);
return this;
}

crossJoin(tableName) {
return this.crossJoinAs(tableName, null, null, null);
}

crossJoinAs(tableName, alias) {
this.join('CROSS', tableName, alias, null, null);
return this;
}

join(kind, tableName, alias, leftKey, rightKey) {
this.operations.join.push({
kind,
table: this.escapeIdentifier(tableName),
alias: this.escapeIdentifier(alias),
leftKey: this.escapeKey(leftKey),
rightKey: this.escapeKey(rightKey),
});
return this;
}

joinCond(kind, tableName, alias, condition) {
checkTypeOrQuery(condition, 'condition', 'string');
this.operations.join.push({
kind,
table: this.escapeIdentifier(tableName),
alias: this.escapeIdentifier(alias),
condition,
});
return this;
}

distinct() {
this.operations.selectDistinct = true;
return this;
Expand Down Expand Up @@ -162,6 +265,26 @@ class SelectBuilder extends QueryConditionsBuilder {
);
}

// #private
_processJoin(joins) {
let clauses = '';
for (const join of joins) {
const alias = join.alias ? ` AS ${join.alias}` : '';
if (join.kind === 'NATURAL' || join.kind === 'CROSS') {
clauses += ` ${join.kind} JOIN ${join.table}${alias}`;
} else if (join.condition) {
const condition =
join.condition instanceof QueryBuilder
? join.condition.build()
: join.condition;
clauses += ` ${join.kind} JOIN ${join.table}${alias} ON ${condition}`;
} else {
clauses += ` ${join.kind} JOIN ${join.table}${alias} ON ${join.leftKey} = ${join.rightKey}`;
}
}
return clauses;
}

// #private
_processOrder(clauses) {
return mapJoinIterable(clauses, (o) => `${o.field} ${o.dir}`, ', ');
Expand All @@ -179,9 +302,7 @@ class SelectBuilder extends QueryConditionsBuilder {
}
query += ` FROM ${this._processFrom(this.operations.from)}`;

for (const { table, leftKey, rightKey } of this.operations.innerJoin) {
query += ` INNER JOIN ${table} ON ${leftKey} = ${rightKey}`;
}
query += this._processJoin(this.operations.join);

const whereClauses = this.whereConditions.build();
if (whereClauses.length > 0) {
Expand Down
3 changes: 2 additions & 1 deletion lib/utils.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
'use strict';

const escapeIdentifier = (name) => `"${name}"`;
const escapeIdentifier = (name) => name && `"${name}"`;

const escapeKey = (key, escapeIdentifier) =>
key &&
key
.split('.')
.map((k) => (k === '*' ? '*' : escapeIdentifier(k)))
Expand Down
Loading

0 comments on commit 9a1cd93

Please sign in to comment.