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
|
@@ -7,30 +7,98 @@ const ModelScope = require('./model-scope');
|
|
|
7
7
|
const FieldScope = require('./field-scope');
|
|
8
8
|
const Utils = require('../utils');
|
|
9
9
|
|
|
10
|
+
/// QueryEngine is the "root level" of a query.
|
|
11
|
+
/// It manages things like `MERGE`, `unscoped`,
|
|
12
|
+
/// and all the database interfaces, such as `all`,
|
|
13
|
+
/// `first`, `pluck`, etc...
|
|
14
|
+
///
|
|
15
|
+
/// Being a Proxy, it will "listen" for key access,
|
|
16
|
+
/// and lookup models if there is a key access where
|
|
17
|
+
/// the key name starts with an upper-case letter. In this
|
|
18
|
+
/// case, it will check the `connection` it owns to see
|
|
19
|
+
/// if such a model (by name) is registered with the
|
|
20
|
+
/// connection. If there is, then it will use the model
|
|
21
|
+
/// found to push a <see>ModelScope</see> onto the "operation stack",
|
|
22
|
+
/// and then return that to the user. Take the following example:
|
|
23
|
+
/// ```javascript
|
|
24
|
+
/// let queryRoot = new QueryEngine({ connection });
|
|
25
|
+
/// let userModelScope = queryRoot.User;
|
|
26
|
+
/// ```
|
|
27
|
+
/// When we attempt to access the key `'User'` on
|
|
28
|
+
/// the QueryEngine scope, we will find that no such key
|
|
29
|
+
/// exists on the `QueryEngine` class. Now that no such property is found
|
|
30
|
+
/// on the `QueryEngine` class, the <see>ProxyClass</see> will call the method
|
|
31
|
+
/// `MISSING` on the `QueryEngine`, and this `MISSING` method
|
|
32
|
+
/// will check the `connection` to see if there is a model
|
|
33
|
+
/// named `'User'`. The `connection` finds this model, and
|
|
34
|
+
/// returns it. The `QueryEngine` then takes this model class,
|
|
35
|
+
/// and uses it to create and return a <see>ModelScope</see>
|
|
36
|
+
/// using <see>QueryEngineBase._newModelScope</see>. Now
|
|
37
|
+
/// that we have a <see>ModelScope</see>, we can continue
|
|
38
|
+
/// chaining by looking up a field `queryRoot.User.id`, and
|
|
39
|
+
/// the process repeats on `ModelScope`, but this time looking
|
|
40
|
+
/// for a field from the `connection` instead of a model. The
|
|
41
|
+
/// model owning the field is already known, because we already
|
|
42
|
+
/// have a `Model` class on our "operation stack" for the query.
|
|
43
|
+
/// So the field `id` is looked up on the `User` model, and if
|
|
44
|
+
/// found, returned as a <see>FieldScope</see> using <see>QueryEngineBase._newFieldScope</see>.
|
|
45
|
+
///
|
|
46
|
+
/// See: QueryEngineBase
|
|
47
|
+
///
|
|
48
|
+
/// See: ModelScope
|
|
49
|
+
///
|
|
50
|
+
/// See: FieldScope
|
|
51
|
+
///
|
|
52
|
+
/// Note:
|
|
53
|
+
/// `QueryEngine` is the primary part "the query engine", and so is generally referred to
|
|
54
|
+
/// simply as the `QueryEngine` as a whole. This is also the case for <see>ModelScope</see>,
|
|
55
|
+
/// and <see>FieldScope</see>, and <see>QueryEngineBase</see> which also make up the `QueryEngine`
|
|
56
|
+
/// as sub-parts, and so are also often referred to simply as "the query engine".
|
|
10
57
|
class QueryEngine extends QueryEngineBase {
|
|
58
|
+
/// Get the `ModelScope` class for the
|
|
59
|
+
/// query engine.
|
|
60
|
+
///
|
|
61
|
+
/// See: QueryEngineBase.getModelScopeClass
|
|
11
62
|
getModelScopeClass() {
|
|
12
63
|
return ModelScope;
|
|
13
64
|
}
|
|
14
65
|
|
|
66
|
+
/// Get the `FieldScope` class for the
|
|
67
|
+
/// query engine.
|
|
68
|
+
///
|
|
69
|
+
/// See: QueryEngineBase.getFieldScopeClass
|
|
15
70
|
getFieldScopeClass() {
|
|
16
71
|
return FieldScope;
|
|
17
72
|
}
|
|
18
73
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
);
|
|
28
|
-
|
|
74
|
+
/// Construct a new QueryEngine instance.
|
|
75
|
+
///
|
|
76
|
+
/// Arguments:
|
|
77
|
+
/// context: object
|
|
78
|
+
/// The "root" "operation context" for the query. This is required to contain a
|
|
79
|
+
/// `connection` property, with a valid <see>Connection</see> instance as a value. Besides that, you can apply
|
|
80
|
+
/// any properties you want here, and they will be available on all "operation contexts".
|
|
81
|
+
constructor(context) {
|
|
29
82
|
super(context);
|
|
30
83
|
|
|
31
|
-
|
|
84
|
+
let operationContext = this.getOperationContext();
|
|
85
|
+
operationContext.currentScopeName = 'queryEngine';
|
|
86
|
+
operationContext.queryEngineScope = this;
|
|
32
87
|
}
|
|
33
88
|
|
|
89
|
+
/// This method will directly fetch a model by
|
|
90
|
+
/// name and return a <see>ModelScope</see>.
|
|
91
|
+
///
|
|
92
|
+
/// This can be useful when you have a name collision,
|
|
93
|
+
/// or when you just want to go the direct route to get
|
|
94
|
+
/// a model operation in place.
|
|
95
|
+
///
|
|
96
|
+
/// Arguments:
|
|
97
|
+
/// modelName: string
|
|
98
|
+
/// The model name to fetch and use for the `ModelScope`.
|
|
99
|
+
///
|
|
100
|
+
/// Return: <see>ModelScope</see>
|
|
101
|
+
/// A new `ModelScope` using the model found by the name provided.
|
|
34
102
|
Model(modelName) {
|
|
35
103
|
let model = this.getModel(modelName);
|
|
36
104
|
if (!model)
|
|
@@ -39,19 +107,55 @@ class QueryEngine extends QueryEngineBase {
|
|
|
39
107
|
return this._newModelScope(model);
|
|
40
108
|
}
|
|
41
109
|
|
|
110
|
+
/// This method will reset the query "operation stack"
|
|
111
|
+
/// back to the root context--and, if possible, to the root
|
|
112
|
+
/// model scope and root model projection.
|
|
113
|
+
/// All other operations will be wiped, including
|
|
114
|
+
/// the any <see>Model.static defaultScope</see> that has been
|
|
115
|
+
/// applied. One would generally call this as the very
|
|
116
|
+
/// first thing on an query to ensure it has been reset
|
|
117
|
+
/// back to its primary parts *before* you would
|
|
118
|
+
/// apply any other operations. For example: `let users = await User.where.unscoped().lastName.EQ('Smith').all();`,
|
|
119
|
+
/// would "ignore" any <see>Model.static defaultScope</see> applied to
|
|
120
|
+
/// the query, including any default `ORDER` that `defaultScope`
|
|
121
|
+
/// applied. `User.where.lastName.EQ('Smith').unscoped()` would
|
|
122
|
+
/// be equivalent to `User.where` (assuming no <see>Model.static defaultScope</see>
|
|
123
|
+
/// is in-play).
|
|
124
|
+
///
|
|
125
|
+
/// Return: <see>QueryEngine</see>
|
|
126
|
+
/// A new query, reset back to only its root context, or the root model
|
|
127
|
+
/// scope (if there is a root model available on the query). If there
|
|
128
|
+
/// is a root model available, then its projection will also be applied
|
|
129
|
+
/// as usual.
|
|
42
130
|
unscoped() {
|
|
43
131
|
let context = this.getOperationContext();
|
|
44
|
-
let QueryEngineClass = this.
|
|
45
|
-
let queryEngine = new QueryEngineClass(
|
|
46
|
-
connection: this.getConnection(),
|
|
47
|
-
});
|
|
132
|
+
let QueryEngineClass = this.getQueryEngineScopeClass();
|
|
133
|
+
let queryEngine = new QueryEngineClass(context.rootContext);
|
|
48
134
|
|
|
49
|
-
if (context.
|
|
50
|
-
queryEngine = queryEngine
|
|
135
|
+
if (context.rootModel)
|
|
136
|
+
queryEngine = queryEngine._newModelScope(context.rootModel);
|
|
51
137
|
|
|
52
138
|
return queryEngine;
|
|
53
139
|
}
|
|
54
140
|
|
|
141
|
+
/// Stringify this query, using the underlying database
|
|
142
|
+
/// connection. This is similar to a `toSQL` method in other ORMs.
|
|
143
|
+
/// It isn't exactly that though... because for non-SQL
|
|
144
|
+
/// database drivers, it might stringify the query into
|
|
145
|
+
/// something that is non-SQL. By default the query will
|
|
146
|
+
/// be turned into a string that represents a `SELECT`
|
|
147
|
+
/// statement (or other type of fetch statement) for the
|
|
148
|
+
/// underlying database driver.
|
|
149
|
+
///
|
|
150
|
+
/// Arguments:
|
|
151
|
+
/// ...args?: Array<any>
|
|
152
|
+
/// Any arguments you want to pass off to the underlying
|
|
153
|
+
/// <see>QueryGenerator.toConnectionString</see> method that
|
|
154
|
+
/// is called to stringify the query.
|
|
155
|
+
///
|
|
156
|
+
/// Return: string
|
|
157
|
+
/// The query, turned into a "fetch" representation for the underlying
|
|
158
|
+
/// database driver. For SQL-based databases this would be a `SELECT` statement.
|
|
55
159
|
toString(...args) {
|
|
56
160
|
let connection = this.getConnection();
|
|
57
161
|
let queryGenerator = connection.getQueryGenerator();
|
|
@@ -59,6 +163,62 @@ class QueryEngine extends QueryEngineBase {
|
|
|
59
163
|
return queryGenerator.toConnectionString(this, ...args);
|
|
60
164
|
}
|
|
61
165
|
|
|
166
|
+
/// Merge one query onto another one.
|
|
167
|
+
///
|
|
168
|
+
/// This method will merge two queries together
|
|
169
|
+
/// by "replaying" the provided one onto `this`
|
|
170
|
+
/// one. For example:
|
|
171
|
+
/// ```javascript
|
|
172
|
+
/// let userQuery = User.where.id.EQ(1);
|
|
173
|
+
/// let roleQuery = Role.where.userID.EQ(User.where.id).name.EQ('admin');
|
|
174
|
+
/// let finalQuery = userQuery.AND.MERGE(roleQuery);
|
|
175
|
+
/// // finalQuery == User.where.id.EQ(1).AND.Role.where.userID.EQ(User.where.id).name.EQ('admin');
|
|
176
|
+
/// ```
|
|
177
|
+
///
|
|
178
|
+
/// This is almost like a "concatenate" operation, but there are a few notable differences:
|
|
179
|
+
/// 1) The first "logical" operator encountered is always skipped
|
|
180
|
+
/// 2) `PROJECT` operations are skipped by default (but can be merged with the use of `options`)
|
|
181
|
+
/// 3) Any `GROUP_BY`, `ORDER`, or `PROJECT` are *merged* together, instead of replacing the previous operation.
|
|
182
|
+
///
|
|
183
|
+
/// The first logical operator is skipped for a good reason. It is to allow one to `OR` merge
|
|
184
|
+
/// the query, or `AND` merge it instead. If we were to simply "concatenate" the stacks together,
|
|
185
|
+
/// then we might have contexts that look like (pseudo/concept code):
|
|
186
|
+
/// `[ User, AND, id, EQ(1) ].OR.MERGE([ Role, AND, userID, EQ(...), ... ])`.
|
|
187
|
+
/// The problem here should be obvious: The `OR` is (nearly) immediately followed by an `AND` in
|
|
188
|
+
/// the query we are merging with. If we didn't skip the first logical operator, then this would
|
|
189
|
+
/// nearly always be the case, especially since the `QueryEngine` turns on the `AND` operator
|
|
190
|
+
/// by default for all new queries. This is why we skip the first logical operator, so that we
|
|
191
|
+
/// are able to `.AND.MERGE(...)`, or `.OR.MERGE(...)` a query together... a distinction which
|
|
192
|
+
/// sometimes can be vitally important.
|
|
193
|
+
///
|
|
194
|
+
/// `PROJECT` operations are skipped by default unless you specify the `options` of `{ projections: true }`.
|
|
195
|
+
/// This is simply because it often isn't desired to have projections merge over from sub-queries that
|
|
196
|
+
/// are being merged in... causing extra data to be pulled from the database. Projections in Mythix ORM
|
|
197
|
+
/// should **always** be deliberate and direct.
|
|
198
|
+
/// On the other hand, `ORDER` and `GROUP_BY` operations are automatically merged for you by default. The
|
|
199
|
+
/// reason should be fairly obvious, if two parts of the query need a specific order, or group-by fields,
|
|
200
|
+
/// then they probably shouldn't be skipped. They can always be skipped if desired by passing the `options`
|
|
201
|
+
/// of `{ orders: false }`, or `{ groupBys: false }` (or both).
|
|
202
|
+
///
|
|
203
|
+
/// Note:
|
|
204
|
+
/// Because merges (and other operations) can get quite complex, it is recommend that you always
|
|
205
|
+
/// apply a projection, order, limit, and offset immediately before you use your query to interact
|
|
206
|
+
/// with the database.
|
|
207
|
+
///
|
|
208
|
+
/// Arguments:
|
|
209
|
+
/// incomingQuery: <see>QueryEngine</see>
|
|
210
|
+
/// The query to merge/replay on-top a clone of `this` query.
|
|
211
|
+
/// options?: object
|
|
212
|
+
/// Options for the operation. See the table below for a list of possible options:
|
|
213
|
+
/// | Option | Type | Default Value | Description |
|
|
214
|
+
/// | ------------- | ---- | ------------- | ----------- |
|
|
215
|
+
/// | `projections` | `boolean` | `false` | If `true`, then also merge projections together. |
|
|
216
|
+
/// | `orders` | `boolean` | `true` | If `true`, then also merge order clauses together. |
|
|
217
|
+
/// | `groupBys` | `boolean` | `true` | If `true`, then also merge group-by clauses together. |
|
|
218
|
+
/// | `connection` | `Connection` | `this.getConnection()` | The connection to supply to the newly created `QueryEngine`. If none is supplied, then `this.getConnection()` is used to retrieve the connection. |
|
|
219
|
+
///
|
|
220
|
+
/// Return: <see>QueryEngine</see>
|
|
221
|
+
/// Return the newly merged query.
|
|
62
222
|
MERGE(_incomingQueryEngine, _options) {
|
|
63
223
|
let incomingQueryEngine = _incomingQueryEngine;
|
|
64
224
|
if (!incomingQueryEngine)
|
|
@@ -119,6 +279,33 @@ class QueryEngine extends QueryEngineBase {
|
|
|
119
279
|
return queryEngine;
|
|
120
280
|
}
|
|
121
281
|
|
|
282
|
+
/// Fetch all rows matching the query from the database.
|
|
283
|
+
///
|
|
284
|
+
/// By default all rows fetched will be converted into
|
|
285
|
+
/// <see>Model</see> instances.
|
|
286
|
+
///
|
|
287
|
+
/// This method will fetch in batches. By default the batch
|
|
288
|
+
/// size is `500`, but can be modified with the `{ batchSize: number; }`
|
|
289
|
+
/// `options`. Even though it fetches from the database in batches,
|
|
290
|
+
/// it won't return until all data has been fetched from the database.
|
|
291
|
+
///
|
|
292
|
+
/// Example:
|
|
293
|
+
/// let smithFamilyUsers = await User.where.lastName.EQ('Smith').all();
|
|
294
|
+
///
|
|
295
|
+
/// Note:
|
|
296
|
+
/// To stream from the database instead, use the
|
|
297
|
+
/// <see>QueryEngine.cursor</see> method.
|
|
298
|
+
///
|
|
299
|
+
/// Arguments:
|
|
300
|
+
/// options?: object
|
|
301
|
+
/// Options to pass into the operation. These are generally connection
|
|
302
|
+
/// specific. However, one option that is always available is the `connection`
|
|
303
|
+
/// option, which can define the <see>Connection</see> to use for the
|
|
304
|
+
/// operation. This specific operation also has a `batchSize: number;`
|
|
305
|
+
/// option that can be used to specify the batch size for fetching.
|
|
306
|
+
///
|
|
307
|
+
/// Return: Array<Model>
|
|
308
|
+
/// An array of all matching root model instances found by the query.
|
|
122
309
|
async all(options) {
|
|
123
310
|
let connection = this.getConnection(options && options.connection);
|
|
124
311
|
|
|
@@ -128,12 +315,70 @@ class QueryEngine extends QueryEngineBase {
|
|
|
128
315
|
return await Utils.collect(connection.select(await this.finalizeQuery('read', options), options));
|
|
129
316
|
}
|
|
130
317
|
|
|
318
|
+
/// Fetch all rows matching the query from the database,
|
|
319
|
+
/// by streaming them, using an async generator.
|
|
320
|
+
///
|
|
321
|
+
/// By default all rows fetched will be converted into
|
|
322
|
+
/// <see>Model</see> instances.
|
|
323
|
+
///
|
|
324
|
+
/// This method will fetch in batches. By default the batch
|
|
325
|
+
/// size is `500`, but can be modified with the `{ batchSize: number; }`
|
|
326
|
+
/// `options`. All data being fetched will be "streamed" to the caller
|
|
327
|
+
/// via an async generator return value.
|
|
328
|
+
///
|
|
329
|
+
/// Example:
|
|
330
|
+
/// for await (let smithFamilyUser of User.where.lastName.EQ('Smith').cursor()) {
|
|
331
|
+
/// console.log(smithFamilyUser);
|
|
332
|
+
/// }
|
|
333
|
+
///
|
|
334
|
+
/// Note:
|
|
335
|
+
/// To collect all results at once instead of streaming,
|
|
336
|
+
/// use the <see>QueryEngine.all</see> method instead.
|
|
337
|
+
///
|
|
338
|
+
/// Arguments:
|
|
339
|
+
/// options?: object
|
|
340
|
+
/// Options to pass into the operation. These are generally connection
|
|
341
|
+
/// specific. However, one option that is always available is the `connection`
|
|
342
|
+
/// option, which can define the <see>Connection</see> to use for the
|
|
343
|
+
/// operation. This specific operation also has a `batchSize: number;`
|
|
344
|
+
/// option that can be used to specify the batch size for fetching.
|
|
345
|
+
///
|
|
346
|
+
/// Return: Array<Model>
|
|
347
|
+
/// An array of all matching root model instances found by the query.
|
|
131
348
|
async *cursor(_options) {
|
|
132
349
|
let options = _options || {};
|
|
133
350
|
let connection = this.getConnection(options && options.connection);
|
|
134
351
|
return yield *connection.select(await this.finalizeQuery('read', options), { ...options, stream: true });
|
|
135
352
|
}
|
|
136
353
|
|
|
354
|
+
/// Fetch the first matching row(s) from the database.
|
|
355
|
+
///
|
|
356
|
+
/// By default all rows fetched will be converted into
|
|
357
|
+
/// <see>Model</see> instances.
|
|
358
|
+
///
|
|
359
|
+
/// This method will always apply a `LIMIT` of `limit`, but it
|
|
360
|
+
/// won't modify the `OFFSET`... so it is possible to get
|
|
361
|
+
/// the nth item(s) by supplying your own `OFFSET` on the query.
|
|
362
|
+
///
|
|
363
|
+
/// Example:
|
|
364
|
+
/// let firstUser = await User.where.lastName.EQ('Smith').ORDER.ASC('User:lastName').first();
|
|
365
|
+
///
|
|
366
|
+
/// Note:
|
|
367
|
+
/// An `ORDER` should be applied on the query before
|
|
368
|
+
/// calling this method... or you might get funky results.
|
|
369
|
+
///
|
|
370
|
+
/// Arguments:
|
|
371
|
+
/// limit: number = 1
|
|
372
|
+
/// The number of rows to fetch from the database. The default is `1`.
|
|
373
|
+
/// options?: object
|
|
374
|
+
/// Options to pass into the operation. These are generally connection
|
|
375
|
+
/// specific. However, one option that is always available is the `connection`
|
|
376
|
+
/// option, which can define the <see>Connection</see> to use for the
|
|
377
|
+
/// operation.
|
|
378
|
+
///
|
|
379
|
+
/// Return: Array<Model> | Model
|
|
380
|
+
/// Return just one `Model` if the `limit` is set to `1`, otherwise
|
|
381
|
+
/// return an `Array` of `Model`.
|
|
137
382
|
async first(_limit, options) {
|
|
138
383
|
let limit = (_limit == null) ? 1 : _limit;
|
|
139
384
|
let connection = this.getConnection(options && options.connection);
|
|
@@ -143,6 +388,39 @@ class QueryEngine extends QueryEngineBase {
|
|
|
143
388
|
return (_limit == null) ? result[0] : result;
|
|
144
389
|
}
|
|
145
390
|
|
|
391
|
+
/// Fetch the last matching row(s) from the database.
|
|
392
|
+
///
|
|
393
|
+
/// By default all rows fetched will be converted into
|
|
394
|
+
/// <see>Model</see> instances.
|
|
395
|
+
///
|
|
396
|
+
/// This method will always apply a `LIMIT` of `limit`, but it
|
|
397
|
+
/// won't modify the `OFFSET`... so it is possible to get
|
|
398
|
+
/// the nth item(s) by supplying your own `OFFSET` on the query.
|
|
399
|
+
///
|
|
400
|
+
/// This method works by forcing the query `ORDER` to invert itself,
|
|
401
|
+
/// and then it selects the first `limit` rows from the top.
|
|
402
|
+
///
|
|
403
|
+
/// Example:
|
|
404
|
+
/// // The `ORDER` operation here will be forcefully inverted
|
|
405
|
+
/// // to select the "last" row from the top.
|
|
406
|
+
/// let firstUser = await User.where.lastName.EQ('Smith').ORDER.ASC('User:lastName').last();
|
|
407
|
+
///
|
|
408
|
+
/// Note:
|
|
409
|
+
/// An `ORDER` should be applied on the query before
|
|
410
|
+
/// calling this method... or you might get funky results.
|
|
411
|
+
///
|
|
412
|
+
/// Arguments:
|
|
413
|
+
/// limit: number = 1
|
|
414
|
+
/// The number of rows to fetch from the database. The default is `1`.
|
|
415
|
+
/// options?: object
|
|
416
|
+
/// Options to pass into the operation. These are generally connection
|
|
417
|
+
/// specific. However, one option that is always available is the `connection`
|
|
418
|
+
/// option, which can define the <see>Connection</see> to use for the
|
|
419
|
+
/// operation.
|
|
420
|
+
///
|
|
421
|
+
/// Return: Array<Model> | Model
|
|
422
|
+
/// Return just one `Model` if the `limit` is set to `1`, otherwise
|
|
423
|
+
/// return an `Array` of `Model`.
|
|
146
424
|
async last(_limit, options) {
|
|
147
425
|
let limit = (_limit == null) ? 1 : _limit;
|
|
148
426
|
let connection = this.getConnection(options && options.connection);
|
|
@@ -152,51 +430,280 @@ class QueryEngine extends QueryEngineBase {
|
|
|
152
430
|
return (_limit == null) ? result[0] : result.reverse();
|
|
153
431
|
}
|
|
154
432
|
|
|
433
|
+
/// Update all matching rows in the database with
|
|
434
|
+
/// the attributes provided.
|
|
435
|
+
///
|
|
436
|
+
/// This method will bulk-write to the rows matching
|
|
437
|
+
/// the query, using the attributes provided to update
|
|
438
|
+
/// matching rows. The attributes provided can be a model instance,
|
|
439
|
+
/// but will generally just be an object of attributes
|
|
440
|
+
/// (where the key names must match model `fieldName`s).
|
|
441
|
+
///
|
|
442
|
+
/// Example:
|
|
443
|
+
/// // Update all users who are 'inactive' to now be 'active'
|
|
444
|
+
/// await User.where.status.EQ('inactive').updateAll({ status: 'active' });
|
|
445
|
+
///
|
|
446
|
+
/// Note:
|
|
447
|
+
/// **This will not call onBefore or onAfter update hooks
|
|
448
|
+
/// on the model instances**. It is a direct bulk-write
|
|
449
|
+
/// operation to the underlying database.
|
|
450
|
+
///
|
|
451
|
+
/// Note:
|
|
452
|
+
/// This operation will only ever update the "root model"
|
|
453
|
+
/// table. The "root model" of a query is the first model
|
|
454
|
+
/// used in the query. For example, `User` is the root model
|
|
455
|
+
/// in the query `User.where`.
|
|
456
|
+
///
|
|
457
|
+
/// Arguments:
|
|
458
|
+
/// attributes: object | <see>Model</see>
|
|
459
|
+
/// The attributes to update across all rows. The key names of
|
|
460
|
+
/// this object must be `fieldName`s from the root model of the
|
|
461
|
+
/// query (**NOT** column names).
|
|
462
|
+
/// options?: object
|
|
463
|
+
/// Options to pass into the operation. These are generally connection
|
|
464
|
+
/// specific. However, one option that is always available is the `connection`
|
|
465
|
+
/// option, which can define the <see>Connection</see> to use for the
|
|
466
|
+
/// operation.
|
|
467
|
+
///
|
|
468
|
+
/// Return: number
|
|
469
|
+
/// Return the number of rows that were updated.
|
|
155
470
|
async updateAll(attributes, options) {
|
|
156
471
|
let connection = this.getConnection(options && options.connection);
|
|
157
472
|
return await connection.updateAll(await this.finalizeQuery('update', options), attributes, options);
|
|
158
473
|
}
|
|
159
474
|
|
|
475
|
+
/// Delete matching rows in the database.
|
|
476
|
+
///
|
|
477
|
+
/// This method will delete all rows matching the query
|
|
478
|
+
/// from the database (for only the root model).
|
|
479
|
+
///
|
|
480
|
+
/// Example:
|
|
481
|
+
/// await QueueEntry.where.status.EQ('completed').destroy();
|
|
482
|
+
///
|
|
483
|
+
/// Note:
|
|
484
|
+
/// This operation will only ever update the "root model"
|
|
485
|
+
/// table. The "root model" of a query is the first model
|
|
486
|
+
/// used in the query. For example, `User` is the root model
|
|
487
|
+
/// in the query `User.where`.
|
|
488
|
+
///
|
|
489
|
+
/// Arguments:
|
|
490
|
+
/// options?: object
|
|
491
|
+
/// Options to pass into the operation. These are generally connection
|
|
492
|
+
/// specific. However, one option that is always available is the `connection`
|
|
493
|
+
/// option, which can define the <see>Connection</see> to use for the
|
|
494
|
+
/// operation.
|
|
495
|
+
///
|
|
496
|
+
/// Return: number
|
|
497
|
+
/// Return the number of rows that were deleted.
|
|
160
498
|
async destroy(options) {
|
|
161
499
|
let connection = this.getConnection(options && options.connection);
|
|
162
500
|
return await connection.destroy(await this.finalizeQuery('delete', options), options);
|
|
163
501
|
}
|
|
164
502
|
|
|
503
|
+
/// Calculate the "average" of all matching rows for
|
|
504
|
+
/// a single column in the database.
|
|
505
|
+
///
|
|
506
|
+
/// Example:
|
|
507
|
+
/// let averageUserAge = await User.where.average('User:age');
|
|
508
|
+
///
|
|
509
|
+
/// Arguments:
|
|
510
|
+
/// field: <see>Field</see> | string | <see>Literal</see>
|
|
511
|
+
/// A field, fully qualified field name, or literal to average across.
|
|
512
|
+
/// options?: object
|
|
513
|
+
/// Options to pass into the operation. These are generally connection
|
|
514
|
+
/// specific. However, one option that is always available is the `connection`
|
|
515
|
+
/// option, which can define the <see>Connection</see> to use for the
|
|
516
|
+
/// operation.
|
|
517
|
+
///
|
|
518
|
+
/// Return: number
|
|
519
|
+
/// The "average" of all matching rows for a single column.
|
|
165
520
|
async average(field, options) {
|
|
166
521
|
let connection = this.getConnection(options && options.connection);
|
|
167
522
|
return await connection.average(await this.finalizeQuery('read', options), field, options);
|
|
168
523
|
}
|
|
169
524
|
|
|
525
|
+
/// Count the number of matching rows.
|
|
526
|
+
///
|
|
527
|
+
/// A `field` can be provided, which is used as the column to count on.
|
|
528
|
+
/// If a `DISTINCT` operation is used in the query, then the `DISTINCT`
|
|
529
|
+
/// field will be used for counting, regardless of what `field`
|
|
530
|
+
/// you specify. If no `field` is provided, then all fields (`*`)
|
|
531
|
+
/// is assumed.
|
|
532
|
+
///
|
|
533
|
+
/// Example:
|
|
534
|
+
/// let smithFamilySize = await User.where.lastName.EQ('Smith').count();
|
|
535
|
+
///
|
|
536
|
+
/// Note:
|
|
537
|
+
/// Due to database limitations, it can sometimes be difficult or
|
|
538
|
+
/// impossible in the underlying database to use an `ORDER`, `LIMIT`, or `OFFSET`,
|
|
539
|
+
/// with this operation. Because of this, any `ORDER` on the query
|
|
540
|
+
/// is simply ignored for now (help solving this problem would be
|
|
541
|
+
/// greatly appreciated!).
|
|
542
|
+
///
|
|
543
|
+
/// Arguments:
|
|
544
|
+
/// field?: <see>Field</see> | string | literal
|
|
545
|
+
/// A field, fully qualified field name, or literal to count on. If
|
|
546
|
+
/// a `DISTINCT` operation is used in the query, then this argument
|
|
547
|
+
/// will be ignored, and the field defined on the `DISTINCT` operation
|
|
548
|
+
/// will be used instead.
|
|
549
|
+
/// options?: object
|
|
550
|
+
/// Options to pass into the operation. These are generally connection
|
|
551
|
+
/// specific. However, one option that is always available is the `connection`
|
|
552
|
+
/// option, which can define the <see>Connection</see> to use for the
|
|
553
|
+
/// operation.
|
|
554
|
+
///
|
|
555
|
+
/// Return: number
|
|
556
|
+
/// The number of rows that match the query.
|
|
170
557
|
async count(field, options) {
|
|
171
558
|
let connection = this.getConnection(options && options.connection);
|
|
172
559
|
return await connection.count(await this.finalizeQuery('read', options), field, options);
|
|
173
560
|
}
|
|
174
561
|
|
|
562
|
+
/// Calculate the "minimum" of all matching rows for
|
|
563
|
+
/// a single column in the database.
|
|
564
|
+
///
|
|
565
|
+
/// Example:
|
|
566
|
+
/// let youngestUserAge = await User.where.min('User:age');
|
|
567
|
+
///
|
|
568
|
+
/// Arguments:
|
|
569
|
+
/// field: <see>Field</see> | string | <see>Literal</see>
|
|
570
|
+
/// A field, fully qualified field name, or literal to calculate on.
|
|
571
|
+
/// options?: object
|
|
572
|
+
/// Options to pass into the operation. These are generally connection
|
|
573
|
+
/// specific. However, one option that is always available is the `connection`
|
|
574
|
+
/// option, which can define the <see>Connection</see> to use for the
|
|
575
|
+
/// operation.
|
|
576
|
+
///
|
|
577
|
+
/// Return: number
|
|
578
|
+
/// The "minimum" value of all matching rows for a single column.
|
|
175
579
|
async min(field, options) {
|
|
176
580
|
let connection = this.getConnection(options && options.connection);
|
|
177
581
|
return await connection.min(await this.finalizeQuery('read', options), field, options);
|
|
178
582
|
}
|
|
179
583
|
|
|
584
|
+
/// Calculate the "maximum" of all matching rows for
|
|
585
|
+
/// a single column in the database.
|
|
586
|
+
///
|
|
587
|
+
/// Example:
|
|
588
|
+
/// let oldestUserAge = await User.where.max('User:age');
|
|
589
|
+
///
|
|
590
|
+
/// Arguments:
|
|
591
|
+
/// field: <see>Field</see> | string | <see>Literal</see>
|
|
592
|
+
/// A field, fully qualified field name, or literal to calculate on.
|
|
593
|
+
/// options?: object
|
|
594
|
+
/// Options to pass into the operation. These are generally connection
|
|
595
|
+
/// specific. However, one option that is always available is the `connection`
|
|
596
|
+
/// option, which can define the <see>Connection</see> to use for the
|
|
597
|
+
/// operation.
|
|
598
|
+
///
|
|
599
|
+
/// Return: number
|
|
600
|
+
/// The "maximum" value of all matching rows for a single column.
|
|
180
601
|
async max(field, options) {
|
|
181
602
|
let connection = this.getConnection(options && options.connection);
|
|
182
603
|
return await connection.max(await this.finalizeQuery('read', options), field, options);
|
|
183
604
|
}
|
|
184
605
|
|
|
606
|
+
/// Calculate the "sum" of all matching rows for
|
|
607
|
+
/// a single column in the database.
|
|
608
|
+
///
|
|
609
|
+
/// Example:
|
|
610
|
+
/// let totalInventoryPrice = await Product.where.sum('Product:price');
|
|
611
|
+
///
|
|
612
|
+
/// Arguments:
|
|
613
|
+
/// field: <see>Field</see> | string | <see>Literal</see>
|
|
614
|
+
/// A field, fully qualified field name, or literal to average across.
|
|
615
|
+
/// options?: object
|
|
616
|
+
/// Options to pass into the operation. These are generally connection
|
|
617
|
+
/// specific. However, one option that is always available is the `connection`
|
|
618
|
+
/// option, which can define the <see>Connection</see> to use for the
|
|
619
|
+
/// operation.
|
|
620
|
+
///
|
|
621
|
+
/// Return: number
|
|
622
|
+
/// The "sum" of all matching rows for a single column.
|
|
185
623
|
async sum(field, options) {
|
|
186
624
|
let connection = this.getConnection(options && options.connection);
|
|
187
625
|
return await connection.sum(await this.finalizeQuery('read', options), field, options);
|
|
188
626
|
}
|
|
189
627
|
|
|
628
|
+
/// Pluck specific fields directly from the database.
|
|
629
|
+
/// These plucked fields will not be converted into
|
|
630
|
+
/// model instances, but instead will be returned raw
|
|
631
|
+
/// to the caller.
|
|
632
|
+
///
|
|
633
|
+
/// If only a single field is provided for `fields` then
|
|
634
|
+
/// an array of column values is returned, i.e. `[ ... ]`. If
|
|
635
|
+
/// however more than one field is specified for `fields`,
|
|
636
|
+
/// then an array of arrays containing column values is
|
|
637
|
+
/// returned, i.e. `[ [ ... ], [ ... ], ... ]`.
|
|
638
|
+
///
|
|
639
|
+
/// Example:
|
|
640
|
+
/// let allSmithFamilyHobbies = await User.where.lastName.EQ('Smith').pluck('User:hobby');
|
|
641
|
+
///
|
|
642
|
+
/// Arguments:
|
|
643
|
+
/// fields: <see>Field</see> | string | Array<<see>Field</see>> | Array<string>
|
|
644
|
+
/// The field(s) to pluck from the database. A single field may be specified.
|
|
645
|
+
/// options?: object
|
|
646
|
+
/// Options to pass into the operation. These are generally connection
|
|
647
|
+
/// specific. However, one option that is always available is the `connection`
|
|
648
|
+
/// option, which can define the <see>Connection</see> to use for the
|
|
649
|
+
/// operation. The option `{ mapToObjects: true }` can be specified to
|
|
650
|
+
/// map the results to objects, instead of returning arrays of raw values.
|
|
651
|
+
///
|
|
652
|
+
/// Return: Array<any> | Array<Array<any>>
|
|
653
|
+
/// If a single field is specified for `fields`, then an array of column values
|
|
654
|
+
/// will be returned. If more than one field is specified, then an array of arrays
|
|
655
|
+
/// of column values will be returned. It matters not if you specify the single field
|
|
656
|
+
/// directly, or as an array, i.e. `.pluck('User:firstName')` is the same as `.pluck([ 'User:firstName' ])`.
|
|
657
|
+
/// The engine only cares if "one field" is used... it doesn't care how it receives
|
|
658
|
+
/// that single field.
|
|
190
659
|
async pluck(fields, options) {
|
|
191
660
|
let connection = this.getConnection(options && options.connection);
|
|
192
661
|
return await connection.pluck(await this.finalizeQuery('read', options), fields, options);
|
|
193
662
|
}
|
|
194
663
|
|
|
664
|
+
/// Check if any rows match the query.
|
|
665
|
+
///
|
|
666
|
+
/// Example:
|
|
667
|
+
/// let smithFamilyHasMembers = await User.where.lastName.EQ('Smith').exists();
|
|
668
|
+
///
|
|
669
|
+
/// Arguments:
|
|
670
|
+
/// options?: object
|
|
671
|
+
/// Options to pass into the operation. These are generally connection
|
|
672
|
+
/// specific. However, one option that is always available is the `connection`
|
|
673
|
+
/// option, which can define the <see>Connection</see> to use for the
|
|
674
|
+
/// operation.
|
|
675
|
+
///
|
|
676
|
+
/// Return: boolean
|
|
677
|
+
/// Return `true` if at least one row matches the query, or `false` otherwise.
|
|
195
678
|
async exists(options) {
|
|
196
679
|
let connection = this.getConnection(options && options.connection);
|
|
197
680
|
return await connection.exists(await this.finalizeQuery('read', options), options);
|
|
198
681
|
}
|
|
199
682
|
|
|
683
|
+
/// Finalize the query before a database operation.
|
|
684
|
+
///
|
|
685
|
+
/// This method simply proxies the request to <see>Connection.finalizeQuery</see>.
|
|
686
|
+
/// If no connection can be found (for whatever reason), then `this`
|
|
687
|
+
/// (the current query) is simply returned.
|
|
688
|
+
///
|
|
689
|
+
/// Note:
|
|
690
|
+
/// This system was designed for "row level permissions" systems,
|
|
691
|
+
/// or simply to allow models themselves to control how queries are
|
|
692
|
+
/// carried out against them.
|
|
693
|
+
///
|
|
694
|
+
/// Arguments:
|
|
695
|
+
/// type: string
|
|
696
|
+
/// The CRUD operation type being performed. Can be one of `'create'`, `'read'`,
|
|
697
|
+
/// `'update'`, or `'delete'`. The `'create'` operation is currently never used
|
|
698
|
+
/// by Mythix ORM, since no query is ever involved in an `INSERT` operation; however
|
|
699
|
+
/// it has been reserved for future use.
|
|
700
|
+
/// options?: object
|
|
701
|
+
/// The options that were provided by the user to the operation being executed.
|
|
702
|
+
/// These are the operation options themselves, but can contain properties
|
|
703
|
+
/// specifically targeted for custom down-stream code.
|
|
704
|
+
///
|
|
705
|
+
/// Return: <see>QueryEngine</see>
|
|
706
|
+
/// Return `this` query, possibly altered by down-stream code.
|
|
200
707
|
async finalizeQuery(crudOperation, options) {
|
|
201
708
|
let connection = this.getConnection();
|
|
202
709
|
if (connection && typeof connection.finalizeQuery === 'function')
|
|
@@ -207,7 +714,7 @@ class QueryEngine extends QueryEngineBase {
|
|
|
207
714
|
|
|
208
715
|
[ProxyClass.MISSING](target, prop) {
|
|
209
716
|
if (prop === 'debug') {
|
|
210
|
-
this.
|
|
717
|
+
this.getOperationContext().rootContext.debug = true;
|
|
211
718
|
return this._fetchScope('model', 'queryEngine');
|
|
212
719
|
}
|
|
213
720
|
|