UNPKG

6.08 kBJavaScriptView Raw
1const assert = require('assert');
2
3// JoinClause
4// -------
5
6// The "JoinClause" is an object holding any necessary info about a join,
7// including the type, and any associated tables & columns being joined.
8function JoinClause(table, type, schema) {
9 this.schema = schema;
10 this.table = table;
11 this.joinType = type;
12 this.and = this;
13 this.clauses = [];
14}
15
16function getClauseFromArguments(compilerType, bool, first, operator, second) {
17 let data = null;
18
19 if (typeof first === 'function') {
20 data = {
21 type: 'onWrapped',
22 value: first,
23 bool: bool,
24 };
25 } else {
26 switch (arguments.length) {
27 case 3: {
28 data = { type: 'onRaw', value: first, bool };
29 break;
30 }
31 case 4:
32 data = {
33 type: compilerType,
34 column: first,
35 operator: '=',
36 value: operator,
37 bool,
38 };
39 break;
40 default:
41 data = {
42 type: compilerType,
43 column: first,
44 operator,
45 value: second,
46 bool,
47 };
48 }
49 }
50
51 return data;
52}
53
54Object.assign(JoinClause.prototype, {
55 grouping: 'join',
56
57 // Adds an "on" clause to the current join object.
58 on(first) {
59 if (typeof first === 'object' && typeof first.toSQL !== 'function') {
60 const keys = Object.keys(first);
61 let i = -1;
62 const method = this._bool() === 'or' ? 'orOn' : 'on';
63 while (++i < keys.length) {
64 this[method](keys[i], first[keys[i]]);
65 }
66 return this;
67 }
68
69 const data = getClauseFromArguments('onBasic', this._bool(), ...arguments);
70
71 if (data) {
72 this.clauses.push(data);
73 }
74
75 return this;
76 },
77
78 // Adds a "using" clause to the current join.
79 using(column) {
80 return this.clauses.push({ type: 'onUsing', column, bool: this._bool() });
81 },
82
83 /*// Adds an "and on" clause to the current join object.
84 andOn() {
85 return this.on.apply(this, arguments);
86 },*/
87
88 // Adds an "or on" clause to the current join object.
89 orOn(first, operator, second) {
90 return this._bool('or').on.apply(this, arguments);
91 },
92
93 onVal(first) {
94 if (typeof first === 'object' && typeof first.toSQL !== 'function') {
95 const keys = Object.keys(first);
96 let i = -1;
97 const method = this._bool() === 'or' ? 'orOnVal' : 'onVal';
98 while (++i < keys.length) {
99 this[method](keys[i], first[keys[i]]);
100 }
101 return this;
102 }
103
104 const data = getClauseFromArguments('onVal', this._bool(), ...arguments);
105
106 if (data) {
107 this.clauses.push(data);
108 }
109
110 return this;
111 },
112
113 andOnVal() {
114 return this.onVal(...arguments);
115 },
116
117 orOnVal() {
118 return this._bool('or').onVal(...arguments);
119 },
120
121 onBetween(column, values) {
122 assert(
123 Array.isArray(values),
124 'The second argument to onBetween must be an array.'
125 );
126 assert(
127 values.length === 2,
128 'You must specify 2 values for the onBetween clause'
129 );
130 this.clauses.push({
131 type: 'onBetween',
132 column,
133 value: values,
134 bool: this._bool(),
135 not: this._not(),
136 });
137 return this;
138 },
139
140 onNotBetween(column, values) {
141 return this._not(true).onBetween(column, values);
142 },
143
144 orOnBetween(column, values) {
145 return this._bool('or').onBetween(column, values);
146 },
147
148 orOnNotBetween(column, values) {
149 return this._bool('or')
150 ._not(true)
151 .onBetween(column, values);
152 },
153
154 onIn(column, values) {
155 if (Array.isArray(values) && values.length === 0) return this.on(1, '=', 0);
156 this.clauses.push({
157 type: 'onIn',
158 column,
159 value: values,
160 not: this._not(),
161 bool: this._bool(),
162 });
163 return this;
164 },
165
166 onNotIn(column, values) {
167 return this._not(true).onIn(column, values);
168 },
169
170 orOnIn(column, values) {
171 return this._bool('or').onIn(column, values);
172 },
173
174 orOnNotIn(column, values) {
175 return this._bool('or')
176 ._not(true)
177 .onIn(column, values);
178 },
179
180 onNull(column) {
181 this.clauses.push({
182 type: 'onNull',
183 column,
184 not: this._not(),
185 bool: this._bool(),
186 });
187 return this;
188 },
189
190 orOnNull(callback) {
191 return this._bool('or').onNull(callback);
192 },
193
194 onNotNull(callback) {
195 return this._not(true).onNull(callback);
196 },
197
198 orOnNotNull(callback) {
199 return this._not(true)
200 ._bool('or')
201 .onNull(callback);
202 },
203
204 onExists(callback) {
205 this.clauses.push({
206 type: 'onExists',
207 value: callback,
208 not: this._not(),
209 bool: this._bool(),
210 });
211 return this;
212 },
213
214 orOnExists(callback) {
215 return this._bool('or').onExists(callback);
216 },
217
218 onNotExists(callback) {
219 return this._not(true).onExists(callback);
220 },
221
222 orOnNotExists(callback) {
223 return this._not(true)
224 ._bool('or')
225 .onExists(callback);
226 },
227
228 // Explicitly set the type of join, useful within a function when creating a grouped join.
229 type(type) {
230 this.joinType = type;
231 return this;
232 },
233
234 _bool(bool) {
235 if (arguments.length === 1) {
236 this._boolFlag = bool;
237 return this;
238 }
239 const ret = this._boolFlag || 'and';
240 this._boolFlag = 'and';
241 return ret;
242 },
243
244 _not(val) {
245 if (arguments.length === 1) {
246 this._notFlag = val;
247 return this;
248 }
249 const ret = this._notFlag;
250 this._notFlag = false;
251 return ret;
252 },
253});
254
255Object.defineProperty(JoinClause.prototype, 'or', {
256 get() {
257 return this._bool('or');
258 },
259});
260
261JoinClause.prototype.andOn = JoinClause.prototype.on;
262JoinClause.prototype.andOnIn = JoinClause.prototype.onIn;
263JoinClause.prototype.andOnNotIn = JoinClause.prototype.onNotIn;
264JoinClause.prototype.andOnNull = JoinClause.prototype.onNull;
265JoinClause.prototype.andOnNotNull = JoinClause.prototype.onNotNull;
266JoinClause.prototype.andOnExists = JoinClause.prototype.onExists;
267JoinClause.prototype.andOnNotExists = JoinClause.prototype.onNotExists;
268JoinClause.prototype.andOnBetween = JoinClause.prototype.onBetween;
269JoinClause.prototype.andOnNotBetween = JoinClause.prototype.onNotBetween;
270
271module.exports = JoinClause;