mythix-orm 1.11.6 → 1.12.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/README.md +100 -9
- package/lib/connection/connection-base.js +173 -39
- package/lib/connection/literals/average-literal.js +67 -0
- package/lib/connection/literals/count-literal.js +80 -0
- package/lib/connection/literals/distinct-literal.js +71 -0
- package/lib/connection/literals/field-literal.js +54 -0
- package/lib/connection/literals/literal-base.d.ts +1 -0
- package/lib/connection/literals/literal-base.js +137 -2
- package/lib/connection/literals/literal-field-base.js +50 -0
- package/lib/connection/literals/literal.js +15 -0
- package/lib/connection/literals/max-literal.js +67 -0
- package/lib/connection/literals/min-literal.js +67 -0
- package/lib/connection/literals/sum-literal.js +67 -0
- package/lib/connection/query-generator-base.js +273 -0
- package/lib/field.js +1 -1
- package/lib/model.js +54 -2
- package/lib/proxy-class/proxy-class.js +156 -21
- package/lib/query-engine/field-scope.js +401 -9
- package/lib/query-engine/model-scope.js +572 -13
- package/lib/query-engine/query-engine-base.js +590 -30
- package/lib/query-engine/query-engine.d.ts +13 -2
- package/lib/query-engine/query-engine.js +525 -18
- package/lib/types/helpers/default-helpers.js +124 -0
- package/lib/types/type.js +297 -3
- package/lib/utils/async-store.js +80 -3
- package/package.json +1 -2
|
@@ -125,16 +125,106 @@ function wrapOrderClause(func) {
|
|
|
125
125
|
return func;
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
+
/// `ModelScope` is the "model level" of a query.
|
|
129
|
+
/// It manages things like `EXISTS`, `PROJECT`,
|
|
130
|
+
/// `ORDER`, `INNER_JOIN`, etc...
|
|
131
|
+
///
|
|
132
|
+
/// Being a Proxy, it will "listen" for key access,
|
|
133
|
+
/// and lookup fields if there is a key access where
|
|
134
|
+
/// the key name isn't found on the class instance itself.
|
|
135
|
+
/// In this case, it will check the previous model specified
|
|
136
|
+
/// on the top of the internal "operation stack" to see if it
|
|
137
|
+
/// owns a field with the name of the missing key.
|
|
138
|
+
/// If there is a matching field on the top-most model of the stack,
|
|
139
|
+
/// then it will use the field found to push a <see>FieldScope</see>
|
|
140
|
+
/// onto the "operation stack", and then return that to the user.
|
|
141
|
+
/// Take the following example:
|
|
142
|
+
/// ```javascript
|
|
143
|
+
/// let queryRoot = new QueryEngine({ connection });
|
|
144
|
+
/// let userIDFieldScope = queryRoot.User.id;
|
|
145
|
+
/// ```
|
|
146
|
+
/// When we attempt to access the key `'id'` on
|
|
147
|
+
/// the `User` "model scope", we will find that no such key
|
|
148
|
+
/// exists on the `ModelScope` class. Now that no such property is found
|
|
149
|
+
/// on the `ModelScope` class, the <see>ProxyClass</see> will call the method
|
|
150
|
+
/// `MISSING` on the `ModelScope`, and this `MISSING` method
|
|
151
|
+
/// will check if the current model (`User`) has a field named `'id'`.
|
|
152
|
+
/// The <see>ModelScope._getField</see> method finds this field, and
|
|
153
|
+
/// returns it. The `ModelScope` then takes this field instance,
|
|
154
|
+
/// and uses it to create and return a <see>FieldScope</see>
|
|
155
|
+
/// using <see>QueryEngineBase._newFieldScope</see>. Now
|
|
156
|
+
/// that we have a <see>FieldScope</see>, we can continue
|
|
157
|
+
/// chaining, now using "field level" operators to act on
|
|
158
|
+
/// the field we just looked up.
|
|
159
|
+
///
|
|
160
|
+
/// See: QueryEngineBase
|
|
161
|
+
///
|
|
162
|
+
/// See: QueryEngine
|
|
163
|
+
///
|
|
164
|
+
/// See: FieldScope
|
|
165
|
+
///
|
|
166
|
+
/// Note:
|
|
167
|
+
/// `ModelScope` is a sub-part of the `QueryEngine`, and so is generally referred to
|
|
168
|
+
/// simply as the `QueryEngine` as a whole. This is also the case for <see>QueryEngineBase</see>,
|
|
169
|
+
/// and <see>FieldScope</see>, which also make up the `QueryEngine` as sub-parts,
|
|
170
|
+
/// and so are also often referred to simply as "the query engine".
|
|
128
171
|
class ModelScope extends QueryEngineBase {
|
|
172
|
+
/// Get the specified field (by name) from the
|
|
173
|
+
/// top-most model on the internal "operation stack".
|
|
174
|
+
///
|
|
175
|
+
/// This method is called when a key can not be found
|
|
176
|
+
/// on the `ModelScope` instance. The <see>ProxyClass</see>
|
|
177
|
+
/// will call the `MISSING` method when the key is not
|
|
178
|
+
/// found, and that method in turn calls this `_getField`
|
|
179
|
+
/// method to see if the key specified was actually a field
|
|
180
|
+
/// name on the most recent model in the "operation stack".
|
|
181
|
+
///
|
|
182
|
+
/// Arguments:
|
|
183
|
+
/// fieldName: string
|
|
184
|
+
/// The field name of the field to fetch from the top-most model on the stack.
|
|
185
|
+
///
|
|
186
|
+
/// Return: <see>Field</see>
|
|
187
|
+
/// The field found, or `undefined` if no such field is found.
|
|
129
188
|
_getField(fieldName) {
|
|
130
189
|
let Model = this.currentContext.Model;
|
|
131
190
|
return Model.getField(fieldName);
|
|
132
191
|
}
|
|
133
192
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
193
|
+
/// Specify a <see>FieldScope</see> directly.
|
|
194
|
+
///
|
|
195
|
+
/// This can be useful if you have a name collision, or if
|
|
196
|
+
/// you just want to go the direct route to specify a <see>FieldScope</see>.
|
|
197
|
+
///
|
|
198
|
+
/// This will find the field specified on the most recent (top-most)
|
|
199
|
+
/// model on the internal "operation stack". If the field is found, then
|
|
200
|
+
/// it will be used to create a new <see>FieldScope</see>, which will
|
|
201
|
+
/// then be pushed onto the "operation stack", and returned to the user.
|
|
202
|
+
///
|
|
203
|
+
/// Example:
|
|
204
|
+
/// // The two queries below are equivalent. The latter
|
|
205
|
+
/// // can be a good way to request a field if the field
|
|
206
|
+
/// // name being specified happens to be a name collision
|
|
207
|
+
/// // (i.e. has the same name as a QueryEngine or ModelScope
|
|
208
|
+
/// // property... such as "ORDER" or "OFFSET" for example).
|
|
209
|
+
/// let query1 = User.where.id.EQ('test');
|
|
210
|
+
/// let query2 = User.where.Field('id').EQ('test');
|
|
211
|
+
///
|
|
212
|
+
/// Note:
|
|
213
|
+
/// An exception will be thrown in the specified field can not be found.
|
|
214
|
+
///
|
|
215
|
+
/// Note:
|
|
216
|
+
/// This is one of the rare places in Mythix ORM where a fully qualified field
|
|
217
|
+
/// name **SHOULD NOT** be used. The reason should be clear: The model in the
|
|
218
|
+
/// operation should already be known.
|
|
219
|
+
///
|
|
220
|
+
/// Arguments:
|
|
221
|
+
/// fieldName: string
|
|
222
|
+
/// A field name (NOT fully qualified), as a string. It must be a field
|
|
223
|
+
/// that exists on the model from the top-most <see>ModelScope</see> on
|
|
224
|
+
/// the internal "operation stack".
|
|
225
|
+
///
|
|
226
|
+
/// Return: <see>FieldScope</see>
|
|
227
|
+
/// A new <see>FieldScope</see>, targeted to the field specified by `fieldName`.
|
|
138
228
|
Field(fieldName) {
|
|
139
229
|
let field = this._getField(fieldName);
|
|
140
230
|
if (!field)
|
|
@@ -155,33 +245,205 @@ class ModelScope extends QueryEngineBase {
|
|
|
155
245
|
return lowerScope[prop];
|
|
156
246
|
}
|
|
157
247
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
248
|
+
/// Merge fields together, replacing, adding, or subtracting from
|
|
249
|
+
/// the field set.
|
|
250
|
+
///
|
|
251
|
+
/// This method is used by <see>ModelScope.ORDER</see>, <see>ModelScope.GROUP_BY</see>,
|
|
252
|
+
/// and <see>ModelScope.PROJECT</see>. It works be replacing, adding, or subtracting
|
|
253
|
+
/// from the current set of fields defined by any of these operations.
|
|
254
|
+
///
|
|
255
|
+
/// If the first field encountered in the provided `incomingFields` isn't prefixed with
|
|
256
|
+
/// a `+` or `-`, and if a `+` or `-` operation doesn't come before the encountered field, then
|
|
257
|
+
/// the operation is considered a "replace" operation. For example, in `query.ORDER('User:id')`
|
|
258
|
+
/// we are "replacing" the order clause with only a single field: `'User:id'`. If however,
|
|
259
|
+
/// we did `query.ORDER('+User:id')` then the `'User:id'` field would be *added* to any current
|
|
260
|
+
/// order clause in the query. We could also prefix the entire operation with a single `'+'`
|
|
261
|
+
/// string, and then all following fields in the list would be in "add" mode. For example, the
|
|
262
|
+
/// following two operations are equivalent: `query.ORDER('+', 'User:firstName', 'User:lastName')`, and
|
|
263
|
+
/// `query.ORDER('+User:firstName', '+User:lastName')`. The subtraction operator `'-'` works the
|
|
264
|
+
/// same, but in reverse, specifying that we want to remove fields instead of add them:
|
|
265
|
+
/// `query.ORDER('-', 'User:firstName', 'User:lastName')`, and `query.ORDER('-User:firstName', '-User:lastName')`.
|
|
266
|
+
/// It is also possible to mix the two together: `query.ORDER('+', 'User:firstName', '-', 'User:lastName')`, or
|
|
267
|
+
/// `query.ORDER('+User:firstName', '-User:lastName')`.
|
|
268
|
+
///
|
|
269
|
+
/// There is one special operator, `'*'`. This operator will add *all* fields from all models that
|
|
270
|
+
/// are currently in use on the query. For example, if we did: `Role.where.userID(User.where.id).PROJECT('*')`,
|
|
271
|
+
/// this would add *all* `Role` and `User` fields to the projection. Replace, add, and subtract rules still
|
|
272
|
+
/// apply with a wildcard. So in our example above we are "replacing" the projection, since no `+` or `-` was
|
|
273
|
+
/// encountered before the operation. If you instead wanted to *add* all model fields to the projection,
|
|
274
|
+
/// you should instead do: `Role.where.userID(User.where.id).PROJECT('+*')` or `Role.where.userID(User.where.id).PROJECT('+', '*')`.
|
|
275
|
+
/// Generally, it probably wouldn't matter much, since you are replacing "every field" anyhow... but it could
|
|
276
|
+
/// matter if for example you had previously projected a literal... in which case it would be replaced
|
|
277
|
+
/// with all model fields. Subtraction rules also apply to wildcard selectors.
|
|
278
|
+
///
|
|
279
|
+
/// The only truly important thing to remember here is that if **no** operation is specified (add or subtract),
|
|
280
|
+
/// then the default operation is "replace", so the field set will be replaced entirely for the operation
|
|
281
|
+
/// in question.
|
|
282
|
+
///
|
|
283
|
+
/// There is one exception to this behavior, for `ORDER.ADD` and `ORDER.REPLACE` **only**. These two operations
|
|
284
|
+
/// allow the field sort direction itself to be defined with `+` and `-`... so a
|
|
285
|
+
/// `query.ORDER.ADD('+User:firstName', '-User:lastName')` is not requesting that we add the `'User:firstName'`
|
|
286
|
+
/// field, and subtract the `'User:lastName'` field... but instead is specifying that we want
|
|
287
|
+
/// `'User:firstName'` in `ASC` sort order, whereas we want `'User:lastName'` in `DESC` sort order.
|
|
288
|
+
/// `ORDER.ADD` and `ORDER.REPLACE` are the only exceptions to the normal logic defined here. There are
|
|
289
|
+
/// two methods so that a user can add to the field set, or replace the field set. A `SUB` (subtract)
|
|
290
|
+
/// operation is not needed, because that can be done with `ORDER` anyway, i.e.
|
|
291
|
+
/// `query.ORDER.ADD('+User:firstName', '+User:lastName').ORDER('-Role')`, which would "add" the
|
|
292
|
+
/// `'User:firstName'` and `'User:lastName'` fields, simultaneously specifying their short direction,
|
|
293
|
+
/// and then `ORDER('-Role')` is subtracting every field from the `Role` model.
|
|
294
|
+
///
|
|
295
|
+
/// Note:
|
|
296
|
+
/// A solo `+` or `-` operation is a no-op: `query.ORDER('+')` and `query.ORDER('-')`
|
|
297
|
+
/// will do nothing at all, and will not modify the operation in the slightest.
|
|
298
|
+
///
|
|
299
|
+
/// Note:
|
|
300
|
+
/// This method supports a short-cut for literals. You can specify a string prefixed
|
|
301
|
+
/// with an `@` symbol to specify a literal. For example, `query.PROJECT('@COUNT("users"."firstName") AS "count"')`.
|
|
302
|
+
/// Doing so will create a new <see>Literal</see> instance with the value provided. This
|
|
303
|
+
/// is not the best way to provide a literal however, because the <see>Literal</see> class
|
|
304
|
+
/// defines a "raw" literal, whereas the typed literal classes (such as <see>CountLiteral</see>)
|
|
305
|
+
/// actually store the field they are operating on, and can report that field back to the engine.
|
|
306
|
+
///
|
|
307
|
+
/// Arguments:
|
|
308
|
+
/// currentFields: Map<string, { value: Field | Literal | string; direction?: '+' | '-'; ... }>
|
|
309
|
+
/// A map of the current fields for the operation. This is the value from the operation
|
|
310
|
+
/// context itself. For example, the `ORDER` method provides this argument via:
|
|
311
|
+
/// `this.getOperationContext().order`, providing any previous "order" operation
|
|
312
|
+
/// as the current fields.
|
|
313
|
+
/// incomingFields: Array<<see>Model</see> | <see>Field</see> | string | literal | '+' | '-' | '*'>
|
|
314
|
+
/// Incoming fields to replace, add, or subtract from the operation in question.
|
|
315
|
+
/// If no fields at all are provided, then this will reset/nullify the operation. For example,
|
|
316
|
+
/// an `ORDER()` would clear any `ORDER BY` clause entirely. If a model is provided, either
|
|
317
|
+
/// as a name, i.e. `'User'`, or as the raw model, i.e. `User`, then *all the fields* from the
|
|
318
|
+
/// specified model will be added or subtracted. A field can be specified as a fully qualified
|
|
319
|
+
/// field name, a raw <see>Field</see> instance, or just the field name itself, i.e. `'firstName'`.
|
|
320
|
+
/// If no model is specified (i.e. a non-fully-qualified field name), then the engine will attempt
|
|
321
|
+
/// to fetch the field specified from the root model of the query. Literals can be used for all
|
|
322
|
+
/// supported operations, `ORDER`, `GROUP_BY`, and `PROJECT`, and they also follow the replace, add
|
|
323
|
+
/// and subtract rules of the engine.
|
|
324
|
+
/// extraData?: object
|
|
325
|
+
/// Extra data to apply to the operation. For example, the `ORDER` operation applies a `direction` property
|
|
326
|
+
/// to each field in the map. The field map is a `Map` instance, where each key is the fully qualified field
|
|
327
|
+
/// name, or expanded literal value. Each value on the map is an object, containing at least a `value` property
|
|
328
|
+
/// that is the field or literal specified. This object can also contain any ancillary operation info, such
|
|
329
|
+
/// as in the case of the `ORDER` operation, which will also add a `direction` property to each field in the
|
|
330
|
+
/// map to specify the field's (or literal's) sort order.
|
|
331
|
+
/// options?: object
|
|
332
|
+
/// An options object to pass off to `Literal.toString` when expanding literals for use as `Map` keys.
|
|
333
|
+
///
|
|
334
|
+
/// Return: Map<string, { value: Field | Literal | string; direction?: '+' | '-'; ... }>
|
|
335
|
+
/// Return the new field set. A `Map` will always be returned, but it is possible
|
|
336
|
+
/// for the `Map` to be empty.
|
|
166
337
|
margeFields(currentFields, incomingFields, extraData, options) {
|
|
167
338
|
return QueryUtils.margeFields(this, currentFields, incomingFields, extraData, options);
|
|
168
339
|
}
|
|
169
340
|
|
|
341
|
+
/// Invert the logic of the following operator.
|
|
342
|
+
///
|
|
343
|
+
/// This method does not need to be called.
|
|
344
|
+
///
|
|
345
|
+
/// Unlike `AND` and `OR` operators, this is not a permanent toggle.
|
|
346
|
+
/// As soon as a following operator is encountered, the `NOT` operation
|
|
347
|
+
/// will be "turned off". `NOT` will invert any operation, so for example
|
|
348
|
+
/// if you did a `User.where.id.NOT.EQ('test')` then this is the same as
|
|
349
|
+
/// `User.where.id.NEQ('test')`--selecting everything NOT EQUAL TO `'test'`.
|
|
350
|
+
/// `NOT` can also be used in combination with `EXISTS`, `ANY`, `ALL`, `IN`,
|
|
351
|
+
/// etc... inverting any operator following `NOT`.
|
|
352
|
+
///
|
|
353
|
+
/// Note:
|
|
354
|
+
/// `NOT` is only ever used once, and then it is toggled back off. For example,
|
|
355
|
+
/// if we did: `User.where.id.NOT.EQ('test').AND.lastName.EQ('Smith')`, then
|
|
356
|
+
/// we would have a query where `id != 'test' AND lastName = 'Smith'`. As you
|
|
357
|
+
/// can see, the `NOT` doesn't apply to the second `lastName` operator, as it
|
|
358
|
+
/// was turned off as soon as the first `EQ` operator was encountered on the `id`
|
|
359
|
+
/// field.
|
|
360
|
+
///
|
|
361
|
+
/// Return: <see>ModelScope</see>
|
|
362
|
+
/// Return a <see>ModelScope</see> to allow the user to continue chaining operations on the query.
|
|
363
|
+
///
|
|
364
|
+
/// SyntaxType: FunctionDeclaration
|
|
170
365
|
NOT = ProxyClass.autoCall(function() {
|
|
171
|
-
this._pushOperationOntoStack({ logical: true, operator: 'NOT', queryProp: 'NOT', not: !this.
|
|
366
|
+
this._pushOperationOntoStack({ logical: true, operator: 'NOT', queryProp: 'NOT', not: !this.getOperationContext().not });
|
|
172
367
|
return this._fetchScope('model');
|
|
173
368
|
});
|
|
174
369
|
|
|
370
|
+
/// Logical `AND`, for ANDing operations together.
|
|
371
|
+
///
|
|
372
|
+
/// This method does not need to be called, but it can
|
|
373
|
+
/// be called if desired.
|
|
374
|
+
///
|
|
375
|
+
/// This is a "toggle", so as soon as it is used,
|
|
376
|
+
/// it will continue to be "active" for all following
|
|
377
|
+
/// operations. In Mythix ORM you don't need to specify
|
|
378
|
+
/// `AND` or `OR` unless you actually need them. By default
|
|
379
|
+
/// `AND` is enabled for all queries. So for example you
|
|
380
|
+
/// can do `User.where.id.EQ('test').firstName.EQ('John').lastName.EQ('Smith')`,
|
|
381
|
+
/// which is exactly the same as `User.where.id.EQ('test').AND.firstName.EQ('John').AND.lastName.EQ('Smith')`.
|
|
382
|
+
///
|
|
383
|
+
/// `AND` can also be called to group conditions. For example, you could create
|
|
384
|
+
/// the following query: `User.where.id.EQ('test').AND(User.where.lastName.EQ('John').OR.lastName.EQ('Brown'))`
|
|
385
|
+
/// to create the following SQL query: `WHERE id = 'test' AND (lastName = 'John' OR lastName = 'Brown')`.
|
|
386
|
+
///
|
|
387
|
+
/// SyntaxType: FunctionDeclaration
|
|
388
|
+
///
|
|
389
|
+
/// Arguments:
|
|
390
|
+
/// query?: <see>QueryEngine</see>
|
|
391
|
+
/// An optional query, which if provided, will create a "condition group" as a result.
|
|
392
|
+
///
|
|
393
|
+
/// Return: <see>ModelScope</see>
|
|
394
|
+
/// Return a <see>ModelScope</see> to allow the user to continue chaining operations on the query.
|
|
175
395
|
AND = ProxyClass.autoCall(function(value) {
|
|
176
396
|
this._pushOperationOntoStack({ logical: true, operator: 'AND', queryProp: 'AND', and: true, or: false, not: false, value });
|
|
177
397
|
return this._fetchScope('model');
|
|
178
398
|
});
|
|
179
399
|
|
|
400
|
+
/// Logical `OR`, for ORing operations together.
|
|
401
|
+
///
|
|
402
|
+
/// This method does not need to be called, but it can
|
|
403
|
+
/// be called if desired.
|
|
404
|
+
///
|
|
405
|
+
/// This is a "toggle", so as soon as it is used,
|
|
406
|
+
/// it will continue to be "active" for all following
|
|
407
|
+
/// operations. In Mythix ORM you don't need to specify
|
|
408
|
+
/// `AND` or `OR` unless you actually need them. By default
|
|
409
|
+
/// `AND` is enabled for all queries. For example you
|
|
410
|
+
/// can do `User.where.id.EQ('test').OR.firstName.EQ('John').lastName.EQ('Smith')`,
|
|
411
|
+
/// which is exactly the same as `User.where.id.EQ('test').OR.firstName.EQ('John').OR.lastName.EQ('Smith')`.
|
|
412
|
+
///
|
|
413
|
+
/// `OR` can also be called to group conditions. For example, you could create
|
|
414
|
+
/// the following query: `User.where.id.EQ('test').OR(User.where.firstName.EQ('John').OR.lastName.EQ('Brown'))`
|
|
415
|
+
/// to create the following SQL query: `WHERE id = 'test' OR (firstName = 'John' AND lastName = 'Brown')`.
|
|
416
|
+
///
|
|
417
|
+
/// SyntaxType: FunctionDeclaration
|
|
418
|
+
///
|
|
419
|
+
/// Arguments:
|
|
420
|
+
/// query?: <see>QueryEngine</see>
|
|
421
|
+
/// An optional query, which if provided, will create a "condition group" as a result.
|
|
422
|
+
///
|
|
423
|
+
/// Return: <see>ModelScope</see>
|
|
424
|
+
/// Return a <see>ModelScope</see> to allow the user to continue chaining operations on the query.
|
|
180
425
|
OR = ProxyClass.autoCall(function(value) {
|
|
181
426
|
this._pushOperationOntoStack({ logical: true, operator: 'OR', queryProp: 'OR', and: false, or: true, not: false, value });
|
|
182
427
|
return this._fetchScope('model');
|
|
183
428
|
});
|
|
184
429
|
|
|
430
|
+
/// Apply a `LIMIT` clause to the query.
|
|
431
|
+
///
|
|
432
|
+
/// Any valid positive integer is acceptable, as well as `Infinity`.
|
|
433
|
+
/// If `Infinity` is used, then the `LIMIT` will either turn into its
|
|
434
|
+
/// max possible value (in the billions... depending on the underlying database),
|
|
435
|
+
/// or it will be stripped from the query entirely.
|
|
436
|
+
/// `NaN`, or anything that isn't a valid positive integer will throw an error.
|
|
437
|
+
///
|
|
438
|
+
/// Note:
|
|
439
|
+
/// Positive floating point numbers are rounded with `Math.round`.
|
|
440
|
+
///
|
|
441
|
+
/// Arguments:
|
|
442
|
+
/// limit: number
|
|
443
|
+
/// The limit to apply to the query.
|
|
444
|
+
///
|
|
445
|
+
/// Return: <see>ModelScope</see>
|
|
446
|
+
/// Return a <see>ModelScope</see> to allow the user to continue chaining operations on the query.
|
|
185
447
|
LIMIT(_value) {
|
|
186
448
|
let value = _value;
|
|
187
449
|
if (typeof value !== 'number' || isNaN(value) || value < 0)
|
|
@@ -193,6 +455,20 @@ class ModelScope extends QueryEngineBase {
|
|
|
193
455
|
return this._fetchScope('model');
|
|
194
456
|
}
|
|
195
457
|
|
|
458
|
+
/// Apply an `OFFSET` clause to the query.
|
|
459
|
+
///
|
|
460
|
+
/// Any valid positive integer is acceptable. `Infinity`, `NaN`,
|
|
461
|
+
/// or anything that isn't a valid positive integer will throw an error.
|
|
462
|
+
///
|
|
463
|
+
/// Note:
|
|
464
|
+
/// Positive floating point numbers are rounded with `Math.round`.
|
|
465
|
+
///
|
|
466
|
+
/// Arguments:
|
|
467
|
+
/// offset: number
|
|
468
|
+
/// The offset to apply to the query.
|
|
469
|
+
///
|
|
470
|
+
/// Return: <see>ModelScope</see>
|
|
471
|
+
/// Return a <see>ModelScope</see> to allow the user to continue chaining operations on the query.
|
|
196
472
|
OFFSET(_value) {
|
|
197
473
|
let value = _value;
|
|
198
474
|
if (typeof value !== 'number' || !isFinite(value) || value < 0)
|
|
@@ -204,10 +480,52 @@ class ModelScope extends QueryEngineBase {
|
|
|
204
480
|
return this._fetchScope('model');
|
|
205
481
|
}
|
|
206
482
|
|
|
483
|
+
/// Apply an `ORDER BY` clause to the query, to sort
|
|
484
|
+
/// the results on the fields specified, in either
|
|
485
|
+
/// `ASC` or `DESC` order.
|
|
486
|
+
///
|
|
487
|
+
/// There are five variants to this method, `ORDER.ASC`,
|
|
488
|
+
/// `ORDER.DESC`, `ORDER.ADD`, `ORDER.REPLACE`, and `ORDER` (which is an alias for `ORDER.ASC`), .
|
|
489
|
+
/// 1) `ORDER` - Alias for `ORDER.ASC`.
|
|
490
|
+
/// 2) `ORDER.ASC` - Follow the rules of <see>ModelScope.margeFields</see>. Each field/literal added is in `ASC` order.
|
|
491
|
+
/// 3) `ORDER.DESC` - Follow the rules of <see>ModelScope.margeFields</see>. Each field/literal added is in `DESC` order.
|
|
492
|
+
/// 4) `ORDER.ADD` - **DO NOT** follow the rules of <see>ModelScope.margeFields</see>, and instead **add** all fields specified, with their sort order being specified instead by the `+` or `-` prefixes on each field.
|
|
493
|
+
/// 5) `ORDER.REPLACE` - **DO NOT** follow the rules of <see>ModelScope.margeFields</see>, and instead **replace** the operation fields to the fields specified, with their sort order being specified instead by the `+` or `-` prefixes on each field.
|
|
494
|
+
///
|
|
495
|
+
/// See <see>ModelScope.margeFields</see> to better understand how this method works.
|
|
496
|
+
///
|
|
497
|
+
/// SyntaxType: FunctionDeclaration
|
|
498
|
+
///
|
|
499
|
+
/// Note:
|
|
500
|
+
/// This method will flatten all provided arguments into a one dimensional array,
|
|
501
|
+
/// so you can provide arrays or deeply nested arrays for the fields specified.
|
|
502
|
+
///
|
|
503
|
+
/// Arguments:
|
|
504
|
+
/// ...args: Array<<see>Field</see> | <see>LiteralBase</see> | string | '+' | '-' | '*'>
|
|
505
|
+
/// New "fields" to supply to `ORDER`, replacing, adding, or subtracting the
|
|
506
|
+
/// specified fields from the `ORDER` operation.
|
|
507
|
+
///
|
|
508
|
+
/// Return: <see>ModelScope</see>
|
|
509
|
+
/// Return a <see>ModelScope</see> to allow the user to continue chaining operations on the query.
|
|
207
510
|
ORDER = wrapOrderClause.call(this, (...args) => {
|
|
208
511
|
return applyOrderClause.call(this, { direction: '+' }, ...args);
|
|
209
512
|
});
|
|
210
513
|
|
|
514
|
+
/// Apply a `GROUP BY` clause to the query.
|
|
515
|
+
///
|
|
516
|
+
/// See <see>ModelScope.margeFields</see> to better understand how this method works.
|
|
517
|
+
///
|
|
518
|
+
/// Note:
|
|
519
|
+
/// This method will flatten all provided arguments into a one dimensional array,
|
|
520
|
+
/// so you can provide arrays or deeply nested arrays for the fields specified.
|
|
521
|
+
///
|
|
522
|
+
/// Arguments:
|
|
523
|
+
/// ...args: Array<<see>Field</see> | <see>LiteralBase</see> | string | '+' | '-' | '*'>
|
|
524
|
+
/// New "fields" to supply to `GROUP BY`, replacing, adding, or subtracting the
|
|
525
|
+
/// specified fields from the `GROUP_BY` operation.
|
|
526
|
+
///
|
|
527
|
+
/// Return: <see>ModelScope</see>
|
|
528
|
+
/// Return a <see>ModelScope</see> to allow the user to continue chaining operations on the query.
|
|
211
529
|
GROUP_BY(...args) {
|
|
212
530
|
let entities = Nife.arrayFlatten(args);
|
|
213
531
|
|
|
@@ -248,11 +566,52 @@ class ModelScope extends QueryEngineBase {
|
|
|
248
566
|
return this._fetchScope('model');
|
|
249
567
|
}
|
|
250
568
|
|
|
569
|
+
/// Apply a `HAVING` clause to the query.
|
|
570
|
+
///
|
|
571
|
+
/// This will only be applied in the underlying database query
|
|
572
|
+
/// if it is also paired with a <see>ModelScope.GROUP_BY</see> operation,
|
|
573
|
+
/// otherwise it will be ignored.
|
|
574
|
+
///
|
|
575
|
+
/// Example:
|
|
576
|
+
/// let adultCountByAge = await User.where
|
|
577
|
+
/// .GROUP_BY('User:age')
|
|
578
|
+
/// .HAVING(User.where.age.GTE(18))
|
|
579
|
+
/// .PROJECT('User:age', new CountLiteral('User:age', { as: 'count' }))
|
|
580
|
+
/// .all();
|
|
581
|
+
///
|
|
582
|
+
/// Arguments:
|
|
583
|
+
/// query: <see>QueryEngine</see>
|
|
584
|
+
/// The query to use to apply conditions to the `HAVING` clause.
|
|
585
|
+
///
|
|
586
|
+
/// Return: <see>ModelScope</see>
|
|
587
|
+
/// Return a <see>ModelScope</see> to allow the user to continue chaining operations on the query.
|
|
251
588
|
HAVING(query) {
|
|
252
589
|
this._pushOperationOntoStack({ control: true, operator: 'HAVING', queryProp: 'HAVING', value: query, having: query });
|
|
253
590
|
return this._fetchScope('model');
|
|
254
591
|
}
|
|
255
592
|
|
|
593
|
+
/// Check if any rows match the query provided.
|
|
594
|
+
///
|
|
595
|
+
/// This is on the <see>ModelScope</see> itself because
|
|
596
|
+
/// it is never paired with a field, and is an operator that
|
|
597
|
+
/// stands all on its own.
|
|
598
|
+
///
|
|
599
|
+
/// It can be used to check the existence of any value in the
|
|
600
|
+
/// database. For example, you might want to query on users,
|
|
601
|
+
/// but only if those users have an "admin" role:
|
|
602
|
+
/// `await User.where.email.EQ('test@example.com').EXISTS(Role.where.name.EQ("admin").userID.EQ(new FieldLiteral('User:id')))`
|
|
603
|
+
///
|
|
604
|
+
/// Note:
|
|
605
|
+
/// You can execute a `NOT EXISTS` operation simply by prefixing `EXISTS` with a `.NOT`
|
|
606
|
+
/// operation, for example `query.NOT.EXISTS(...)`.
|
|
607
|
+
///
|
|
608
|
+
/// Arguments:
|
|
609
|
+
/// query: <see>QueryEngine</see>
|
|
610
|
+
/// The sub-query to execute to check for existence. Use a <see>FieldLiteral</see>
|
|
611
|
+
/// to pair it with the primary query.
|
|
612
|
+
///
|
|
613
|
+
/// Return: <see>ModelScope</see>
|
|
614
|
+
/// Return a <see>ModelScope</see> to allow the user to continue chaining operations on the query.
|
|
256
615
|
EXISTS(_query) {
|
|
257
616
|
let query = _query;
|
|
258
617
|
let queryContext = (QueryEngineBase.isQuery(query)) ? query.getOperationContext() : null;
|
|
@@ -282,6 +641,28 @@ class ModelScope extends QueryEngineBase {
|
|
|
282
641
|
return this._fetchScope('model');
|
|
283
642
|
}
|
|
284
643
|
|
|
644
|
+
/// Replace, add to, or subtract from the projection of the query.
|
|
645
|
+
///
|
|
646
|
+
/// See <see>ModelScope.margeFields</see> to better understand how this method works.
|
|
647
|
+
///
|
|
648
|
+
/// Note:
|
|
649
|
+
/// This method will flatten all provided arguments into a one dimensional array,
|
|
650
|
+
/// so you can provide arrays or deeply nested arrays for the fields specified.
|
|
651
|
+
///
|
|
652
|
+
/// Note:
|
|
653
|
+
/// Mythix ORM collects and returns models (or partial models) based on the projection.
|
|
654
|
+
/// By default only the "root model" of a query will be projected and converted into
|
|
655
|
+
/// model instances. If you want to fetch more than just the root model while querying,
|
|
656
|
+
/// make sure to project the models (or fields from other models) that you want to
|
|
657
|
+
/// collect into model instances during load.
|
|
658
|
+
///
|
|
659
|
+
/// Arguments:
|
|
660
|
+
/// ...args: Array<<see>Field</see> | <see>LiteralBase</see> | string | '+' | '-' | '*'>
|
|
661
|
+
/// New "fields" to supply to the projection, replacing, adding, or subtracting the
|
|
662
|
+
/// specified fields from the `PROJECT` operation.
|
|
663
|
+
///
|
|
664
|
+
/// Return: <see>ModelScope</see>
|
|
665
|
+
/// Return a <see>ModelScope</see> to allow the user to continue chaining operations on the query.
|
|
285
666
|
PROJECT(...args) {
|
|
286
667
|
let entities = Nife.arrayFlatten(args);
|
|
287
668
|
|
|
@@ -328,6 +709,62 @@ class ModelScope extends QueryEngineBase {
|
|
|
328
709
|
return this._fetchScope('model');
|
|
329
710
|
}
|
|
330
711
|
|
|
712
|
+
/// Apply a DISTINCT clause to the query.
|
|
713
|
+
///
|
|
714
|
+
/// This method does not need to be called, but it can
|
|
715
|
+
/// be called if desired.
|
|
716
|
+
///
|
|
717
|
+
/// A field is required for this operation to keep the interface
|
|
718
|
+
/// consistent across all database drivers... however, the field
|
|
719
|
+
/// specified may not actually be used, depending on database support,
|
|
720
|
+
/// or other operations being carried out in the query.
|
|
721
|
+
///
|
|
722
|
+
/// If the underlying database supports it (i.e. PostgreSQL), then
|
|
723
|
+
/// this will turn into a `DISTINCT ON(field)` operation. If the
|
|
724
|
+
/// database (or operation being carried out), doesn't support `DISTINCT ON`,
|
|
725
|
+
/// then it will fallback to just a `DISTINCT` across the entire projection.
|
|
726
|
+
/// Any `DISTINCT` operation applied to the query will always be the very
|
|
727
|
+
/// first part of the projection, regardless of whatever else is projected.
|
|
728
|
+
///
|
|
729
|
+
/// This method is *optionally* callable. If not called, then the primary key
|
|
730
|
+
/// of the model specified will be used (if available). If no primary key exists
|
|
731
|
+
/// on the specified model, then it will fallback to a raw `DISTINCT` clause
|
|
732
|
+
/// prefixing the projection. For example: `User.where.DISTINCT.lastName.EQ('Smith')`
|
|
733
|
+
/// would be distinct on the primary key of `User` (which would be `id` in our example).
|
|
734
|
+
/// If instead we call the operator, then we can supply our own field or literal:
|
|
735
|
+
/// `User.where.DISTINCT('User:firstName').lastName.EQ('Smith')`.
|
|
736
|
+
///
|
|
737
|
+
/// To turn off any previous `DISTINCT` applied, pass the argument `false` to
|
|
738
|
+
/// `DISTINCT`: `User.where.DISTINCT.lastName.EQ('Smith').DISTINCT(false)`.
|
|
739
|
+
/// In this example the resulting query would have no `DISTINCT` clause at all,
|
|
740
|
+
/// since it was disabled when `DISTINCT(false)` was called.
|
|
741
|
+
///
|
|
742
|
+
/// `DISTINCT` changes the nature of the query, and might change how it is carried out
|
|
743
|
+
/// by the underlying database driver. For example, a distinct combined with a `count`
|
|
744
|
+
/// call would modify the `count` query: `await User.where.DISTINCT('User:id').count()` would
|
|
745
|
+
/// actually turn into the following SQL: `SELECT COUNT(DISTINCT "users"."id")`. This
|
|
746
|
+
/// is just one example however... just know that the underlying database driver might
|
|
747
|
+
/// alter the query, or take a completely different path to query if a `DISTINCT` operation
|
|
748
|
+
/// is in-play.
|
|
749
|
+
///
|
|
750
|
+
/// Note:
|
|
751
|
+
/// The support for a `DISTINCT` clause changes wildly across databases. Some might support
|
|
752
|
+
/// `ON` for a specific column, some may not. Some databases might force a certain `ORDER`
|
|
753
|
+
/// when using `DISTINCT`, some may not... `DISTINCT` may be supported in sub-queries, or
|
|
754
|
+
/// it may not... or might require the query be written differently. Mythix ORM does its best
|
|
755
|
+
/// to make a "standard" out of this very non-standard situation (does any SQL actually follow a standard?),
|
|
756
|
+
/// but just be aware that `DISTINCT` might bite you if you are changing database drivers to a
|
|
757
|
+
/// database that behaves differently.
|
|
758
|
+
///
|
|
759
|
+
/// SyntaxType: FunctionDeclaration
|
|
760
|
+
///
|
|
761
|
+
/// Arguments:
|
|
762
|
+
/// field: <see>Field</see> | <see>LiteralBase</see> | string | false = Model.getPrimaryKeyField()
|
|
763
|
+
/// The field or literal to be `DISTINCT ON` (if the database supports it). If `false`
|
|
764
|
+
/// is specified, then any previous `DISTINCT` operation is cleared.
|
|
765
|
+
///
|
|
766
|
+
/// Return: <see>ModelScope</see>
|
|
767
|
+
/// Return a <see>ModelScope</see> to allow the user to continue chaining operations on the query.
|
|
331
768
|
DISTINCT = ProxyClass.autoCall(function(_fullyQualifiedName) {
|
|
332
769
|
let fullyQualifiedName = _fullyQualifiedName;
|
|
333
770
|
let currentQuery = this;
|
|
@@ -363,31 +800,149 @@ class ModelScope extends QueryEngineBase {
|
|
|
363
800
|
return this._fetchScope('model');
|
|
364
801
|
});
|
|
365
802
|
|
|
803
|
+
/// Specify an `INNER JOIN` operation for joining tables.
|
|
804
|
+
///
|
|
805
|
+
/// This method does not need to be called, but it can
|
|
806
|
+
/// be called if desired.
|
|
807
|
+
///
|
|
808
|
+
/// All Mythix ORM "join" operations should come immediately
|
|
809
|
+
/// before a table join. For example: `User.where.INNER_JOIN.id.EQ(Role.where.userID)`.
|
|
810
|
+
/// They are "toggles", and so will remain "on" once used. In short, if
|
|
811
|
+
/// you specify an `INNER_JOIN` at the very beginning of your query, then
|
|
812
|
+
/// **ALL** table joins in the query will be `INNER JOIN`, unless you specify
|
|
813
|
+
/// other join types.
|
|
814
|
+
///
|
|
815
|
+
/// Note:
|
|
816
|
+
/// `INNER_JOIN` is the default table join type in Mythix ORM if no other table
|
|
817
|
+
/// join type is specified in the query.
|
|
818
|
+
///
|
|
819
|
+
/// SyntaxType: FunctionDeclaration
|
|
820
|
+
///
|
|
821
|
+
/// Return: <see>ModelScope</see>
|
|
822
|
+
/// Return a <see>ModelScope</see> to allow the user to continue chaining operations on the query.
|
|
366
823
|
INNER_JOIN = ProxyClass.autoCall(function() {
|
|
367
824
|
this._pushOperationOntoStack({ join: true, operator: 'JOIN', queryProp: 'INNER_JOIN', value: 'inner', joinType: 'inner', joinOuter: false });
|
|
368
825
|
return this._fetchScope('model');
|
|
369
826
|
});
|
|
370
827
|
|
|
828
|
+
/// Specify a `LEFT JOIN` operation for joining tables.
|
|
829
|
+
///
|
|
830
|
+
/// This method does not need to be called, but it can
|
|
831
|
+
/// be called if desired.
|
|
832
|
+
///
|
|
833
|
+
/// All Mythix ORM "join" operations should come immediately
|
|
834
|
+
/// before a table join. For example: `User.where.LEFT_JOIN.id.EQ(Role.where.userID)`.
|
|
835
|
+
/// They are "toggles", and so will remain "on" once used. In short, if
|
|
836
|
+
/// you specify a `LEFT_JOIN` at the very beginning of your query, then
|
|
837
|
+
/// **ALL** table joins in the query will be `LEFT JOIN`, unless you specify
|
|
838
|
+
/// other join types, i.e. `User.where.LEFT_JOIN.id.EQ(Role.where.userID).AND.INNER_JOIN.id.EQ(Organization.where.userID)`.
|
|
839
|
+
///
|
|
840
|
+
/// SyntaxType: FunctionDeclaration
|
|
841
|
+
///
|
|
842
|
+
/// Arguments:
|
|
843
|
+
/// outerJoin: boolean = false
|
|
844
|
+
/// If `true`, then this will result in a `LEFT OUTER JOIN` if the database supports it.
|
|
845
|
+
///
|
|
846
|
+
/// Return: <see>ModelScope</see>
|
|
847
|
+
/// Return a <see>ModelScope</see> to allow the user to continue chaining operations on the query.
|
|
371
848
|
LEFT_JOIN = ProxyClass.autoCall(function(outerJoin) {
|
|
372
849
|
this._pushOperationOntoStack({ join: true, operator: 'JOIN', queryProp: 'LEFT_JOIN', value: 'left', joinType: 'left', joinOuter: !!outerJoin });
|
|
373
850
|
return this._fetchScope('model');
|
|
374
851
|
});
|
|
375
852
|
|
|
853
|
+
/// Specify a `RIGHT JOIN` operation for joining tables.
|
|
854
|
+
///
|
|
855
|
+
/// This method does not need to be called, but it can
|
|
856
|
+
/// be called if desired.
|
|
857
|
+
///
|
|
858
|
+
/// All Mythix ORM "join" operations should come immediately
|
|
859
|
+
/// before a table join. For example: `User.where.RIGHT_JOIN.id.EQ(Role.where.userID)`.
|
|
860
|
+
/// They are "toggles", and so will remain "on" once used. In short, if
|
|
861
|
+
/// you specify a `RIGHT_JOIN` at the very beginning of your query, then
|
|
862
|
+
/// **ALL** table joins in the query will be `RIGHT JOIN`, unless you specify
|
|
863
|
+
/// other join types, i.e. `User.where.RIGHT_JOIN.id.EQ(Role.where.userID).AND.INNER_JOIN.id.EQ(Organization.where.userID)`.
|
|
864
|
+
///
|
|
865
|
+
/// SyntaxType: FunctionDeclaration
|
|
866
|
+
///
|
|
867
|
+
/// Arguments:
|
|
868
|
+
/// outerJoin: boolean = false
|
|
869
|
+
/// If `true`, then this will result in a `RIGHT OUTER JOIN` if the database supports it.
|
|
870
|
+
///
|
|
871
|
+
/// Return: <see>ModelScope</see>
|
|
872
|
+
/// Return a <see>ModelScope</see> to allow the user to continue chaining operations on the query.
|
|
376
873
|
RIGHT_JOIN = ProxyClass.autoCall(function(outerJoin) {
|
|
377
874
|
this._pushOperationOntoStack({ join: true, operator: 'JOIN', queryProp: 'RIGHT_JOIN', value: 'right', joinType: 'right', joinOuter: !!outerJoin });
|
|
378
875
|
return this._fetchScope('model');
|
|
379
876
|
});
|
|
380
877
|
|
|
878
|
+
/// Specify a `FULL JOIN` operation for joining tables.
|
|
879
|
+
///
|
|
880
|
+
/// This method does not need to be called, but it can
|
|
881
|
+
/// be called if desired.
|
|
882
|
+
///
|
|
883
|
+
/// All Mythix ORM "join" operations should come immediately
|
|
884
|
+
/// before a table join. For example: `User.where.FULL_JOIN.id.EQ(Role.where.userID)`.
|
|
885
|
+
/// They are "toggles", and so will remain "on" once used. In short, if
|
|
886
|
+
/// you specify a `FULL_JOIN` at the very beginning of your query, then
|
|
887
|
+
/// **ALL** table joins in the query will be `FULL JOIN`, unless you specify
|
|
888
|
+
/// other join types, i.e. `User.where.FULL_JOIN.id.EQ(Role.where.userID).AND.INNER_JOIN.id.EQ(Organization.where.userID)`.
|
|
889
|
+
///
|
|
890
|
+
/// SyntaxType: FunctionDeclaration
|
|
891
|
+
///
|
|
892
|
+
/// Arguments:
|
|
893
|
+
/// outerJoin: boolean = false
|
|
894
|
+
/// If `true`, then this will result in a `FULL OUTER JOIN` if the database supports it.
|
|
895
|
+
///
|
|
896
|
+
/// Return: <see>ModelScope</see>
|
|
897
|
+
/// Return a <see>ModelScope</see> to allow the user to continue chaining operations on the query.
|
|
381
898
|
FULL_JOIN = ProxyClass.autoCall(function(outerJoin) {
|
|
382
899
|
this._pushOperationOntoStack({ join: true, operator: 'JOIN', queryProp: 'FULL_JOIN', value: 'full', joinType: 'full', joinOuter: !!outerJoin });
|
|
383
900
|
return this._fetchScope('model');
|
|
384
901
|
});
|
|
385
902
|
|
|
903
|
+
/// Specify a `CROSS JOIN` operation for joining tables.
|
|
904
|
+
///
|
|
905
|
+
/// This method does not need to be called.
|
|
906
|
+
///
|
|
907
|
+
/// All Mythix ORM "join" operations should come immediately
|
|
908
|
+
/// before a table join. For example: `User.where.CROSS_JOIN.id.EQ(Role.where.userID)`.
|
|
909
|
+
/// They are "toggles", and so will remain "on" once used. In short, if
|
|
910
|
+
/// you specify a `CROSS_JOIN` at the very beginning of your query, then
|
|
911
|
+
/// **ALL** table joins in the query will be `CROSS JOIN`, unless you specify
|
|
912
|
+
/// other join types, i.e. `User.where.CROSS_JOIN.id.EQ(Role.where.userID).AND.INNER_JOIN.id.EQ(Organization.where.userID)`.
|
|
913
|
+
///
|
|
914
|
+
/// SyntaxType: FunctionDeclaration
|
|
915
|
+
///
|
|
916
|
+
/// Return: <see>ModelScope</see>
|
|
917
|
+
/// Return a <see>ModelScope</see> to allow the user to continue chaining operations on the query.
|
|
386
918
|
CROSS_JOIN = ProxyClass.autoCall(function() {
|
|
387
919
|
this._pushOperationOntoStack({ join: true, operator: 'JOIN', queryProp: 'CROSS_JOIN', value: 'cross', joinType: 'cross', joinOuter: false });
|
|
388
920
|
return this._fetchScope('model');
|
|
389
921
|
});
|
|
390
922
|
|
|
923
|
+
/// Specify a custom join operation for the underlying database.
|
|
924
|
+
/// It is recommended that you use a literal, though that isn't required.
|
|
925
|
+
///
|
|
926
|
+
/// All Mythix ORM "join" operations should come immediately
|
|
927
|
+
/// before a table join. For example: `User.where.JOIN('RIGHT INNER JOIN').id.EQ(Role.where.userID)`.
|
|
928
|
+
/// They are "toggles", and so will remain "on" once used.
|
|
929
|
+
///
|
|
930
|
+
/// Note:
|
|
931
|
+
/// This method should be avoided if at all possible, since it will
|
|
932
|
+
/// likely be database specific, making your code not as portable to
|
|
933
|
+
/// another database driver.
|
|
934
|
+
///
|
|
935
|
+
/// Note:
|
|
936
|
+
/// Is there a standard join type that Mythix ORM missed, that should be
|
|
937
|
+
/// supported across most or all databases? If so, let us know by opening
|
|
938
|
+
/// an Issue or PR on our GitHub page. Thank you!
|
|
939
|
+
///
|
|
940
|
+
/// Arguments:
|
|
941
|
+
/// type: <see>Literal</see> | string
|
|
942
|
+
/// The join type to use in the underlying database (a literal value).
|
|
943
|
+
///
|
|
944
|
+
/// Return: <see>ModelScope</see>
|
|
945
|
+
/// Return a <see>ModelScope</see> to allow the user to continue chaining operations on the query.
|
|
391
946
|
JOIN(type) {
|
|
392
947
|
if (!(Nife.instanceOf(type, 'string') || LiteralBase.isLiteral(type)))
|
|
393
948
|
throw new Error('QueryEngine::ModelScope::JOIN: Invalid value provided. Value must be a valid string or Literal specifying JOIN type.');
|
|
@@ -395,6 +950,10 @@ class ModelScope extends QueryEngineBase {
|
|
|
395
950
|
this._pushOperationOntoStack({ join: true, operator: 'JOIN', queryProp: 'JOIN', value: type, joinType: type, joinOuter: false });
|
|
396
951
|
return this._fetchScope('model');
|
|
397
952
|
}
|
|
953
|
+
|
|
954
|
+
toString(...args) {
|
|
955
|
+
return this.getOperationContext().queryEngineScope.toString(...args);
|
|
956
|
+
}
|
|
398
957
|
}
|
|
399
958
|
|
|
400
959
|
module.exports = ModelScope;
|