1 | "use strict";
|
2 | var __importDefault = (this && this.__importDefault) || function (mod) {
|
3 | return (mod && mod.__esModule) ? mod : { "default": mod };
|
4 | };
|
5 | Object.defineProperty(exports, "__esModule", { value: true });
|
6 | exports.Query = void 0;
|
7 | const stream_1 = __importDefault(require("stream"));
|
8 | const lodash_1 = __importDefault(require("lodash"));
|
9 |
|
10 |
|
11 |
|
12 | class Query {
|
13 | |
14 |
|
15 |
|
16 | constructor(model) {
|
17 | this._find_single_id = false;
|
18 | this._used = false;
|
19 | this._model = model;
|
20 | this._name = model._name;
|
21 | this._connection = model._connection;
|
22 | this._adapter = model._connection._adapter;
|
23 | this._ifs = [];
|
24 | this._current_if = true;
|
25 | this._conditions = [];
|
26 | this._includes = [];
|
27 | this._options = {
|
28 | conditions_of_group: [],
|
29 | lean: model.lean_query,
|
30 | node: 'master',
|
31 | select_single: false,
|
32 | };
|
33 | }
|
34 | clone() {
|
35 | const cloned = new Query(this._model);
|
36 | cloned._ifs = lodash_1.default.cloneDeep(this._ifs);
|
37 | cloned._current_if = this._current_if;
|
38 | cloned._conditions = lodash_1.default.cloneDeep(this._conditions);
|
39 | cloned._includes = lodash_1.default.cloneDeep(this._includes);
|
40 | cloned._options = lodash_1.default.cloneDeep(this._options);
|
41 | cloned._find_single_id = this._find_single_id;
|
42 | cloned._used = false;
|
43 | return cloned;
|
44 | }
|
45 | find(id) {
|
46 | if (!this._current_if) {
|
47 | return this;
|
48 | }
|
49 | if (Array.isArray(id)) {
|
50 | this._id = lodash_1.default.uniq(id);
|
51 | this._find_single_id = false;
|
52 | }
|
53 | else {
|
54 | this._id = id;
|
55 | this._find_single_id = true;
|
56 | }
|
57 | return this;
|
58 | }
|
59 | |
60 |
|
61 |
|
62 | findPreserve(ids) {
|
63 | if (!this._current_if) {
|
64 | return this;
|
65 | }
|
66 | this._id = lodash_1.default.uniq(ids);
|
67 | this._find_single_id = false;
|
68 | this._preserve_order_ids = ids;
|
69 | return this;
|
70 | }
|
71 | |
72 |
|
73 |
|
74 | near(target) {
|
75 | if (!this._current_if) {
|
76 | return this;
|
77 | }
|
78 | this._options.near = target;
|
79 | return this;
|
80 | }
|
81 | |
82 |
|
83 |
|
84 | where(condition) {
|
85 | if (!this._current_if) {
|
86 | return this;
|
87 | }
|
88 | if (Array.isArray(condition)) {
|
89 | condition.forEach((cond) => {
|
90 | this._addCondition(cond);
|
91 | });
|
92 | }
|
93 | else if (condition != null) {
|
94 | this._addCondition(condition);
|
95 | }
|
96 | return this;
|
97 | }
|
98 | select(columns) {
|
99 | if (!this._current_if) {
|
100 | return this;
|
101 | }
|
102 | this._options.select_columns = undefined;
|
103 | this._options.select_single = false;
|
104 | if (columns != null) {
|
105 | if (typeof columns === 'string') {
|
106 | columns = columns.split(/\s+/);
|
107 | columns.push('id');
|
108 | }
|
109 | if (columns.length > 0) {
|
110 | this._options.select_columns = columns;
|
111 | }
|
112 | }
|
113 | return this;
|
114 | }
|
115 | selectSingle(column) {
|
116 | if (!this._current_if) {
|
117 | return this;
|
118 | }
|
119 | this._options.select_columns = [column];
|
120 | this._options.select_single = true;
|
121 | return this;
|
122 | }
|
123 | |
124 |
|
125 |
|
126 | order(orders) {
|
127 | if (!this._current_if) {
|
128 | return this;
|
129 | }
|
130 | this._options.orders = orders;
|
131 | return this;
|
132 | }
|
133 | group(group_by, fields) {
|
134 | if (!this._current_if) {
|
135 | return this;
|
136 | }
|
137 | this._options.group_by = undefined;
|
138 | if (group_by) {
|
139 | if (typeof group_by === 'string') {
|
140 | group_by = group_by.split(/\s+/);
|
141 | }
|
142 | this._options.group_by = group_by;
|
143 | }
|
144 | this._options.group_fields = fields;
|
145 | return this;
|
146 | }
|
147 | |
148 |
|
149 |
|
150 |
|
151 |
|
152 | one() {
|
153 | if (!this._current_if) {
|
154 | return this;
|
155 | }
|
156 | this._options.limit = 1;
|
157 | this._options.one = true;
|
158 | return this;
|
159 | }
|
160 | |
161 |
|
162 |
|
163 | limit(limit) {
|
164 | if (!this._current_if) {
|
165 | return this;
|
166 | }
|
167 | this._options.limit = limit;
|
168 | return this;
|
169 | }
|
170 | |
171 |
|
172 |
|
173 | skip(skip) {
|
174 | if (!this._current_if) {
|
175 | return this;
|
176 | }
|
177 | this._options.skip = skip;
|
178 | return this;
|
179 | }
|
180 | |
181 |
|
182 |
|
183 |
|
184 | lean(lean = true) {
|
185 | if (!this._current_if) {
|
186 | return this;
|
187 | }
|
188 | this._options.lean = lean;
|
189 | return this;
|
190 | }
|
191 | |
192 |
|
193 |
|
194 |
|
195 | if(condition) {
|
196 | this._ifs.push(condition);
|
197 | this._current_if = this._current_if && condition;
|
198 | return this;
|
199 | }
|
200 | |
201 |
|
202 |
|
203 |
|
204 |
|
205 | endif() {
|
206 | this._ifs.pop();
|
207 | this._current_if = true;
|
208 | for (const condition of this._ifs) {
|
209 | this._current_if = this._current_if && condition;
|
210 | }
|
211 | return this;
|
212 | }
|
213 | |
214 |
|
215 |
|
216 |
|
217 |
|
218 |
|
219 |
|
220 |
|
221 | cache(options) {
|
222 | if (!this._current_if) {
|
223 | return this;
|
224 | }
|
225 | this._options.cache = options;
|
226 | return this;
|
227 | }
|
228 | |
229 |
|
230 |
|
231 | include(column, select) {
|
232 | if (!this._current_if) {
|
233 | return this;
|
234 | }
|
235 | this._includes.push({ column, select });
|
236 | return this;
|
237 | }
|
238 | transaction(transaction) {
|
239 | this._options.transaction = transaction;
|
240 | return this;
|
241 | }
|
242 | using(node) {
|
243 | this._options.node = node;
|
244 | return this;
|
245 | }
|
246 | index_hint(hint) {
|
247 | this._options.index_hint = hint;
|
248 | return this;
|
249 | }
|
250 | |
251 |
|
252 |
|
253 |
|
254 |
|
255 | async exec(options) {
|
256 | this._setUsed();
|
257 | await this._model._checkReady();
|
258 | if (this._options.cache && this._options.cache.key) {
|
259 | try {
|
260 |
|
261 | return await this._model._loadFromCache(this._options.cache.key, this._options.cache.refresh);
|
262 | }
|
263 | catch (error) {
|
264 |
|
265 | const records = await this._execAndInclude(options);
|
266 |
|
267 | await this._model._saveToCache(this._options.cache.key, this._options.cache.ttl, records);
|
268 | return records;
|
269 | }
|
270 | }
|
271 | else {
|
272 | return await this._execAndInclude(options);
|
273 | }
|
274 | }
|
275 | |
276 |
|
277 |
|
278 |
|
279 |
|
280 | stream() {
|
281 | this._setUsed();
|
282 | const transformer = new stream_1.default.Transform({ objectMode: true });
|
283 | transformer._transform = function (chunk, encoding, callback) {
|
284 | this.push(chunk);
|
285 | callback();
|
286 | };
|
287 | this._model._checkReady().then(() => {
|
288 | this._adapter.stream(this._name, this._conditions, this._getAdapterFindOptions())
|
289 | .on('error', (error) => {
|
290 | transformer.emit('error', error);
|
291 | }).pipe(transformer);
|
292 | });
|
293 | return transformer;
|
294 | }
|
295 | |
296 |
|
297 |
|
298 | async explain() {
|
299 | this._options.cache = undefined;
|
300 | this._options.explain = true;
|
301 | this._includes = [];
|
302 | return await this.exec({ skip_log: true });
|
303 | }
|
304 | then(onfulfilled, onrejected) {
|
305 | return this.exec().then(onfulfilled, onrejected);
|
306 | }
|
307 | |
308 |
|
309 |
|
310 |
|
311 | async count() {
|
312 | this._setUsed();
|
313 | await this._model._checkReady();
|
314 | if (this._id || this._find_single_id) {
|
315 | this._conditions.push({ id: this._id });
|
316 | delete this._id;
|
317 | }
|
318 | return await this._adapter.count(this._name, this._conditions, this._options);
|
319 | }
|
320 | |
321 |
|
322 |
|
323 |
|
324 | async update(updates) {
|
325 | this._setUsed();
|
326 | await this._model._checkReady();
|
327 | const errors = [];
|
328 | const data = {};
|
329 | this._validateAndBuildSaveData(errors, data, updates, '', updates);
|
330 | if (errors.length > 0) {
|
331 | throw new Error(errors.join(','));
|
332 | }
|
333 | if (this._id || this._find_single_id) {
|
334 | this._conditions.push({ id: this._id });
|
335 | delete this._id;
|
336 | }
|
337 | this._connection.log(this._name, 'update', { data, conditions: this._conditions, options: this._options });
|
338 | return await this._adapter.updatePartial(this._name, data, this._conditions, this._options);
|
339 | }
|
340 | |
341 |
|
342 |
|
343 |
|
344 | async upsert(updates) {
|
345 | this._setUsed();
|
346 | await this._model._checkReady();
|
347 | const errors = [];
|
348 | const data = {};
|
349 | this._validateAndBuildSaveData(errors, data, updates, '', updates);
|
350 | if (errors.length > 0) {
|
351 | throw new Error(errors.join(','));
|
352 | }
|
353 | if (this._id || this._find_single_id) {
|
354 | this._conditions.push({ id: this._id });
|
355 | delete this._id;
|
356 | }
|
357 | this._connection.log(this._name, 'upsert', { data, conditions: this._conditions, options: this._options });
|
358 | return await this._adapter.upsert(this._name, data, this._conditions, this._options);
|
359 | }
|
360 | |
361 |
|
362 |
|
363 |
|
364 | async delete(options) {
|
365 | this._setUsed();
|
366 | await this._model._checkReady();
|
367 | if (this._id || this._find_single_id) {
|
368 | this._conditions.push({ id: this._id });
|
369 | delete this._id;
|
370 | }
|
371 | if (!(options && options.skip_log)) {
|
372 | this._connection.log(this._name, 'delete', { conditions: this._conditions });
|
373 | }
|
374 | await this._doArchiveAndIntegrity(options);
|
375 | return await this._adapter.delete(this._name, this._conditions, { transaction: this._options.transaction });
|
376 | }
|
377 | async _exec(find_options, options) {
|
378 | if (this._find_single_id && this._conditions.length === 0) {
|
379 | if (!(options && options.skip_log)) {
|
380 | this._connection.log(this._name, 'find by id', { id: this._id, options: find_options });
|
381 | }
|
382 | if (!this._id) {
|
383 | throw new Error('not found');
|
384 | }
|
385 | let record;
|
386 | try {
|
387 | record = await this._adapter.findById(this._name, this._id, find_options);
|
388 | }
|
389 | catch (error) {
|
390 | throw new Error('not found');
|
391 | }
|
392 | if (!record) {
|
393 | throw new Error('not found');
|
394 | }
|
395 | return record;
|
396 | }
|
397 | let expected_count;
|
398 | if (this._id || this._find_single_id) {
|
399 | if (Array.isArray(this._id)) {
|
400 | if (this._id.length === 0) {
|
401 | return [];
|
402 | }
|
403 | this._conditions.push({ id: { $in: this._id } });
|
404 | expected_count = this._id.length;
|
405 | }
|
406 | else {
|
407 | this._conditions.push({ id: this._id });
|
408 | expected_count = 1;
|
409 | }
|
410 | }
|
411 | if (!(options && options.skip_log)) {
|
412 | this._connection.log(this._name, 'find', { conditions: this._conditions, options: find_options });
|
413 | }
|
414 | let records = await this._adapter.find(this._name, this._conditions, find_options);
|
415 | if (expected_count != null) {
|
416 | if (records.length !== expected_count) {
|
417 | throw new Error('not found');
|
418 | }
|
419 | }
|
420 | if (this._preserve_order_ids) {
|
421 | records = this._preserve_order_ids.map((id) => {
|
422 | for (const record of records) {
|
423 | if (record.id === id) {
|
424 | return record;
|
425 | }
|
426 | }
|
427 | });
|
428 | }
|
429 | if (this._options.one) {
|
430 | if (records.length > 1) {
|
431 | throw new Error('unknown error');
|
432 | }
|
433 | if (records.length === 1) {
|
434 | return records[0];
|
435 | }
|
436 | else {
|
437 | return null;
|
438 | }
|
439 | }
|
440 | else {
|
441 | return records;
|
442 | }
|
443 | }
|
444 | _getAdapterFindOptions() {
|
445 | const select = [];
|
446 | const select_raw = [];
|
447 | if (this._options.select_columns) {
|
448 | const schema_columns = Object.keys(this._model._schema);
|
449 | const intermediate_paths = this._model._intermediate_paths;
|
450 | this._options.select_columns.forEach((column) => {
|
451 | if (schema_columns.indexOf(column) >= 0) {
|
452 | select.push(column);
|
453 | select_raw.push(column);
|
454 | }
|
455 | else if (intermediate_paths[column]) {
|
456 |
|
457 | select_raw.push(column);
|
458 | column += '.';
|
459 | schema_columns.forEach((sc) => {
|
460 | if (sc.startsWith(column)) {
|
461 | select.push(sc);
|
462 | }
|
463 | });
|
464 | }
|
465 | });
|
466 | }
|
467 | let group_by;
|
468 | if (this._options.group_by) {
|
469 | group_by = this._options.group_by.map((column) => {
|
470 | return this._model._schema[column]._dbname_us;
|
471 | }).filter((column) => column != null);
|
472 | }
|
473 | const orders = [];
|
474 | if (typeof this._options.orders === 'string') {
|
475 | const avaliable_columns = ['id'];
|
476 | avaliable_columns.push(...Object.keys(this._model._schema));
|
477 | if (this._options.group_fields) {
|
478 | avaliable_columns.push(...Object.keys(this._options.group_fields));
|
479 | }
|
480 | this._options.orders.split(/\s+/).forEach((order) => {
|
481 | let asc = true;
|
482 | if (order.startsWith('-')) {
|
483 | asc = false;
|
484 | order = order.slice(1);
|
485 | }
|
486 | if (avaliable_columns.indexOf(order) >= 0) {
|
487 | orders.push(asc ? order : '-' + order);
|
488 | }
|
489 | });
|
490 | }
|
491 | return Object.assign({ conditions_of_group: this._options.conditions_of_group, explain: this._options.explain, group_by, group_fields: this._options.group_fields, lean: this._options.lean, limit: this._options.limit, near: this._options.near, node: this._options.node, index_hint: this._options.index_hint, orders, skip: this._options.skip, transaction: this._options.transaction }, (select_raw.length > 0 && { select, select_raw }));
|
492 | }
|
493 | async _execAndInclude(options) {
|
494 | const records = await this._exec(this._getAdapterFindOptions(), options);
|
495 | if (this._options.select_single) {
|
496 | if (Array.isArray(records)) {
|
497 | return lodash_1.default.map(records, this._options.select_columns[0]);
|
498 | }
|
499 | else {
|
500 | if (records) {
|
501 | return records[this._options.select_columns[0]];
|
502 | }
|
503 | else {
|
504 | return null;
|
505 | }
|
506 | }
|
507 | }
|
508 | await Promise.all(this._includes.map(async (include) => {
|
509 | await this._connection.fetchAssociated(records, include.column, include.select, {
|
510 | lean: this._options.lean,
|
511 | model: this._model,
|
512 | transaction: this._options.transaction,
|
513 | });
|
514 | }));
|
515 | return records;
|
516 | }
|
517 | _validateAndBuildSaveData(errors, data, updates, path, object) {
|
518 | const model = this._model;
|
519 | const schema = model._schema;
|
520 | for (let column in object) {
|
521 | const property = schema[path + column];
|
522 | if (property) {
|
523 | try {
|
524 | model._validateColumn(updates, path + column, property, true);
|
525 | }
|
526 | catch (error) {
|
527 | errors.push(error.message);
|
528 | }
|
529 | model._buildSaveDataColumn(data, updates, path + column, property, true);
|
530 | }
|
531 | else if (!object[column] && model._intermediate_paths[column]) {
|
532 |
|
533 | column += '.';
|
534 | const temp = {};
|
535 | Object.keys(schema).forEach((sc) => {
|
536 | if (sc.startsWith(column)) {
|
537 | temp[sc.substr(column.length)] = null;
|
538 | }
|
539 | });
|
540 | this._validateAndBuildSaveData(errors, data, updates, path + column, temp);
|
541 | }
|
542 | else if (typeof object[column] === 'object') {
|
543 | this._validateAndBuildSaveData(errors, data, updates, path + column + '.', object[column]);
|
544 | }
|
545 | }
|
546 | }
|
547 | async _doIntegrityActions(integrities, ids) {
|
548 | const promises = integrities.map(async (integrity) => {
|
549 | if (integrity.type === 'parent_nullify') {
|
550 | await integrity.child.update(lodash_1.default.zipObject([integrity.column], [null]), lodash_1.default.zipObject([integrity.column], [ids]));
|
551 | }
|
552 | else if (integrity.type === 'parent_restrict') {
|
553 | const count = (await integrity.child.count(lodash_1.default.zipObject([integrity.column], [ids])));
|
554 | if (count > 0) {
|
555 | throw new Error('rejected');
|
556 | }
|
557 | }
|
558 | else if (integrity.type === 'parent_delete') {
|
559 | await integrity.child.delete(lodash_1.default.zipObject([integrity.column], [ids]));
|
560 | }
|
561 | });
|
562 | await Promise.all(promises);
|
563 | }
|
564 | async _doArchiveAndIntegrity(options) {
|
565 | const need_archive = this._model.archive;
|
566 | const integrities = this._model._integrities.filter((integrity) => integrity.type.substr(0, 7) === 'parent_');
|
567 | const need_child_archive = integrities.some((integrity) => integrity.child.archive);
|
568 | const need_integrity = need_child_archive || (integrities.length > 0 && !this._adapter.native_integrity);
|
569 | if (!need_archive && !need_integrity) {
|
570 | return;
|
571 | }
|
572 |
|
573 | const query = this._model.where(this._conditions);
|
574 | if (!need_archive) {
|
575 | query.select('');
|
576 | }
|
577 | const records = await query.exec({ skip_log: options && options.skip_log });
|
578 | if (need_archive) {
|
579 | const archive_records = records.map((record) => {
|
580 | return { model: this._name, data: record };
|
581 | });
|
582 | await this._connection.models._Archive.createBulk(archive_records);
|
583 | }
|
584 | if (!need_integrity) {
|
585 | return;
|
586 | }
|
587 | if (records.length === 0) {
|
588 | return;
|
589 | }
|
590 | const ids = records.map((record) => record.id);
|
591 | await this._doIntegrityActions(integrities, ids);
|
592 | }
|
593 | _addCondition(condition) {
|
594 | if (this._options.group_fields) {
|
595 | const keys = Object.keys(condition);
|
596 | if (keys.length === 1 && Object.prototype.hasOwnProperty.call(this._options.group_fields, keys[0])) {
|
597 | this._options.conditions_of_group.push(condition);
|
598 | }
|
599 | }
|
600 | else {
|
601 | this._conditions.push(condition);
|
602 | }
|
603 | }
|
604 | _setUsed() {
|
605 | if (this._used) {
|
606 | throw new Error('Query object is already used');
|
607 | }
|
608 | this._used = true;
|
609 | }
|
610 | }
|
611 | exports.Query = Query;
|