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.
@@ -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._addToQuery({ 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._addToQuery({ 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._addToQuery({ 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._addToQuery({ 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._addToQuery({ 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
- let values = Nife.arrayFlatten(args);
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
- values = Nife.toArray(values).filter((value) => {
150
+ entities = Nife.toArray(entities).map((value) => {
90
151
  if (value == null)
91
- return false;
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::ORDER: Invalid value provided. All values provided must be strings. 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" ]).');
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
- return true;
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._addToQuery({ control: true, operator: 'ORDER', value: values, order: values });
100
218
  return this._fetchScope('model');
101
219
  }
102
220
 
103
221
  PROJECT(...args) {
104
- let values = Nife.arrayFlatten(args);
222
+ let entities = Nife.arrayFlatten(args);
105
223
 
106
- values = Nife.toArray(values).map((value) => {
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
- throw new Error('QueryEngine::ModelScope::PROJECT: Invalid value provided. All values provided must be strings.');
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._addToQuery({ control: true, operator: 'PROJECT', value: values });
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._getRawQueryContext();
138
273
  let rootModel = context.rootModel;
139
- if (!rootModel)
140
- throw new Error(`${this.constructor.name}::DISTINCT: Attempted to apply DISTINCT to the root model of the query, but no root model was found.`);
141
-
142
- let pkFieldName = rootModel.getPrimaryKeyFieldName();
143
- if (!pkFieldName)
144
- throw new Error(`${this.constructor.name}::DISTINCT: Attempted to apply DISTINCT to the root model of the query, but the root model has no primary key. Try directly specifying the DISTINCT field instead.`);
145
-
146
- distinctValue = new DistinctLiteral(`${rootModel.getModelName()}:${pkFieldName}`);
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._addToQuery({ control: true, operator: 'DISTINCT', value: distinctValue, distinct: distinctValue });
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._addToQuery({ control: true, operator: 'JOIN', value: 'inner', joinType: 'inner', joinOuter: false });
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._addToQuery({ control: true, operator: 'JOIN', value: 'left', joinType: 'left', joinOuter: !!outerJoin });
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._addToQuery({ control: true, operator: 'JOIN', value: 'right', joinType: 'right', joinOuter: !!outerJoin });
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._addToQuery({ control: true, operator: 'JOIN', value: 'full', joinType: 'full', joinOuter: !!outerJoin });
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._addToQuery({ control: true, operator: 'JOIN', value: 'cross', joinType: 'cross', joinOuter: false });
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._addToQuery({ control: true, operator: 'JOIN', value: type, joinType: type, joinOuter: false });
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 isQueryContext(value) {
17
- return !!(value && value.isQueryContext);
16
+ static isQueryOperationContext(value) {
17
+ return !!(value && value.isQueryOperationContext);
18
18
  }
19
19
 
20
20
  static isQuery(value) {
@@ -27,13 +27,13 @@ class QueryEngineBase extends ProxyClass {
27
27
  if (value._isQueryEngine)
28
28
  return true;
29
29
 
30
- if (typeof value._getRawQueryContext === 'function')
30
+ if (typeof value.getOperationContext === 'function')
31
31
  return true;
32
32
 
33
33
  return false;
34
34
  }
35
35
 
36
- static queryContextType(queryContext) {
36
+ static queryOperationInfo(queryContext) {
37
37
  let contextParams = {
38
38
  hasCondition: false,
39
39
  hasField: false,
@@ -63,6 +63,10 @@ class QueryEngineBase extends ProxyClass {
63
63
  return this.getQueryEngineScope().getFieldScopeClass();
64
64
  }
65
65
 
66
+ getQueryEngineScopeClass() {
67
+ return this.getQueryEngineScope().constructor;
68
+ }
69
+
66
70
  _inheritContext(context, name, ...args) {
67
71
  let newContext = Object.assign(
68
72
  Object.create(context),
@@ -82,7 +86,7 @@ class QueryEngineBase extends ProxyClass {
82
86
  }
83
87
 
84
88
  _fetchScope(...scopeNames) {
85
- let context = this._getRawQueryContext();
89
+ let context = this.getOperationContext();
86
90
 
87
91
  for (let i = 0, il = scopeNames.length; i < il; i++) {
88
92
  let scopeName = scopeNames[i];
@@ -120,8 +124,10 @@ class QueryEngineBase extends ProxyClass {
120
124
  let modelName = Model.getModelName();
121
125
  let context = this.currentContext;
122
126
  let extra = {};
127
+ let isFirst = !context.rootModel;
128
+ let connection = this.getConnection();
123
129
 
124
- if (!context.rootModel) {
130
+ if (isFirst) {
125
131
  extra.rootModelName = modelName;
126
132
  extra.rootModel = Model;
127
133
  }
@@ -137,19 +143,19 @@ class QueryEngineBase extends ProxyClass {
137
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.`);
138
144
  }
139
145
 
140
- if (!dialect) {
141
- let connection = this.getConnection();
142
- if (connection)
143
- dialect = connection.dialect;
144
- }
146
+ if (!dialect && connection)
147
+ dialect = connection.dialect;
145
148
 
146
- 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);
147
150
  let newScope = new ModelScopeClass(newContext);
148
151
 
149
152
  // We shouldn't add this scope if this is
150
153
  // already the current model of the scope
151
154
  if (context.Model !== Model)
152
- context.queryRoot.push(newContext);
155
+ context.operationStack.push(newContext);
156
+
157
+ if (isFirst)
158
+ newScope = newScope.PROJECT(Model);
153
159
 
154
160
  // But we always need to return the scope
155
161
  // for the proxy to work properly
@@ -167,13 +173,13 @@ class QueryEngineBase extends ProxyClass {
167
173
  extra.rootField = Field;
168
174
  }
169
175
 
170
- 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);
171
177
  let newScope = new FieldScopeClass(newContext);
172
178
 
173
179
  // We shouldn't add this scope if this is
174
180
  // already the current field of the scope
175
181
  if (context.Field !== Field)
176
- context.queryRoot.push(newContext);
182
+ context.operationStack.push(newContext);
177
183
 
178
184
  // But we always need to return the scope
179
185
  // for the proxy to work properly
@@ -193,8 +199,8 @@ class QueryEngineBase extends ProxyClass {
193
199
  context.rootContext = context;
194
200
  }
195
201
 
196
- if (!context.queryRoot)
197
- context.queryRoot = [];
202
+ if (!context.operationStack)
203
+ context.operationStack = [];
198
204
 
199
205
  if (!('and' in context))
200
206
  context.and = true;
@@ -204,40 +210,40 @@ class QueryEngineBase extends ProxyClass {
204
210
 
205
211
  // console.log(`Creating new ${this.constructor.name} scope: `, context, Object.getPrototypeOf(context));
206
212
 
207
- let queryRoot = context.queryRoot;
213
+ let operationStack = context.operationStack;
208
214
  Object.defineProperties(this, {
209
- queryRoot: {
215
+ operationStack: {
210
216
  writable: false,
211
217
  enumerable: false,
212
218
  configurable: true,
213
- value: queryRoot,
219
+ value: operationStack,
214
220
  },
215
221
  currentContext: {
216
222
  enumerable: false,
217
223
  configurable: true,
218
224
  get: () => {
219
- let currentContext = queryRoot[queryRoot.length - 1];
225
+ let currentContext = operationStack[operationStack.length - 1];
220
226
  return currentContext || context;
221
227
  },
222
- set: () => {},
228
+ set: () => {},
223
229
  },
224
230
  });
225
231
  }
226
232
 
227
- _getTopContextID() {
233
+ getQueryID() {
228
234
  return this.currentContext.contextID;
229
235
  }
230
236
 
231
- _getRawQueryContext() {
237
+ getOperationContext() {
232
238
  return this.currentContext;
233
239
  }
234
240
 
235
- _getRawQuery() {
236
- return this.currentContext.queryRoot;
241
+ getOperationStack() {
242
+ return this.currentContext.operationStack;
237
243
  }
238
244
 
239
- _isLastPartControl() {
240
- let queryParts = this._getRawQuery();
245
+ isLastOperationControl() {
246
+ let queryParts = this.getOperationStack();
241
247
  let lastPart = queryParts[queryParts.length - 1];
242
248
 
243
249
  if (Object.prototype.hasOwnProperty.call(lastPart, 'control') && lastPart.control === true)
@@ -246,8 +252,8 @@ class QueryEngineBase extends ProxyClass {
246
252
  return false;
247
253
  }
248
254
 
249
- _isLastPartCondition() {
250
- let queryParts = this._getRawQuery();
255
+ isLastOperationCondition() {
256
+ let queryParts = this.getOperationStack();
251
257
  let lastPart = queryParts[queryParts.length - 1];
252
258
 
253
259
  if (Object.prototype.hasOwnProperty.call(lastPart, 'condition') && lastPart.condition === true)
@@ -256,13 +262,13 @@ class QueryEngineBase extends ProxyClass {
256
262
  return false;
257
263
  }
258
264
 
259
- _queryHasConditions() {
260
- let context = this._getRawQueryContext();
265
+ queryHasConditions() {
266
+ let context = this.getOperationContext();
261
267
  return context.hasCondition;
262
268
  }
263
269
 
264
- _queryHasJoins() {
265
- let queryParts = this._getRawQuery();
270
+ queryHasJoins() {
271
+ let queryParts = this.getOperationStack();
266
272
  for (let i = 0, il = queryParts.length; i < il; i++) {
267
273
  let queryPart = queryParts[i];
268
274
  if (QueryEngineBase.isQuery(queryPart.value) && !queryPart.value.hasCondition)
@@ -272,8 +278,8 @@ class QueryEngineBase extends ProxyClass {
272
278
  return false;
273
279
  }
274
280
 
275
- _debugQuery() {
276
- let query = this._getRawQuery();
281
+ logQueryOperations() {
282
+ let query = this.getOperationStack();
277
283
  for (let i = 0, il = query.length; i < il; i++) {
278
284
  let queryPart = query[i];
279
285
  let operator = queryPart.operator;
@@ -285,14 +291,13 @@ class QueryEngineBase extends ProxyClass {
285
291
  else
286
292
  console.log(`${operator} -> ${queryPart.value}`);
287
293
  }
288
-
289
294
  }
290
295
 
291
- _addToQuery(queryPart, _context) {
292
- let context = _context || this._getRawQueryContext();
293
- let queryRoot = context.queryRoot;
296
+ _pushOperationOntoStack(queryPart, _context) {
297
+ let context = _context || this.getOperationContext();
298
+ let operationStack = context.operationStack;
294
299
 
295
- queryRoot.push(
300
+ operationStack.push(
296
301
  this._inheritContext(
297
302
  context,
298
303
  null,
@@ -327,14 +332,115 @@ class QueryEngineBase extends ProxyClass {
327
332
  }
328
333
 
329
334
  clone() {
330
- const Klass = this.constructor;
331
- let context = this._getRawQueryContext();
332
- let queryRootCopy = this._getRawQuery().slice();
333
- let newContext = Object.assign(Object.create(context), { queryRoot: queryRootCopy });
335
+ return this.map((part) => part)._fetchScope('model');
336
+ }
334
337
 
335
- queryRootCopy.push(newContext);
338
+ filter(callback) {
339
+ const Klass = this.getQueryEngineScopeClass();
340
+ let context = this.getOperationContext();
341
+ let parts = this.getOperationStack();
342
+ let query = new Klass({
343
+ ...context.rootContext,
344
+ connection: this.getConnection(),
345
+ operationStack: [],
346
+ });
347
+
348
+ for (let i = 0, il = parts.length; i < il; i++) {
349
+ let part = parts[i];
350
+ if (!part)
351
+ continue;
352
+
353
+ if (!callback(part, i, parts, query))
354
+ continue;
355
+
356
+ query._pushOperationOntoStack({ ...part });
357
+ }
358
+
359
+ return query;
360
+ }
361
+
362
+ map(callback) {
363
+ const Klass = this.getQueryEngineScopeClass();
364
+ let context = this.getOperationContext();
365
+ let parts = this.getOperationStack();
366
+ let query = new Klass({
367
+ ...context.rootContext,
368
+ connection: this.getConnection(),
369
+ operationStack: [],
370
+ });
371
+
372
+ for (let i = 0, il = parts.length; i < il; i++) {
373
+ let part = parts[i];
374
+ if (!part)
375
+ continue;
376
+
377
+ let newPart = callback({ ...part }, i, parts, query);
378
+ if (!newPart || typeof newPart !== 'object')
379
+ continue;
380
+
381
+ query._pushOperationOntoStack(newPart);
382
+ }
383
+
384
+ return query;
385
+ }
386
+
387
+ walk(callback, _checkContextKeys) {
388
+ const walkQueries = (query, parent, contextKey, depth) => {
389
+ let parts = query.getOperationStack();
390
+ for (let i = 0, il = parts.length; i < il; i++) {
391
+ let part = parts[i];
392
+ if (!part)
393
+ continue;
394
+
395
+ for (let j = 0, jl = checkContextKeys.length; j < jl; j++) {
396
+ let contextKey = checkContextKeys[j];
397
+ if (!Object.prototype.hasOwnProperty.call(part, contextKey))
398
+ continue;
399
+
400
+ let value = part[contextKey];
401
+ if (value && this.constructor.isQuery(value))
402
+ walkQueries(value, part, contextKey, depth + 1);
403
+ }
404
+ }
405
+
406
+ if (parent !== null)
407
+ callback(query, parent, contextKey, depth);
408
+ };
409
+
410
+ let checkContextKeys = _checkContextKeys || [ 'value' ];
411
+ walkQueries(this, null, null, 0);
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
+ }
336
440
 
337
- return new Klass(newContext);
441
+ isModelUsedInQuery(Model) {
442
+ let allModels = this.getAllModelsUsedInQuery();
443
+ return (allModels.indexOf(Model) >= 0);
338
444
  }
339
445
  }
340
446