mythix-orm 1.8.3 → 1.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/connection/connection-base.d.ts +1 -0
- package/lib/connection/connection-base.js +71 -20
- package/lib/connection/literals/literal-base.js +20 -1
- package/lib/connection/query-generator-base.d.ts +1 -15
- package/lib/connection/query-generator-base.js +56 -584
- package/lib/field.js +9 -2
- package/lib/model.d.ts +17 -2
- package/lib/model.js +63 -71
- package/lib/query-engine/field-scope.js +58 -25
- package/lib/query-engine/model-scope.js +168 -35
- package/lib/query-engine/query-engine-base.js +153 -47
- package/lib/query-engine/query-engine.d.ts +37 -28
- package/lib/query-engine/query-engine.js +75 -71
- package/lib/types/concrete/datetime-type.js +1 -1
- package/lib/types/concrete/serialized-type.js +2 -2
- package/lib/types/virtual/model-type.js +3 -3
- package/lib/types/virtual/models-type.js +1 -1
- package/lib/types/virtual/relational-type-base.js +26 -26
- package/lib/utils/index.js +2 -2
- package/lib/utils/misc-utils.d.ts +0 -1
- package/lib/utils/misc-utils.js +0 -24
- package/lib/utils/model-utils.js +20 -19
- package/lib/utils/query-utils.d.ts +7 -0
- package/lib/utils/query-utils.js +175 -4
- package/package.json +1 -1
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const Nife
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const { Model: ModelBase } = require('../model');
|
|
7
|
-
const LiteralBase = Literals.LiteralBase;
|
|
8
|
-
const LITERAL_IS_DISTINCT_RE = (/^DISTINCT[\s(]/i);
|
|
3
|
+
const Nife = require('nife');
|
|
4
|
+
const Literals = require('./literals');
|
|
5
|
+
const LiteralBase = Literals.LiteralBase;
|
|
9
6
|
|
|
10
7
|
/// The base query generator class.
|
|
11
8
|
///
|
|
@@ -32,14 +29,6 @@ class QueryGeneratorBase {
|
|
|
32
29
|
return newObj;
|
|
33
30
|
}
|
|
34
31
|
|
|
35
|
-
getOptionsCache(options, keyPath, initialValue) {
|
|
36
|
-
return Nife.get(options, `_cache.${keyPath}`, (typeof initialValue === 'function') ? initialValue() : initialValue);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
setOptionsCache(options, keyPath, value) {
|
|
40
|
-
Nife.set(options, `_cache.${keyPath}`, value);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
32
|
escape(...args) {
|
|
44
33
|
return this.connection.escape(...args);
|
|
45
34
|
}
|
|
@@ -94,7 +83,7 @@ class QueryGeneratorBase {
|
|
|
94
83
|
if (options && options.noProjectionAliases)
|
|
95
84
|
return this.getEscapedColumnName(Model, field, options);
|
|
96
85
|
else
|
|
97
|
-
return `${this.getEscapedColumnName(Model, field, options)} AS ${this.getEscapedFieldName(Model, field, options)}`;
|
|
86
|
+
return `${this.getEscapedColumnName(Model, field, options)} AS ${(options && options.as) ? this.escapeID(options.as) : this.getEscapedFieldName(Model, field, options)}`;
|
|
98
87
|
}
|
|
99
88
|
|
|
100
89
|
// eslint-disable-next-line no-unused-vars
|
|
@@ -121,504 +110,55 @@ class QueryGeneratorBase {
|
|
|
121
110
|
return fields;
|
|
122
111
|
}
|
|
123
112
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
let options = _options || {};
|
|
127
|
-
let queryEngineContextID = queryEngine._getTopContextID();
|
|
128
|
-
let cache = this.getOptionsCache(_options, `getAllModelsUsedInQuery.${queryEngineContextID}`);
|
|
129
|
-
if (cache)
|
|
130
|
-
return cache;
|
|
131
|
-
|
|
132
|
-
let Models = new Map();
|
|
133
|
-
let query = queryEngine._getRawQuery();
|
|
134
|
-
|
|
135
|
-
for (let i = 0, il = query.length; i < il; i++) {
|
|
136
|
-
let queryPart = query[i];
|
|
137
|
-
|
|
138
|
-
if (Object.prototype.hasOwnProperty.call(queryPart, 'operator') && queryPart.operator === 'MODEL') {
|
|
139
|
-
let Model = queryPart.Model;
|
|
140
|
-
Models.set(Model, Model);
|
|
141
|
-
} else if (Object.prototype.hasOwnProperty.call(queryPart, 'condition') && queryPart.condition === true) {
|
|
142
|
-
let operatorValue = queryPart.value;
|
|
143
|
-
if (!QueryEngine.isQuery(operatorValue))
|
|
144
|
-
continue;
|
|
145
|
-
|
|
146
|
-
let SubModels = this.getAllModelsUsedInQuery(operatorValue, options);
|
|
147
|
-
for (let j = 0, jl = SubModels.length; j < jl; j++) {
|
|
148
|
-
let Model = SubModels[j];
|
|
149
|
-
Models.set(Model, Model);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
let allModels = Array.from(Models.values());
|
|
155
|
-
|
|
156
|
-
if (_options)
|
|
157
|
-
this.setOptionsCache(_options, `getAllModelsUsedInQuery.${queryEngineContextID}`, allModels);
|
|
158
|
-
|
|
159
|
-
return allModels;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
getProjectionRequiredFields(queryEngine, options) {
|
|
163
|
-
let { order } = this.getOrderLimitOffset(queryEngine, options);
|
|
164
|
-
if (!order)
|
|
165
|
-
return new Map();
|
|
166
|
-
|
|
167
|
-
let orderFieldMap = new Map();
|
|
168
|
-
for (let i = 0, il = order.length; i < il; i++) {
|
|
169
|
-
let field = order[i];
|
|
170
|
-
if (!field)
|
|
171
|
-
continue;
|
|
172
|
-
|
|
173
|
-
if (LiteralBase.isLiteral(field)) {
|
|
174
|
-
let result = field.toString(this.connection, options);
|
|
175
|
-
let projectionField;
|
|
176
|
-
|
|
177
|
-
if (typeof field.getField === 'function')
|
|
178
|
-
projectionField = field.getField(this.connection);
|
|
179
|
-
else
|
|
180
|
-
projectionField = this.parseFieldProjection(result, true);
|
|
181
|
-
|
|
182
|
-
if (projectionField === result) {
|
|
183
|
-
// not able to parse projection
|
|
184
|
-
continue;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
field = projectionField;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
let Field = field.Field || field;
|
|
191
|
-
let Model = Field.Model;
|
|
192
|
-
let modelName = Model.getModelName();
|
|
193
|
-
let fullFieldName = `${modelName}:${Field.fieldName}`;
|
|
194
|
-
|
|
195
|
-
orderFieldMap.set(fullFieldName, {
|
|
196
|
-
projectedName: this.getEscapedProjectionName(Model, Field, options),
|
|
197
|
-
Model: Model,
|
|
198
|
-
modelName: modelName,
|
|
199
|
-
Field: Field,
|
|
200
|
-
fieldName: Field.fieldName,
|
|
201
|
-
direction: '+',
|
|
202
|
-
fullFieldName,
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
return orderFieldMap;
|
|
113
|
+
isFieldIdentifier(str) {
|
|
114
|
+
return (/^"[^"]+"."[^"]+"|"\w+:[\w.]+"/i).test(str);
|
|
207
115
|
}
|
|
208
116
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
// the list of projected fields (required
|
|
214
|
-
// by some databases)
|
|
215
|
-
const distinctSortOrder = (a, b) => {
|
|
216
|
-
let x = LiteralBase.isLiteral(a);
|
|
217
|
-
let y = LiteralBase.isLiteral(b);
|
|
218
|
-
let xStr = (x) ? a.toString(this.connection, options) : a;
|
|
219
|
-
let yStr = (y) ? b.toString(this.connection, options) : b;
|
|
220
|
-
let xIsDistinct = (typeof xStr === 'string' && LITERAL_IS_DISTINCT_RE.test(xStr));
|
|
221
|
-
let yIsDistinct = (typeof yStr === 'string' && LITERAL_IS_DISTINCT_RE.test(yStr));
|
|
222
|
-
|
|
223
|
-
if (xIsDistinct || yIsDistinct) {
|
|
224
|
-
if (xIsDistinct === yIsDistinct)
|
|
225
|
-
return 0;
|
|
226
|
-
|
|
227
|
-
if (xIsDistinct && !yIsDistinct)
|
|
228
|
-
return -1;
|
|
229
|
-
|
|
230
|
-
if (yIsDistinct && !xIsDistinct)
|
|
231
|
-
return 1;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// Neither is a literal, so simply
|
|
235
|
-
// return and continue processing
|
|
236
|
-
if (!x && !y)
|
|
237
|
-
return;
|
|
238
|
-
|
|
239
|
-
// We know one of them must be a literal
|
|
240
|
-
// when a literal is encountered, the
|
|
241
|
-
// sort order is not modified
|
|
242
|
-
return 0;
|
|
243
|
-
};
|
|
244
|
-
|
|
245
|
-
let distinctOrder = distinctSortOrder(a, b);
|
|
246
|
-
if (distinctOrder != null)
|
|
247
|
-
return distinctOrder;
|
|
248
|
-
|
|
249
|
-
let x = (typeof a === 'string') ? a : `${a.modelName}:${a.fieldName}`;
|
|
250
|
-
let y = (typeof b === 'string') ? b : `${b.modelName}:${b.fieldName}`;
|
|
251
|
-
|
|
252
|
-
if (x === y)
|
|
253
|
-
return 0;
|
|
117
|
+
getQueryEngineOrder(queryEngine, _options) {
|
|
118
|
+
let options = _options || {};
|
|
119
|
+
let context = queryEngine.getOperationContext();
|
|
120
|
+
let order = context.order;
|
|
254
121
|
|
|
255
|
-
|
|
256
|
-
});
|
|
122
|
+
return (order && order.size) ? order : this.connection.getDefaultOrder(context.rootModel, options);
|
|
257
123
|
}
|
|
258
124
|
|
|
259
|
-
|
|
260
|
-
let
|
|
261
|
-
let
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
if (
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
let field = fields[i];
|
|
274
|
-
|
|
275
|
-
if (LiteralBase.isLiteral(field))
|
|
276
|
-
return true;
|
|
277
|
-
|
|
278
|
-
if (ModelBase.isModelClass(field))
|
|
279
|
-
return true;
|
|
280
|
-
|
|
281
|
-
if (!Nife.instanceOf(field, 'string'))
|
|
282
|
-
true;
|
|
283
|
-
|
|
284
|
-
if (field === '-')
|
|
285
|
-
return true;
|
|
286
|
-
|
|
287
|
-
if (field === '*')
|
|
288
|
-
continue;
|
|
289
|
-
|
|
290
|
-
if (!(/^\s*[+-]/).test(field))
|
|
291
|
-
return true;
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
return false;
|
|
295
|
-
};
|
|
296
|
-
|
|
297
|
-
const collectProjectionValuesFromQuery = (RootModel, query) => {
|
|
298
|
-
let projections = [ RootModel ];
|
|
299
|
-
|
|
300
|
-
for (let i = 0, il = query.length; i < il; i++) {
|
|
301
|
-
let queryPart = query[i];
|
|
302
|
-
|
|
303
|
-
if (!Object.prototype.hasOwnProperty.call(queryPart, 'control'))
|
|
304
|
-
continue;
|
|
305
|
-
|
|
306
|
-
if (queryPart.control !== true)
|
|
307
|
-
continue;
|
|
308
|
-
|
|
309
|
-
if (queryPart.operator !== 'PROJECT')
|
|
310
|
-
continue;
|
|
311
|
-
|
|
312
|
-
let fields = queryPart.value;
|
|
313
|
-
if (shouldResetProjection(fields)) {
|
|
314
|
-
fields = Nife.arrayFlatten(fields);
|
|
315
|
-
|
|
316
|
-
let addIndex = fields.indexOf('+');
|
|
317
|
-
let newProjections = fields.slice();
|
|
318
|
-
|
|
319
|
-
if (addIndex >= 0)
|
|
320
|
-
newProjections[addIndex] = Nife.arrayFlatten(projections);
|
|
321
|
-
|
|
322
|
-
projections = newProjections;
|
|
323
|
-
} else {
|
|
324
|
-
projections = projections.concat(fields);
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
return Nife.uniq(Nife.arrayFlatten(projections));
|
|
329
|
-
};
|
|
330
|
-
|
|
331
|
-
const modelFieldsToProjection = (fieldSet, Model, removeFromProjection) => {
|
|
332
|
-
let modelName = Model.getModelName();
|
|
333
|
-
|
|
334
|
-
Model.iterateFields(({ field, fieldName }) => {
|
|
335
|
-
if (field.type.isVirtual())
|
|
336
|
-
return;
|
|
337
|
-
|
|
338
|
-
let fullFieldName = `${modelName}:${fieldName}`;
|
|
339
|
-
|
|
340
|
-
if (removeFromProjection) {
|
|
341
|
-
fieldSet.delete(fullFieldName);
|
|
342
|
-
return;
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
fieldSet.set(fullFieldName, {
|
|
346
|
-
projectedName: this.getEscapedProjectionName(Model, field, options),
|
|
347
|
-
Model: Model,
|
|
348
|
-
Field: field,
|
|
349
|
-
direction: '+',
|
|
350
|
-
fullFieldName,
|
|
351
|
-
fieldName,
|
|
352
|
-
modelName,
|
|
353
|
-
});
|
|
354
|
-
});
|
|
355
|
-
};
|
|
356
|
-
|
|
357
|
-
const addRequiredFieldsToProjection = (fieldSet) => {
|
|
358
|
-
let requiredProjectionFields = this.getProjectionRequiredFields(queryEngine, options);
|
|
359
|
-
|
|
360
|
-
for (let [ key, value ] of requiredProjectionFields) {
|
|
361
|
-
if (fieldSet.has(key))
|
|
362
|
-
continue;
|
|
363
|
-
|
|
364
|
-
fieldSet.set(key, value);
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
return fieldSet;
|
|
368
|
-
};
|
|
369
|
-
|
|
370
|
-
const finalizeProjection = (_projectedFields) => {
|
|
371
|
-
let projectedFields = _projectedFields;
|
|
372
|
-
let result;
|
|
373
|
-
|
|
374
|
-
let subQueryOperator = (options && options.subQueryOperator);
|
|
375
|
-
if (options && options.isSubQuery && !(subQueryOperator === 'EXISTS' || subQueryOperator === 'NOT EXISTS')) {
|
|
376
|
-
// If we are sub-selecting then only one
|
|
377
|
-
// field in the projection is allowed
|
|
378
|
-
|
|
379
|
-
result = Array.from(projectedFields.values());
|
|
380
|
-
if (result.length !== 1)
|
|
381
|
-
throw new Error(`${this.constructor.name}::getProjectionFromQueryEngine: Only one field is allowed in the projection of a sub-query.`);
|
|
382
|
-
|
|
383
|
-
let subQueryField = result[0];
|
|
384
|
-
|
|
385
|
-
if (LiteralBase.isLiteral(subQueryField))
|
|
386
|
-
result = [ subQueryField ];
|
|
387
|
-
else if (typeof subQueryField === 'string')
|
|
388
|
-
result = [ subQueryField ];
|
|
389
|
-
else
|
|
390
|
-
result = [ new Literals.DistinctLiteral(subQueryField.fullFieldName) ];
|
|
391
|
-
} else {
|
|
392
|
-
projectedFields = addRequiredFieldsToProjection(projectedFields);
|
|
393
|
-
|
|
394
|
-
// If distinct specifies a field, then
|
|
395
|
-
// make sure that field isn't specified twice
|
|
396
|
-
if (hasDistinct) {
|
|
397
|
-
let distinctFieldName = hasDistinct.getFullyQualifiedFieldName();
|
|
398
|
-
if (distinctFieldName)
|
|
399
|
-
projectedFields.delete(distinctFieldName);
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
result = Array.from(projectedFields.values());
|
|
403
|
-
|
|
404
|
-
// Convert projection fields to array and sort
|
|
405
|
-
result = this.sortedProjectedFields(result, options);
|
|
406
|
-
|
|
407
|
-
// Now prefix the projection fields with the distinct
|
|
408
|
-
// literal if one exists on the query
|
|
409
|
-
if (hasDistinct)
|
|
410
|
-
result = [ hasDistinct ].concat(result);
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
return result;
|
|
414
|
-
};
|
|
415
|
-
|
|
416
|
-
let rawQueryContext = queryEngine._getRawQueryContext();
|
|
417
|
-
let RootModel = rawQueryContext.rootModel;
|
|
418
|
-
if (!RootModel)
|
|
419
|
-
throw new Error('QueryGeneratorBase::getProjectionFromQueryEngine: No root model found for query. Root model is required to generate a projection.');
|
|
420
|
-
|
|
421
|
-
let hasDistinct = rawQueryContext.distinct;
|
|
422
|
-
let projections = collectProjectionValuesFromQuery(RootModel, queryEngine._getRawQuery());
|
|
423
|
-
let projectedFields = new Map();
|
|
424
|
-
let allModels = this.getAllModelsUsedInQuery(queryEngine, options);
|
|
425
|
-
let isAdding = true;
|
|
426
|
-
|
|
427
|
-
// If projection is empty, then return
|
|
428
|
-
// the projection of the root model
|
|
429
|
-
if (Nife.isEmpty(projections)) {
|
|
430
|
-
modelFieldsToProjection(projectedFields, RootModel);
|
|
431
|
-
return finalizeProjection(projectedFields);
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
for (let i = 0, il = projections.length; i < il; i++) {
|
|
435
|
-
let projectionValue = projections[i];
|
|
436
|
-
if (!projectionValue)
|
|
437
|
-
continue;
|
|
438
|
-
|
|
439
|
-
if (projectionValue === '+') {
|
|
440
|
-
isAdding = true;
|
|
441
|
-
continue;
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
if (projectionValue === '-') {
|
|
445
|
-
isAdding = false;
|
|
446
|
-
continue;
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
if (projectionValue === '*') {
|
|
450
|
-
for (let i = 0, il = allModels.length; i < il; i++) {
|
|
451
|
-
let Model = allModels[i];
|
|
452
|
-
modelFieldsToProjection(projectedFields, Model);
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
continue;
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
if (LiteralBase.isLiteral(projectionValue)) {
|
|
459
|
-
// If we already have distinct specified on the query
|
|
460
|
-
// then skip any distinct values specified by the user
|
|
461
|
-
if (hasDistinct && Literals.DistinctLiteral.isLiteralType(projectionValue))
|
|
462
|
-
continue;
|
|
463
|
-
|
|
464
|
-
let key = projectionValue.toString(this.connection, options);
|
|
465
|
-
if (isAdding)
|
|
466
|
-
projectedFields.set(key, projectionValue);
|
|
467
|
-
else
|
|
468
|
-
projectedFields.delete(key);
|
|
469
|
-
|
|
470
|
-
continue;
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
if (ModelBase.isModelClass(projectionValue)) {
|
|
474
|
-
if (allModels.indexOf(projectionValue) >= 0)
|
|
475
|
-
modelFieldsToProjection(projectedFields, projectionValue, !isAdding);
|
|
476
|
-
|
|
477
|
-
continue;
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
let { fieldName, direction, hasDirection } = this.getFieldDirectionSpecifier(projectionValue);
|
|
481
|
-
if (!fieldName)
|
|
482
|
-
continue;
|
|
483
|
-
|
|
484
|
-
if (!hasDirection) {
|
|
485
|
-
direction = (isAdding) ? '+' : '-';
|
|
486
|
-
hasDirection = true;
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
let def = this.connection.parseQualifiedName(fieldName);
|
|
490
|
-
if (!def.modelName) {
|
|
491
|
-
if (Nife.isNotEmpty(def.fieldNames) && allModels.length > 1)
|
|
492
|
-
throw new Error(`QueryGeneratorBase::getProjectionFromQueryEngine: "${def.fieldNames[0]}" ambiguous. You must use a fully qualified field name for an ORDER clause. Example: "+Model:id".`);
|
|
493
|
-
|
|
494
|
-
def.modelName = RootModel.getModelName();
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
let ProjectionModel = this.connection.getModel(def.modelName);
|
|
498
|
-
if (!ProjectionModel) {
|
|
499
|
-
if (hasDirection && direction === '-') {
|
|
500
|
-
projectedFields.delete(projectionValue);
|
|
501
|
-
continue;
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
// Proceed blindly... as the user may be
|
|
505
|
-
// querying something we are unaware of
|
|
506
|
-
projectedFields.set(projectionValue, projectionValue);
|
|
507
|
-
continue;
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
if (allModels.indexOf(ProjectionModel) < 0)
|
|
511
|
-
continue;
|
|
512
|
-
|
|
513
|
-
if (!def.fieldNames.length) {
|
|
514
|
-
// If there are no field names, but we have a model name
|
|
515
|
-
// then let the projection generator generate all model
|
|
516
|
-
// fields
|
|
517
|
-
|
|
518
|
-
if (ProjectionModel) {
|
|
519
|
-
modelFieldsToProjection(projectedFields, ProjectionModel, (hasDirection && direction === '-'));
|
|
520
|
-
continue;
|
|
125
|
+
getProjectedFields(queryEngine, _options, asMap) {
|
|
126
|
+
let options = this.stackAssign(_options || {}, { isProjection: true });
|
|
127
|
+
let context = queryEngine.getOperationContext();
|
|
128
|
+
let queryProjection = new Map(context.projection);
|
|
129
|
+
let order = this.getQueryEngineOrder(queryEngine, options);
|
|
130
|
+
let allProjectionFields = new Map();
|
|
131
|
+
let allModelsUsedInQuery = queryEngine.getAllModelsUsedInQuery();
|
|
132
|
+
|
|
133
|
+
if (!options.isSubQuery && order && order.size) {
|
|
134
|
+
let contextOrderSupport = this.connection.isOrderSupportedInContext(options);
|
|
135
|
+
if (contextOrderSupport) {
|
|
136
|
+
for (let [ fullyQualifiedFieldName, orderScope ] of order) {
|
|
137
|
+
if (!queryProjection.has(fullyQualifiedFieldName))
|
|
138
|
+
queryProjection.set(fullyQualifiedFieldName, orderScope);
|
|
521
139
|
}
|
|
522
140
|
}
|
|
523
|
-
|
|
524
|
-
let field = this.connection.getField(def.fieldNames[0], def.modelName);
|
|
525
|
-
let modelName = (field) ? field.Model.getModelName() : ProjectionModel.getModelName();
|
|
526
|
-
|
|
527
|
-
if (!field) {
|
|
528
|
-
let projectionFieldName = def.fieldNames[0];
|
|
529
|
-
let fullFieldName = `${modelName}:${projectionFieldName}`;
|
|
530
|
-
|
|
531
|
-
if (hasDirection && direction === '-') {
|
|
532
|
-
projectedFields.delete(fullFieldName);
|
|
533
|
-
continue;
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
// Proceed blindly... as the user may be
|
|
537
|
-
// querying something we are unaware of
|
|
538
|
-
projectedFields.set(fullFieldName, {
|
|
539
|
-
projectedName: this.getEscapedProjectionName(ProjectionModel, fieldName, options),
|
|
540
|
-
Model: ProjectionModel,
|
|
541
|
-
Field: null,
|
|
542
|
-
fieldName: projectionFieldName,
|
|
543
|
-
direction: '+',
|
|
544
|
-
modelName,
|
|
545
|
-
fullFieldName,
|
|
546
|
-
});
|
|
547
|
-
|
|
548
|
-
continue;
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
let projectionFieldName = field.fieldName;
|
|
552
|
-
let fullFieldName = `${modelName}:${projectionFieldName}`;
|
|
553
|
-
|
|
554
|
-
if (hasDirection && direction === '-') {
|
|
555
|
-
projectedFields.delete(fullFieldName);
|
|
556
|
-
continue;
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
projectedFields.set(fullFieldName, {
|
|
560
|
-
projectedName: this.getEscapedProjectionName(field.Model, field, options),
|
|
561
|
-
Model: field.Model,
|
|
562
|
-
Field: field,
|
|
563
|
-
fieldName: field.fieldName,
|
|
564
|
-
fullFieldName,
|
|
565
|
-
modelName,
|
|
566
|
-
direction,
|
|
567
|
-
});
|
|
568
141
|
}
|
|
569
142
|
|
|
570
|
-
let
|
|
571
|
-
|
|
572
|
-
if (options)
|
|
573
|
-
this.setOptionsCache(options, `getProjectionFromQueryEngine.${queryEngineContextID}`, result);
|
|
574
|
-
|
|
575
|
-
return result;
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
isFieldIdentifier(str) {
|
|
579
|
-
return (/^"[^"]+"."[^"]+"|"\w+:[\w.]+"/i).test(str);
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
getProjectedFields(queryEngine, _options, asMap) {
|
|
583
|
-
let options = _options || {};
|
|
584
|
-
let queryProjection = this.getProjectionFromQueryEngine(queryEngine, options);
|
|
585
|
-
let allProjectionFields = new Map();
|
|
143
|
+
for (let [ fullyQualifiedName, projectedScope ] of queryProjection) {
|
|
144
|
+
let { value } = projectedScope;
|
|
586
145
|
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
146
|
+
if (Nife.instanceOf(value, 'string')) {
|
|
147
|
+
// Raw string is treated as a literal
|
|
148
|
+
allProjectionFields.set(fullyQualifiedName, value);
|
|
590
149
|
continue;
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
let fullFieldName;
|
|
595
|
-
|
|
596
|
-
if (typeof projectionField.getFullyQualifiedFieldName === 'function')
|
|
597
|
-
fullFieldName = projectionField.getFullyQualifiedFieldName();
|
|
598
|
-
else
|
|
599
|
-
fullFieldName = this.parseFieldProjection(result);
|
|
600
|
-
|
|
601
|
-
if (!fullFieldName)
|
|
602
|
-
fullFieldName = result;
|
|
603
|
-
|
|
604
|
-
if (fullFieldName && result)
|
|
605
|
-
allProjectionFields.set(fullFieldName, result);
|
|
606
|
-
else
|
|
607
|
-
allProjectionFields.set(result, result);
|
|
150
|
+
} else if (LiteralBase.isLiteral(value)) {
|
|
151
|
+
let result = value.toString(this.connection, options);
|
|
152
|
+
allProjectionFields.set(result || fullyQualifiedName, result || fullyQualifiedName);
|
|
608
153
|
|
|
609
154
|
continue;
|
|
610
155
|
}
|
|
611
156
|
|
|
612
|
-
if (
|
|
157
|
+
if (allModelsUsedInQuery.indexOf(value.Model) < 0)
|
|
613
158
|
continue;
|
|
614
159
|
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
continue;
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
let projectedName = projectionField.projectedName;
|
|
621
|
-
allProjectionFields.set(projectionField.fullFieldName, projectedName);
|
|
160
|
+
let escapedFieldName = this.getEscapedProjectionName(value.Model, value, options);
|
|
161
|
+
allProjectionFields.set(`${value.Model.getModelName()}:${value.fieldName}`, escapedFieldName);
|
|
622
162
|
}
|
|
623
163
|
|
|
624
164
|
if (asMap === true)
|
|
@@ -705,90 +245,19 @@ class QueryGeneratorBase {
|
|
|
705
245
|
};
|
|
706
246
|
}
|
|
707
247
|
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
let
|
|
711
|
-
let cache = this.getOptionsCache(options, `getOrderLimitOffset.${queryEngineContextID}`);
|
|
712
|
-
if (cache)
|
|
713
|
-
return cache;
|
|
714
|
-
|
|
715
|
-
let query = queryEngine._getRawQuery();
|
|
716
|
-
let rootModel = queryEngine._getRawQueryContext().rootModel;
|
|
717
|
-
let limit;
|
|
718
|
-
let offset;
|
|
719
|
-
let order;
|
|
720
|
-
|
|
721
|
-
for (let i = 0, il = query.length; i < il; i++) {
|
|
722
|
-
let queryPart = query[i];
|
|
723
|
-
|
|
724
|
-
if (!Object.prototype.hasOwnProperty.call(queryPart, 'control'))
|
|
725
|
-
continue;
|
|
726
|
-
|
|
727
|
-
if (queryPart.control !== true)
|
|
728
|
-
continue;
|
|
729
|
-
|
|
730
|
-
let queryOperator = queryPart.operator;
|
|
731
|
-
|
|
732
|
-
if (queryOperator === 'LIMIT')
|
|
733
|
-
limit = queryPart.value;
|
|
734
|
-
else if (queryOperator === 'OFFSET')
|
|
735
|
-
offset = queryPart.value;
|
|
736
|
-
else if (queryOperator === 'ORDER')
|
|
737
|
-
order = queryPart.value;
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
if (order === undefined) {
|
|
741
|
-
if (options && options.selectStatement === true)
|
|
742
|
-
order = this.connection.getDefaultOrder(rootModel, options);
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
if (Nife.isNotEmpty(order) && !LiteralBase.isLiteral(order)) {
|
|
746
|
-
let allModels = this.getAllModelsUsedInQuery(queryEngine, options);
|
|
747
|
-
|
|
748
|
-
order = order.map((_fieldName) => {
|
|
749
|
-
if (LiteralBase.isLiteral(_fieldName))
|
|
750
|
-
return _fieldName;
|
|
751
|
-
|
|
752
|
-
let { fieldName, direction } = this.getFieldDirectionSpecifier(_fieldName);
|
|
753
|
-
if (!fieldName)
|
|
754
|
-
return;
|
|
755
|
-
|
|
756
|
-
let def = this.connection.parseQualifiedName(fieldName);
|
|
757
|
-
if (!def.modelName) {
|
|
758
|
-
if (allModels.length > 1)
|
|
759
|
-
throw new Error(`QueryGeneratorBase::getOrderLimitOffset: "${fieldName}" ambiguous. You must use a fully qualified field name for an ORDER clause. Example: "+Model:id".`);
|
|
760
|
-
|
|
761
|
-
def.modelName = allModels[0].getModelName();
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
if (!def.fieldNames.length)
|
|
765
|
-
throw new Error(`QueryGeneratorBase::getOrderLimitOffset: No field names found for "${fieldName}".`);
|
|
766
|
-
|
|
767
|
-
let field = this.connection.getField(def.fieldNames[0], def.modelName);
|
|
768
|
-
if (!field)
|
|
769
|
-
throw new Error(`QueryGeneratorBase::getOrderLimitOffset: Unable to locate field "${def.modelName}"."${def.fieldNames[0]}".`);
|
|
770
|
-
|
|
771
|
-
return {
|
|
772
|
-
Model: field.Model,
|
|
773
|
-
Field: field,
|
|
774
|
-
direction,
|
|
775
|
-
};
|
|
776
|
-
});
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
let orderLimitOffset = { limit, order, offset };
|
|
780
|
-
|
|
781
|
-
if (options)
|
|
782
|
-
this.setOptionsCache(options, `getOrderLimitOffset.${queryEngineContextID}`, orderLimitOffset);
|
|
248
|
+
getQuerySliceFromQueryPart(queryPart) {
|
|
249
|
+
let operationStack = queryPart.operationStack;
|
|
250
|
+
let index = operationStack.indexOf(queryPart);
|
|
783
251
|
|
|
784
|
-
return
|
|
252
|
+
return operationStack.slice(index);
|
|
785
253
|
}
|
|
786
254
|
|
|
787
|
-
|
|
788
|
-
let
|
|
789
|
-
|
|
255
|
+
_getLiteralAlias(literal, options) {
|
|
256
|
+
let as = (literal.options && literal.options.as) || (options && options.as);
|
|
257
|
+
if (Nife.isEmpty(as))
|
|
258
|
+
return '';
|
|
790
259
|
|
|
791
|
-
return
|
|
260
|
+
return ` AS ${this.escapeID(as)}`;
|
|
792
261
|
}
|
|
793
262
|
|
|
794
263
|
_averageLiteralToString(literal, options) {
|
|
@@ -803,7 +272,7 @@ class QueryGeneratorBase {
|
|
|
803
272
|
else
|
|
804
273
|
escapedFieldName = this.getEscapedColumnName(field.Model, field, this.stackAssign(options, literal.options));
|
|
805
274
|
|
|
806
|
-
return `AVG(${escapedFieldName})`;
|
|
275
|
+
return `AVG(${escapedFieldName})${this._getLiteralAlias(literal, options)}`;
|
|
807
276
|
}
|
|
808
277
|
|
|
809
278
|
_countLiteralToString(literal, options) {
|
|
@@ -822,7 +291,7 @@ class QueryGeneratorBase {
|
|
|
822
291
|
escapedFieldName = '*';
|
|
823
292
|
}
|
|
824
293
|
|
|
825
|
-
return `COUNT(${escapedFieldName})`;
|
|
294
|
+
return `COUNT(${escapedFieldName})${this._getLiteralAlias(literal, options)}`;
|
|
826
295
|
}
|
|
827
296
|
|
|
828
297
|
_distinctLiteralToString(literal, options) {
|
|
@@ -830,10 +299,13 @@ class QueryGeneratorBase {
|
|
|
830
299
|
return;
|
|
831
300
|
|
|
832
301
|
let field = literal.getField(this.connection);
|
|
302
|
+
if (!field)
|
|
303
|
+
return 'DISTINCT';
|
|
304
|
+
|
|
833
305
|
if (LiteralBase.isLiteral(field))
|
|
834
|
-
return `DISTINCT ${field.toString(this.connection, options)}`;
|
|
306
|
+
return `DISTINCT ON(${field.toString(this.connection, this.stackAssign(options, { noProjectionAliases: true }))})`;
|
|
835
307
|
|
|
836
|
-
return `DISTINCT ${this.
|
|
308
|
+
return `DISTINCT ON(${this.getEscapedColumnName(field.Model, field, this.stackAssign(options, literal.options, { noProjectionAliases: true }))})`;
|
|
837
309
|
}
|
|
838
310
|
|
|
839
311
|
_fieldLiteralToString(literal, options) {
|
|
@@ -844,7 +316,7 @@ class QueryGeneratorBase {
|
|
|
844
316
|
if (LiteralBase.isLiteral(field))
|
|
845
317
|
return field.toString(this.connection, options);
|
|
846
318
|
|
|
847
|
-
return this.
|
|
319
|
+
return this.getEscapedProjectionName(field.Model, field, this.stackAssign(options, { noProjectionAliases: (options && !options.isProjection) }, literal.options));
|
|
848
320
|
}
|
|
849
321
|
|
|
850
322
|
_maxLiteralToString(literal, options) {
|
|
@@ -859,7 +331,7 @@ class QueryGeneratorBase {
|
|
|
859
331
|
else
|
|
860
332
|
escapedFieldName = this.getEscapedColumnName(field.Model, field, this.stackAssign(options, literal.options));
|
|
861
333
|
|
|
862
|
-
return `MAX(${escapedFieldName})`;
|
|
334
|
+
return `MAX(${escapedFieldName})${this._getLiteralAlias(literal, options)}`;
|
|
863
335
|
}
|
|
864
336
|
|
|
865
337
|
_minLiteralToString(literal, options) {
|
|
@@ -874,7 +346,7 @@ class QueryGeneratorBase {
|
|
|
874
346
|
else
|
|
875
347
|
escapedFieldName = this.getEscapedColumnName(field.Model, field, this.stackAssign(options, literal.options));
|
|
876
348
|
|
|
877
|
-
return `MIN(${escapedFieldName})`;
|
|
349
|
+
return `MIN(${escapedFieldName})${this._getLiteralAlias(literal, options)}`;
|
|
878
350
|
}
|
|
879
351
|
|
|
880
352
|
_sumLiteralToString(literal, options) {
|
|
@@ -889,7 +361,7 @@ class QueryGeneratorBase {
|
|
|
889
361
|
else
|
|
890
362
|
escapedFieldName = this.getEscapedColumnName(field.Model, field, this.stackAssign(options, literal.options));
|
|
891
363
|
|
|
892
|
-
return `SUM(${escapedFieldName})`;
|
|
364
|
+
return `SUM(${escapedFieldName})${this._getLiteralAlias(literal, options)}`;
|
|
893
365
|
}
|
|
894
366
|
|
|
895
367
|
// eslint-disable-next-line no-unused-vars
|