Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement more join types #46

Merged
merged 3 commits into from
Sep 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading