mythix-orm 1.10.2 → 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.js +4 -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 +55 -583
- package/lib/field.js +9 -2
- package/lib/model.d.ts +0 -2
- package/lib/model.js +58 -71
- package/lib/query-engine/field-scope.js +57 -24
- package/lib/query-engine/model-scope.js +168 -35
- package/lib/query-engine/query-engine-base.js +46 -13
- package/lib/query-engine/query-engine.d.ts +8 -4
- package/lib/query-engine/query-engine.js +47 -54
- 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/relational-type-base.js +24 -24
- 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 +17 -17
- package/lib/utils/query-utils.d.ts +7 -0
- package/lib/utils/query-utils.js +175 -4
- package/package.json +1 -1
|
@@ -3,11 +3,64 @@
|
|
|
3
3
|
const Nife = require('nife');
|
|
4
4
|
const ProxyClass = require('../proxy-class');
|
|
5
5
|
const QueryEngineBase = require('./query-engine-base');
|
|
6
|
+
const QueryUtils = require('../utils/query-utils');
|
|
6
7
|
const {
|
|
7
8
|
LiteralBase,
|
|
8
9
|
DistinctLiteral,
|
|
9
10
|
} = require('../connection/literals');
|
|
10
11
|
|
|
12
|
+
function applyOrderClause(extraData, ...args) {
|
|
13
|
+
let entities = Nife.arrayFlatten(args);
|
|
14
|
+
|
|
15
|
+
entities = Nife.toArray(entities).map((value) => {
|
|
16
|
+
if (value == null)
|
|
17
|
+
return;
|
|
18
|
+
|
|
19
|
+
// Pass literals directly through
|
|
20
|
+
if (LiteralBase.isLiteral(value))
|
|
21
|
+
return value;
|
|
22
|
+
|
|
23
|
+
// Is the projection a field?
|
|
24
|
+
if (value.Model && value.fieldName)
|
|
25
|
+
return `${value.Model.getModelName()}:${value.fieldName}`;
|
|
26
|
+
|
|
27
|
+
if (!Nife.instanceOf(value, 'string'))
|
|
28
|
+
throw new Error('QueryEngine::ModelScope::ORDER: Invalid value provided. All values provided must be strings, fields, or literals. If you want to change the sort order of a given column, add "+" (ASC) or "-" (DESC) to be beginning of the field name. Example: .ORDER("+createdAt"), or .ORDER([ "-name", "+createdAt" ]).');
|
|
29
|
+
|
|
30
|
+
return value;
|
|
31
|
+
}).filter(Boolean);
|
|
32
|
+
|
|
33
|
+
let context = this.getOperationContext();
|
|
34
|
+
let order = this.margeFields(
|
|
35
|
+
context.order,
|
|
36
|
+
entities,
|
|
37
|
+
extraData,
|
|
38
|
+
{ isOrderBy: true },
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
this._pushOperationOntoStack({
|
|
42
|
+
control: true,
|
|
43
|
+
operator: 'ORDER',
|
|
44
|
+
queryProp: 'ORDER',
|
|
45
|
+
value: entities,
|
|
46
|
+
order,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
return this._fetchScope('model');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function wrapOrderClause(func) {
|
|
53
|
+
func.DESC = (...args) => {
|
|
54
|
+
return applyOrderClause.call(this, { direction: '-' }, ...args);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
func.ASC = (...args) => {
|
|
58
|
+
return applyOrderClause.call(this, { direction: '+' }, ...args);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
return func;
|
|
62
|
+
}
|
|
63
|
+
|
|
11
64
|
class ModelScope extends QueryEngineBase {
|
|
12
65
|
_getField(fieldName) {
|
|
13
66
|
let Model = this.currentContext.Model;
|
|
@@ -46,18 +99,22 @@ class ModelScope extends QueryEngineBase {
|
|
|
46
99
|
return this.currentContext.queryEngineScope.toString(...args);
|
|
47
100
|
}
|
|
48
101
|
|
|
102
|
+
margeFields(currentFields, incomingFields, extraData, options) {
|
|
103
|
+
return QueryUtils.margeFields(this, currentFields, incomingFields, extraData, options);
|
|
104
|
+
}
|
|
105
|
+
|
|
49
106
|
NOT = ProxyClass.autoCall(function() {
|
|
50
|
-
this._pushOperationOntoStack({ logical: true, operator: 'NOT', not: !this.currentContext.not });
|
|
107
|
+
this._pushOperationOntoStack({ logical: true, operator: 'NOT', queryProp: 'NOT', not: !this.currentContext.not });
|
|
51
108
|
return this._fetchScope('model');
|
|
52
109
|
});
|
|
53
110
|
|
|
54
111
|
AND = ProxyClass.autoCall(function(value) {
|
|
55
|
-
this._pushOperationOntoStack({ logical: true, operator: 'AND', and: true, or: false, not: false, value });
|
|
112
|
+
this._pushOperationOntoStack({ logical: true, operator: 'AND', queryProp: 'AND', and: true, or: false, not: false, value });
|
|
56
113
|
return this._fetchScope('model');
|
|
57
114
|
});
|
|
58
115
|
|
|
59
116
|
OR = ProxyClass.autoCall(function(value) {
|
|
60
|
-
this._pushOperationOntoStack({ logical: true, operator: 'OR', and: false, or: true, not: false, value });
|
|
117
|
+
this._pushOperationOntoStack({ logical: true, operator: 'OR', queryProp: 'OR', and: false, or: true, not: false, value });
|
|
61
118
|
return this._fetchScope('model');
|
|
62
119
|
});
|
|
63
120
|
|
|
@@ -67,7 +124,7 @@ class ModelScope extends QueryEngineBase {
|
|
|
67
124
|
throw new Error('QueryEngine::ModelScope::LIMIT: Value provided must be a valid positive number, or Infinity.');
|
|
68
125
|
|
|
69
126
|
value = Math.round(value);
|
|
70
|
-
this._pushOperationOntoStack({ control: true, operator: 'LIMIT', value, limit: value });
|
|
127
|
+
this._pushOperationOntoStack({ control: true, operator: 'LIMIT', queryProp: 'LIMIT', value, limit: value });
|
|
71
128
|
|
|
72
129
|
return this._fetchScope('model');
|
|
73
130
|
}
|
|
@@ -78,32 +135,93 @@ class ModelScope extends QueryEngineBase {
|
|
|
78
135
|
throw new Error('QueryEngine::ModelScope::OFFSET: Value provided must be a valid positive number.');
|
|
79
136
|
|
|
80
137
|
value = Math.round(value);
|
|
81
|
-
this._pushOperationOntoStack({ control: true, operator: 'OFFSET', value, offset: value });
|
|
138
|
+
this._pushOperationOntoStack({ control: true, operator: 'OFFSET', queryProp: 'OFFSET', value, offset: value });
|
|
82
139
|
|
|
83
140
|
return this._fetchScope('model');
|
|
84
141
|
}
|
|
85
142
|
|
|
86
|
-
ORDER(...args) {
|
|
87
|
-
|
|
143
|
+
ORDER = wrapOrderClause.call(this, (...args) => {
|
|
144
|
+
return applyOrderClause.call(this, { direction: '+' }, ...args);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
GROUP_BY(...args) {
|
|
148
|
+
let entities = Nife.arrayFlatten(args);
|
|
88
149
|
|
|
89
|
-
|
|
150
|
+
entities = Nife.toArray(entities).map((value) => {
|
|
90
151
|
if (value == null)
|
|
91
|
-
return
|
|
152
|
+
return;
|
|
153
|
+
|
|
154
|
+
// Pass literals directly through
|
|
155
|
+
if (LiteralBase.isLiteral(value))
|
|
156
|
+
return value;
|
|
157
|
+
|
|
158
|
+
// Is the projection a field?
|
|
159
|
+
if (value.Model && value.fieldName)
|
|
160
|
+
return `${value.Model.getModelName()}:${value.fieldName}`;
|
|
92
161
|
|
|
93
162
|
if (!Nife.instanceOf(value, 'string'))
|
|
94
|
-
throw new Error('QueryEngine::ModelScope::
|
|
163
|
+
throw new Error('QueryEngine::ModelScope::GROUP_BY: Invalid value provided. All values provided must be strings, fields, or literals. If you want to change the sort order of a given column, add "+" (ASC) or "-" (DESC) to be beginning of the field name. Example: .ORDER("+createdAt"), or .ORDER([ "-name", "+createdAt" ]).');
|
|
164
|
+
|
|
165
|
+
return value;
|
|
166
|
+
}).filter(Boolean);
|
|
167
|
+
|
|
168
|
+
let context = this.getOperationContext();
|
|
169
|
+
let groupBy = this.margeFields(
|
|
170
|
+
context.groupBy,
|
|
171
|
+
entities,
|
|
172
|
+
{},
|
|
173
|
+
{ isGroupBy: true },
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
this._pushOperationOntoStack({
|
|
177
|
+
control: true,
|
|
178
|
+
operator: 'GROUP_BY',
|
|
179
|
+
queryProp: 'GROUP_BY',
|
|
180
|
+
value: entities,
|
|
181
|
+
groupBy,
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
return this._fetchScope('model');
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
HAVING(query) {
|
|
188
|
+
this._pushOperationOntoStack({ control: true, operator: 'HAVING', queryProp: 'HAVING', value: query, having: query });
|
|
189
|
+
return this._fetchScope('model');
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
EXISTS(_query) {
|
|
193
|
+
let query = _query;
|
|
194
|
+
let queryContext = (QueryEngineBase.isQuery(query)) ? query.getOperationContext() : null;
|
|
195
|
+
if (!queryContext || !queryContext.hasCondition)
|
|
196
|
+
throw new Error('QueryEngine::ModelScope::EXISTS: Provided value must be a query with conditions.');
|
|
197
|
+
|
|
198
|
+
if (!queryContext.projection) {
|
|
199
|
+
let Model = queryContext.Model;
|
|
200
|
+
let pkField = Model.getPrimaryKeyField();
|
|
201
|
+
|
|
202
|
+
if (pkField)
|
|
203
|
+
query = query.clone().PROJECT(pkField);
|
|
204
|
+
else
|
|
205
|
+
throw new Error('QueryEngine::ModelScope::EXISTS: Provided query must have only a single field projected.');
|
|
206
|
+
}
|
|
95
207
|
|
|
96
|
-
|
|
208
|
+
this._pushOperationOntoStack({
|
|
209
|
+
condition: true,
|
|
210
|
+
operator: 'EXISTS',
|
|
211
|
+
inverseOperator: 'NOT EXISTS',
|
|
212
|
+
queryProp: 'EXISTS',
|
|
213
|
+
value: query,
|
|
214
|
+
having: query,
|
|
215
|
+
hasCondition: true,
|
|
97
216
|
});
|
|
98
217
|
|
|
99
|
-
this._pushOperationOntoStack({ control: true, operator: 'ORDER', value: values, order: values });
|
|
100
218
|
return this._fetchScope('model');
|
|
101
219
|
}
|
|
102
220
|
|
|
103
221
|
PROJECT(...args) {
|
|
104
|
-
let
|
|
222
|
+
let entities = Nife.arrayFlatten(args);
|
|
105
223
|
|
|
106
|
-
|
|
224
|
+
entities = Nife.toArray(entities).map((value) => {
|
|
107
225
|
if (value == null)
|
|
108
226
|
return;
|
|
109
227
|
|
|
@@ -119,63 +237,78 @@ class ModelScope extends QueryEngineBase {
|
|
|
119
237
|
if (value.Model && value.fieldName)
|
|
120
238
|
return value;
|
|
121
239
|
|
|
122
|
-
if (!Nife.instanceOf(value, 'string'))
|
|
123
|
-
|
|
240
|
+
if (!Nife.instanceOf(value, 'string')) {
|
|
241
|
+
console.log(entities);
|
|
242
|
+
throw new Error(`QueryEngine::ModelScope::PROJECT: Invalid value provided [${value.toString()}]. All values provided must be strings.`);
|
|
243
|
+
}
|
|
124
244
|
|
|
125
245
|
return value;
|
|
126
246
|
}).filter(Boolean);
|
|
127
247
|
|
|
128
|
-
this.
|
|
248
|
+
let context = this.getOperationContext();
|
|
249
|
+
let projection = this.margeFields(
|
|
250
|
+
context.projection,
|
|
251
|
+
entities,
|
|
252
|
+
{},
|
|
253
|
+
{ isProjection: true },
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
this._pushOperationOntoStack({
|
|
257
|
+
control: true,
|
|
258
|
+
operator: 'PROJECT',
|
|
259
|
+
queryProp: 'PROJECT',
|
|
260
|
+
value: entities,
|
|
261
|
+
projection,
|
|
262
|
+
});
|
|
263
|
+
|
|
129
264
|
return this._fetchScope('model');
|
|
130
265
|
}
|
|
131
266
|
|
|
132
267
|
DISTINCT = ProxyClass.autoCall(function(fullyQualifiedName) {
|
|
133
268
|
let currentQuery = this;
|
|
134
269
|
let distinctValue = fullyQualifiedName;
|
|
270
|
+
let context = this.getOperationContext();
|
|
135
271
|
|
|
136
272
|
if (arguments.length === 0) {
|
|
137
|
-
let context = this.getOperationContext();
|
|
138
273
|
let rootModel = context.rootModel;
|
|
139
|
-
if (
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
currentQuery = this.PROJECT(`-${rootModel.getModelName()}:${pkFieldName}`);
|
|
274
|
+
if (rootModel) {
|
|
275
|
+
let pkFieldName = rootModel.getPrimaryKeyFieldName();
|
|
276
|
+
if (pkFieldName)
|
|
277
|
+
distinctValue = new DistinctLiteral(`${rootModel.getModelName()}:${pkFieldName}`);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (!distinctValue)
|
|
281
|
+
distinctValue = new DistinctLiteral();
|
|
148
282
|
} else if (fullyQualifiedName) {
|
|
149
283
|
distinctValue = new DistinctLiteral(fullyQualifiedName);
|
|
150
|
-
currentQuery = this.PROJECT(`-${fullyQualifiedName}`);
|
|
151
284
|
}
|
|
152
285
|
|
|
153
|
-
currentQuery._pushOperationOntoStack({
|
|
286
|
+
currentQuery._pushOperationOntoStack({ sqlFunc: true, operator: 'DISTINCT', queryProp: 'DISTINCT', value: distinctValue, distinct: distinctValue });
|
|
154
287
|
return this._fetchScope('model');
|
|
155
288
|
});
|
|
156
289
|
|
|
157
290
|
INNER_JOIN = ProxyClass.autoCall(function() {
|
|
158
|
-
this._pushOperationOntoStack({
|
|
291
|
+
this._pushOperationOntoStack({ join: true, operator: 'JOIN', queryProp: 'INNER_JOIN', value: 'inner', joinType: 'inner', joinOuter: false });
|
|
159
292
|
return this._fetchScope('model');
|
|
160
293
|
});
|
|
161
294
|
|
|
162
295
|
LEFT_JOIN = ProxyClass.autoCall(function(outerJoin) {
|
|
163
|
-
this._pushOperationOntoStack({
|
|
296
|
+
this._pushOperationOntoStack({ join: true, operator: 'JOIN', queryProp: 'LEFT_JOIN', value: 'left', joinType: 'left', joinOuter: !!outerJoin });
|
|
164
297
|
return this._fetchScope('model');
|
|
165
298
|
});
|
|
166
299
|
|
|
167
300
|
RIGHT_JOIN = ProxyClass.autoCall(function(outerJoin) {
|
|
168
|
-
this._pushOperationOntoStack({
|
|
301
|
+
this._pushOperationOntoStack({ join: true, operator: 'JOIN', queryProp: 'RIGHT_JOIN', value: 'right', joinType: 'right', joinOuter: !!outerJoin });
|
|
169
302
|
return this._fetchScope('model');
|
|
170
303
|
});
|
|
171
304
|
|
|
172
305
|
FULL_JOIN = ProxyClass.autoCall(function(outerJoin) {
|
|
173
|
-
this._pushOperationOntoStack({
|
|
306
|
+
this._pushOperationOntoStack({ join: true, operator: 'JOIN', queryProp: 'FULL_JOIN', value: 'full', joinType: 'full', joinOuter: !!outerJoin });
|
|
174
307
|
return this._fetchScope('model');
|
|
175
308
|
});
|
|
176
309
|
|
|
177
310
|
CROSS_JOIN = ProxyClass.autoCall(function() {
|
|
178
|
-
this._pushOperationOntoStack({
|
|
311
|
+
this._pushOperationOntoStack({ join: true, operator: 'JOIN', queryProp: 'CROSS_JOIN', value: 'cross', joinType: 'cross', joinOuter: false });
|
|
179
312
|
return this._fetchScope('model');
|
|
180
313
|
});
|
|
181
314
|
|
|
@@ -183,7 +316,7 @@ class ModelScope extends QueryEngineBase {
|
|
|
183
316
|
if (!(Nife.instanceOf(type, 'string') || LiteralBase.isLiteral(type)))
|
|
184
317
|
throw new Error('QueryEngine::ModelScope::JOIN: Invalid value provided. Value must be a valid string or Literal specifying JOIN type.');
|
|
185
318
|
|
|
186
|
-
this._pushOperationOntoStack({
|
|
319
|
+
this._pushOperationOntoStack({ join: true, operator: 'JOIN', queryProp: 'JOIN', value: type, joinType: type, joinOuter: false });
|
|
187
320
|
return this._fetchScope('model');
|
|
188
321
|
}
|
|
189
322
|
}
|
|
@@ -13,8 +13,8 @@ class QueryEngineBase extends ProxyClass {
|
|
|
13
13
|
return uuid++;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
static
|
|
17
|
-
return !!(value && value.
|
|
16
|
+
static isQueryOperationContext(value) {
|
|
17
|
+
return !!(value && value.isQueryOperationContext);
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
static isQuery(value) {
|
|
@@ -33,7 +33,7 @@ class QueryEngineBase extends ProxyClass {
|
|
|
33
33
|
return false;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
static
|
|
36
|
+
static queryOperationInfo(queryContext) {
|
|
37
37
|
let contextParams = {
|
|
38
38
|
hasCondition: false,
|
|
39
39
|
hasField: false,
|
|
@@ -124,8 +124,10 @@ class QueryEngineBase extends ProxyClass {
|
|
|
124
124
|
let modelName = Model.getModelName();
|
|
125
125
|
let context = this.currentContext;
|
|
126
126
|
let extra = {};
|
|
127
|
+
let isFirst = !context.rootModel;
|
|
128
|
+
let connection = this.getConnection();
|
|
127
129
|
|
|
128
|
-
if (
|
|
130
|
+
if (isFirst) {
|
|
129
131
|
extra.rootModelName = modelName;
|
|
130
132
|
extra.rootModel = Model;
|
|
131
133
|
}
|
|
@@ -141,13 +143,10 @@ class QueryEngineBase extends ProxyClass {
|
|
|
141
143
|
throw new Error(`QueryEngine: Model "${Model.getModelName()}" is on a different connection dialect than the current query dialect of "${context.dialect}". You can not match different connection dialects in the same query.`);
|
|
142
144
|
}
|
|
143
145
|
|
|
144
|
-
if (!dialect)
|
|
145
|
-
|
|
146
|
-
if (connection)
|
|
147
|
-
dialect = connection.dialect;
|
|
148
|
-
}
|
|
146
|
+
if (!dialect && connection)
|
|
147
|
+
dialect = connection.dialect;
|
|
149
148
|
|
|
150
|
-
let newContext = this._inheritContext(context, 'model', { operator: 'MODEL', Model, modelName, dialect }, extra);
|
|
149
|
+
let newContext = this._inheritContext(context, 'model', { operator: 'MODEL', queryProp: 'Model', Model, modelName, dialect, value: modelName }, extra);
|
|
151
150
|
let newScope = new ModelScopeClass(newContext);
|
|
152
151
|
|
|
153
152
|
// We shouldn't add this scope if this is
|
|
@@ -155,6 +154,9 @@ class QueryEngineBase extends ProxyClass {
|
|
|
155
154
|
if (context.Model !== Model)
|
|
156
155
|
context.operationStack.push(newContext);
|
|
157
156
|
|
|
157
|
+
if (isFirst)
|
|
158
|
+
newScope = newScope.PROJECT(Model);
|
|
159
|
+
|
|
158
160
|
// But we always need to return the scope
|
|
159
161
|
// for the proxy to work properly
|
|
160
162
|
return newScope;
|
|
@@ -171,7 +173,7 @@ class QueryEngineBase extends ProxyClass {
|
|
|
171
173
|
extra.rootField = Field;
|
|
172
174
|
}
|
|
173
175
|
|
|
174
|
-
let newContext = this._inheritContext(context, 'field', { operator: 'FIELD', fieldName, Field }, extra);
|
|
176
|
+
let newContext = this._inheritContext(context, 'field', { operator: 'FIELD', queryProp: 'Field', fieldName, Field, value: fieldName }, extra);
|
|
175
177
|
let newScope = new FieldScopeClass(newContext);
|
|
176
178
|
|
|
177
179
|
// We shouldn't add this scope if this is
|
|
@@ -223,7 +225,7 @@ class QueryEngineBase extends ProxyClass {
|
|
|
223
225
|
let currentContext = operationStack[operationStack.length - 1];
|
|
224
226
|
return currentContext || context;
|
|
225
227
|
},
|
|
226
|
-
set:
|
|
228
|
+
set: () => {},
|
|
227
229
|
},
|
|
228
230
|
});
|
|
229
231
|
}
|
|
@@ -289,7 +291,6 @@ class QueryEngineBase extends ProxyClass {
|
|
|
289
291
|
else
|
|
290
292
|
console.log(`${operator} -> ${queryPart.value}`);
|
|
291
293
|
}
|
|
292
|
-
|
|
293
294
|
}
|
|
294
295
|
|
|
295
296
|
_pushOperationOntoStack(queryPart, _context) {
|
|
@@ -409,6 +410,38 @@ class QueryEngineBase extends ProxyClass {
|
|
|
409
410
|
let checkContextKeys = _checkContextKeys || [ 'value' ];
|
|
410
411
|
walkQueries(this, null, null, 0);
|
|
411
412
|
}
|
|
413
|
+
|
|
414
|
+
getAllModelsUsedInQuery() {
|
|
415
|
+
let Models = new Set();
|
|
416
|
+
let query = this.getOperationStack();
|
|
417
|
+
|
|
418
|
+
for (let i = 0, il = query.length; i < il; i++) {
|
|
419
|
+
let queryPart = query[i];
|
|
420
|
+
|
|
421
|
+
if (Object.prototype.hasOwnProperty.call(queryPart, 'operator') && queryPart.operator === 'MODEL') {
|
|
422
|
+
let Model = queryPart.Model;
|
|
423
|
+
Models.add(Model);
|
|
424
|
+
} else if (Object.prototype.hasOwnProperty.call(queryPart, 'condition') && queryPart.condition === true) {
|
|
425
|
+
let operatorValue = queryPart.value;
|
|
426
|
+
if (!QueryEngineBase.isQuery(operatorValue) || operatorValue.queryHasConditions())
|
|
427
|
+
continue;
|
|
428
|
+
|
|
429
|
+
let SubModels = operatorValue.getAllModelsUsedInQuery();
|
|
430
|
+
for (let j = 0, jl = SubModels.length; j < jl; j++) {
|
|
431
|
+
let Model = SubModels[j];
|
|
432
|
+
Models.add(Model);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
let allModels = Array.from(Models.values());
|
|
438
|
+
return allModels;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
isModelUsedInQuery(Model) {
|
|
442
|
+
let allModels = this.getAllModelsUsedInQuery();
|
|
443
|
+
return (allModels.indexOf(Model) >= 0);
|
|
444
|
+
}
|
|
412
445
|
}
|
|
413
446
|
|
|
414
447
|
module.exports = QueryEngineBase;
|
|
@@ -19,9 +19,9 @@ export declare interface CallableInterface {
|
|
|
19
19
|
export declare class QueryEngine<T = ConnectionBase> {
|
|
20
20
|
// QueryEngineBase
|
|
21
21
|
static generateID(): number;
|
|
22
|
-
static
|
|
22
|
+
static isQueryOperationContext(value: any): boolean;
|
|
23
23
|
static isQuery(value: any): boolean;
|
|
24
|
-
static
|
|
24
|
+
static queryOperationInfo(queryContext: GenericObject): { hasCondition: boolean; hasField: boolean; hasModel: boolean; };
|
|
25
25
|
|
|
26
26
|
public getModelScopeClass(): QueryEngine;
|
|
27
27
|
public getFieldScopeClass(): QueryEngine;
|
|
@@ -48,6 +48,7 @@ export declare class QueryEngine<T = ConnectionBase> {
|
|
|
48
48
|
public filter(callback: (operation: GenericObject, index: number, operations: Array<GenericObject>, query: QueryEngine) => GenericObject): QueryEngine;
|
|
49
49
|
public map(callback: (operation: GenericObject, index: number, operations: Array<GenericObject>, query: QueryEngine) => GenericObject): QueryEngine;
|
|
50
50
|
public walk(callback: (query: QueryEngine, parent: GenericObject | null, contextKey: string, depth: number) => GenericObject, checkContextKeys?: Array<string>): void;
|
|
51
|
+
public getAllModelsUsedInQuery(): Array<ModelClass>;
|
|
51
52
|
|
|
52
53
|
// QueryEngine
|
|
53
54
|
public getModelScopeClass(): QueryEngineClass;
|
|
@@ -78,8 +79,11 @@ export declare class QueryEngine<T = ConnectionBase> {
|
|
|
78
79
|
public Field(fieldName: string): QueryEngine;
|
|
79
80
|
public LIMIT(value: number): QueryEngine;
|
|
80
81
|
public OFFSET(value: number): QueryEngine;
|
|
81
|
-
public ORDER(...args: Array<string | Array<string>>): QueryEngine;
|
|
82
|
-
public
|
|
82
|
+
public ORDER(...args: Array<LiteralBase | Field | string | Array<LiteralBase | Field | string>>): QueryEngine;
|
|
83
|
+
public GROUP_BY(...args: Array<LiteralBase | Field | string | Array<LiteralBase | Field | string>>): QueryEngine;
|
|
84
|
+
public HAVING(query: QueryEngine): QueryEngine;
|
|
85
|
+
public EXISTS(query: QueryEngine): QueryEngine;
|
|
86
|
+
public PROJECT(...args: Array<string | ModelClass | LiteralBase | Field | Array<string | ModelClass | LiteralBase | Field>>): QueryEngine;
|
|
83
87
|
|
|
84
88
|
declare public NOT: {
|
|
85
89
|
(): QueryEngine;
|
|
@@ -20,9 +20,9 @@ class QueryEngine extends QueryEngineBase {
|
|
|
20
20
|
let context = Object.assign(
|
|
21
21
|
Object.create(_context || {}),
|
|
22
22
|
{
|
|
23
|
-
currentScopeName:
|
|
24
|
-
|
|
25
|
-
contextID:
|
|
23
|
+
currentScopeName: 'queryEngine',
|
|
24
|
+
isQueryOperationContext: true,
|
|
25
|
+
contextID: QueryEngineBase.generateID(),
|
|
26
26
|
},
|
|
27
27
|
);
|
|
28
28
|
|
|
@@ -39,15 +39,15 @@ class QueryEngine extends QueryEngineBase {
|
|
|
39
39
|
return this._newModelScope(model);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
unscoped(
|
|
42
|
+
unscoped() {
|
|
43
|
+
let context = this.getOperationContext();
|
|
43
44
|
let QueryEngineClass = this.constructor;
|
|
44
|
-
let currentContext = context || this.currentContext;
|
|
45
45
|
let queryEngine = new QueryEngineClass({
|
|
46
|
-
connection:
|
|
46
|
+
connection: this.getConnection(),
|
|
47
47
|
});
|
|
48
48
|
|
|
49
|
-
if (
|
|
50
|
-
queryEngine = queryEngine[
|
|
49
|
+
if (context.rootModelName)
|
|
50
|
+
queryEngine = queryEngine[context.rootModelName];
|
|
51
51
|
|
|
52
52
|
return queryEngine;
|
|
53
53
|
}
|
|
@@ -59,36 +59,23 @@ class QueryEngine extends QueryEngineBase {
|
|
|
59
59
|
return queryGenerator.toConnectionString(this, ...args);
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
MERGE(
|
|
63
|
-
let
|
|
64
|
-
if (!
|
|
62
|
+
MERGE(_incomingQueryEngine, _options) {
|
|
63
|
+
let incomingQueryEngine = _incomingQueryEngine;
|
|
64
|
+
if (!incomingQueryEngine)
|
|
65
65
|
return this;
|
|
66
66
|
|
|
67
|
-
let
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
let
|
|
73
|
-
let
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
// as they are provided by the
|
|
80
|
-
// parent queryEngine
|
|
81
|
-
'connection',
|
|
82
|
-
'fieldContext',
|
|
83
|
-
'isQueryContext',
|
|
84
|
-
'modelContext',
|
|
85
|
-
'queryEngineScope',
|
|
86
|
-
'operationStack',
|
|
87
|
-
'rootContext',
|
|
88
|
-
'rootModel',
|
|
89
|
-
'rootModelName',
|
|
90
|
-
'_queryFinalized',
|
|
91
|
-
]);
|
|
67
|
+
let options = _options || {};
|
|
68
|
+
let thisQueryContext = this.getOperationContext();
|
|
69
|
+
if (!QueryEngine.isQuery(incomingQueryEngine) && Nife.instanceOf(incomingQueryEngine, 'array', 'object', 'map', 'set'))
|
|
70
|
+
incomingQueryEngine = Utils.generateQueryFromFilter(this.getConnection(options.connection), thisQueryContext.rootModel, incomingQueryEngine);
|
|
71
|
+
|
|
72
|
+
let incomingOperationStack = incomingQueryEngine.getOperationStack();
|
|
73
|
+
let skippingLogical = true;
|
|
74
|
+
let logicalOperatorEncountered = false;
|
|
75
|
+
let queryEngine = this.clone();
|
|
76
|
+
|
|
77
|
+
for (let i = 0, il = incomingOperationStack.length; i < il; i++) {
|
|
78
|
+
let queryPart = incomingOperationStack[i];
|
|
92
79
|
|
|
93
80
|
// For merges, we want to skip the first logical operators
|
|
94
81
|
// found before any other operation.
|
|
@@ -98,32 +85,38 @@ class QueryEngine extends QueryEngineBase {
|
|
|
98
85
|
// Since the result we want here is OR merge, not AND merge
|
|
99
86
|
// we skip the first "AND" we encounter, leaving the "OR" as
|
|
100
87
|
// the current logical operator.
|
|
101
|
-
|
|
102
|
-
|
|
88
|
+
// Logical operators do not always come first in the query,
|
|
89
|
+
// so we need to rely on "logicalOperatorEncountered" to
|
|
90
|
+
// ensure we skip only the first one (or first few in a sequence).
|
|
91
|
+
if (skippingLogical && Object.prototype.hasOwnProperty.call(queryPart, 'logical') && queryPart.logical) {
|
|
92
|
+
if (queryPart.value == null && (queryPart.operator === 'AND' || queryPart.operator === 'OR')) {
|
|
93
|
+
logicalOperatorEncountered = true;
|
|
103
94
|
continue;
|
|
95
|
+
}
|
|
104
96
|
}
|
|
105
97
|
|
|
106
|
-
if (
|
|
107
|
-
|
|
108
|
-
if (mergeContext.operator === 'MODEL') {
|
|
109
|
-
if (mergeContext.Model === currentModel)
|
|
110
|
-
continue;
|
|
98
|
+
if (logicalOperatorEncountered)
|
|
99
|
+
skippingLogical = false;
|
|
111
100
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
101
|
+
let value = queryPart.value;
|
|
102
|
+
if (Object.prototype.hasOwnProperty.call(queryPart, 'control') && queryPart.control === true) {
|
|
103
|
+
if (queryPart.operator === 'PROJECT' && options.projections !== true)
|
|
104
|
+
continue;
|
|
105
|
+
|
|
106
|
+
if (queryPart.operator === 'ORDER' && options.orders === false)
|
|
107
|
+
continue;
|
|
108
|
+
|
|
109
|
+
if (queryPart.operator === 'GROUP_BY' && options.groupBys === false)
|
|
110
|
+
continue;
|
|
111
|
+
|
|
112
|
+
if (queryPart.operator === 'PROJECT' || queryPart.operator === 'ORDER' || queryPart.operator === 'GROUP_BY')
|
|
113
|
+
value = [ '+' ].concat(value);
|
|
116
114
|
}
|
|
117
115
|
|
|
118
|
-
|
|
119
|
-
Model: queryPart.Model,
|
|
120
|
-
modelName: queryPart.modelName,
|
|
121
|
-
Field: queryPart.Field,
|
|
122
|
-
fieldName: queryPart.fieldName,
|
|
123
|
-
}, mergeContext));
|
|
116
|
+
queryEngine = queryEngine[queryPart.queryProp](value, ...(queryPart.queryExtraArgs || []));
|
|
124
117
|
}
|
|
125
118
|
|
|
126
|
-
return
|
|
119
|
+
return queryEngine;
|
|
127
120
|
}
|
|
128
121
|
|
|
129
122
|
async all(options) {
|
|
@@ -110,13 +110,13 @@ class SerializedType extends Type {
|
|
|
110
110
|
options = { type: options };
|
|
111
111
|
|
|
112
112
|
options = {
|
|
113
|
-
serialize:
|
|
113
|
+
serialize: ({ value }) => {
|
|
114
114
|
if (value == null)
|
|
115
115
|
return value;
|
|
116
116
|
|
|
117
117
|
return JSON.stringify(value);
|
|
118
118
|
},
|
|
119
|
-
deserialize:
|
|
119
|
+
deserialize: ({ value }) => {
|
|
120
120
|
if (value == null)
|
|
121
121
|
return value;
|
|
122
122
|
|
|
@@ -72,12 +72,12 @@ const TYPE_OPERATIONS = {
|
|
|
72
72
|
if (!model.isDirty())
|
|
73
73
|
return model;
|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
await connection.update(model.getModel(), [ model ], options);
|
|
76
76
|
|
|
77
77
|
// Update this model to reflect the update
|
|
78
|
-
ModelUtils.setRelationalValues(connection, this.getModel(), this,
|
|
78
|
+
ModelUtils.setRelationalValues(connection, this.getModel(), this, model.getModel(), model);
|
|
79
79
|
|
|
80
|
-
return
|
|
80
|
+
return model;
|
|
81
81
|
}, options);
|
|
82
82
|
|
|
83
83
|
// Save needs to go outside of the transaction
|