UNPKG

7.47 kBJavaScriptView Raw
1/**
2 * @copyright Copyright (c) 2019 Maxim Khorin <maksimovichu@gmail.com>
3 */
4'use strict';
5
6const Base = require('../base/Base');
7
8module.exports = class Query extends Base {
9
10 _index = null;
11 _limit = null;
12 _offset = null;
13 _order = null;
14 _where = null;
15
16 db (db) {
17 this._db = db;
18 return this;
19 }
20
21 from (table) {
22 this._from = table;
23 return this;
24 }
25
26 index (column) {
27 this._index = column;
28 return this;
29 }
30
31 raw () {
32 return this;
33 }
34
35 getDb () {
36 return this._db;
37 }
38
39 getTable () {
40 return this._from;
41 }
42
43 getIndex () {
44 return this._index;
45 }
46
47 getRaw () {
48 return this._raw;
49 }
50
51 // SELECT
52
53 select (data) {
54 if (Array.isArray(data)) {
55 for (const item of data) {
56 this.addSelect(item);
57 }
58 } else if (typeof data === 'string') {
59 this._select = {[data]: 1};
60 } else {
61 this._select = data; // { attr1: 1, attr2: 0, ... }
62 }
63 return this;
64 }
65
66 addSelect (data) {
67 if (Array.isArray(data)) {
68 for (const item of data) {
69 this.addSelect(item);
70 }
71 } else if (!this._select) {
72 return this.select(data);
73 }
74 if (typeof data === 'string') {
75 this._select[data] = 1;
76 } else {
77 Object.assign(this._select, data);
78 }
79 return this;
80 }
81
82 // WHERE
83
84 where (condition) {
85 this._where = condition;
86 return this;
87 }
88
89 getWhere () {
90 return this._where;
91 }
92
93 addWhere (operator, ...conditions) {
94 if (!this._where) {
95 this._where = conditions.length > 1 ? [operator, ...conditions] : conditions[0];
96 } else if (this._where[0] === operator) {
97 this._where.push(...conditions);
98 } else {
99 this._where = [operator, this._where, ...conditions];
100 }
101 }
102
103 and () {
104 if (arguments[0]) {
105 this.addWhere('AND', ...arguments);
106 }
107 return this;
108 }
109
110 or () {
111 if (arguments[0]) {
112 this.addWhere('OR', ...arguments);
113 }
114 return this;
115 }
116
117 filter (condition) {
118 return this.where(this.filterCondition(condition));
119 }
120
121 andFilter (condition) {
122 return this.and(this.filterCondition(condition));
123 }
124
125 orFilter (condition) {
126 return this.or(this.filterCondition(condition));
127 }
128
129 andNotIn (key, value) {
130 return this.and(['NOT IN', key, value]);
131 }
132
133 // ORDER
134 /**
135 * @param data - { attr1: 1, attr2: -1 }
136 */
137 order (data) {
138 this._order = data;
139 return this;
140 }
141
142 addOrder (data) {
143 this._order = Object.assign(this._order || {}, data);
144 return this;
145 }
146
147 orderByKeys (keys) {
148 this._orderByKeys = keys;
149 return this;
150 }
151
152 // LIMIT
153
154 limit (limit) {
155 this._limit = limit === null ? null : parseInt(limit);
156 return this;
157 }
158
159 getLimit () {
160 return this._limit;
161 }
162
163 // OFFSET
164
165 offset (value) {
166 this._offset = value === null ? null : parseInt(value);
167 return this;
168 }
169
170 getOffset () {
171 return this._offset;
172 }
173
174 // COMMAND
175
176 all () {
177 return this._db.queryAll(this);
178 }
179
180 one () {
181 return this._db.queryOne(this);
182 }
183
184 column (key) {
185 return this._db.queryColumn(this, key);
186 }
187
188 distinct (key) {
189 return this._db.queryDistinct(this, key);
190 }
191
192 scalar (key) {
193 return this._db.queryScalar(this, key);
194 }
195
196 insert (data) {
197 return this._db.queryInsert(this, data);
198 }
199
200 update (data) {
201 return this._db.queryUpdate(this, data);
202 }
203
204 updateAll (data) {
205 return this._db.queryUpdateAll(this, data);
206 }
207
208 upsert (data) {
209 return this._db.queryUpsert(this, data);
210 }
211
212 delete () {
213 return this._db.queryDelete(this);
214 }
215
216 count () {
217 return this._db.queryCount(this);
218 }
219
220 max (key) {
221 this._order = {[key]: -1};
222 this._limit = 1;
223 return this.scalar(key);
224 }
225
226 min (key) {
227 this._order = {[key]: 1};
228 this._limit = 1;
229 return this.scalar(key);
230 }
231
232 //
233
234 isEmptyValue (value) {
235 return value === undefined || value === null || value === ''
236 || (typeof value === 'string' && value.trim() === '')
237 || (typeof value === 'object' && !Object.values(value).length);
238 }
239
240 prepare () {
241 }
242
243 afterBuild () {
244 // restore _where after filter by via relation models
245 if (this._whereBeforeFilter !== undefined) {
246 this._where = this._whereBeforeFilter;
247 delete this._whereBeforeFilter;
248 }
249 }
250
251 populate (docs) {
252 return this._index
253 ? QueryHelper.indexObjects(docs, this._index)
254 : docs;
255 }
256
257 filterCondition (data) {
258 return Array.isArray(data)
259 ? this.filterOperatorCondition(data)
260 : data ? this.filterHashCondition(data)
261 : null;
262 }
263
264 // operator format: [operator, operand 1, operand 2, ...]
265 filterOperatorCondition (data) {
266 switch (data[0]) {
267 case 'NOT':
268 case 'AND':
269 case 'OR':
270 return this.filterSerialCondition();
271 case 'BETWEEN':
272 case 'NOT BETWEEN':
273 return !this.isEmptyValue(data[1]) && !this.isEmptyValue(data[2]) ? data : null;
274 }
275 return this.isEmptyValue(data[1]) ? null : data;
276 }
277
278 filterSerialCondition (data) { // OR AND NOT
279 for (let i = data.length - 1; i > 0; --i) {
280 const item = this.filterCondition(data[i]);
281 if (this.isEmptyValue(item)) {
282 data.splice(i, 1);
283 } else {
284 data[i] = item;
285 }
286 }
287 return data.length > 1 ? data : null;
288 }
289
290 // hash format: { column1: value1, column2: value2, ... }
291 filterHashCondition (data) {
292 const result = {};
293 for (const key of Object.keys(data)) {
294 if (!this.isEmptyValue(data[key])) {
295 result[key] = data[key];
296 }
297 }
298 return Object.values(result).length ? result : null;
299 }
300
301 // sort an array of objects with a key attribute by an array of keys
302 sortOrderByKeys (docs) {
303 const keys = this._orderByKeys;
304 if (!Array.isArray(keys) || keys.length < 2) {
305 return docs;
306 }
307 // documents can be with equal key values
308 const data = IndexHelper.indexObjectArrays(docs, this.refKey);
309 const result = [];
310 for (const key of keys) {
311 if (Array.isArray(data[key])) {
312 result.push(...data[key]);
313 delete data[key]; // ignore same keys
314 }
315 }
316 return result;
317 }
318};
319
320const IndexHelper = require('../helper/IndexHelper');
321const QueryHelper = require('../helper/QueryHelper');
\No newline at end of file