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.
@@ -2,11 +2,78 @@
2
2
 
3
3
  const LiteralFieldBase = require('./literal-field-base');
4
4
 
5
+ /// Define an "average" literal for the underlying database.
6
+ ///
7
+ /// Literals are special types in Mythix ORM that are used to
8
+ /// define "literal values" for the underlying database.
9
+ ///
10
+ /// This literal defines an "average" operation across a single
11
+ /// column. It is used by <see>Connection.average</see> to get
12
+ /// the average across all rows for a single column in the underlying
13
+ /// database. When serialized using the <see>QueryGenerator</see>
14
+ /// for the connection, it will turn into a database method that
15
+ /// is appropriate for the underlying database. For example, with
16
+ /// SQL type databases this would turn into `SUM(column)`. This is
17
+ /// often used by the projection engine, to project it as a column
18
+ /// to be selected. For example `SELECT SUM(column) ...`. It can
19
+ /// be used in other places in the query however, such as `ORDER`,
20
+ /// `GROUP BY`, and `HAVING` clauses.
21
+ ///
22
+ /// There are two primary ways to access literals in Mythix ORM. The
23
+ /// first is to simply import them. The second way literals can be
24
+ /// accessed is via the connection class itself. All Mythix ORM connection
25
+ /// classes export all literals on the `static Literals` attribute of
26
+ /// the class. So for example, you could access literals like `SQLiteConnection.Literals.SumLiteral`.
27
+ ///
28
+ /// All built-in Mythix ORM literals--except `Literal` and `LiteralBase`--accept a field
29
+ /// as their first argument. This field can be a fully qualified field name, an actual
30
+ /// <see>Field</see> instance, or another literal. The second argument to all literal constructors
31
+ /// is an `options` object, that generally contains connection-specific (and operation-specific) options...
32
+ /// however, there are common options that can be supplied, such as `as: string;` which allows you to
33
+ /// define an alias for the defined field, and `noProjectionAliases: boolean;`, which allows you to disable
34
+ /// the column alias entirely.
35
+ ///
36
+ /// Example:
37
+ /// const { Literals } = require('mythix-orm');
38
+ /// const { SQLiteConnection } = require('mythix-orm-sqlite');
39
+ /// let literal1 = new Literals.SumLiteral('User:age');
40
+ /// let literal2 = new SQLiteConnection.Literals.SumLiteral('User:age');
41
+ ///
42
+ /// See: LiteralFieldBase
43
+ ///
44
+ /// See: LiteralBase
5
45
  class SumLiteral extends LiteralFieldBase {
46
+ /// Return `true`, letting the caller know that
47
+ /// this is an "aggregating literal".
48
+ ///
49
+ /// Return: boolean
50
+ /// Return `true`, informing the caller that this literal is used for aggregate operations.
6
51
  static isAggregate() {
7
52
  return true;
8
53
  }
9
54
 
55
+ /// Convert this literal to a string to be used in a database query.
56
+ ///
57
+ /// This method proxies the conversion of this literal to the connection
58
+ /// by calling <see>Connection.literalToString</see>. If no connection
59
+ /// is provided when this is called, then the literal will be converted
60
+ /// to a string representing it for debugging, i.e. `'SumLiteral {}'`.
61
+ ///
62
+ /// Note:
63
+ /// Ultimately, for most connections, this will end up calling
64
+ /// <see>QueryGenerator._sumLiteralToString</see>.
65
+ ///
66
+ /// Arguments:
67
+ /// connection?: <see>Connection</see>
68
+ /// The connection to use to stringify this literal. If none is provided,
69
+ /// then a string representing this object will be returned instead.
70
+ /// options?: object
71
+ /// A connection and operation specific set of options that can be provided.
72
+ /// This might for example be `{ as: 'name' }` to provided a field alias, or
73
+ /// `{ isProjection: true }` to define that this is being stringified for use
74
+ /// as a field in the query projection. Normally the end-user won't care about
75
+ /// any literal options, except `as`, which is commonly used to give your literal
76
+ /// an alias.
10
77
  toString(connection, options) {
11
78
  if (!connection)
12
79
  return `${this.constructor.name} {}`;
@@ -2,8 +2,49 @@
2
2
 
3
3
  /// The base query generator class.
4
4
  ///
5
+ /// A "query generator" is an interface that will take
6
+ /// parameters (usually a <see>QueryEngine</see> or a
7
+ /// <see>Model</see>) and generate database query statements
8
+ /// from the input. For SQL type databases this would mean
9
+ /// generating `SELECT`, `INSERT`, `UPDATE`, and `DELETE`
10
+ /// statements, as well as generators for creating and altering
11
+ /// tables, among other things.
12
+ ///
13
+ /// The methods of this class are generally many, with the
14
+ /// design pattern for most generators being that nearly
15
+ /// all methods are split apart and added to the class, allowing
16
+ /// by deliberate design much finer control when using overloaded
17
+ /// methods, to modify or replace any parts of the generator.
18
+ ///
19
+ /// Any connection can be provided a custom query generator
20
+ /// interface via the `queryGenerator` option that can be
21
+ /// used when instantiating a connection. Most connections
22
+ /// will supply their own by default.
23
+ ///
24
+ /// A connection should always be bound to a query generator
25
+ /// instance. If not when first created, then at least when
26
+ /// provided to a connection. The connection is a required
27
+ /// part of the generator interface, and will do things, such
28
+ /// as for example, escaping values and ids using the connection
29
+ /// itself. The connection may be used for other operations as
30
+ /// well.
31
+ ///
32
+ /// Note:
33
+ /// Some database drivers may not have a generator at all. Though
34
+ /// database drivers commonly do have a database statement generator,
35
+ /// a connection isn't required to have one.
36
+ ///
5
37
  /// Alias: QueryGenerator
6
38
  class QueryGeneratorBase {
39
+ /// Construct a new query generator
40
+ ///
41
+ /// Arguments:
42
+ /// connection?: <see>Connection</see>
43
+ /// The connection that this interface is for. Sometimes the connection
44
+ /// isn't yet available when creating the query generator, so this argument
45
+ /// is optional. When provided to a <see>Connection</see>, the connection
46
+ /// will call <see>QueryGenerator.setConnection</see> with itself to set
47
+ /// the connection for the query generator.
7
48
  constructor(connection) {
8
49
  Object.defineProperties(this, {
9
50
  'connection': {
@@ -15,57 +56,289 @@ class QueryGeneratorBase {
15
56
  });
16
57
  }
17
58
 
59
+ /// Get the <see>Connection</see> bound to this
60
+ /// query generator.
61
+ ///
62
+ /// Return: <see>Connection</see>
63
+ /// The connection bound to this query generator. A connection
64
+ /// should always be bound before any generating methods of
65
+ /// the class are called.
66
+ getConnection() {
67
+ return this.connection;
68
+ }
69
+
70
+ /// Set the <see>Connection</see> bound to this
71
+ /// query generator.
72
+ ///
73
+ /// Arguments:
74
+ /// connection: <see>Connection</see>
75
+ /// The connection to bind to this query generator.
76
+ ///
77
+ /// Return: <see>QueryGenerator</see>
78
+ /// Return `this` to allow for chaining.
79
+ setConnection(connection) {
80
+ this.connection = connection;
81
+ return this;
82
+ }
83
+
84
+ /// This call proxies to <see>Connection.stackAssign</see>.
85
+ /// Refer to the documentation of that method for more information.
86
+ ///
87
+ /// See: Connection.stackAssign
18
88
  stackAssign(obj, ...args) {
19
89
  return this.connection.stackAssign(obj, ...args);
20
90
  }
21
91
 
92
+ /// This call proxies to <see>Connection.escape</see>.
93
+ /// Refer to the documentation of that method for more information.
94
+ ///
95
+ /// See: Connection.escape
22
96
  escape(...args) {
23
97
  return this.connection.escape(...args);
24
98
  }
25
99
 
100
+ /// This call proxies to <see>Connection.escapeID</see>.
101
+ /// Refer to the documentation of that method for more information.
102
+ ///
103
+ /// See: Connection.escapeID
26
104
  escapeID(...args) {
27
105
  return this.connection.escapeID(...args);
28
106
  }
29
107
 
108
+ /// Convert an <see>AverageLiteral</see> into a
109
+ /// string representation for the underlying database.
110
+ ///
111
+ /// It is expected that each database driver will implement
112
+ /// this method. By default it will simply throw an
113
+ /// "unsupported" error.
114
+ ///
115
+ /// Refer to the specific documentation for your database
116
+ /// driver for more information.
117
+ ///
118
+ /// Arguments:
119
+ /// literal: <see>AverageLiteral</see>
120
+ /// The literal to stringify for the underlying database.
121
+ /// options?: object
122
+ /// Options for the stringify process. These are often database
123
+ /// driver specific. However, one common option is the `as`
124
+ /// option, which will allow you to give your literal an alias.
125
+ ///
126
+ /// Return: string
127
+ /// The literal, converted into the proper string for the underlying
128
+ /// database.
30
129
  // eslint-disable-next-line no-unused-vars
31
130
  _averageLiteralToString(literal, options) {
32
131
  throw new Error(`${this.constructor.name}::_averageLiteralToString: This operation is not supported for this connection type.`);
33
132
  }
34
133
 
134
+ /// Convert an <see>CountLiteral</see> into a
135
+ /// string representation for the underlying database.
136
+ ///
137
+ /// It is expected that each database driver will implement
138
+ /// this method. By default it will simply throw an
139
+ /// "unsupported" error.
140
+ ///
141
+ /// Refer to the specific documentation for your database
142
+ /// driver for more information.
143
+ ///
144
+ /// Arguments:
145
+ /// literal: <see>CountLiteral</see>
146
+ /// The literal to stringify for the underlying database.
147
+ /// options?: object
148
+ /// Options for the stringify process. These are often database
149
+ /// driver specific. However, one common option is the `as`
150
+ /// option, which will allow you to give your literal an alias.
151
+ ///
152
+ /// Return: string
153
+ /// The literal, converted into the proper string for the underlying
154
+ /// database.
35
155
  // eslint-disable-next-line no-unused-vars
36
156
  _countLiteralToString(literal, options) {
37
157
  throw new Error(`${this.constructor.name}::_countLiteralToString: This operation is not supported for this connection type.`);
38
158
  }
39
159
 
160
+ /// Convert an <see>DistinctLiteral</see> into a
161
+ /// string representation for the underlying database.
162
+ ///
163
+ /// It is expected that each database driver will implement
164
+ /// this method. By default it will simply throw an
165
+ /// "unsupported" error.
166
+ ///
167
+ /// Refer to the specific documentation for your database
168
+ /// driver for more information.
169
+ ///
170
+ /// Arguments:
171
+ /// literal: <see>DistinctLiteral</see>
172
+ /// The literal to stringify for the underlying database.
173
+ /// options?: object
174
+ /// Options for the stringify process. These are often database
175
+ /// driver specific. However, one common option is the `as`
176
+ /// option, which will allow you to give your literal an alias.
177
+ ///
178
+ /// Return: string
179
+ /// The literal, converted into the proper string for the underlying
180
+ /// database.
40
181
  // eslint-disable-next-line no-unused-vars
41
182
  _distinctLiteralToString(literal, options) {
42
183
  throw new Error(`${this.constructor.name}::_distinctLiteralToString: This operation is not supported for this connection type.`);
43
184
  }
44
185
 
186
+ /// Convert an <see>FieldLiteral</see> into a
187
+ /// string representation for the underlying database.
188
+ ///
189
+ /// It is expected that each database driver will implement
190
+ /// this method. By default it will simply throw an
191
+ /// "unsupported" error.
192
+ ///
193
+ /// Refer to the specific documentation for your database
194
+ /// driver for more information.
195
+ ///
196
+ /// Arguments:
197
+ /// literal: <see>FieldLiteral</see>
198
+ /// The literal to stringify for the underlying database.
199
+ /// options?: object
200
+ /// Options for the stringify process. These are often database
201
+ /// driver specific. However, one common option is the `as`
202
+ /// option, which will allow you to give your literal an alias.
203
+ ///
204
+ /// Return: string
205
+ /// The literal, converted into the proper string for the underlying
206
+ /// database.
45
207
  // eslint-disable-next-line no-unused-vars
46
208
  _fieldLiteralToString(literal, options) {
47
209
  throw new Error(`${this.constructor.name}::_fieldLiteralToString: This operation is not supported for this connection type.`);
48
210
  }
49
211
 
212
+ /// Convert an <see>MaxLiteral</see> into a
213
+ /// string representation for the underlying database.
214
+ ///
215
+ /// It is expected that each database driver will implement
216
+ /// this method. By default it will simply throw an
217
+ /// "unsupported" error.
218
+ ///
219
+ /// Refer to the specific documentation for your database
220
+ /// driver for more information.
221
+ ///
222
+ /// Arguments:
223
+ /// literal: <see>MaxLiteral</see>
224
+ /// The literal to stringify for the underlying database.
225
+ /// options?: object
226
+ /// Options for the stringify process. These are often database
227
+ /// driver specific. However, one common option is the `as`
228
+ /// option, which will allow you to give your literal an alias.
229
+ ///
230
+ /// Return: string
231
+ /// The literal, converted into the proper string for the underlying
232
+ /// database.
50
233
  // eslint-disable-next-line no-unused-vars
51
234
  _maxLiteralToString(literal, options) {
52
235
  throw new Error(`${this.constructor.name}::_maxLiteralToString: This operation is not supported for this connection type.`);
53
236
  }
54
237
 
238
+ /// Convert an <see>MinLiteral</see> into a
239
+ /// string representation for the underlying database.
240
+ ///
241
+ /// It is expected that each database driver will implement
242
+ /// this method. By default it will simply throw an
243
+ /// "unsupported" error.
244
+ ///
245
+ /// Refer to the specific documentation for your database
246
+ /// driver for more information.
247
+ ///
248
+ /// Arguments:
249
+ /// literal: <see>MinLiteral</see>
250
+ /// The literal to stringify for the underlying database.
251
+ /// options?: object
252
+ /// Options for the stringify process. These are often database
253
+ /// driver specific. However, one common option is the `as`
254
+ /// option, which will allow you to give your literal an alias.
255
+ ///
256
+ /// Return: string
257
+ /// The literal, converted into the proper string for the underlying
258
+ /// database.
55
259
  // eslint-disable-next-line no-unused-vars
56
260
  _minLiteralToString(literal, options) {
57
261
  throw new Error(`${this.constructor.name}::_minLiteralToString: This operation is not supported for this connection type.`);
58
262
  }
59
263
 
264
+ /// Convert an <see>SumLiteral</see> into a
265
+ /// string representation for the underlying database.
266
+ ///
267
+ /// It is expected that each database driver will implement
268
+ /// this method. By default it will simply throw an
269
+ /// "unsupported" error.
270
+ ///
271
+ /// Refer to the specific documentation for your database
272
+ /// driver for more information.
273
+ ///
274
+ /// Arguments:
275
+ /// literal: <see>SumLiteral</see>
276
+ /// The literal to stringify for the underlying database.
277
+ /// options?: object
278
+ /// Options for the stringify process. These are often database
279
+ /// driver specific. However, one common option is the `as`
280
+ /// option, which will allow you to give your literal an alias.
281
+ ///
282
+ /// Return: string
283
+ /// The literal, converted into the proper string for the underlying
284
+ /// database.
60
285
  // eslint-disable-next-line no-unused-vars
61
286
  _sumLiteralToString(literal, options) {
62
287
  throw new Error(`${this.constructor.name}::_sumLiteralToString: This operation is not supported for this connection type.`);
63
288
  }
64
289
 
290
+ /// Take a <see>QueryEngine</see> instance and
291
+ /// convert it into a query. For SQL type databases
292
+ /// this would turn a <see>QueryEngine</see> into a
293
+ /// `SELECT` statement. For other types of databases,
294
+ /// this should return a "fetch" query--or string representation
295
+ /// of such a query--in the database's native query language.
296
+ ///
297
+ /// Arguments:
298
+ /// queryEngine: <see>QueryEngine</see>
299
+ /// The query engine instance to stringify.
300
+ /// options?: object
301
+ /// Connection and operation specific options. These
302
+ /// generally aren't needed, but are provided in case
303
+ /// the underlying connection needs them.
304
+ ///
305
+ /// Return: string
306
+ /// A "fetch" query in the databases native query language,
307
+ /// generated from the provided `queryEngine`.
65
308
  // eslint-disable-next-line no-unused-vars
66
309
  toConnectionString(queryEngine, options) {
67
310
  return '<not supported by connection>';
68
311
  }
312
+
313
+ /// Get the "default value" for the given field
314
+ /// for the underlying database. This is used
315
+ /// primarily for "CREATE TABLE" statements.
316
+ ///
317
+ /// By default, the implementation of this method
318
+ /// is empty. It is expected that each database driver
319
+ /// will implement their own version of this method.
320
+ ///
321
+ /// Arguments:
322
+ /// field: <see>Field</see>
323
+ /// The field instance we are getting a "default value"
324
+ /// from.
325
+ /// fieldName: string
326
+ /// The name of the field that we are getting the "default value"
327
+ /// from. This should always be the same as `field.fieldName`.
328
+ /// options?: object
329
+ /// Options for the operation. These will likely be connection
330
+ /// specific. Please refer to the documentation of your specific
331
+ /// connection for more details.
332
+ ///
333
+ /// Return: any
334
+ /// Though in most cases this method will return a string for
335
+ /// most database drivers in most situations, it may return other
336
+ /// types as well, such as literals, or other raw values.
337
+ /// Please refer to the documentation of your specific
338
+ /// connection for more details.
339
+ // eslint-disable-next-line no-unused-vars
340
+ getFieldDefaultValue(field, fieldName, _options) {
341
+ }
69
342
  }
70
343
 
71
344
  module.exports = QueryGeneratorBase;
package/lib/field.js CHANGED
@@ -133,7 +133,7 @@
133
133
  /// it is a function, it will be called when the model is initialized
134
134
  /// if no value for the field was provided to the model during instantiation.
135
135
  /// As a method, it can have numerous different "flags" set on it, via
136
- /// the <see>DefaultHelpers.defaultValueFlags</see> factory. Reference this
136
+ /// the <see>Helpers.defaultValueFlags</see> factory. Reference this
137
137
  /// methods documentation for a better understanding of what default value
138
138
  /// flags are, and what they do.
139
139
  /// get: Function(context: GetSetContext)
package/lib/model.js CHANGED
@@ -345,7 +345,7 @@ class Model {
345
345
  /// Just be cautious if you set a `connection` property, as you might overwrite any
346
346
  /// connection currently set on the context. Any properties you set here will be available
347
347
  /// in any child function calls (inside this context scope), and can be accessed via a
348
- /// call to <see>Model.getModelContext</see>.
348
+ /// call to <see>Model.static getModelContext</see>.
349
349
  ///
350
350
  /// Note:
351
351
  /// An `AsyncLocalStorage` context might not exist when this call is made,
@@ -381,7 +381,7 @@ class Model {
381
381
  /// This method will return a connection in the following
382
382
  /// order: 1) The provided `connection` argument, if supplied,
383
383
  /// 2) The `connection` property on the "model context" from
384
- /// <see>Model.getModelContext</see> from `AsyncLocalStorage`,
384
+ /// <see>Model.static getModelContext</see> from `AsyncLocalStorage`,
385
385
  /// if one is available, and 3) Finally the bound connection, if
386
386
  /// available, which is found by searching the `static Model._mythixBoundConnection`
387
387
  /// property of the class.
@@ -677,6 +677,58 @@ class Model {
677
677
  return this.constructor.defaultScope(queryEngine);
678
678
  }
679
679
 
680
+ /// Finalize a query before using it for database operations.
681
+ ///
682
+ /// `finalizeQuery` is called on **every** query immediately
683
+ /// before it is used for a database operation. Its only purpose
684
+ /// is to potentially modify the query before the database operation
685
+ /// occurs. This can be extremely useful for things like "row level permissions",
686
+ /// or ensuring that a certain type of query always has certain conditions.
687
+ ///
688
+ /// Because it is asynchronous by design, it is possible to finalize the
689
+ /// query using other asynchronous operations; for example validating
690
+ /// authentication tokens, or fetching user roles from the database.
691
+ ///
692
+ /// This is called by <see>Connection.finalizeQuery</see> for each unique
693
+ /// model found in the query.
694
+ ///
695
+ /// Interface:
696
+ /// interface FinalizeQueryContext {
697
+ /// type: 'create' | 'read' | 'updated' | 'delete'; // The operation type that is about to happen in the underlying database.
698
+ /// query: QueryEngine; // The query being operated upon. Modify and return this.
699
+ /// queryDepth: number; // The depth we are at while walking the query. Will be 0 for root level, 1 for first sub-query, etc...
700
+ /// operationIndex: number; // The "operation stack" index we are at
701
+ /// operation: object; // The "operation" on the query operation stack we are working on
702
+ /// operations: Array<object>; // The query "operation stack"
703
+ /// parent: object | null; // The parent "operation"
704
+ /// contextKey: string; // The name of the key that this query is a value of for this "operation" object. This is usually "value" for most operations, but it could be something else.
705
+ /// options: object; // The options passed into the operation by the user
706
+ /// }
707
+ ///
708
+ ///
709
+ /// Note:
710
+ /// "With great power comes great responsibility." Use `finalizeQuery` intelligently. It can
711
+ /// cause a noticeable performance hit, and can really make things hard to debug and
712
+ /// understand if you don't clearly document.
713
+ ///
714
+ /// Note:
715
+ /// `'create'` isn't actually currently supported, since no queries are
716
+ /// ever used in an `INSERT` operation. It is reserved for future use.
717
+ ///
718
+ /// Arguments:
719
+ /// context: FinalizeQueryContext
720
+ /// All provided values in a single context. You will want to modify `query`
721
+ /// and return it... or if you don't modify it, simply return it. The `contextKey`
722
+ /// might be a little confusing. It is the name of the key the query is a value on.
723
+ /// For example, in an `EQ` operation, this would be "value".
724
+ /// i.e. an `EQ` operation looks something like the following on the operation stack:
725
+ /// `{ operator: 'EQ', value: query }`. The reason it is provided is if you need to
726
+ //// directly get or set a value on the operation frame (`operation`).
727
+ ///
728
+ /// Return: <see>QueryEngine</see>
729
+ /// The query provided, possibly altered.
730
+ ///
731
+ /// See: Connection.finalizeQuery
680
732
  // eslint-disable-next-line no-unused-vars
681
733
  static finalizeQuery({ type, query, queryDepth, operationIndex, operation, operations, parent, contextKey, options }) {
682
734
  return query;