2016-03-02 17:07:05 +01:00
|
|
|
|
2016-05-18 20:22:50 +10:00
|
|
|
import { assign } from 'lodash'
|
2016-09-19 18:20:12 +02:00
|
|
|
import assert from 'assert';
|
2016-03-02 17:07:05 +01:00
|
|
|
|
|
|
|
// JoinClause
|
|
|
|
// -------
|
|
|
|
|
|
|
|
// The "JoinClause" is an object holding any necessary info about a join,
|
|
|
|
// including the type, and any associated tables & columns being joined.
|
|
|
|
function JoinClause(table, type, schema) {
|
2016-05-18 19:59:24 +10:00
|
|
|
this.schema = schema;
|
|
|
|
this.table = table;
|
2016-03-02 17:07:05 +01:00
|
|
|
this.joinType = type;
|
2016-05-18 19:59:24 +10:00
|
|
|
this.and = this;
|
|
|
|
this.clauses = [];
|
2016-03-02 17:07:05 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
assign(JoinClause.prototype, {
|
|
|
|
|
|
|
|
grouping: 'join',
|
|
|
|
|
|
|
|
// Adds an "on" clause to the current join object.
|
2016-05-17 01:01:34 +10:00
|
|
|
on(first, operator, second) {
|
2016-05-11 15:26:53 +02:00
|
|
|
if (typeof first === 'function') {
|
|
|
|
this.clauses.push({
|
|
|
|
type: 'onWrapped',
|
|
|
|
value: first,
|
|
|
|
bool: this._bool()
|
|
|
|
});
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2016-05-17 01:01:34 +10:00
|
|
|
let data;
|
2016-09-19 18:20:12 +02:00
|
|
|
const bool = this._bool();
|
2016-03-02 17:07:05 +01:00
|
|
|
switch (arguments.length) {
|
|
|
|
case 1: {
|
|
|
|
if (typeof first === 'object' && typeof first.toSQL !== 'function') {
|
2016-05-17 01:01:34 +10:00
|
|
|
const keys = Object.keys(first);
|
|
|
|
let i = -1;
|
|
|
|
const method = bool === 'or' ? 'orOn' : 'on'
|
2016-03-02 17:07:05 +01:00
|
|
|
while (++i < keys.length) {
|
|
|
|
this[method](keys[i], first[keys[i]])
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
} else {
|
2016-05-17 01:01:34 +10:00
|
|
|
data = {type: 'onRaw', value: first, bool};
|
2016-03-02 17:07:05 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2016-05-17 01:01:34 +10:00
|
|
|
case 2: data = {type: 'onBasic', column: first, operator: '=', value: operator, bool}; break;
|
|
|
|
default: data = {type: 'onBasic', column: first, operator, value: second, bool};
|
2016-03-02 17:07:05 +01:00
|
|
|
}
|
|
|
|
this.clauses.push(data);
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
// Adds a "using" clause to the current join.
|
2016-05-17 01:01:34 +10:00
|
|
|
using(column) {
|
|
|
|
return this.clauses.push({type: 'onUsing', column, bool: this._bool()});
|
2016-03-02 17:07:05 +01:00
|
|
|
},
|
|
|
|
|
2016-09-19 18:20:12 +02:00
|
|
|
/*// Adds an "and on" clause to the current join object.
|
2016-05-17 01:01:34 +10:00
|
|
|
andOn() {
|
2016-03-02 17:07:05 +01:00
|
|
|
return this.on.apply(this, arguments);
|
2016-09-19 18:20:12 +02:00
|
|
|
},*/
|
2016-03-02 17:07:05 +01:00
|
|
|
|
|
|
|
// Adds an "or on" clause to the current join object.
|
2016-05-17 01:01:34 +10:00
|
|
|
orOn(first, operator, second) {
|
2016-03-02 17:07:05 +01:00
|
|
|
return this._bool('or').on.apply(this, arguments);
|
|
|
|
},
|
|
|
|
|
2016-09-19 18:20:12 +02:00
|
|
|
onBetween(column, values) {
|
|
|
|
assert(Array.isArray(values), 'The second argument to onBetween must be an array.')
|
|
|
|
assert(values.length === 2, 'You must specify 2 values for the onBetween clause')
|
|
|
|
this.clauses.push({
|
|
|
|
type: 'onBetween',
|
|
|
|
column,
|
|
|
|
value: values,
|
|
|
|
bool: this._bool(),
|
|
|
|
not: this._not()
|
|
|
|
});
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
onNotBetween(column, values) {
|
|
|
|
return this._not(true).onBetween(column, values);
|
|
|
|
},
|
|
|
|
|
|
|
|
orOnBetween(column, values) {
|
|
|
|
return this._bool('or').onBetween(column, values);
|
|
|
|
},
|
|
|
|
|
|
|
|
orOnNotBetween(column, values) {
|
|
|
|
return this._bool('or')._not(true).onBetween(column, values);
|
|
|
|
},
|
|
|
|
|
|
|
|
onIn(column, values) {
|
|
|
|
if (Array.isArray(values) && values.length === 0) return this.where(this._not());
|
|
|
|
this.clauses.push({
|
|
|
|
type: 'onIn',
|
|
|
|
column,
|
|
|
|
value: values,
|
|
|
|
not: this._not(),
|
|
|
|
bool: this._bool()
|
|
|
|
});
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
onNotIn(column, values) {
|
|
|
|
return this._not(true).onIn(column, values);
|
|
|
|
},
|
|
|
|
|
|
|
|
orOnIn(column, values) {
|
|
|
|
return this._bool('or').onIn(column, values);
|
|
|
|
},
|
|
|
|
|
|
|
|
orOnNotIn(column, values) {
|
|
|
|
return this._bool('or')._not(true).onIn(column, values);
|
|
|
|
},
|
|
|
|
|
|
|
|
onNull(column) {
|
|
|
|
this.clauses.push({
|
|
|
|
type: 'onNull',
|
|
|
|
column,
|
|
|
|
not: this._not(),
|
|
|
|
bool: this._bool()
|
|
|
|
});
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
orOnNull(callback) {
|
|
|
|
return this._bool('or').onNull(callback);
|
|
|
|
},
|
|
|
|
|
|
|
|
onNotNull(callback) {
|
|
|
|
return this._not(true).onNull(callback);
|
|
|
|
},
|
|
|
|
|
|
|
|
orOnNotNull(callback) {
|
|
|
|
return this._not(true)._bool('or').onNull(callback);
|
|
|
|
},
|
|
|
|
|
|
|
|
onExists(callback) {
|
|
|
|
this.clauses.push({
|
|
|
|
type: 'onExists',
|
|
|
|
value: callback,
|
|
|
|
not: this._not(),
|
|
|
|
bool: this._bool()
|
|
|
|
});
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
orOnExists(callback) {
|
|
|
|
return this._bool('or').onExists(callback);
|
|
|
|
},
|
|
|
|
|
|
|
|
onNotExists(callback) {
|
|
|
|
return this._not(true).onExists(callback);
|
|
|
|
},
|
|
|
|
|
|
|
|
orOnNotExists(callback) {
|
|
|
|
return this._not(true)._bool('or').onExists(callback);
|
|
|
|
},
|
|
|
|
|
2016-03-02 17:07:05 +01:00
|
|
|
// Explicitly set the type of join, useful within a function when creating a grouped join.
|
2016-05-17 01:01:34 +10:00
|
|
|
type(type) {
|
2016-03-02 17:07:05 +01:00
|
|
|
this.joinType = type;
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
2016-05-17 01:01:34 +10:00
|
|
|
_bool(bool) {
|
2016-03-02 17:07:05 +01:00
|
|
|
if (arguments.length === 1) {
|
|
|
|
this._boolFlag = bool;
|
|
|
|
return this;
|
|
|
|
}
|
2016-05-17 01:01:34 +10:00
|
|
|
const ret = this._boolFlag || 'and';
|
2016-03-02 17:07:05 +01:00
|
|
|
this._boolFlag = 'and';
|
|
|
|
return ret;
|
2016-09-19 18:20:12 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
_not(val) {
|
|
|
|
if (arguments.length === 1) {
|
|
|
|
this._notFlag = val;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
const ret = this._notFlag;
|
|
|
|
this._notFlag = false;
|
|
|
|
return ret;
|
|
|
|
},
|
2016-03-02 17:07:05 +01:00
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
Object.defineProperty(JoinClause.prototype, 'or', {
|
2016-05-17 01:01:34 +10:00
|
|
|
get () {
|
2016-03-02 17:07:05 +01:00
|
|
|
return this._bool('or');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2016-09-19 18:20:12 +02:00
|
|
|
JoinClause.prototype.andOn = JoinClause.prototype.on
|
|
|
|
JoinClause.prototype.andOnIn = JoinClause.prototype.onIn
|
|
|
|
JoinClause.prototype.andOnNotIn = JoinClause.prototype.onNotIn
|
|
|
|
JoinClause.prototype.andOnNull = JoinClause.prototype.onNull
|
|
|
|
JoinClause.prototype.andOnNotNull = JoinClause.prototype.onNotNull
|
|
|
|
JoinClause.prototype.andOnExists = JoinClause.prototype.onExists
|
|
|
|
JoinClause.prototype.andOnNotExists = JoinClause.prototype.onNotExists
|
|
|
|
JoinClause.prototype.andOnBetween = JoinClause.prototype.onBetween
|
|
|
|
JoinClause.prototype.andOnNotBetween = JoinClause.prototype.onNotBetween
|
|
|
|
|
2016-05-17 01:01:34 +10:00
|
|
|
export default JoinClause;
|