mythix-orm 1.8.2 → 1.10.2

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.
@@ -15,15 +15,73 @@ const LiteralBase = Literals.LiteralBase;
15
15
 
16
16
  /// ConnectionBase is the base class that
17
17
  /// all connection classes should inherit from.
18
+ /// It provides common functionality to all
19
+ /// connections, and converts literals and
20
+ /// types into their native database representation.
21
+ ///
22
+ /// A "connection" in Mythix ORM is essentially
23
+ /// the "application" for the ORM. It stores all
24
+ /// models used by the connection, stores and defines
25
+ /// the query generator (if any), converts literals
26
+ /// and types, and also provides the query engine.
27
+ ///
28
+ /// Multiple connections can be used at the same time
29
+ /// in Mythix ORM. It is also planned that someday
30
+ /// Mythix ORM (or the community) will provide a
31
+ /// multiplex connection, to mix multiple connections
32
+ /// into a single connection for the entire application.
33
+ ///
34
+ /// For now you can use multiple connections at once.
35
+ /// Just know that you should read and fully understand
36
+ /// [Connection Binding](./ConnectionBinding) if you plan
37
+ /// on using more than one connection for your application.
38
+ ///
39
+ /// ConnectionBase is also an [Event Emitter](https://nodejs.org/docs/latest-v16.x/api/events.html),
40
+ /// so you can bind events to a connection instance with `.on`, and unbind
41
+ /// with `.off`. Events are connection specific, but common events that
42
+ /// can be bound to are:
43
+ ///
44
+ /// 1. `connect` - Commonly fired when a connection is successfully established
45
+ /// 2. `acquire` - Commonly fired when a connection is acquired from a connection pool
46
+ /// 3. `error` - Commonly fired when a database level error occurs
47
+ /// 4. `disconnect` - Commonly fired when a connection is disconnected
48
+ ///
49
+ /// *This is just an example of common connection driver events.
50
+ /// Please see documentation for your specific database connection for the proper events.*
51
+ ///
52
+ /// Properties:
53
+ /// static Literals: Literals
54
+ /// All default Mythix ORM literal classes,
55
+ /// provided for convenient access.
56
+ /// static dialect: string
57
+ /// The dialect of the database, i.e. `'sqlite'`, or `'postgresql'`.
58
+ /// dialect: string
59
+ /// The dialect of the database, i.e. `'sqlite'`, or `'postgresql'`.
60
+ /// static _isMythixConnection: boolean
61
+ /// Helper for type checking methods. Should always be `true`.
18
62
  ///
19
63
  /// Alias: Connection
20
64
  class ConnectionBase extends EventEmitter {
21
65
  static Literals = Literals;
22
66
 
67
+ static DefaultQueryGenerator = QueryGeneratorBase;
68
+
23
69
  static dialect = 'none';
24
70
 
25
71
  static _isMythixConnection = true;
26
72
 
73
+ /// Use this method to check if a class
74
+ /// is a Mythix ORM connection. It will return
75
+ /// `true` if the provided value is a class
76
+ /// that inherits from <see>ConnectionBase</see>, or
77
+ /// if the provided value has an attribute
78
+ /// named `_isMythixConnection` that is truthy.
79
+ ///
80
+ /// Return: boolean
81
+ ///
82
+ /// Arguments:
83
+ /// value: Function
84
+ /// Value to check.
27
85
  static isConnectionClass(value) {
28
86
  if (!value)
29
87
  return false;
@@ -37,6 +95,22 @@ class ConnectionBase extends EventEmitter {
37
95
  return false;
38
96
  }
39
97
 
98
+ /// Check to see if the provided value is
99
+ /// an *instance* of a Mythix ORM <see>ConnectionBase</see>.
100
+ /// Unlike <see>ConnectionBase.static isConnectionClass</see>, which
101
+ /// checks if a *class* is a <see>ConnectionBase</see>, this will check
102
+ /// to see if an *instance* is an instance of a
103
+ /// Mythix ORM <see>ConnectionBase</see>. It will return
104
+ /// `true` if the provided value is an `instanceof`
105
+ /// <see>ConnectionBase</see>, or if the value's `constructor`
106
+ /// property has a truthy `_isMythixConnection` property
107
+ /// (`value.constructor._isMythixConnection`)
108
+ ///
109
+ /// Return: boolean
110
+ ///
111
+ /// Arguments:
112
+ /// value: any
113
+ /// Value to check
40
114
  static isConnection(value) {
41
115
  if (!value)
42
116
  return false;
@@ -50,20 +124,72 @@ class ConnectionBase extends EventEmitter {
50
124
  return false;
51
125
  }
52
126
 
127
+ /// Fetch a literal class by its name.
128
+ ///
129
+ /// The following is a table that displays
130
+ /// the `name` argument that can be provided,
131
+ /// and the resulting Literal class that would
132
+ /// be returned for that name.
133
+ /// | `name` | Resulting literal class |
134
+ /// | --------------- | --------------------------- |
135
+ /// | `'Average'` | <see>AverageLiteral</see> |
136
+ /// | `'Base'` | <see>LiteralBase</see> |
137
+ /// | `'Count'` | <see>CountLiteral</see> |
138
+ /// | `'Distinct'` | <see>DistinctLiteral</see> |
139
+ /// | `'Field'` | <see>FieldLiteral</see> |
140
+ /// | `'FieldBase'` | <see>LiteralFieldBase</see> |
141
+ /// | `'Literal'` | <see>Literal</see> |
142
+ /// | `'Max'` | <see>MaxLiteral</see> |
143
+ /// | `'Min'` | <see>MinLiteral</see> |
144
+ /// | `'Sum'` | <see>SumLiteral</see> |
145
+ ///
146
+ /// Arguments:
147
+ /// name: string
148
+ /// The name of the literal class to return.
149
+ ///
150
+ /// Return: class <see>LiteralBase</see>
151
+ /// Return the literal class requested.
53
152
  static getLiteralClassByName(_name) {
54
153
  if (!_name)
55
154
  return;
56
155
 
57
- let name = Nife.capitalize(_name.toLowerCase());
156
+ let Klass = Literals[_name];
157
+ if (Klass)
158
+ return Klass;
58
159
 
160
+ let name = Nife.capitalize(_name.toLowerCase());
59
161
  if (name === 'Literal')
60
162
  return Literals.Literal;
163
+ if (name === 'FieldBase')
164
+ return Literals.LiteralFieldBase;
61
165
  else if (name === 'Base')
62
166
  return Literals.LiteralBase;
63
167
 
64
168
  return Literals[`${name}Literal`];
65
169
  }
66
170
 
171
+ /// Create the literal specified by name.
172
+ ///
173
+ /// Whereas <see>ConnectionBase.static getLiteralClassByName</see> will
174
+ /// simply return the literal class specified, this will create
175
+ /// the specified literal, with the provided arguments.
176
+ ///
177
+ /// Please see <see>ConnectionBase.static getLiteralClassByName</see> for
178
+ /// possible names that can be provided as the `name` argument.
179
+ ///
180
+ /// See: ConnectionBase.static getLiteralClassByName
181
+ ///
182
+ /// Arguments:
183
+ /// name: string
184
+ /// The name of the literal to create. See
185
+ /// <see>ConnectionBase.static getLiteralClassByName</see> for
186
+ /// possible values.
187
+ /// ...args: Array<any>
188
+ /// Arguments to provide to the literal constructor.
189
+ ///
190
+ /// Return: <see>LiteralBase</see>
191
+ /// Return an instance the literal requested,
192
+ /// using the arguments specified.
67
193
  static Literal(name, ...args) {
68
194
  const LiteralClass = this.getLiteralClassByName(name);
69
195
  if (!LiteralClass)
@@ -73,6 +199,26 @@ class ConnectionBase extends EventEmitter {
73
199
  return literal;
74
200
  }
75
201
 
202
+ /// Create a connection.
203
+ ///
204
+ /// The `options` argument is generally connection
205
+ /// specific. However, there are a few generic options,
206
+ /// which you will find listed in the table below:
207
+ /// | Option | Type | Default Value | Description |
208
+ /// | ------------- | ---- | ------------- | ----------- |
209
+ /// | `bindModels` | `boolean` | `true` | If `true`, then bind the provided `models` to this connection. Models are bound to a connection by setting a static `_mythixBoundConnection` property on each model class to this connection instance. |
210
+ /// | `forceConnectionBinding` | `boolean` | `false` | Normally attempting to bind a connection to a model more than once will result in an exception being thrown. However, if you set this property to `true`, then Mythix ORM will rebind the connection without complaint, even if a connection is already bound to your models. Make sure you know what you are doing if you use this option. |
211
+ /// | `models` | `Array` or `Object` of <see>Models</see> | `undefined` | Models to provide to this connection. This can be either an array of <see>Models</see>, or an Object where each value is a <see>Model</see> |
212
+ /// | `QueryEngine` | class <see>QueryEngine</see> | <see>QueryEngine</see> | The QueryEngine class to use for this connection and all this connection's models. You can provide your own class if you wish to add onto the query interface. |
213
+ /// | `queryGenerator` | <see>QueryGenerator</see> | *Connection specific* | The query generator for this connection. If a <see>QueryGenerator</see> instance is provided (the correct one for the connection you are using), then this provided query generator will be used. If one isn't provided, then the connection will create its own query generator that is specific to the connection type. |
214
+ ///
215
+ /// Arguments:
216
+ /// options: object
217
+ /// Connection specific options to supply to your connection. These
218
+ /// will commonly include things like a hostname to connect to, a
219
+ /// user name and password, and any connection specific parameters.
220
+ ///
221
+ /// Return: <see>Connection</see>
76
222
  constructor(_options) {
77
223
  super();
78
224
 
@@ -124,6 +270,36 @@ class ConnectionBase extends EventEmitter {
124
270
  this.registerModels(options.models);
125
271
  }
126
272
 
273
+ /// This method is an internal method that parses
274
+ /// the "lock mode" options passed to a call to
275
+ /// <see>Connection.transaction</see>.
276
+ ///
277
+ /// These might be different depending on the connection driver
278
+ /// you are using. Please refer to the documentation
279
+ /// for your connection driver for more details.
280
+ ///
281
+ /// Arguments:
282
+ /// options: any
283
+ /// Possibly connection specific lock options for a
284
+ /// connection's <see>Connection.transaction</see> method.
285
+ /// Generally this will either be a `true` value, a Model name
286
+ /// (for which table to lock), or a complete lock options object,
287
+ /// which generally will look something like:
288
+ /// `{ lock: boolean; modelName: string; read: boolean; write: boolean }`
289
+ /// <br>
290
+ /// <br>`lock` - If `true`, then lock the transaction
291
+ /// <br>`modelName` - The name of the table(s) to lock
292
+ /// <br>`read` - If `true`, then lock for reads
293
+ /// <br>`write` - If `true`, then lock for writes
294
+ ///
295
+ /// Return: object
296
+ /// Return the lock options for a transaction. These might change
297
+ /// based on the connection driver you are using, but will generally
298
+ /// look like `{ lock: boolean; modelName: string; read: boolean; write: boolean }`.
299
+ /// 1. `lock` - If `true`, then lock the transaction
300
+ /// 2. `modelName` - The name of the table(s) to lock
301
+ /// 3. `read` - If `true`, then lock for reads
302
+ /// 4. `write` - If `true`, then lock for writes
127
303
  getLockMode(options) {
128
304
  if (!options)
129
305
  return { lock: false, read: false, write: false };
@@ -150,6 +326,25 @@ class ConnectionBase extends EventEmitter {
150
326
  }
151
327
  }
152
328
 
329
+ /// Get the default order for selecting rows
330
+ /// from the database. This will call the
331
+ /// Model's <see name="Model.defaultOrder">Model.static defaultOrder</see> method
332
+ /// first to see if the model specifies a default order
333
+ /// for itself. If it doesn't, then the connection
334
+ /// driver itself might specify a default order for
335
+ /// each table.
336
+ ///
337
+ /// Arguments:
338
+ /// Model: class <see>Model</see>
339
+ /// The model/table to fetch the default order for.
340
+ /// options: object
341
+ /// Operation specific options (i.e. options for a "select" call)
342
+ ///
343
+ /// Return: Array<string>
344
+ /// An array of fully qualified field names for this model should
345
+ /// be returned by this method. An empty array, `null`, or `undefined`
346
+ /// are also valid return values (in which case no order will be
347
+ /// applied to the given operation).
153
348
  getDefaultOrder(Model, options) {
154
349
  let order = Model.defaultOrder(options);
155
350
  if (!order)
@@ -172,10 +367,30 @@ class ConnectionBase extends EventEmitter {
172
367
  return order.map((value) => ((value.indexOf(':') < 0) ? `${modelName}:${value}` : value));
173
368
  }
174
369
 
370
+ /// This method is called (and often provided)
371
+ /// by the underlying database driver to see
372
+ /// if a `LIMIT` clause is allowed to appear in
373
+ /// a given context/operation.
374
+ ///
375
+ /// Arguments:
376
+ /// options: object
377
+ /// Driver specific options for the context.
378
+ ///
379
+ /// Return: boolean
175
380
  isLimitSupportedInContext(options) {
176
381
  return true;
177
382
  }
178
383
 
384
+ /// This method is called (and often provided)
385
+ /// by the underlying database driver to see
386
+ /// if an `ORDER BY` clause is allowed to appear in
387
+ /// a given context/operation.
388
+ ///
389
+ /// Arguments:
390
+ /// options: object
391
+ /// Driver specific options for the context.
392
+ ///
393
+ /// Return: boolean
179
394
  isOrderSupportedInContext(_options) {
180
395
  let options = _options || {};
181
396
  if (options.isSubQuery) {
@@ -213,14 +428,52 @@ class ConnectionBase extends EventEmitter {
213
428
  return value;
214
429
  }
215
430
 
431
+ /// Get the options for this connection.
432
+ ///
433
+ /// These will be the options provided to
434
+ /// the connection during creation, plus any
435
+ /// other options the connection driver itself
436
+ /// internally sets.
437
+ ///
438
+ /// Return: object
439
+ /// The options for this connection.
216
440
  getOptions() {
217
441
  return this._options;
218
442
  }
219
443
 
444
+ /// Check to see if `start` has already been called
445
+ /// on this connection. This is used to know if a
446
+ /// connection is "active" or not.
447
+ ///
448
+ /// Return: boolean
220
449
  isStarted() {
221
450
  return true;
222
451
  }
223
452
 
453
+ /// This will take something that can
454
+ /// be turned into a query and turn it
455
+ /// into a query.
456
+ ///
457
+ /// If a <see>QueryEngine</see> instance
458
+ /// is provided, it will simply be returned.
459
+ ///
460
+ /// If a Model is provided, then `Model.where`
461
+ /// will be returned, returning a <see>QueryEngine</see>
462
+ /// for the model provided.
463
+ ///
464
+ /// In the future this may also accept other possible
465
+ /// values that could be turned into a query.
466
+ ///
467
+ /// This is often internally called by methods of the connection
468
+ /// on a given argument provided by the user, which could validly
469
+ /// be either a model or a query. <see>Connection.destroyModels</see>
470
+ /// is one example of this.
471
+ ///
472
+ /// Arguments:
473
+ /// value: any
474
+ /// The value to attempt to turn into a <see>QueryEngine</see>.
475
+ ///
476
+ /// Return: <see>QueryEngine</see> | undefined
224
477
  toQueryEngine(_queryEngine) {
225
478
  let queryEngine = _queryEngine;
226
479
  if (!queryEngine)
@@ -236,6 +489,104 @@ class ConnectionBase extends EventEmitter {
236
489
  return queryEngine;
237
490
  }
238
491
 
492
+ async finalizeQuery(crudOperation, queryEngine, options) {
493
+ if (!QueryEngine.isQuery(queryEngine))
494
+ return queryEngine;
495
+
496
+ const finalizeQueryForModel = async (query, parent, contextKey, depth) => {
497
+ let operations = query.getOperationStack();
498
+
499
+ // Has query already been finalized?
500
+ let lastOperation = operations[operations.length - 1];
501
+ if (lastOperation && Object.prototype.hasOwnProperty.call(lastOperation, '_queryFinalized') && lastOperation._queryFinalized)
502
+ return query;
503
+
504
+ let newQuery = query.clone();
505
+ let alreadyVisitedModels = new Set();
506
+
507
+ for (let i = 0, il = operations.length; i < il; i++) {
508
+ let operation = operations[i];
509
+ if (!Object.prototype.hasOwnProperty.call(operation, 'modelName'))
510
+ continue;
511
+
512
+ let modelName = operation.modelName;
513
+ if (alreadyVisitedModels.has(modelName))
514
+ continue;
515
+
516
+ alreadyVisitedModels.add(modelName);
517
+
518
+ let Model = operation.Model;
519
+ if (typeof Model.finalizeQuery !== 'function')
520
+ continue;
521
+
522
+ newQuery = await Model.finalizeQuery({
523
+ type: crudOperation,
524
+ query: newQuery,
525
+ queryDepth: depth,
526
+ operationIndex: i,
527
+ connection: this,
528
+ Model,
529
+ modelName,
530
+ operation,
531
+ operations,
532
+ parent,
533
+ contextKey,
534
+ options,
535
+ });
536
+
537
+ if (parent && contextKey)
538
+ parent[contextKey] = newQuery;
539
+ }
540
+
541
+ // Mark as finalized
542
+ newQuery.getOperationContext()._queryFinalized = true;
543
+
544
+ return newQuery;
545
+ };
546
+
547
+ let promises = [];
548
+
549
+ queryEngine.walk((query, parent, contextKey, depth) => {
550
+ promises.push(finalizeQueryForModel(query, parent, contextKey, depth));
551
+ });
552
+
553
+ if (promises.length > 0)
554
+ await Promise.all(promises);
555
+
556
+ return await finalizeQueryForModel(queryEngine, null, null, 0);
557
+ }
558
+
559
+ /// Register the provided model class.
560
+ ///
561
+ /// This will register the provided model class with this
562
+ /// connection. The model must not already be bound to
563
+ /// another connection, or you must specify the `option`
564
+ /// `{ forceConnectionBinding: true }` if it is. You
565
+ /// can specify the `option` of `{ bindModels: false }`
566
+ /// if you don't wish to bind this model to this connection.
567
+ ///
568
+ /// Any `options` provided are optional, and will override
569
+ /// the same options provided to the connection itself when
570
+ /// it was created.
571
+ ///
572
+ /// Arguments:
573
+ /// Model: class <see>Model</see>
574
+ /// The model class to register with this connection. If not bound, then it
575
+ /// will simply exist in the model pool for this connection. If bound, then
576
+ /// this connection will bind itself to the model being registered.
577
+ /// options?: object
578
+ /// Options looks like `{ forceConnectionBinding: boolean; bindModels: boolean; }`.
579
+ /// This is an optional argument. Both options will default to the same options
580
+ /// provided to the connection when it was created. If you specify either of these
581
+ /// options they simply override the connection's default.
582
+ ///
583
+ /// Return: class <see>Model</see>
584
+ /// The registered model class, **which may have changed during registration**.
585
+ /// It is not uncommon for the connection driver itself to modify the model
586
+ /// class, or to return a new model class that inherits from your model class.
587
+ /// The class that is returned should be the class that you use for this connection,
588
+ /// and will be the same class returned by a call to <see>Connection.getModel</see>,
589
+ /// or <see>Connection.getModels</see>.
239
590
  registerModel(_Model, options) {
240
591
  let Model = _Model.bindConnection(this, options);
241
592
 
@@ -244,6 +595,37 @@ class ConnectionBase extends EventEmitter {
244
595
  return Model;
245
596
  }
246
597
 
598
+ /// Register multiple models at the same time.
599
+ ///
600
+ /// This will register the provided models with this
601
+ /// connection. The models provided must not already be bound to
602
+ /// another connection, or you must specify the `option`
603
+ /// `{ forceConnectionBinding: true }` if any of them are. You
604
+ /// can specify the `option` of `{ bindModels: false }`
605
+ /// if you don't wish to bind these models to this connection.
606
+ ///
607
+ /// Any `options` provided are optional, and will override
608
+ /// the same options provided to the connection itself when
609
+ /// it was created.
610
+ ///
611
+ /// Arguments:
612
+ /// Model: Array<class <see>Model</see>> | { [key: string]: class <see>Model</see> }
613
+ /// The model classes to register with this connection. If no models are bound, then
614
+ /// they will simply exist in the model pool for this connection. If bound, then
615
+ /// this connection will bind itself to every model being registered.
616
+ /// options?: object
617
+ /// Options looks like `{ forceConnectionBinding: boolean; bindModels: boolean; }`.
618
+ /// This is an optional argument. Both options will default to the same options
619
+ /// provided to the connection when it was created. If you specify either of these
620
+ /// options they simply override the connection's default.
621
+ ///
622
+ /// Return: class <see>Model</see>
623
+ /// The registered model classes, **which may have changed during registration**.
624
+ /// It is not uncommon for the connection driver itself to modify the model
625
+ /// classes, or to return a new model classes that inherit from your model classes.
626
+ /// The classes that are returned should be the classes that you use for this connection,
627
+ /// and will be the same classes returned by a call to <see>Connection.getModel</see>,
628
+ /// or <see>Connection.getModels</see>.
247
629
  registerModels(models, options) {
248
630
  if (!models)
249
631
  return;
@@ -310,6 +692,30 @@ class ConnectionBase extends EventEmitter {
310
692
  return Utils.setContextValue(key, value);
311
693
  }
312
694
 
695
+ /// This builds a "connection context" to provide to
696
+ /// the [AsyncLocalStorage](https://nodejs.org/docs/latest-v16.x/api/async_context.html) context
697
+ /// of a <see>Connection.createContext</see> call.
698
+ ///
699
+ /// By default, this will set a `connection` property on the context,
700
+ /// which will be the value of this connection instance. It also sets
701
+ /// a `connection` property on the sub-contexts for each model. This
702
+ /// is because models might have different connections, and so need
703
+ /// a context per-model to work properly.
704
+ ///
705
+ /// "How could a model have different connections?" you ask? Well, it
706
+ /// probably won't. But being as this is an [AsyncLocalStorage](https://nodejs.org/docs/latest-v16.x/api/async_context.html)
707
+ /// context, it provides the connection to **every** model inside the context.
708
+ /// Your application might be using other models that aren't part of this connection,
709
+ /// and we wouldn't want those to get the wrong connection for those models.
710
+ ///
711
+ /// Arguments:
712
+ /// connection?: <see>Connection</see> = this
713
+ /// The connection to create the context for. This can be provided, and
714
+ /// is used in place of `this` instance, for example, in the case of transactions.
715
+ /// Transactions generally are the "same" *connection*, but not the same *instance*.
716
+ ///
717
+ /// Return: Map<any, any>
718
+ /// The new context that will be used for <see>Connection.createContext</see>.
313
719
  buildConnectionContext(_connection) {
314
720
  let connection = _connection || this;
315
721
  let models = connection._models;
@@ -329,38 +735,118 @@ class ConnectionBase extends EventEmitter {
329
735
  return newContext;
330
736
  }
331
737
 
738
+ /// Create a connection context to serve all code
739
+ /// running inside it this connection.
740
+ ///
741
+ /// This uses [AsyncLocalStorage](https://nodejs.org/docs/latest-v16.x/api/async_context.html)
742
+ /// to create a context that is passed through the entire call stack of the callback. In this
743
+ /// way, a connection can be provided to every model and every operation within the call.
744
+ ///
745
+ /// Arguments:
746
+ /// callback: async Function
747
+ /// The method to provide the context to. Every call inside this call stack
748
+ /// will be provided the connection.
749
+ /// connection?: <see>Connection</see> = this
750
+ /// The connection to provide. Generally this will just be `this` connection instance,
751
+ /// however, it can be specified, and is for example inside transactions.
752
+ /// thisArg?: any = this
753
+ /// The `this` value to provide to the given `callback`.
754
+ ///
755
+ /// Return: any
756
+ /// The return value of the given `callback`.
332
757
  async createContext(callback, _connection, thisArg) {
333
758
  let connection = _connection || this;
334
759
  let context = this.buildConnectionContext(connection);
335
760
 
336
761
  return await Utils.runInContext(context, async () => {
337
- return await callback.call(thisArg, connection);
762
+ return await callback.call(thisArg || this, connection);
338
763
  });
339
764
  }
340
765
 
766
+ /// Find a specific field across all registered models.
767
+ ///
768
+ /// This method is similar to [Array.find](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find),
769
+ /// except that the arguments to this `finder` method are those provided by
770
+ /// <see name="Model.iterateFields">Model.static iterateFields</see>. If this `finder` method
771
+ /// returns a truthy value for a given field, then that is the field that will be returned.
772
+ ///
773
+ /// Note:
774
+ /// This will search for the field across all models registered to the connection.
775
+ /// To find a specific field for a known model use <see>Connection.getField</see> instead.
776
+ ///
777
+ /// Arguments:
778
+ /// finder: Function
779
+ /// A function to assist in finding the specified field. The signature for this
780
+ /// function must match that used by <see name="Model.iterateFields">Model.static iterateFields</see>.
781
+ ///
782
+ /// Return: Field | undefined
783
+ /// The field found, if any.
341
784
  findModelField(finder) {
342
785
  let modelMap = this.getModels();
343
786
  let modelNames = Object.keys(modelMap);
344
- let results = [];
345
787
 
346
788
  for (let i = 0, il = modelNames.length; i < il; i++) {
347
789
  let modelName = modelNames[i];
348
790
  let Model = modelMap[modelName];
791
+ let value = Model.iterateFields(finder, null, false, true);
349
792
 
350
- results = results.concat(Model.iterateFields(finder));
793
+ if (value)
794
+ return value;
351
795
  }
352
-
353
- return results;
354
796
  }
355
797
 
798
+ /// This is simply a convenience method that
799
+ /// calls <see>ModelUtils.parseQualifiedName</see>.
800
+ ///
801
+ /// See: ModelUtils.parseQualifiedName
802
+ ///
803
+ /// Arguments:
804
+ /// fieldName: string
805
+ /// The field name, fully qualified field name, or model name to parse.
806
+ /// See <see>ModelUtils.parseQualifiedName</see> for more information.
807
+ ///
808
+ /// Return: object
809
+ /// The result. See <see>ModelUtils.parseQualifiedName</see> for more information.
356
810
  parseQualifiedName(str) {
357
811
  return Utils.parseQualifiedName(str);
358
812
  }
359
813
 
814
+ /// Get all models known to this connection.
815
+ /// Models are returned as a raw `Object`, and
816
+ /// so can be destructured. The keys of the returned
817
+ /// object are the model's names.
818
+ ///
819
+ /// Models can be registered to a connection with the
820
+ /// <see>ConnectionBase.registerModel</see> or
821
+ /// <see>ConnectionBase.registerModels</see> methods.
822
+ ///
823
+ /// Return: object
824
+ /// An object of all models known to the connection, where
825
+ /// each key is a model name, and each property value is
826
+ /// the model's class.
360
827
  getModels() {
361
828
  return this._models;
362
829
  }
363
830
 
831
+ /// Retrieve a single model by its name.
832
+ /// The model must be registered with this
833
+ /// connection to be found.
834
+ ///
835
+ /// Models can be registered to a connection with the
836
+ /// <see>ConnectionBase.registerModel</see> or
837
+ /// <see>ConnectionBase.registerModels</see> methods.
838
+ ///
839
+ /// This method is "fully qualified aware", meaning you
840
+ /// can pass a fully qualified name, such as "User:id"
841
+ /// and it will fetch the "User" model.
842
+ ///
843
+ /// Return: class <see>Model</see>
844
+ /// The model class found by the model's name, or
845
+ /// `undefined` if no model is found.
846
+ ///
847
+ /// Arguments:
848
+ /// modelName: string
849
+ /// The model's name, or a fully qualified field name.
364
850
  getModel(modelName) {
365
851
  if (typeof modelName === 'symbol')
366
852
  return;
@@ -369,6 +855,20 @@ class ConnectionBase extends EventEmitter {
369
855
  return this._models[def.modelName];
370
856
  }
371
857
 
858
+ /// Get a field by its fully qualified name,
859
+ /// or by a fieldName + a modelName.
860
+ ///
861
+ /// Return: <see>Field</see>
862
+ /// The field found, or `undefined` if the specified field is
863
+ /// not found.
864
+ ///
865
+ /// Arguments:
866
+ /// fieldName: string
867
+ /// Fully qualified field name. If a fully qualified field name
868
+ /// isn't provided, then you *must* provide the `modelName` argument.
869
+ /// modelName?: string
870
+ /// The name of the model that the field exists on. This argument is
871
+ /// optional if the provided `fieldName` is a fully qualified name.
372
872
  getField(fieldName, modelName) {
373
873
  let def = this.parseQualifiedName(fieldName);
374
874
  if (def.modelName == null)
@@ -381,19 +881,86 @@ class ConnectionBase extends EventEmitter {
381
881
  return Model.getField(def.fieldNames[0]);
382
882
  }
383
883
 
884
+ /// Return the <see>QueryEngine</see> class used
885
+ /// for this connection, all its models, and all
886
+ /// query operations. By default this is just the
887
+ /// built-in <see>QueryEngine</see> that Mythix ORM
888
+ /// provides. You can create your own, and provide
889
+ /// it as the `QueryEngine` option to the connection
890
+ /// when you create the connection.
891
+ ///
892
+ /// Return: class <see>QueryEngine</see>
893
+ /// The QueryEngine to use for this connection, all
894
+ /// its models, and all query operations for this
895
+ /// connection.
384
896
  getQueryEngineClass() {
385
897
  let options = this.getOptions();
386
898
  return options.QueryEngine;
387
899
  }
388
900
 
901
+ /// Get the <see>QueryGenerator</see> instance
902
+ /// for this connection, if it has one.
903
+ ///
904
+ /// All connection drivers may not have a query generator.
905
+ /// The query generator is the class that takes care of
906
+ /// generating queries for the underlying database (i.e. SQL).
907
+ /// If a query generator exists on a connection, it is very
908
+ /// likely to be unique to the connection (i.e. PostgreSQL and MySQL
909
+ /// have different query generators).
910
+ ///
911
+ /// Return: <see>QueryGenerator</see> | null
912
+ /// Return the <see>QueryGenerator</see> for this connection,
913
+ /// or return `null` if none is defined for this connection.
389
914
  getQueryGenerator() {
390
915
  return this.queryGenerator;
391
916
  }
392
917
 
918
+ /// Set the <see>QueryGenerator</see> instance for this
919
+ /// connection. This is rarely used as the <see>QueryGenerator</see>
920
+ /// is often supplied as connection options when creating
921
+ /// the connection (as the `queryGenerator` option). However,
922
+ /// it can be set to a new instance at any time with this
923
+ /// method.
924
+ ///
925
+ /// Return: undefined
926
+ ///
927
+ /// Arguments:
928
+ /// queryGenerator: <see>QueryGenerator</see> | null
929
+ /// The new <see>QueryGenerator</see> instance to use
930
+ /// for generating underlying database queries. Set
931
+ /// to `null` to specify no query generator (note:
932
+ /// this might break whatever connection you are using
933
+ /// as most connections require their query generator).
393
934
  setQueryGenerator(queryGenerator) {
394
935
  this.queryGenerator = queryGenerator;
395
936
  }
396
937
 
938
+ /// The low-level DB interface for escaping a
939
+ /// value. By default this function uses the
940
+ /// [sqlstring](https://www.npmjs.com/package/sqlstring)
941
+ /// module to escape values. However, the `escape`
942
+ /// method for whatever database the connection is
943
+ /// using should be used instead of this. This is
944
+ /// a "default implementation" that is meant as a
945
+ /// fallback when a connection doesn't provide its
946
+ /// own, but each connection should provide its own
947
+ /// when it is able.
948
+ ///
949
+ /// Note:
950
+ /// This method escapes "values" that are given in
951
+ /// the underlying query language of the database.
952
+ /// To escape identifiers, use the <see>ConnectionBase._escapeID</see>
953
+ /// instead.
954
+ ///
955
+ /// Return: string
956
+ /// The value provided, escaped for the specific
957
+ /// underlying database driver.
958
+ ///
959
+ /// Arguments:
960
+ /// value: any
961
+ /// The value to escape. This could be a number, a boolean,
962
+ /// a string, or anything else that can be provided to your
963
+ /// specific database.
397
964
  _escape(value) {
398
965
  if (Nife.instanceOf(value, 'string'))
399
966
  return `'${value.replace(/'/g, '\'\'')}'`;
@@ -401,6 +968,64 @@ class ConnectionBase extends EventEmitter {
401
968
  return SqlString.escape(value);
402
969
  }
403
970
 
971
+ /// Unlike <see>ConnectionBase._escape</see> --which is
972
+ /// a low-level interface for the database-- this method
973
+ /// will escape specific values in specific ways needed
974
+ /// by Mythix ORM. Said another way, whereas <see>ConnectionBase._escape</see>
975
+ /// is a "low level database method", this is a "high level
976
+ /// Mythix ORM" method.
977
+ ///
978
+ /// If this method is provided a literal, then it will convert
979
+ /// the literal into a string, and return the resulting string.
980
+ /// This is the purpose of the `options` argument. The `options`
981
+ /// argument will be passed to the literal's `toString` method.
982
+ ///
983
+ /// For any non-literal value, it is first passed through the
984
+ /// field's `serialize` method, for example `value = field.type.serialize(value, thisConnection);`.
985
+ /// This generally won't modify the incoming value, but it might,
986
+ /// for example, with DATE, or DATETIME types, that are modified
987
+ /// to match an acceptable format for the underlying database.
988
+ ///
989
+ /// If this method is provided a boolean, then it will return
990
+ /// an upper-cased version of the boolean as a string, i.e. `true` will be returned
991
+ /// as `'TRUE'`, and `false` will be returned as `'FALSE'`.
992
+ ///
993
+ /// If this method is provided a BigInt, then it will convert the
994
+ /// bigint into a string representation of the number, i.e. 42n will
995
+ /// be returned as `'42'`.
996
+ ///
997
+ /// If this method is provided an `Array` value, then the array
998
+ /// is processed by the connection-specific `prepareArrayValuesForSQL`
999
+ /// method. By default, this method will complete the following operations
1000
+ /// on the provided array:
1001
+ ///
1002
+ /// 1. It will flatten the provided array into a 1D array
1003
+ /// 2. It will filter the array, such that it removes `undefined`, and any value that isn't a <see>LiteralBase</see>, a `null`, a `string`, a `boolean`, a `number`, or a `bigint`
1004
+ /// 3. It will further filter out duplicate values from the array, such that the processed array only contains unique values
1005
+ ///
1006
+ /// After the array is processed with the `prepareArrayValuesForSQL` method,
1007
+ /// it will then be mapped such that each value is passed through this
1008
+ /// `escape` method, and then all remaining values in the array will be
1009
+ /// joined with a `,` character between each element. The idea here is that
1010
+ /// if you are providing an array to the underlying database, it is usually
1011
+ /// for an `IN` or `NOT IN` operator, so the result will generally be used for
1012
+ /// one of these. Keep in mind however that each connection driver might escape
1013
+ /// values (including arrays) differently.
1014
+ ///
1015
+ /// All other provided values are simply handed off to <see>ConnectionBase._escape</see>.
1016
+ ///
1017
+ /// Return: string
1018
+ /// The escaped value, as a string.
1019
+ ///
1020
+ /// Arguments:
1021
+ /// field: <see>Field</see>
1022
+ /// The field this value is coming from.
1023
+ /// value: any
1024
+ /// The value to escape.
1025
+ /// options?: object
1026
+ /// The options to provide to a Literal's `toString`
1027
+ /// method. This `options` object is only ever used
1028
+ /// if the provided `value` is a Literal.
404
1029
  escape(field, _value, options) {
405
1030
  var value = _value;
406
1031
  if (LiteralBase.isLiteral(value))
@@ -425,11 +1050,49 @@ class ConnectionBase extends EventEmitter {
425
1050
  return this._escape(value);
426
1051
  }
427
1052
 
1053
+ /// Low-level database method for escaping an identifier.
1054
+ /// Each database driver should provide its own version of
1055
+ /// this method. This is the "default" method Mythix ORM
1056
+ /// provides as a "fallback" to database drivers that don't
1057
+ /// supply their own.
1058
+ ///
1059
+ /// It works by first stripping all quotes (single `'`, double `"`, and backtick `` ` ``)
1060
+ /// from the provided `value`. After this, it will split on the period (dot) character
1061
+ /// `.`, and then will map each resulting part through [sqlstring](https://www.npmjs.com/package/sqlstring)
1062
+ /// `escapeId` method, finally re-joining the parts with a period `.` character.
1063
+ ///
1064
+ /// The extra processing is to allow for already escaped identifiers to not be double-escaped.
1065
+ ///
1066
+ /// Return: string
1067
+ /// The provided identifier, escaped for the underlying database.
1068
+ ///
1069
+ /// Arguments:
1070
+ /// value: string
1071
+ /// The identifier to escape.
428
1072
  _escapeID(value) {
429
1073
  let parts = value.replace(/['"`]/g, '').split(/\.+/g);
430
1074
  return parts.map((part) => SqlString.escapeId(part).replace(/^`/, '"').replace(/`$/, '"')).join('.');
431
1075
  }
432
1076
 
1077
+ /// This method is very similar to <see>ConnectionBase._escapeID</see>,
1078
+ /// except that instead of being a "low level database method" that the
1079
+ /// database driver itself provides, this is the "Mythix ORM" implementation
1080
+ /// of escaping identifiers. The only difference from <see>ConnectionBase._escapeID</see>
1081
+ /// is that if the provided value is a Literal, it will be converted to a
1082
+ /// string and returned *without* being escaped. Literals are never modified,
1083
+ /// and are always provided to the underlying database exactly as they were defined.
1084
+ ///
1085
+ /// Return: string
1086
+ /// The escaped identifier, or if `value` is a Literal, the Literal
1087
+ /// converted to a string.
1088
+ ///
1089
+ /// Arguments:
1090
+ /// value: string
1091
+ /// The identifier to escape. If this is a Literal instead of a string,
1092
+ /// then the Literal will be converted to a string and returned.
1093
+ /// options?: object
1094
+ /// The options to pass to `Literal.toString`. This options object is only
1095
+ /// used if the provided `value` is a Literal instance.
433
1096
  escapeID(value, options) {
434
1097
  if (LiteralBase.isLiteral(value))
435
1098
  return value.toString(this.connection, options);
@@ -437,6 +1100,34 @@ class ConnectionBase extends EventEmitter {
437
1100
  return this._escapeID(value);
438
1101
  }
439
1102
 
1103
+ /// Convert the provided <see>AverageLiteral</see> to a string
1104
+ /// for the underlying database driver. The conversion
1105
+ /// will be database specific. The database driver connection
1106
+ /// is free to override this method.
1107
+ ///
1108
+ /// Note:
1109
+ /// This method is a proxy method for <see>QueryGenerator._averageLiteralToString</see>.
1110
+ /// If the connection has no instance of a `queryGenerator` available to it,
1111
+ /// then this method will simply return `undefined`.
1112
+ ///
1113
+ /// Note:
1114
+ /// Generally speaking, Literal "options" are literal (and connection) specific. They often aren't
1115
+ /// needed or used directly by the user, but instead are used by the underlying
1116
+ /// connection itself, to define context specific options. For example, the underlying
1117
+ /// connection might have an option to tell a literal that it is the "DEFAULT" value
1118
+ /// of a field, instead of just a raw value to inject in the generated query stream,
1119
+ /// and this might change the output of the literal when converted to a string.
1120
+ ///
1121
+ /// Return: string
1122
+ /// The provided <see>AverageLiteral</see> converted to
1123
+ /// a string for the underlying database driver.
1124
+ ///
1125
+ /// Arguments:
1126
+ /// literal: <see>AverageLiteral</see>
1127
+ /// The literal to convert to a string for the database.
1128
+ /// options?: object
1129
+ /// Optional options to pass to the <see>QueryGenerator._averageLiteralToString</see>
1130
+ /// method.
440
1131
  _averageLiteralToString(literal, options) {
441
1132
  if (!literal || !LiteralBase.isLiteral(literal))
442
1133
  return;
@@ -448,6 +1139,34 @@ class ConnectionBase extends EventEmitter {
448
1139
  return queryGenerator._averageLiteralToString(literal, options);
449
1140
  }
450
1141
 
1142
+ /// Convert the provided <see>CountLiteral</see> to a string
1143
+ /// for the underlying database driver. The conversion
1144
+ /// will be database specific. The database driver connection
1145
+ /// is free to override this method.
1146
+ ///
1147
+ /// Note:
1148
+ /// This method is a proxy method for <see>QueryGenerator._countLiteralToString</see>.
1149
+ /// If the connection has no instance of a `queryGenerator` available to it,
1150
+ /// then this method will simply return `undefined`.
1151
+ ///
1152
+ /// Note:
1153
+ /// Generally speaking, Literal "options" are literal (and connection) specific. They often aren't
1154
+ /// needed or used directly by the user, but instead are used by the underlying
1155
+ /// connection itself, to define context specific options. For example, the underlying
1156
+ /// connection might have an option to tell a literal that it is the "DEFAULT" value
1157
+ /// of a field, instead of just a raw value to inject in the generated query stream,
1158
+ /// and this might change the output of the literal when converted to a string.
1159
+ ///
1160
+ /// Return: string
1161
+ /// The provided <see>CountLiteral</see> converted to
1162
+ /// a string for the underlying database driver.
1163
+ ///
1164
+ /// Arguments:
1165
+ /// literal: <see>CountLiteral</see>
1166
+ /// The literal to convert to a string for the database.
1167
+ /// options?: object
1168
+ /// Optional options to pass to the <see>QueryGenerator._countLiteralToString</see>
1169
+ /// method.
451
1170
  _countLiteralToString(literal, options) {
452
1171
  if (!literal || !LiteralBase.isLiteral(literal))
453
1172
  return;
@@ -459,6 +1178,34 @@ class ConnectionBase extends EventEmitter {
459
1178
  return queryGenerator._countLiteralToString(literal, options);
460
1179
  }
461
1180
 
1181
+ /// Convert the provided <see>DistinctLiteral</see> to a string
1182
+ /// for the underlying database driver. The conversion
1183
+ /// will be database specific. The database driver connection
1184
+ /// is free to override this method.
1185
+ ///
1186
+ /// Note:
1187
+ /// This method is a proxy method for <see>QueryGenerator._distinctLiteralToString</see>.
1188
+ /// If the connection has no instance of a `queryGenerator` available to it,
1189
+ /// then this method will simply return `undefined`.
1190
+ ///
1191
+ /// Note:
1192
+ /// Generally speaking, Literal "options" are literal (and connection) specific. They often aren't
1193
+ /// needed or used directly by the user, but instead are used by the underlying
1194
+ /// connection itself, to define context specific options. For example, the underlying
1195
+ /// connection might have an option to tell a literal that it is the "DEFAULT" value
1196
+ /// of a field, instead of just a raw value to inject in the generated query stream,
1197
+ /// and this might change the output of the literal when converted to a string.
1198
+ ///
1199
+ /// Return: string
1200
+ /// The provided <see>DistinctLiteral</see> converted to
1201
+ /// a string for the underlying database driver.
1202
+ ///
1203
+ /// Arguments:
1204
+ /// literal: <see>DistinctLiteral</see>
1205
+ /// The literal to convert to a string for the database.
1206
+ /// options?: object
1207
+ /// Optional options to pass to the <see>QueryGenerator._distinctLiteralToString</see>
1208
+ /// method.
462
1209
  _distinctLiteralToString(literal, options) {
463
1210
  if (!literal || !LiteralBase.isLiteral(literal))
464
1211
  return;
@@ -470,6 +1217,34 @@ class ConnectionBase extends EventEmitter {
470
1217
  return queryGenerator._distinctLiteralToString(literal, options);
471
1218
  }
472
1219
 
1220
+ /// Convert the provided <see>FieldLiteral</see> to a string
1221
+ /// for the underlying database driver. The conversion
1222
+ /// will be database specific. The database driver connection
1223
+ /// is free to override this method.
1224
+ ///
1225
+ /// Note:
1226
+ /// This method is a proxy method for <see>QueryGenerator._fieldLiteralToString</see>.
1227
+ /// If the connection has no instance of a `queryGenerator` available to it,
1228
+ /// then this method will simply return `undefined`.
1229
+ ///
1230
+ /// Note:
1231
+ /// Generally speaking, Literal "options" are literal (and connection) specific. They often aren't
1232
+ /// needed or used directly by the user, but instead are used by the underlying
1233
+ /// connection itself, to define context specific options. For example, the underlying
1234
+ /// connection might have an option to tell a literal that it is the "DEFAULT" value
1235
+ /// of a field, instead of just a raw value to inject in the generated query stream,
1236
+ /// and this might change the output of the literal when converted to a string.
1237
+ ///
1238
+ /// Return: string
1239
+ /// The provided <see>FieldLiteral</see> converted to
1240
+ /// a string for the underlying database driver.
1241
+ ///
1242
+ /// Arguments:
1243
+ /// literal: <see>FieldLiteral</see>
1244
+ /// The literal to convert to a string for the database.
1245
+ /// options?: object
1246
+ /// Optional options to pass to the <see>QueryGenerator._fieldLiteralToString</see>
1247
+ /// method.
473
1248
  _fieldLiteralToString(literal, options) {
474
1249
  if (!literal || !LiteralBase.isLiteral(literal))
475
1250
  return;
@@ -481,6 +1256,34 @@ class ConnectionBase extends EventEmitter {
481
1256
  return queryGenerator._fieldLiteralToString(literal, options);
482
1257
  }
483
1258
 
1259
+ /// Convert the provided <see>MaxLiteral</see> to a string
1260
+ /// for the underlying database driver. The conversion
1261
+ /// will be database specific. The database driver connection
1262
+ /// is free to override this method.
1263
+ ///
1264
+ /// Note:
1265
+ /// This method is a proxy method for <see>QueryGenerator._maxLiteralToString</see>.
1266
+ /// If the connection has no instance of a `queryGenerator` available to it,
1267
+ /// then this method will simply return `undefined`.
1268
+ ///
1269
+ /// Note:
1270
+ /// Generally speaking, Literal "options" are literal (and connection) specific. They often aren't
1271
+ /// needed or used directly by the user, but instead are used by the underlying
1272
+ /// connection itself, to define context specific options. For example, the underlying
1273
+ /// connection might have an option to tell a literal that it is the "DEFAULT" value
1274
+ /// of a field, instead of just a raw value to inject in the generated query stream,
1275
+ /// and this might change the output of the literal when converted to a string.
1276
+ ///
1277
+ /// Return: string
1278
+ /// The provided <see>MaxLiteral</see> converted to
1279
+ /// a string for the underlying database driver.
1280
+ ///
1281
+ /// Arguments:
1282
+ /// literal: <see>MaxLiteral</see>
1283
+ /// The literal to convert to a string for the database.
1284
+ /// options?: object
1285
+ /// Optional options to pass to the <see>QueryGenerator._maxLiteralToString</see>
1286
+ /// method.
484
1287
  _maxLiteralToString(literal, options) {
485
1288
  if (!literal || !LiteralBase.isLiteral(literal))
486
1289
  return;
@@ -492,6 +1295,34 @@ class ConnectionBase extends EventEmitter {
492
1295
  return queryGenerator._maxLiteralToString(literal, options);
493
1296
  }
494
1297
 
1298
+ /// Convert the provided <see>MinLiteral</see> to a string
1299
+ /// for the underlying database driver. The conversion
1300
+ /// will be database specific. The database driver connection
1301
+ /// is free to override this method.
1302
+ ///
1303
+ /// Note:
1304
+ /// This method is a proxy method for <see>QueryGenerator._minLiteralToString</see>.
1305
+ /// If the connection has no instance of a `queryGenerator` available to it,
1306
+ /// then this method will simply return `undefined`.
1307
+ ///
1308
+ /// Note:
1309
+ /// Generally speaking, Literal "options" are literal (and connection) specific. They often aren't
1310
+ /// needed or used directly by the user, but instead are used by the underlying
1311
+ /// connection itself, to define context specific options. For example, the underlying
1312
+ /// connection might have an option to tell a literal that it is the "DEFAULT" value
1313
+ /// of a field, instead of just a raw value to inject in the generated query stream,
1314
+ /// and this might change the output of the literal when converted to a string.
1315
+ ///
1316
+ /// Return: string
1317
+ /// The provided <see>MinLiteral</see> converted to
1318
+ /// a string for the underlying database driver.
1319
+ ///
1320
+ /// Arguments:
1321
+ /// literal: <see>MinLiteral</see>
1322
+ /// The literal to convert to a string for the database.
1323
+ /// options?: object
1324
+ /// Optional options to pass to the <see>QueryGenerator._minLiteralToString</see>
1325
+ /// method.
495
1326
  _minLiteralToString(literal, options) {
496
1327
  if (!literal || !LiteralBase.isLiteral(literal))
497
1328
  return;
@@ -503,6 +1334,34 @@ class ConnectionBase extends EventEmitter {
503
1334
  return queryGenerator._minLiteralToString(literal, options);
504
1335
  }
505
1336
 
1337
+ /// Convert the provided <see>SumLiteral</see> to a string
1338
+ /// for the underlying database driver. The conversion
1339
+ /// will be database specific. The database driver connection
1340
+ /// is free to override this method.
1341
+ ///
1342
+ /// Note:
1343
+ /// This method is a proxy method for <see>QueryGenerator._sumLiteralToString</see>.
1344
+ /// If the connection has no instance of a `queryGenerator` available to it,
1345
+ /// then this method will simply return `undefined`.
1346
+ ///
1347
+ /// Note:
1348
+ /// Generally speaking, Literal "options" are literal (and connection) specific. They often aren't
1349
+ /// needed or used directly by the user, but instead are used by the underlying
1350
+ /// connection itself, to define context specific options. For example, the underlying
1351
+ /// connection might have an option to tell a literal that it is the "DEFAULT" value
1352
+ /// of a field, instead of just a raw value to inject in the generated query stream,
1353
+ /// and this might change the output of the literal when converted to a string.
1354
+ ///
1355
+ /// Return: string
1356
+ /// The provided <see>SumLiteral</see> converted to
1357
+ /// a string for the underlying database driver.
1358
+ ///
1359
+ /// Arguments:
1360
+ /// literal: <see>SumLiteral</see>
1361
+ /// The literal to convert to a string for the database.
1362
+ /// options?: object
1363
+ /// Optional options to pass to the <see>QueryGenerator._sumLiteralToString</see>
1364
+ /// method.
506
1365
  _sumLiteralToString(literal, options) {
507
1366
  if (!literal || !LiteralBase.isLiteral(literal))
508
1367
  return;
@@ -514,6 +1373,35 @@ class ConnectionBase extends EventEmitter {
514
1373
  return queryGenerator._sumLiteralToString(literal, options);
515
1374
  }
516
1375
 
1376
+ /// Convert the provided Literal to a string
1377
+ /// for the underlying database driver. The conversion
1378
+ /// will be database specific. The database driver connection
1379
+ /// is free to override this method.
1380
+ ///
1381
+ /// This method will convert a <see>AverageLiteral</see>, a
1382
+ /// <see>CountLiteral</see>, a <see>DistinctLiteral</see>,
1383
+ /// a <see>FieldLiteral</see>, a <see>MaxLiteral</see>, a
1384
+ /// <see>MinLiteral</see>, a <see>SumLiteral</see>, or a
1385
+ /// <see>Literal</see> to a string. If the provided literal
1386
+ /// is not one of these types, than an exception will be thrown.
1387
+ ///
1388
+ /// If you want to add custom literals to your application, then
1389
+ /// you will need to overload this method, and handle those custom
1390
+ /// literals manually (or simply stick to using <see>Literal</see>).
1391
+ ///
1392
+ /// Return: string
1393
+ /// The provided literal, converted to a string for the underlying
1394
+ /// database driver.
1395
+ ///
1396
+ /// Arguments:
1397
+ /// literal: <see>LiteralBase</see>
1398
+ /// The literal to convert.
1399
+ /// options?: object
1400
+ /// Optional options that can be passed to the literal conversion
1401
+ /// method. These are generally not provided by the user, but rather
1402
+ /// are often provided by the connection itself, for context-specific
1403
+ /// literal conversions. These options can and will changed based on
1404
+ /// the literal being converted, and the underlying connection.
517
1405
  literalToString(literal, options) {
518
1406
  if (Literals.AverageLiteral.isLiteralType(literal))
519
1407
  return this._averageLiteralToString(literal, options);
@@ -535,86 +1423,308 @@ class ConnectionBase extends EventEmitter {
535
1423
  throw new Error(`${this.constructor.name}::literalToString: Unsupported literal ${literal}.`);
536
1424
  }
537
1425
 
1426
+ /// Convert a "BIGINT" field type to a type
1427
+ /// acceptable by the underlying database driver.
1428
+ ///
1429
+ /// For most SQL connections this would be `BIGINT`,
1430
+ /// however, it may be different based on what database you are using.
1431
+ ///
1432
+ /// Return: string
1433
+ /// The field type needed by the underlying database driver.
1434
+ ///
1435
+ /// Arguments:
1436
+ /// type: <see>Type</see>
1437
+ /// The field type to convert to use in the underlying database.
538
1438
  // eslint-disable-next-line no-unused-vars
539
1439
  _bigintTypeToString(type) {
540
1440
  return 'BIGINT';
541
1441
  }
542
1442
 
1443
+ /// Convert a "BLOB" field type to a type
1444
+ /// acceptable by the underlying database driver.
1445
+ ///
1446
+ /// For most SQL connections this would be `BLOB`,
1447
+ /// however, it may be different based on what database you are using.
1448
+ ///
1449
+ /// Return: string
1450
+ /// The field type needed by the underlying database driver.
1451
+ ///
1452
+ /// Arguments:
1453
+ /// type: <see>Type</see>
1454
+ /// The field type to convert to use in the underlying database.
543
1455
  // eslint-disable-next-line no-unused-vars
544
1456
  _blobTypeToString(type) {
545
1457
  return 'BLOB';
546
1458
  }
547
1459
 
1460
+ /// Convert a "BOOLEAN" field type to a type
1461
+ /// acceptable by the underlying database driver.
1462
+ ///
1463
+ /// For most SQL connections this would be `BOOLEAN`,
1464
+ /// however, it may be different based on what database you are using.
1465
+ ///
1466
+ /// Return: string
1467
+ /// The field type needed by the underlying database driver.
1468
+ ///
1469
+ /// Arguments:
1470
+ /// type: <see>Type</see>
1471
+ /// The field type to convert to use in the underlying database.
548
1472
  // eslint-disable-next-line no-unused-vars
549
1473
  _booleanTypeToString(type) {
550
1474
  return 'BOOLEAN';
551
1475
  }
552
1476
 
1477
+ /// Convert a "CHAR" field type to a type
1478
+ /// acceptable by the underlying database driver.
1479
+ ///
1480
+ /// For most SQL connections this would be `CHAR`,
1481
+ /// however, it may be different based on what database you are using.
1482
+ ///
1483
+ /// Return: string
1484
+ /// The field type needed by the underlying database driver.
1485
+ ///
1486
+ /// Arguments:
1487
+ /// type: <see>Type</see>
1488
+ /// The field type to convert to use in the underlying database.
553
1489
  // eslint-disable-next-line no-unused-vars
554
1490
  _charTypeToString(type) {
555
1491
  return 'CHAR';
556
1492
  }
557
1493
 
1494
+ /// Convert a "DATE" field type to a type
1495
+ /// acceptable by the underlying database driver.
1496
+ ///
1497
+ /// For most SQL connections this would be `BIGINT`,
1498
+ /// however, it may be different based on what database you are using.
1499
+ ///
1500
+ /// Note:
1501
+ /// Mythix ORM always stores DATE and DATETIME types as a timestamp
1502
+ /// (in milliseconds from the UNIX Epoch) whenever it is able to.
1503
+ /// This is why the default type for most connection drivers is
1504
+ /// `BIGINT`. Another common type for this conversion is `TIMESTAMP`.
1505
+ ///
1506
+ /// Return: string
1507
+ /// The field type needed by the underlying database driver.
1508
+ ///
1509
+ /// Arguments:
1510
+ /// type: <see>Type</see>
1511
+ /// The field type to convert to use in the underlying database.
558
1512
  // eslint-disable-next-line no-unused-vars
559
1513
  _dateTypeToString(type) {
560
1514
  return 'TIMESTAMP';
561
1515
  }
562
1516
 
1517
+ /// Convert a "DATETIME" field type to a type
1518
+ /// acceptable by the underlying database driver.
1519
+ ///
1520
+ /// For most SQL connections this would be `BIGINT`,
1521
+ /// however, it may be different based on what database you are using.
1522
+ ///
1523
+ /// Note:
1524
+ /// Mythix ORM always stores DATE and DATETIME types as a timestamp
1525
+ /// (in milliseconds from the UNIX Epoch) whenever it is able to.
1526
+ /// This is why the default type for most connection drivers is
1527
+ /// `BIGINT`. Another common type for this conversion is `TIMESTAMP`.
1528
+ ///
1529
+ /// Return: string
1530
+ /// The field type needed by the underlying database driver.
1531
+ ///
1532
+ /// Arguments:
1533
+ /// type: <see>Type</see>
1534
+ /// The field type to convert to use in the underlying database.
563
1535
  // eslint-disable-next-line no-unused-vars
564
1536
  _datetimeTypeToString(type) {
565
1537
  return 'TIMESTAMP';
566
1538
  }
567
1539
 
1540
+ /// Convert a "NUMERIC" field type to a type
1541
+ /// acceptable by the underlying database driver.
1542
+ ///
1543
+ /// For most SQL connections this would be `NUMERIC`, `DECIMAL`,
1544
+ /// or `NUMBER`, however, it may be different based on what database
1545
+ /// you are using.
1546
+ ///
1547
+ /// Return: string
1548
+ /// The field type needed by the underlying database driver.
1549
+ ///
1550
+ /// Arguments:
1551
+ /// type: <see>Type</see>
1552
+ /// The field type to convert to use in the underlying database.
568
1553
  // eslint-disable-next-line no-unused-vars
569
1554
  _numericTypeToString(type) {
570
1555
  return `NUMERIC(${type.precision}, ${type.scale})`;
571
1556
  }
572
1557
 
1558
+ /// Convert a "REAL" field type to a type
1559
+ /// acceptable by the underlying database driver.
1560
+ ///
1561
+ /// For most SQL connections this would be `FLOAT`,
1562
+ /// however, it may be different based on what database you are using.
1563
+ ///
1564
+ /// Return: string
1565
+ /// The field type needed by the underlying database driver.
1566
+ ///
1567
+ /// Arguments:
1568
+ /// type: <see>Type</see>
1569
+ /// The field type to convert to use in the underlying database.
573
1570
  // eslint-disable-next-line no-unused-vars
574
1571
  _realTypeToString(type) {
575
1572
  return 'FLOAT';
576
1573
  }
577
1574
 
1575
+ /// Convert a "INTEGER" field type to a type
1576
+ /// acceptable by the underlying database driver.
1577
+ ///
1578
+ /// For most SQL connections this would be `INTEGER`,
1579
+ /// however, it may be different based on what database you are using.
1580
+ ///
1581
+ /// Return: string
1582
+ /// The field type needed by the underlying database driver.
1583
+ ///
1584
+ /// Arguments:
1585
+ /// type: <see>Type</see>
1586
+ /// The field type to convert to use in the underlying database.
578
1587
  // eslint-disable-next-line no-unused-vars
579
1588
  _integerTypeToString(type) {
580
1589
  return 'INTEGER';
581
1590
  }
582
1591
 
1592
+ /// Convert a "STRING" field type to a type
1593
+ /// acceptable by the underlying database driver.
1594
+ ///
1595
+ /// For most SQL connections this would be `VARCHAR`,
1596
+ /// however, it may be different based on what database you are using.
1597
+ ///
1598
+ /// Return: string
1599
+ /// The field type needed by the underlying database driver.
1600
+ ///
1601
+ /// Arguments:
1602
+ /// type: <see>Type</see>
1603
+ /// The field type to convert to use in the underlying database.
583
1604
  // eslint-disable-next-line no-unused-vars
584
1605
  _stringTypeToString(type) {
585
1606
  return `VARCHAR(${type.length})`;
586
1607
  }
587
1608
 
1609
+ /// Convert a "TEXT" field type to a type
1610
+ /// acceptable by the underlying database driver.
1611
+ ///
1612
+ /// For most SQL connections this would be `TEXT`,
1613
+ /// however, it may be different based on what database you are using.
1614
+ ///
1615
+ /// Return: string
1616
+ /// The field type needed by the underlying database driver.
1617
+ ///
1618
+ /// Arguments:
1619
+ /// type: <see>Type</see>
1620
+ /// The field type to convert to use in the underlying database.
588
1621
  // eslint-disable-next-line no-unused-vars
589
1622
  _textTypeToString(type) {
590
1623
  return 'TEXT';
591
1624
  }
592
1625
 
1626
+ /// Convert a "UUIDV1" field type to a type
1627
+ /// acceptable by the underlying database driver.
1628
+ ///
1629
+ /// For most SQL connections this would be `VARCHAR`,
1630
+ /// however, it may be different based on what database you are using.
1631
+ ///
1632
+ /// Return: string
1633
+ /// The field type needed by the underlying database driver.
1634
+ ///
1635
+ /// Arguments:
1636
+ /// type: <see>Type</see>
1637
+ /// The field type to convert to use in the underlying database.
593
1638
  // eslint-disable-next-line no-unused-vars
594
1639
  _uuidV1TypeToString(type) {
595
1640
  return `VARCHAR(${type.getTotalLength()})`;
596
1641
  }
597
1642
 
1643
+ /// Convert a "UUIDV3" field type to a type
1644
+ /// acceptable by the underlying database driver.
1645
+ ///
1646
+ /// For most SQL connections this would be `VARCHAR`,
1647
+ /// however, it may be different based on what database you are using.
1648
+ ///
1649
+ /// Return: string
1650
+ /// The field type needed by the underlying database driver.
1651
+ ///
1652
+ /// Arguments:
1653
+ /// type: <see>Type</see>
1654
+ /// The field type to convert to use in the underlying database.
598
1655
  // eslint-disable-next-line no-unused-vars
599
1656
  _uuidV3TypeToString(type) {
600
1657
  return `VARCHAR(${type.getTotalLength()})`;
601
1658
  }
602
1659
 
1660
+ /// Convert a "UUIDV4" field type to a type
1661
+ /// acceptable by the underlying database driver.
1662
+ ///
1663
+ /// For most SQL connections this would be `VARCHAR`,
1664
+ /// however, it may be different based on what database you are using.
1665
+ ///
1666
+ /// Return: string
1667
+ /// The field type needed by the underlying database driver.
1668
+ ///
1669
+ /// Arguments:
1670
+ /// type: <see>Type</see>
1671
+ /// The field type to convert to use in the underlying database.
603
1672
  // eslint-disable-next-line no-unused-vars
604
1673
  _uuidV4TypeToString(type) {
605
1674
  return `VARCHAR(${type.getTotalLength()})`;
606
1675
  }
607
1676
 
1677
+ /// Convert a "UUIDV5" field type to a type
1678
+ /// acceptable by the underlying database driver.
1679
+ ///
1680
+ /// For most SQL connections this would be `VARCHAR`,
1681
+ /// however, it may be different based on what database you are using.
1682
+ ///
1683
+ /// Return: string
1684
+ /// The field type needed by the underlying database driver.
1685
+ ///
1686
+ /// Arguments:
1687
+ /// type: <see>Type</see>
1688
+ /// The field type to convert to use in the underlying database.
608
1689
  // eslint-disable-next-line no-unused-vars
609
1690
  _uuidV5TypeToString(type) {
610
1691
  return `VARCHAR(${type.getTotalLength()})`;
611
1692
  }
612
1693
 
1694
+ /// Convert a "XID" field type to a type
1695
+ /// acceptable by the underlying database driver.
1696
+ ///
1697
+ /// For most SQL connections this would be `VARCHAR`,
1698
+ /// however, it may be different based on what database you are using.
1699
+ ///
1700
+ /// Return: string
1701
+ /// The field type needed by the underlying database driver.
1702
+ ///
1703
+ /// Arguments:
1704
+ /// type: <see>Type</see>
1705
+ /// The field type to convert to use in the underlying database.
613
1706
  // eslint-disable-next-line no-unused-vars
614
1707
  _xidTypeToString(type) {
615
1708
  return `VARCHAR(${type.getTotalLength()})`;
616
1709
  }
617
1710
 
1711
+ /// Convert any field type to the type needed for the underlying
1712
+ /// database driver. Only built-in Mythix ORM fields are supported.
1713
+ /// If a custom field type that is not supported is provided then
1714
+ /// an exception will be thrown.
1715
+ ///
1716
+ /// If you need to support a custom field type, simply subclass the
1717
+ /// connection you are using, and overload this method to handle
1718
+ /// your custom field types.
1719
+ ///
1720
+ /// Return: string
1721
+ /// The field type needed by the underlying database driver.
1722
+ ///
1723
+ /// Arguments:
1724
+ /// type: <see>Type</see>
1725
+ /// The field type to convert to use in the underlying database.
1726
+ /// options?: object
1727
+ /// Optional options to pass to the underlying conversion method.
618
1728
  typeToString(type, options) {
619
1729
  if (Types.BigIntType.isSameType(type))
620
1730
  return this._bigintTypeToString(type, options);
@@ -652,6 +1762,37 @@ class ConnectionBase extends EventEmitter {
652
1762
  throw new Error(`${this.constructor.name}::typeToString: Unsupported type ${type}.`);
653
1763
  }
654
1764
 
1765
+ /// Convert a given "time" type object to
1766
+ /// the value needed by the underlying database.
1767
+ ///
1768
+ /// By default this method will take a
1769
+ /// Luxon `DateTime` instance, or a `Date` instance,
1770
+ /// and convert it to the value needed by the
1771
+ /// underlying field in the database. This is
1772
+ /// generally a `BIGINT`, or `TIMESTAMP` value,
1773
+ /// as Mythix ORM will always try to store dates
1774
+ /// and times as millisecond timestamps
1775
+ /// (number of milliseconds since the UNIX Epoch).
1776
+ ///
1777
+ /// If a `number`, `bigint`, or a `string` type is
1778
+ /// provided, then Mythix ORM will use Luxon to try
1779
+ /// and parse the provided value. If the value is parsed
1780
+ /// correctly, it will then be converted to the proper
1781
+ /// value as needed by the underlying field in the database.
1782
+ ///
1783
+ /// Return: any
1784
+ /// Return the value needed by the underlying database field.
1785
+ /// Generally this will be a `bigint` type that is returned,
1786
+ /// but may be something different depending on the field and
1787
+ /// the database itself.
1788
+ ///
1789
+ /// Arguments:
1790
+ /// value: any
1791
+ /// The incoming date/time type to convert to the proper
1792
+ /// database value for the underlying field.
1793
+ /// type: <see>Type</see>
1794
+ /// The field type that this conversion is for. This will
1795
+ /// generally be a `DATE` or `DATETIME` type.
655
1796
  convertDateToDBTime(value, type) {
656
1797
  if (Nife.instanceOf(value, 'number'))
657
1798
  return value;
@@ -667,6 +1808,35 @@ class ConnectionBase extends EventEmitter {
667
1808
  return value;
668
1809
  }
669
1810
 
1811
+ /// This method will ensure all provided "models"
1812
+ /// are instances of the provided model class.
1813
+ ///
1814
+ /// This method is used by the `insert`, `upsert`, and `update`
1815
+ /// methods of the connection to ensure every model
1816
+ /// provided by the user is an actual model instance.
1817
+ ///
1818
+ /// For example, it is perfectly valid to create a model
1819
+ /// like `await Model.create({ ...attributes })`. As you
1820
+ /// can see, the provided "model" is actually just a raw
1821
+ /// object. Nearly any call in Mythix ORM will accept a
1822
+ /// raw object in-place of a model instance, and this
1823
+ /// method is used to ensure all provided "models" are
1824
+ /// actually model instances.
1825
+ ///
1826
+ /// Return: Array<Model>
1827
+ /// Return all provided models, converted to model instances.
1828
+ /// Any provided model that is already a model instance will
1829
+ /// not be modified, and instead will simply be returned.
1830
+ ///
1831
+ /// Arguments:
1832
+ /// Model: class <see>Model</see>
1833
+ /// The model class to use for instantiating models.
1834
+ /// models: object | <see>Model</see> | Array<Model | object>
1835
+ /// The models to convert to model instances (if needed).
1836
+ /// options: object
1837
+ /// Options for the operation. This options object is used
1838
+ /// to pass the `startIndex` and `endIndex` (or `batchSize`)
1839
+ /// as provided by the user when calling `insert`, `upsert`, or `update`.
670
1840
  ensureAllModelsAreInstances(Model, _models, options) {
671
1841
  if (!_models)
672
1842
  return [];
@@ -696,6 +1866,38 @@ class ConnectionBase extends EventEmitter {
696
1866
  return instantiatedModels;
697
1867
  }
698
1868
 
1869
+ /// This method will prepare all provided models for
1870
+ /// an `insert`, `upsert`, or `update` operation.
1871
+ ///
1872
+ /// What it does is ensure every provided model is a model
1873
+ /// instance, checks if the model is persisted, and if each
1874
+ /// model has dirty fields.
1875
+ ///
1876
+ /// Models that are persisted and not dirty will be filtered out,
1877
+ /// so that proceeding operations will "ignore" them entirely.
1878
+ ///
1879
+ /// All dirty fields across all models are combined into a unified
1880
+ /// list of dirty fields. This unified list is then used for the list
1881
+ /// of columns in an `insert`, `upsert`, or `updateAll` operation.
1882
+ ///
1883
+ /// Return: PreparedModels
1884
+ /// Return an object with the shape `{ models: Array<Model>, dirtyFields: Array<Field>, dirtyModels: Array<Model> }`
1885
+ /// which is known to Mythix ORM as "prepared models". Mythix ORM can understand
1886
+ /// "prepared models" verses simple "models" in most contexts. PreparedModels are often
1887
+ /// passed around after first receiving models from the user, so as not to "prepare" them
1888
+ /// more than once.
1889
+ ///
1890
+ /// Arguments:
1891
+ /// Model: class <see>Model</see>
1892
+ /// The model class to use for preparing the provided models. All models
1893
+ /// provided must be of this model type.
1894
+ /// models: object | <see>Model</see> | Array<Model | object>
1895
+ /// The models to prepare. Any model that is a "raw object" will
1896
+ /// be instantiated into the Model class provided.
1897
+ /// options: object
1898
+ /// Options for the operation. These include `startIndex`, `endIndex`
1899
+ /// (or `batchSize`) for batch operations, and `skipPersisted`, `isUpdateOperation`,
1900
+ /// or `isInsertOperation` for other context-specific operations.
699
1901
  prepareAllModelsForOperation(Model, _models, _options) {
700
1902
  if (!_models)
701
1903
  return {};
@@ -775,6 +1977,32 @@ class ConnectionBase extends EventEmitter {
775
1977
  return finalResult;
776
1978
  }
777
1979
 
1980
+ /// Recursively walk all provided models and split models into a map
1981
+ /// of model name and model instances.
1982
+ ///
1983
+ /// This method is used to split apart provided models by the user.
1984
+ /// For example, it is fully valid to provide "sub models" during most persisting
1985
+ /// operations, such as `new User({ primaryRole: new Role({ name: 'admin' }) })`.
1986
+ ///
1987
+ /// This method would find the sub model "Role" in the above example, and split
1988
+ /// it out to be processed separately in the persisting operation.
1989
+ ///
1990
+ /// Return: Map<string, Set<Model>>
1991
+ /// Return a map of all models found, where the model name is the key for the
1992
+ /// `Map`, and each model instance is added to the `Set` for that key.
1993
+ /// Using the above example, the return value would be: `new Map({ User: new Set([ user ]), Role: new Set([ role ]) })`.
1994
+ ///
1995
+ /// Arguments:
1996
+ /// Model: class <see>Model</see>
1997
+ /// The model class of the primary model being scanned. The primary model could
1998
+ /// also be called the "root model". In our example above the primary model is `User`.
1999
+ /// primaryModel: <see>Model</see>
2000
+ /// The model instance to scan for "sub models". In the example above this would be
2001
+ /// `User` (though `Role` itself will also be passed through this method to check if
2002
+ /// it also has any sub models).
2003
+ /// _relationMap?: Map<string, Set<Model>>
2004
+ /// This argument is provided internally while recursing, and should **not** be provided
2005
+ /// unless you know exactly what you are doing.
778
2006
  splitModelAndSubModels(Model, primaryModel, _relationMap) {
779
2007
  const addModelInstance = (modelName, self) => {
780
2008
  let relatedModels = relationMap.get(modelName);
@@ -832,6 +2060,56 @@ class ConnectionBase extends EventEmitter {
832
2060
  return relationMap;
833
2061
  }
834
2062
 
2063
+ /// Recursively prepare all models for a persisting operation
2064
+ /// while also splitting each model out into its own separate
2065
+ /// list, and finally sorting the models based on insertion order.
2066
+ ///
2067
+ /// This method will use <see>ConnectionBase.splitModelAndSubModels</see>
2068
+ /// to split provided models out into their own separate space. See this
2069
+ /// method for a better explanation of what this means.
2070
+ ///
2071
+ /// It will also assign any foreign key values across models that it is able to.
2072
+ /// For example, in the example provided in the documentation for <see>ConnectionBase.splitModelAndSubModels</see>,
2073
+ /// we had `new User({ primaryRole: new Role({ name: 'admin' }) })`, where the `Role`
2074
+ /// model is being persisted (as a child of `User`) at the same time the `User` is being
2075
+ /// persisted. If `Role` has a generated id, such as one of the `UUID` or `XID` types,
2076
+ /// then in our example here the `primaryRoleID` of the `User` could be set before `User`
2077
+ /// is persisted, saving an extra query to the database to update this foreign key after
2078
+ /// `Role` is persisted. In short, this method will also assign any foreign key values
2079
+ /// that it already has before persisting any models.
2080
+ ///
2081
+ /// Finally, after it has "prepared" the models, and split all models into their own
2082
+ /// space, it will sort the resulting `Map` such that the models are in the correct
2083
+ /// order for insertion. In the example we have been using, if `User` has a `primaryRoleID`
2084
+ /// that is a foreign key, then it might require a non-null value before the `User`
2085
+ /// model can be persisted. Because of this, it is important that the `Role` model
2086
+ /// is stored first, so that we have the value we need for this `primaryRoleID` foreign key.
2087
+ /// This is why the models are sorted in what is known as "creation order", or "insertion order".
2088
+ /// This order is defined by the foreign keys themselves. Mythix ORM will walk all foreign keys
2089
+ /// involved in the operation, and decide based on these what the "creation order" should be.
2090
+ /// If you have two models that both have foreign keys pointing to each other, then the sort
2091
+ /// order is undefined. If this is the case, the work-around is to simply manually persist
2092
+ /// your models in the correct order (i.e. save `Role` first, and then supply the foreign key
2093
+ /// to `User` yourself from the result).
2094
+ ///
2095
+ /// Return: Map<string, Set<Model>>
2096
+ /// The models processed, and put into their own named `Set`. The keys for
2097
+ /// the `Map` are the names of the models themselves.
2098
+ ///
2099
+ /// Arguments:
2100
+ /// Model: class <see>Model</see>
2101
+ /// The model class of the primary model being persisted. The primary model could
2102
+ /// also be called the "root model". In our example above the primary model is `User`.
2103
+ /// models: object | <see>Model</see> | Array<Model | object>
2104
+ /// The models to prepare. Any model that is a "raw object" will
2105
+ /// be instantiated into the Model class provided. Any "sub models" will be
2106
+ /// split into their own space in the resulting `Map`.
2107
+ /// options?: object
2108
+ /// Optional options to supply to this operation. This options object
2109
+ /// isn't used by Mythix ORM, but is provided in case any driver specific
2110
+ /// connection needs them when overloading this model. These options come
2111
+ /// from the options provided to a database operation, such as the options
2112
+ /// provided to an `insert`, `upsert`, or `update` call.
835
2113
  // eslint-disable-next-line no-unused-vars
836
2114
  prepareAllModelsAndSubModelsForOperation(Model, models, _options) {
837
2115
  let primaryModelRelationMap = new Map();
@@ -908,6 +2186,60 @@ class ConnectionBase extends EventEmitter {
908
2186
  return sortedGroupedModelMap;
909
2187
  }
910
2188
 
2189
+ /// This is a low-level "helper" method that is used by all
2190
+ /// "bulk operations", such as `insert`, `upsert`, `update`,
2191
+ /// or `destroyModels`. It prepares all provided models for operation,
2192
+ /// splits out sub-models provided, skips already persisted models,
2193
+ /// and also calls the model hooks `onBeforeSave`, `onAfterSave`, etc...
2194
+ /// This method will also always operate in batches, as defined by the
2195
+ /// `batchSize` option (default is `500` if not provided).
2196
+ ///
2197
+ /// Lastly, when it is all done running the "batch operation" on all models,
2198
+ /// it will update any foreign keys on each model, and if any model has again
2199
+ /// been marked dirty from this update it will finally persist those updated
2200
+ /// models.
2201
+ ///
2202
+ /// When it is fully complete, this method will return all "primary" models supplied
2203
+ /// to the method (i.e. if saving `User` models, then all supplied users will be returned
2204
+ /// and if any sub models were involved those won't be returned). The "primary model" is
2205
+ /// the model whose model class was specified as the `Model` argument on invocation of this
2206
+ /// method, and should match the type of the input `models`.
2207
+ ///
2208
+ /// Return: Array<Model>
2209
+ /// Return all input models, converted to model instances, and updated. How they
2210
+ /// are updated depends on the operation being performed.
2211
+ ///
2212
+ /// Arguments:
2213
+ /// Model: class <see>Model</see>
2214
+ /// The model class for the input models being provided.
2215
+ /// models: Array<Model>
2216
+ /// The models to bulk operate on. All of them must be instances of the class
2217
+ /// provided as the `Model` argument.
2218
+ /// options?: object
2219
+ /// Options to supply to the method. Refer to the following table for a list of
2220
+ /// possible options.
2221
+ /// | Option | Type | Default Value | Description |
2222
+ /// | ------ | ---- | ------------- | ----------- |
2223
+ /// | `batchSize` | `number` | `500` | The number of models for each batch |
2224
+ /// | `isInsertOperation` | `boolean` | `false` | `true` if this is an insert operation. If this is an insert operation, then dirty models will be collected post-insert, and re-saved if any are re-marked dirty after foreign key updates. |
2225
+ /// | `isDeleteOperation` | `boolean` | `false` | `true` if this is a delete operation. If this is a delete operation, then model preparation and splitting is skipped, and the models are processed directly. |
2226
+ /// beforeCallback?: (Model: typeof <see>Model</see>, batchModelInstances: Array<Model>, options: object, queryGenerator: <see>QueryGenerator</see>) => Promise<void>
2227
+ /// Callback that is called for each batch of models, before the current operation (i.e. insert) operates on them.
2228
+ /// It is generally in this callback that `onBefore*` model hooks are called.
2229
+ /// callback: (Model: typeof <see>Model</see>, preparedModels: <see name="PreparedModels">ConnectionBase.prepareAllModelsForOperation</see>, options: object, queryGenerator: <see>QueryGenerator</see>) => Promise<void>
2230
+ /// Callback to call to process each batch of models. The models are in "prepared model" format, meaning they
2231
+ /// are supplied as an object with `models`, `dirtyFields`, and `dirtyModels` properties.
2232
+ /// afterCallback?: (Model: typeof <see>Model</see>, models: Array<Model>, options: object, queryGenerator: <see>QueryGenerator</see>) => Promise<void>
2233
+ /// Callback that is called for each entire model set (not in batches). For example, if you bulk insert
2234
+ /// 1000 users, and your `batchSize` is `100`, then this will be called once, with all
2235
+ /// 1000 processed users, not 10 times with 100 users each. It is generally in this callback
2236
+ /// that `onAfter*` model hooks are called.
2237
+ /// afterOperationCallback?: (Model: typeof <see>Model</see>, dirtyModels: Set<Model>, options: object, queryGenerator: <see>QueryGenerator</see>) => Promise<void>
2238
+ /// Callback that is called after the entire operation completes successfully. This will only be called
2239
+ /// if `dirtyModels` is not empty. This method generally will re-save any models that got dirty during
2240
+ /// the operation... for example, when foreign keys have been updated on the models being processed.
2241
+ /// Models won't be added to the `dirtyModels` set if they are marked dirty in `onAfter*` model hooks,
2242
+ /// but only if the foreign key update process marked the model as dirty.
911
2243
  async bulkModelOperation(Model, _models, _options, beforeCallback, callback, afterCallback, afterOperationCallback) {
912
2244
  let models = _models;
913
2245
  if (!models)
@@ -1057,6 +2389,26 @@ class ConnectionBase extends EventEmitter {
1057
2389
  return (inputIsArray || !primaryResult) ? primaryResult : primaryResult[0];
1058
2390
  }
1059
2391
 
2392
+ /// Mark all models provided as "persisted".
2393
+ ///
2394
+ /// Every Mythix ORM model instance has a non-enumerable property `_persisted`.
2395
+ /// If this is `true`, then Mythix ORM will treat the model as persisted.
2396
+ ///
2397
+ /// This method iterates all provided models, and marks each as persisted
2398
+ /// by setting this `_persisted` property to `true`.
2399
+ /// For each model iterated, this method checks if the instance property
2400
+ /// `_mythixModelInstance` is `true`. If this is not the case, then that
2401
+ /// instance (whatever it is) will be silently skipped.
2402
+ ///
2403
+ /// Return: undefined
2404
+ /// This method modifies `models` directly, and returns nothing.
2405
+ ///
2406
+ /// Arguments:
2407
+ /// models: Array<Model> | <see name="PreparedModels">ConnectionBase.prepareAllModelsForOperation</see>
2408
+ /// Models to mark as persisted.
2409
+ /// value:
2410
+ /// If `true`, models will be marked as persisted. If `false`, models will be marked
2411
+ /// as not-persisted.
1060
2412
  setPersisted(_models, value) {
1061
2413
  let models = _models;
1062
2414
  if (models._mythixPreparedModels)
@@ -1074,14 +2426,83 @@ class ConnectionBase extends EventEmitter {
1074
2426
  }
1075
2427
  }
1076
2428
 
2429
+ /// Start this connection.
2430
+ ///
2431
+ /// The default implementation will throw an exception.
2432
+ ///
2433
+ /// Every connection is expected to overload this
2434
+ /// and provide connection specific startup code
2435
+ /// (such as connecting to the database).
2436
+ ///
2437
+ /// Return: Promise<void>
1077
2438
  async start() {
1078
2439
  throw new Error(`${this.constructor.name}::start: Child class is required to implement "start".`);
1079
2440
  }
1080
2441
 
2442
+ /// Stop (shutdown) this connection.
2443
+ ///
2444
+ /// The default implementation will throw an exception.
2445
+ ///
2446
+ /// Every connection is expected to overload this
2447
+ /// and provide connection specific shutdown code
2448
+ /// (such as disconnecting from the database).
2449
+ ///
2450
+ /// Return: Promise<void>
1081
2451
  async stop() {
1082
2452
  this.removeAllListeners();
1083
2453
  }
1084
2454
 
2455
+ /// Run model hooks by name.
2456
+ ///
2457
+ /// This method will run the hooks for a model instance.
2458
+ /// `operationHookName` will be one of `onBeforeCreate`, `onBeforeUpdate`,
2459
+ /// `onAfterCreate`, or `onAfterUpdate`.
2460
+ ///
2461
+ /// `operationHookName` will be one of `onBeforeSave`, or `onAfterSave`.
2462
+ /// The idea is that any of the `onBefore*` operations will always be
2463
+ /// followed by an `onBeforeSave`, whereas the `onAfter*` operations
2464
+ /// are all followed by an `onAfterSave`.
2465
+ ///
2466
+ /// Note:
2467
+ /// Hooks for models are run serially for a given model, but all model
2468
+ /// hooks are run in parallel across all models.
2469
+ ///
2470
+ /// Note:
2471
+ /// The `onValidate` hook is called from the `onBeforeSave` hook itself.
2472
+ /// This is so that the user can decide if model validations should be
2473
+ /// ran or not (simply by calling `this.onValidate` themselves... or not).
2474
+ /// **If you overload the `onBeforeSave` method, make certain you call
2475
+ /// `super.onBeforeSave.apply(this, arguments)` (or `this.onValidate.apply(this, arguments)`
2476
+ /// directly), because if you don't
2477
+ /// your model validations will be skipped.**
2478
+ ///
2479
+ /// Return: Promise<Array<any>>
2480
+ /// The result of all model hooks. Mythix ORM ignores the return value
2481
+ /// from model hooks, but it collects them and returns them for the user.
2482
+ ///
2483
+ /// Arguments:
2484
+ /// Model: class <see>Model</see>
2485
+ /// The model class we are running hooks for. All `models` provided should
2486
+ /// be instances of this class.
2487
+ /// models: Array<<see>Model</see>>
2488
+ /// All model instances to run hooks on. These should all be instances of the
2489
+ /// provided `Model` class.
2490
+ /// operationHookName: `'onBeforeCreate'` | `'onBeforeUpdate'` | `'onAfterCreate'` | `'onAfterUpdate'`
2491
+ /// The hook to call (if not skipped by the `skipHooks` option).
2492
+ /// saveHookName: `'onBeforeSave'` | `'onAfterSave'`
2493
+ /// The name of the save hook to call after the operation hook
2494
+ /// has completed (if not skipped by the `skipHooks` option).
2495
+ /// options: object
2496
+ /// Options for the operation being completed. For example, if this is an
2497
+ /// `insert` operation, then these "options" will be the options for the
2498
+ /// `insert` operation. One other useful option that can be supplied here
2499
+ /// is the `skipHooks: boolean | { [key: 'onBeforeCreate' | 'onBeforeUpdate' | 'onAfterCreate' | 'onAfterUpdate' | 'onBeforeSave' | 'onAfterSave' ]: boolean; }` option.
2500
+ /// If this is `true`, then all hooks will be bypassed (not called).
2501
+ /// If this is an object, then each key should be a hook name, and if it has
2502
+ /// a `true` value, then that specific hook will be bypassed (not called).
2503
+ ///
2504
+ /// This allows the caller of an operation such as `insert`, or `update` to
2505
+ /// request that specific hooks not be called.
1085
2506
  async runSaveHooks(Model, models, operationHookName, saveHookName, _options) {
1086
2507
  const throwError = (error) => {
1087
2508
  throw error;
@@ -1125,10 +2546,68 @@ class ConnectionBase extends EventEmitter {
1125
2546
  await Promise.all(promises);
1126
2547
  }
1127
2548
 
2549
+ /// Drop a table/bucket from the database.
2550
+ ///
2551
+ /// This uses the provided `Model` class to
2552
+ /// find the table/bucket name to drop, and then
2553
+ /// will drop it from the underlying database.
2554
+ ///
2555
+ /// The `options` argument is database specific,
2556
+ /// but might contain options such as `ifExists`,
2557
+ /// or `cascade`, for example.
2558
+ ///
2559
+ /// Return: any
2560
+ /// A database specific return value for the drop table
2561
+ /// operation.
2562
+ ///
2563
+ /// Arguments:
2564
+ /// Model: class <see>Model</see>
2565
+ /// The model to drop from the database. The method <see name="Model.getTableName">Model.static getTableName</see>
2566
+ /// is called on the model class to figure out what table/bucket to
2567
+ /// drop from the database.
2568
+ /// options?: object
2569
+ /// Database specific operations for dropping the table/bucket. Please
2570
+ /// refer to the documentation of the driver you are using for further
2571
+ /// information.
2572
+ // eslint-disable-next-line no-unused-vars
1128
2573
  async dropTable(Model, options) {
1129
2574
  throw new Error(`${this.constructor.name}::dropTable: This operation is not supported for this connection type.`);
1130
2575
  }
1131
2576
 
2577
+ /// Drop all specified tables/buckets from the database.
2578
+ ///
2579
+ /// This uses the provided `Models` classes to
2580
+ /// find the table/bucket names to drop, and then
2581
+ /// will drop all of them from the underlying database.
2582
+ ///
2583
+ /// The `options` argument is database specific,
2584
+ /// but might contain options such as `ifExists`,
2585
+ /// or `cascade`, for example.
2586
+ ///
2587
+ /// The model classes provided are first sorted in
2588
+ /// "creation order" using the <see>Utils.sortModelNamesByCreationOrder</see>
2589
+ /// method, and then the tables/buckets are dropped in the
2590
+ /// reverse order. This is to ensure that any foreign key
2591
+ /// constraints in play will play nicely with the operation
2592
+ /// and not throw errors.
2593
+ ///
2594
+ /// This method simply calls <see>ConnectionBase.dropTable</see> for every
2595
+ /// model provided--after sorting the models based on their
2596
+ /// foreign keys.
2597
+ ///
2598
+ /// Return: any
2599
+ /// A database specific return value for the drop tables
2600
+ /// operation.
2601
+ ///
2602
+ /// Arguments:
2603
+ /// Models: Array<class <see>Model</see>>
2604
+ /// All the models to drop from the database. The method <see name="Model.getTableName">Model.static getTableName</see>
2605
+ /// is called on each model class to figure out what table/bucket to
2606
+ /// drop from the database.
2607
+ /// options?: object
2608
+ /// Database specific operations for dropping the table/bucket. Please
2609
+ /// refer to the documentation of the driver you are using for further
2610
+ /// information.
1132
2611
  async dropTables(_Models, options) {
1133
2612
  if (!_Models)
1134
2613
  return;
@@ -1165,10 +2644,67 @@ class ConnectionBase extends EventEmitter {
1165
2644
  return results;
1166
2645
  }
1167
2646
 
2647
+ /// Create a table/bucket using the provided model class.
2648
+ ///
2649
+ /// The provided `options` are database specific,
2650
+ /// but might contain things like `ifExists`, for
2651
+ /// example.
2652
+ ///
2653
+ /// Return: any
2654
+ /// A connection specific return value for the operation.
2655
+ ///
2656
+ /// Arguments:
2657
+ /// Model: class <see>Model</see>
2658
+ /// The model class used to create the table/bucket. The <see name="Model.getTableName">Model.static getTableName</see>
2659
+ /// method will be called to get the table/bucket name to create. Then
2660
+ /// the `static fields` property on the model class is used to create the
2661
+ /// columns/fields for the table/bucket. Only "concrete" fields are created
2662
+ /// in the underlying database. Any "virtual" or "relational" fields will
2663
+ /// be skipped.
2664
+ /// options?: object
2665
+ /// Database specific operations for creating the table/bucket. Please
2666
+ /// refer to the documentation of the driver you are using for further
2667
+ /// information.
1168
2668
  async createTable(Model, options) {
1169
2669
  throw new Error(`${this.constructor.name}::createTable: This operation is not supported for this connection type.`);
1170
2670
  }
1171
2671
 
2672
+ /// Create all specified tables/buckets in the database.
2673
+ ///
2674
+ /// This uses the provided `Models` classes to
2675
+ /// create the tables/buckets specified.
2676
+ ///
2677
+ /// The `options` argument is database specific,
2678
+ /// but might contain options such as `ifNotExists`,
2679
+ /// for example.
2680
+ ///
2681
+ /// The model classes provided are first sorted in
2682
+ /// "creation order" using the <see>Utils.sortModelNamesByCreationOrder</see>
2683
+ /// method, and then the tables/buckets are created in the
2684
+ /// that order. This is to ensure that any foreign key
2685
+ /// constraints in play will play nicely with the operation
2686
+ /// and not throw errors.
2687
+ ///
2688
+ /// This method simply calls <see>ConnectionBase.createTable</see> for every
2689
+ /// model provided--after sorting the models based on their
2690
+ /// foreign keys.
2691
+ ///
2692
+ /// Return: any
2693
+ /// A database specific return value for the create tables
2694
+ /// operation.
2695
+ ///
2696
+ /// Arguments:
2697
+ /// Models: Array<class <see>Model</see>>
2698
+ /// The model classes used to create the tables/buckets. The <see name="Model.getTableName">Model.static getTableName</see>
2699
+ /// method will be called for each model to get the table/bucket name to create. Then
2700
+ /// the `static fields` property on each model class is used to create the
2701
+ /// columns/fields for the table/bucket. Only "concrete" fields are created
2702
+ /// in the underlying database. Any "virtual" or "relational" fields will
2703
+ /// be skipped.
2704
+ /// options?: object
2705
+ /// Database specific operations for dropping the table/bucket. Please
2706
+ /// refer to the documentation of the driver you are using for further
2707
+ /// information.
1172
2708
  async createTables(_Models, options) {
1173
2709
  if (!_Models)
1174
2710
  return;
@@ -1220,125 +2756,886 @@ class ConnectionBase extends EventEmitter {
1220
2756
 
1221
2757
  // Alter operations
1222
2758
 
2759
+ /// Alter a table/bucket based on the provided attributes for
2760
+ /// the model.
2761
+ ///
2762
+ /// For SQL based drivers this might run a statement like the following
2763
+ /// `ALTER TABLE "users" RENAME TO "old_users";`
2764
+ ///
2765
+ /// Please refer to the documentation of the database driver
2766
+ /// you are using for more information.
2767
+ ///
2768
+ /// Return: Promise<void>
2769
+ ///
2770
+ /// Arguments:
2771
+ /// Model: class <see>Model</see>
2772
+ /// The model to alter. This is used to alter the underlying
2773
+ /// table/bucket in the database.
2774
+ /// newAttributes: object
2775
+ /// The attributes to alter. Please refer to the documentation
2776
+ /// of the database driver you are using for more information.
1223
2777
  async alterTable(Model, newModelAttributes, options) {
1224
2778
  throw new Error(`${this.constructor.name}::renameTable: This operation is not supported for this connection type.`);
1225
2779
  }
1226
2780
 
2781
+ /// Drop the specified column/field from the database.
2782
+ ///
2783
+ /// The table/bucket to drop the field from is known
2784
+ /// by the `Model` property (model class) on the field
2785
+ /// itself.
2786
+ ///
2787
+ /// The provided `options` are specific to the database
2788
+ /// you are using.
2789
+ /// Please refer to the documentation of the database driver
2790
+ /// you are using for more information.
2791
+ ///
2792
+ /// Return: any
2793
+ /// A database specific return value for the operation completed.
2794
+ ///
2795
+ /// Arguments:
2796
+ /// Field: <see>Field</see>
2797
+ /// The column/field to drop from the database.
2798
+ /// options?: object
2799
+ /// Database specific option for the operation. Please refer to the
2800
+ /// documentation of the database driver you are using for more information.
1227
2801
  async dropColumn(Field, options) {
1228
2802
  throw new Error(`${this.constructor.name}::dropColumn: This operation is not supported for this connection type.`);
1229
2803
  }
1230
2804
 
2805
+ /// Alter the specified column/field in the database.
2806
+ ///
2807
+ /// This will take the two fields, `Field` and `NewField`,
2808
+ /// and will compare them. It will generate multiple alter
2809
+ /// table statements internally, and will alter the column/field
2810
+ /// based on the differences it detects between the two fields.
2811
+ ///
2812
+ /// If `NewField` is provided as a raw object, then it will be
2813
+ /// converted into a <see>Field</see>.
2814
+ ///
2815
+ /// This method will check for the following differences between
2816
+ /// the two fields, in this order:
2817
+ /// 1. `allowNull`
2818
+ /// 2. `type`
2819
+ /// 3. `defaultValue`
2820
+ /// 4. `primaryKey`
2821
+ /// 5. `unique`
2822
+ /// 6. `index` (will calculate index differences, and do the minimum work required)
2823
+ /// 7. `columnName`
2824
+ ///
2825
+ /// Return: Promise<void>
2826
+ ///
2827
+ /// Arguments:
2828
+ /// Field: <see>Field</see>
2829
+ /// The current column/field (as it is in the database) that we are changing.
2830
+ /// NewField: <see>Field</see> | object
2831
+ /// The new field properties to compare. Only the provided properties will be compared.
2832
+ /// For example, if you only supply a `defaultValue` property, then only that will be
2833
+ /// altered (if it differs from `Field`).
2834
+ /// options?: object
2835
+ /// Operation specific options. These will change depending on the database
2836
+ /// driver you are using. Please refer to the documentation for your specific
2837
+ /// driver for more information.
1231
2838
  async alterColumn(Field, newFieldAttributes, options) {
1232
2839
  throw new Error(`${this.constructor.name}::alterColumn: This operation is not supported for this connection type.`);
1233
2840
  }
1234
2841
 
2842
+ /// Add the column/field specified to the database.
2843
+ ///
2844
+ /// The table/bucket to add the field to is fetched from the
2845
+ /// `Model` property on the supplied field.
2846
+ ///
2847
+ /// Return: Promise<void>
2848
+ ///
2849
+ /// Arguments:
2850
+ /// Field: <see>Field</see>
2851
+ /// The new field to add to the underlying database.
2852
+ /// options?: object
2853
+ /// Operation specific options. These will change depending on the database
2854
+ /// driver you are using. Please refer to the documentation for your specific
2855
+ /// driver for more information.
1235
2856
  async addColumn(Field, options) {
1236
2857
  throw new Error(`${this.constructor.name}::addColumn: This operation is not supported for this connection type.`);
1237
2858
  }
1238
2859
 
2860
+ /// Create an index (or combo index) in the database.
2861
+ ///
2862
+ /// This will create a new index for the field(s) specified.
2863
+ /// The `indexFields` argument must be an array of field names
2864
+ /// as strings. It can contain more than one field. If it does
2865
+ /// contain more than one field, then a combo index will be created
2866
+ /// for all specified fields (if the database you are using supports
2867
+ /// combined indexes).
2868
+ ///
2869
+ /// All the provided field names must exist on the provided `Model`.
2870
+ /// If they don't, then an exception will be thrown. The field names
2871
+ /// can be fully qualified, but they don't need to be. If they are
2872
+ /// fully qualified, then they must all still be owned by the provided
2873
+ /// `Model`. You can not for example use a fully qualified field name
2874
+ /// from another model.
2875
+ ///
2876
+ /// Combo indexes are created by combining two or more fields to create
2877
+ /// the index. For example, you could create a combo index for Users
2878
+ /// like `[ 'firstName', 'lastName', 'email' ]` if it is common for your
2879
+ /// application to query on all three of these fields at once.
2880
+ ///
2881
+ /// If you just want to index a single column/field, simply provide only
2882
+ /// one field name, i.e. `[ 'firstName' ]`.
2883
+ ///
2884
+ /// Return: Promise<void>
2885
+ ///
2886
+ /// Arguments:
2887
+ /// Model: class <see>Model</see>
2888
+ /// The model that owns the specified fields.
2889
+ /// indexFields: Array<string>
2890
+ /// The field names to use to create the index. One field name
2891
+ /// is valid if you only wish to index a single field. These are
2892
+ /// used to generate the index name, along with which fields to
2893
+ /// index.
2894
+ /// options?: object
2895
+ /// Operation specific options. These will change depending on the database
2896
+ /// driver you are using. Please refer to the documentation for your specific
2897
+ /// driver for more information.
1239
2898
  async addIndex(Model, indexFields, options) {
1240
2899
  throw new Error(`${this.constructor.name}::addIndex: This operation is not supported for this connection type.`);
1241
2900
  }
1242
2901
 
2902
+ /// Drop the index from the database based on the specified fields.
2903
+ ///
2904
+ /// This is the exact inverse of <see>ConnectionBase.addIndex</see>,
2905
+ /// and it functions nearly identically, except that it will drop
2906
+ /// the specified index instead of creating it.
2907
+ ///
2908
+ /// Return: Promise<void>
2909
+ ///
2910
+ /// Arguments:
2911
+ /// Model: class <see>Model</see>
2912
+ /// The model that owns the specified fields.
2913
+ /// indexFields: Array<string>
2914
+ /// The field names to used to drop the index. These are used
2915
+ /// to generate the index name, which will then be dropped.
2916
+ /// options?: object
2917
+ /// Operation specific options. These will change depending on the database
2918
+ /// driver you are using. Please refer to the documentation for your specific
2919
+ /// driver for more information.
1243
2920
  async dropIndex(Model, indexFields, options) {
1244
2921
  throw new Error(`${this.constructor.name}::addIndex: This operation is not supported for this connection type.`);
1245
2922
  }
1246
2923
 
2924
+ /// Insert the specified models into the specified
2925
+ /// table/bucket (based on the provided `Model`).
2926
+ ///
2927
+ /// This will insert one or more models into the database.
2928
+ /// Like nearly all such Mythix ORM methods, bulk operations are supported
2929
+ /// out-of-the-box. You can provide a single model to `models`,
2930
+ /// or you can provide an array of models.
2931
+ ///
2932
+ /// The provided "models" can be either an object, an array of
2933
+ /// objects, a model instance, or an array of model instances,
2934
+ /// or a mix of both. Mythix ORM will ensure any provided raw
2935
+ /// objects are first converted into model instances using the
2936
+ /// provided `Model` before it inserts anything.
2937
+ ///
2938
+ /// You can also supply "sub models", and those will also be
2939
+ /// inserted in the correct order (and any foreign keys will
2940
+ /// also be updated for you). For example, if you have a `User`
2941
+ /// model that has a virtual `Type.Model` `primaryRole` field,
2942
+ /// then you can supply a new `Role` model upon insertion and
2943
+ /// Mythix ORM will handle this properly for you. For example:
2944
+ /// `await connection.insert(User, new User({ primaryRole: new Role({ ... }) }))`.
2945
+ /// This type of sub model save also works on through-tables.
2946
+ /// If `primaryRole` was targeting a `Role` model, but through
2947
+ /// another table(s), then Mythix ORM will also create the through-table
2948
+ /// relationships (if it is able to).
2949
+ ///
2950
+ /// This **will not work** for `Types.Models` (multi-relations).
2951
+ /// Mythix ORM doesn't know what you intend for multi-relations
2952
+ /// (overwrite the set? add to the set? what?) so it will deliberately
2953
+ /// skip multi-relational fields. "sub models on insert" only work for
2954
+ /// single-relation fields defined with `Types.Model`. For multi-relation
2955
+ /// fields you must manually work through the relation yourself. For example,
2956
+ /// if our user instead had a `Types.Models` `roles` field (plural), then you
2957
+ /// would instead need to:
2958
+ ///
2959
+ /// Example:
2960
+ /// let user = await connection.insert(User, new User({ ... }));
2961
+ /// let role = await connection.insert(Role, { ... }); // model instance is not required
2962
+ /// await user.addToRoles(role);
2963
+ ///
2964
+ ///
2965
+ /// Return: Promise<Array<Model> | Model>
2966
+ /// If you provide an array of models, then an array of models will be
2967
+ /// returned. If you provide only a single model, then a single
2968
+ /// model will be returned. If you provided "sub models" then those
2969
+ /// will be returned as "related models" on the primary model. For example,
2970
+ /// using the above `User` example, the newly created `Role` model that was
2971
+ /// stored for the `primaryRole` would be available on the returned `User`
2972
+ /// model as `user.Roles[0]`, or you could also access it via the field you
2973
+ /// set it on `user.primaryRole`.
2974
+ ///
2975
+ /// Arguments:
2976
+ /// Model: class <see>Model</see>
2977
+ /// The model class used for the operation. This defines what
2978
+ /// table/bucket to insert the specified models into.
2979
+ /// models: Array<Model | object> | object | <see>Model</see>
2980
+ /// The model(s) to insert into the database. If raw objects are provided,
2981
+ /// then the properties of each object must match the required attributes
2982
+ /// for the model class.
2983
+ /// options?: object
2984
+ /// Most of these options are database/driver specific. However, the following
2985
+ /// options are common across all database drivers:
2986
+ /// | Option | Type | Default Value | Description |
2987
+ /// | ------------- | ---- | ------------- | ----------- |
2988
+ /// | `skipHooks` | `boolean` &#124; `object` | `undefined` | Skip specific hooks. See <see>ConnectionBase.runSaveHooks</see> for more information. |
2989
+ /// | `batchSize` | `number` | `500` | The size of each batch during a multi-model insert operation. |
1247
2990
  // eslint-disable-next-line no-unused-vars
1248
2991
  async insert(Model, models, _options) {
1249
2992
  throw new Error(`${this.constructor.name}::insert: This operation is not supported for this connection type.`);
1250
2993
  }
1251
2994
 
2995
+ /// Insert or update (upsert) models into the database.
2996
+ ///
2997
+ /// This method is only supported by some databases. Database
2998
+ /// drivers that don't support `upsert` natively may attempt to emulate
2999
+ /// the operation (at the cost of speed).
3000
+ ///
3001
+ /// This method should function identically to <see>ConnectionBase.insert</see>,
3002
+ /// with the exception that it should update rows that already exist in the database
3003
+ /// instead of inserting new rows.
3004
+ ///
3005
+ /// See: ConnectionBase.insert
3006
+ ///
3007
+ /// Return: Promise<Array<Model> | Model>
3008
+ /// If you provide an array of models, then an array of models will be
3009
+ /// returned. If you provide only a single model, then a single
3010
+ /// model will be returned. If you provided "sub models" then those
3011
+ /// will be returned as "related models" on the primary model. For example,
3012
+ /// using the above `User` example, the newly created `Role` model that was
3013
+ /// stored for the `primaryRole` would be available on the returned `User`
3014
+ /// model as `user.Roles[0]`, or you could also access it via the field you
3015
+ /// set it on `user.primaryRole`.
3016
+ ///
3017
+ /// Arguments:
3018
+ /// Model: class <see>Model</see>
3019
+ /// The model class used for the operation. This defines what
3020
+ /// table/bucket to insert the specified models into.
3021
+ /// models: Array<Model | object> | object | <see>Model</see>
3022
+ /// The model(s) to insert into the database. If raw objects are provided,
3023
+ /// then the properties of each object must match the required attributes
3024
+ /// for the model class.
3025
+ /// options?: object
3026
+ /// Most of these options are database/driver specific. However, the following
3027
+ /// options are common across all database drivers:
3028
+ /// | Option | Type | Default Value | Description |
3029
+ /// | ------------- | ---- | ------------- | ----------- |
3030
+ /// | `skipHooks` | `boolean` &#124; `object` | `undefined` | Skip specific hooks. See <see>ConnectionBase.runSaveHooks</see> for more information. |
3031
+ /// | `batchSize` | `number` | `500` | The size of each batch during a multi-model upsert operation. |
1252
3032
  // eslint-disable-next-line no-unused-vars
1253
3033
  async upsert(Model, models, _options) {
1254
3034
  throw new Error(`${this.constructor.name}::upsert: This operation is not supported for this connection type.`);
1255
3035
  }
1256
3036
 
3037
+ /// Update the specified models in the database.
3038
+ ///
3039
+ /// Many databases don't have good (or even decent) support
3040
+ /// for bulk updates, so unfortunately this method is fairly
3041
+ /// slow, and will usually make a query to the database for each
3042
+ /// model updated.
3043
+ ///
3044
+ /// If you want to update many models at the same time (using the same
3045
+ /// attributes across all models), then consider using the <see>ConnectionBase.updateAll</see>
3046
+ /// method instead.
3047
+ ///
3048
+ /// Note:
3049
+ /// Models will only be updated if they are dirty. Also, only the
3050
+ /// dirty attributes for each model will be updated (some attributes
3051
+ /// are always dirty, for example `updatedAt` fields are forced to
3052
+ /// always be dirty based on the configuration of their `defaultValue`).
3053
+ ///
3054
+ /// Return: Promise<Array<Model> | Model>
3055
+ /// If you provide an array of models, then an array of models will be
3056
+ /// returned. If you provide only a single model, then a single
3057
+ /// model will be returned.
3058
+ ///
3059
+ /// Arguments:
3060
+ /// Model: class <see>Model</see>
3061
+ /// The model class used for the operation. This defines what
3062
+ /// table/bucket to update.
3063
+ /// models: Array<Model | object> | object | <see>Model</see>
3064
+ /// The model(s) to update in the database. If raw objects are provided,
3065
+ /// then the properties of each object must match the required attributes
3066
+ /// for the model class.
3067
+ /// options?: object
3068
+ /// Most of these options are database/driver specific. However, the following
3069
+ /// options are common across all database drivers:
3070
+ /// | Option | Type | Default Value | Description |
3071
+ /// | ------------- | ---- | ------------- | ----------- |
3072
+ /// | `skipHooks` | `boolean` &#124; `object` | `undefined` | Skip specific hooks. See <see>ConnectionBase.runSaveHooks</see> for more information. |
3073
+ /// | `batchSize` | `number` | `500` | The size of each batch during a multi-model update operation. |
1257
3074
  // eslint-disable-next-line no-unused-vars
1258
3075
  async update(Model, models, _options) {
1259
3076
  throw new Error(`${this.constructor.name}::update: This operation is not supported for this connection type.`);
1260
3077
  }
1261
3078
 
3079
+ /// Update multiple models at the same time (bulk update).
3080
+ ///
3081
+ /// This will update multiple models at the same time
3082
+ /// using the provided `query` to select which models to update.
3083
+ /// All matching rows will set the provided `attributes` upon them.
3084
+ ///
3085
+ /// The provided `attributes` can be a model instance, or
3086
+ /// a raw object. If a raw object is provided, then they
3087
+ /// will be converted into a model instance using the provided
3088
+ /// `Model` class. This also means that you can *only* bulk update
3089
+ /// columns/fields that exist on the model itself (i.e. you might have
3090
+ /// other columns in your table not related to this model, and
3091
+ /// those can **not** be updated using this method).
3092
+ ///
3093
+ /// Note:
3094
+ /// As always with Mythix ORM, you will **never** supply
3095
+ /// raw column names as the `attributes`. You must always
3096
+ /// provide model field names in Mythix ORM.
3097
+ ///
3098
+ /// Note:
3099
+ /// This will be an update operation across all matching rows,
3100
+ /// using the data provided. This method is really only useful
3101
+ /// when you want to update multiple rows to the **same values**.
3102
+ /// If you need to update each row to different values per-row,
3103
+ /// then use the <see>ConnectionBase.update</see> method instead.
3104
+ ///
3105
+ /// Return: Promise<any>
3106
+ /// A database specific result from the `UPDATE` statement.
3107
+ /// In the future all "database specific" results will be
3108
+ /// abstracted away. So in the future, this will likely return
3109
+ /// the number of rows updated as a `number` (**HELP WANTED**).
3110
+ ///
3111
+ /// Arguments:
3112
+ /// query: <see>QueryEngine</see>
3113
+ /// The query used to select which models/rows to update.
3114
+ /// The "root model" of the query is the table/bucket that
3115
+ /// will be updated.
3116
+ /// attributes: object | Model
3117
+ /// The attributes to set across all updated rows.
3118
+ /// options?: object
3119
+ /// Operation specific options. These will change depending on the database
3120
+ /// driver you are using. Please refer to the documentation for your specific
3121
+ /// driver for more information.
1262
3122
  // eslint-disable-next-line no-unused-vars
1263
3123
  async updateAll(_queryEngine, model, _options) {
1264
3124
  throw new Error(`${this.constructor.name}::updateAll: This operation is not supported for this connection type.`);
1265
3125
  }
1266
3126
 
3127
+ /// Destroy the provided models.
3128
+ ///
3129
+ /// This method will bulk-iterate the specified
3130
+ /// models and will destroy each one. A primary key field
3131
+ /// is required for every model, or this method will throw
3132
+ /// an exception.
3133
+ ///
3134
+ /// The `skipHooks` option (see <see>ConnectionBase.runSaveHooks</see>) won't
3135
+ /// do anything here, because Mythix ORM doesn't have any `on*Destroy` hooks.
3136
+ /// Mythix ORM doesn't have any destroy hooks for performance reasons.
3137
+ /// However, the `batchSize` option is still useful for this method.
3138
+ ///
3139
+ /// Note:
3140
+ /// **WARNING!!!**: If you supply `null` or `undefined` as the `models`
3141
+ /// argument then this method will silently return. If you also supply
3142
+ /// the option `{ truncate: true }` then the entire table will be truncated.
3143
+ /// This option is an "opt-in", to make sure you don't truncate an entire
3144
+ /// table on accident.
3145
+ ///
3146
+ /// Note:
3147
+ /// Some databases (i.e. SQLite) don't support a `TRUNCATE` statement,
3148
+ /// and so will deliberately call this method with `null` for the `models`
3149
+ /// argument for `truncate` operations. When doing so they will also
3150
+ /// deliberately supply the `{ truncate: true }` option.
3151
+ ///
3152
+ /// Return: Promise<Array<Model> | Model>
3153
+ /// If you provide an array of models, then an array of models will be
3154
+ /// returned. If you provide only a single model, then a single
3155
+ /// model will be returned.
3156
+ ///
3157
+ /// Arguments:
3158
+ /// Model: class <see>Model</see>
3159
+ /// The model class used for the operation. This defines what
3160
+ /// table/bucket to delete from.
3161
+ /// models: Array<Model | object> | object | <see>Model</see>
3162
+ /// The model(s) to delete from the database. If raw objects are provided,
3163
+ /// then they must contain a `primaryKey` for each model. If any of the
3164
+ /// models provided don't contain a `primaryKey` field, then an exception
3165
+ /// will be thrown. If you have a table/bucket that has no primary key column,
3166
+ /// then you will need to destroy rows manually yourself using a manual query
3167
+ /// with <see>ConnectionBase.query</see>, or by using the <see>ConnectionBase.destroy</see> method.
3168
+ /// options?: object
3169
+ /// Most of these options are database/driver specific. However, the following
3170
+ /// options are common across all database drivers (`skipHooks` is not supported in this context):
3171
+ /// | Option | Type | Default Value | Description |
3172
+ /// | ------------- | ---- | ------------- | ----------- |
3173
+ /// | `batchSize` | `number` | `500` | The size of each batch during a multi-model destroy operation. |
1267
3174
  // eslint-disable-next-line no-unused-vars
1268
3175
  async destroyModels(Model, _models, _options) {
1269
3176
  throw new Error(`${this.constructor.name}::destroyModels: This operation is not supported for this connection type.`);
1270
3177
  }
1271
3178
 
3179
+ /// Destroy multiple models by query, or by the
3180
+ /// provided models themselves. If models are provided,
3181
+ /// then each model must have a valid primary key field,
3182
+ /// or an exception will be thrown.
3183
+ ///
3184
+ /// If models are provided as the second argument, then
3185
+ /// it is required that the first argument be a <see>Model</see>
3186
+ /// class. In this case, this method simply calls
3187
+ /// <see>ConnectionBase.destroyModels</see> to complete the operation.
3188
+ ///
3189
+ /// If the first argument is a <see>QueryEngine</see> instance,
3190
+ /// then rows will be deleted from the database using the provided query.
3191
+ /// In this case, it is expected that the second argument
3192
+ /// will be the "options" for this operation instead of an array of models.
3193
+ ///
3194
+ /// Return: Promise<Array<Models> | Model | number>
3195
+ /// Return the models deleted (if models were provided),
3196
+ /// or the number of rows deleted (if a query was provided).
3197
+ ///
3198
+ /// Arguments:
3199
+ /// queryOrModel: class <see>Model</see> | <see>QueryEngine</see>
3200
+ /// A <see>QueryEngine</see> instance to specify which models to
3201
+ /// delete via a query, or a <see>Model</see> class if models are
3202
+ /// being provided to be deleted.
3203
+ /// modelsOrOptions: Array<Model> | Model | object
3204
+ /// If a <see>QueryEngine</see> instance is provided as the first argument
3205
+ /// then this is expected to be the "options" for the operation. If however
3206
+ /// a <see>Model</see> class is provided as the first argument, then this
3207
+ /// should be a <see>Model</see> instance, or an array of <see>Model</see> instances.
3208
+ /// options?: object | undefined
3209
+ /// Most of these options are database/driver specific.
3210
+ /// The `batchSize` option is ignored if a <see>QueryEngine</see> instance is provided as the
3211
+ /// first argument to the call. If a <see>Model</see> class is provided as the first argument to
3212
+ /// the call, then this `options` object will be at argument index `1` instead (the second argument, `modelsOrOptions`).
3213
+ /// The following options are common across all database drivers
3214
+ /// (`skipHooks` is not supported in this context):
3215
+ /// | Option | Type | Default Value | Description |
3216
+ /// | ------------- | ---- | ------------- | ----------- |
3217
+ /// | `batchSize` | `number` | `500` | The size of each batch during a multi-model destroy operation. |
1272
3218
  // eslint-disable-next-line no-unused-vars
1273
3219
  async destroy(_queryEngineOrModel, modelsOrOptions, _options) {
1274
3220
  throw new Error(`${this.constructor.name}::destroy: This operation is not supported for this connection type.`);
1275
3221
  }
1276
3222
 
3223
+ /// Select data from the underlying database.
3224
+ ///
3225
+ /// To select data from the database you will use this method,
3226
+ /// providing a `query` as a <see>QueryEngine</see> instance.
3227
+ /// The provided query will be used to generate the underlying
3228
+ /// request to the database, will collect the response, and return
3229
+ /// the results to the caller.
3230
+ ///
3231
+ /// This method is an async generator method, and is designed for
3232
+ /// "streaming" results from the database, one "row" at a time. Many
3233
+ /// methods that call this method (such as <see>QueryEngine.all</see>)
3234
+ /// collect all the results from the async generator to return an array
3235
+ /// of results to the caller. This is because it is often tedious to
3236
+ /// collect the results yourself from the async generator, and often the
3237
+ /// caller simply wants a small amount of data from the database.
3238
+ /// However, if you intend to fetch large amounts of data from your database,
3239
+ /// it is a good idea to call this method directly, and iterate the results
3240
+ /// of the async generator manually. All methods that in-turn call this
3241
+ /// method (such as <see>QueryEngine.all</see>) will generally have a
3242
+ /// `{ stream: true }` option that can be provided, causing the method to
3243
+ /// return the async generator, instead of the collected results.
3244
+ ///
3245
+ ///
3246
+ /// Return: AsyncGenerator<Model>
3247
+ ///
3248
+ /// Arguments:
3249
+ /// query: <see>QueryEngine</see>
3250
+ /// The query to run against the underlying database. This is generated
3251
+ /// from a <see>QueryEngine</see> interface. The result of this <see>QueryEngine</see>
3252
+ /// instance will then be converted to a query, or generated code to
3253
+ /// interact with the underlying database.
3254
+ /// options?: object
3255
+ /// Operation specific options. These will change depending on the database
3256
+ /// driver you are using. Please refer to the documentation for your specific
3257
+ /// driver for more information.
1277
3258
  // eslint-disable-next-line no-unused-vars, require-yield
1278
3259
  async *select(_queryEngine, _options) {
1279
3260
  throw new Error(`${this.constructor.name}::select: This operation is not supported for this connection type.`);
1280
3261
  }
1281
3262
 
3263
+ /// Aggregate data across rows.
3264
+ ///
3265
+ /// Though this method can be called directly, it is generally called from
3266
+ /// one of <see>ConnectionBase.average</see>, <see>ConnectionBase.sum</see>,
3267
+ /// <see>ConnectionBase.count</see>, <see>ConnectionBase.min</see>,
3268
+ /// or <see>ConnectionBase.max</see>, or one of the other aggregate methods
3269
+ /// provided by the connection.
3270
+ ///
3271
+ /// It takes a `query` which is used to generate a query for the underlying
3272
+ /// database, and sets the query `PROJECTION` to the aggregate `literal` provided.
3273
+ /// For example, a `sum` method call would call this method with a `SumLiteral`,
3274
+ /// which would change the `query` projection to the expanded result of the
3275
+ /// `SumLiteral`, returning the "sum" of all column values targeted by the literal field.
3276
+ /// The results will then be collected, and always returned as a `number` primitive
3277
+ /// to the caller.
3278
+ ///
3279
+ /// Return: Promise<number>
3280
+ /// The result of the aggregate operation, as a number.
3281
+ ///
3282
+ /// Arguments:
3283
+ /// query: <see>QueryEngine</see>
3284
+ /// The query used to select which rows to aggregate across.
3285
+ /// literal: <see>LiteralBase</see>
3286
+ /// A literal, used as the aggregate function. For example, if a
3287
+ /// <see>CountLiteral</see> is provided, then the count of all rows
3288
+ /// matching the provided query will be the result.
3289
+ /// options?: object
3290
+ /// Operation specific options. These will change depending on the database
3291
+ /// driver you are using. Please refer to the documentation for your specific
3292
+ /// driver for more information.
1282
3293
  // eslint-disable-next-line no-unused-vars
1283
3294
  async aggregate(_queryEngine, _literal, options) {
1284
3295
  throw new Error(`${this.constructor.name}::aggregate: This operation is not supported for this connection type.`);
1285
3296
  }
1286
3297
 
3298
+ /// Get the average for a single column, spanning all matching rows.
3299
+ ///
3300
+ /// This will return the average of all values in a column,
3301
+ /// across all matching rows, as a `number` primitive.
3302
+ ///
3303
+ /// Return: Promise<number>
3304
+ /// The average of all matching columns.
3305
+ ///
3306
+ /// Arguments:
3307
+ /// query: <see>QueryEngine</see>
3308
+ /// The query used to select which rows are used to calculate the average.
3309
+ /// field: <see>Field</see> | string
3310
+ /// A field instance, or a fully qualified field name, used as the target
3311
+ /// column in the underlying database to calculate an average across all matching values.
3312
+ /// options?: object
3313
+ /// Operation specific options. These will change depending on the database
3314
+ /// driver you are using. Please refer to the documentation for your specific
3315
+ /// driver for more information.
1287
3316
  // eslint-disable-next-line no-unused-vars
1288
3317
  async average(_queryEngine, _field, options) {
1289
3318
  throw new Error(`${this.constructor.name}::average: This operation is not supported for this connection type.`);
1290
3319
  }
1291
3320
 
3321
+ /// Get the number of rows matching the query.
3322
+ ///
3323
+ /// This will return the number of rows matching
3324
+ /// the provided query, as a `number` primitive.
3325
+ ///
3326
+ /// Note:
3327
+ /// In most databases, if the `field` argument is not
3328
+ /// specified, then the count operation will be across
3329
+ /// all table columns (`COUNT(*)`).
3330
+ ///
3331
+ /// Return: Promise<number>
3332
+ /// The count (number) of all rows matching the provided query.
3333
+ ///
3334
+ /// Arguments:
3335
+ /// query: <see>QueryEngine</see>
3336
+ /// The query used to select which rows to count.
3337
+ /// field: <see>Field</see> | string
3338
+ /// A field instance, or a fully qualified field name, used as the target
3339
+ /// column in the underlying database to count the rows. If not specified,
3340
+ /// then most database drivers will count across all columns (i.e. `COUNT(*)`).
3341
+ /// options?: object
3342
+ /// Operation specific options. These will change depending on the database
3343
+ /// driver you are using. Please refer to the documentation for your specific
3344
+ /// driver for more information.
1292
3345
  // eslint-disable-next-line no-unused-vars
1293
3346
  async count(_queryEngine, _field, options) {
1294
3347
  throw new Error(`${this.constructor.name}::count: This operation is not supported for this connection type.`);
1295
3348
  }
1296
3349
 
3350
+ /// Get the minimum value for a column, spanning all matching rows.
3351
+ ///
3352
+ /// This will return the minimum of all values in a column,
3353
+ /// across all matching rows, as a `number` primitive.
3354
+ ///
3355
+ /// Return: Promise<number>
3356
+ /// The minimum value found across all matching rows.
3357
+ ///
3358
+ /// Arguments:
3359
+ /// query: <see>QueryEngine</see>
3360
+ /// The query used to select which rows are used to calculate the minimum.
3361
+ /// field: <see>Field</see> | string
3362
+ /// A field instance, or a fully qualified field name, used as the target
3363
+ /// column in the underlying database to find the minimum across all matching values.
3364
+ /// options?: object
3365
+ /// Operation specific options. These will change depending on the database
3366
+ /// driver you are using. Please refer to the documentation for your specific
3367
+ /// driver for more information.
1297
3368
  // eslint-disable-next-line no-unused-vars
1298
3369
  async min(_queryEngine, _field, options) {
1299
3370
  throw new Error(`${this.constructor.name}::min: This operation is not supported for this connection type.`);
1300
3371
  }
1301
3372
 
3373
+ /// Get the maximum value for a column, spanning all matching rows.
3374
+ ///
3375
+ /// This will return the maximum of all values in a column,
3376
+ /// across all matching rows, as a `number` primitive.
3377
+ ///
3378
+ /// Return: Promise<number>
3379
+ /// The maximum value found across all matching rows.
3380
+ ///
3381
+ /// Arguments:
3382
+ /// query: <see>QueryEngine</see>
3383
+ /// The query used to select which rows are used to calculate the maximum.
3384
+ /// field: <see>Field</see> | string
3385
+ /// A field instance, or a fully qualified field name, used as the target
3386
+ /// column in the underlying database to find the maximum across all matching values.
3387
+ /// options?: object
3388
+ /// Operation specific options. These will change depending on the database
3389
+ /// driver you are using. Please refer to the documentation for your specific
3390
+ /// driver for more information.
1302
3391
  // eslint-disable-next-line no-unused-vars
1303
3392
  async max(_queryEngine, _field, options) {
1304
3393
  throw new Error(`${this.constructor.name}::max: This operation is not supported for this connection type.`);
1305
3394
  }
1306
3395
 
3396
+ /// Get the sum of all values for a column, spanning all matching rows.
3397
+ ///
3398
+ /// This will return the sum of all values in a column,
3399
+ /// across all matching rows, as a `number` primitive.
3400
+ ///
3401
+ /// Return: Promise<number>
3402
+ /// The sum of all values found across all matching rows.
3403
+ ///
3404
+ /// Arguments:
3405
+ /// query: <see>QueryEngine</see>
3406
+ /// The query used to select which rows are used to calculate the sum.
3407
+ /// field: <see>Field</see> | string
3408
+ /// A field instance, or a fully qualified field name, used as the target
3409
+ /// column in the underlying database to find the sum of all matching values.
3410
+ /// options?: object
3411
+ /// Operation specific options. These will change depending on the database
3412
+ /// driver you are using. Please refer to the documentation for your specific
3413
+ /// driver for more information.
1307
3414
  // eslint-disable-next-line no-unused-vars
1308
3415
  async sum(_queryEngine, _field, options) {
1309
3416
  throw new Error(`${this.constructor.name}::sum: This operation is not supported for this connection type.`);
1310
3417
  }
1311
3418
 
3419
+ /// Pluck only specific columns/fields from the
3420
+ /// underlying database, using the provided `query`.
3421
+ ///
3422
+ /// This method will return only the specified `fields`
3423
+ /// from the database--as raw values--from rows matched
3424
+ /// on by the provided query.
3425
+ ///
3426
+ /// This method is often much faster than a normal `select` operation,
3427
+ /// in that only the data requested will be transmitted from the database,
3428
+ /// and models won't be constructed and stitched together on load. Use this
3429
+ /// when you just need an "array of values" across certain columns from a table.
3430
+ ///
3431
+ /// Return: Array<any> | Array<Array<any>> | Array<object>
3432
+ /// If the provided `fields` argument is an array of fields
3433
+ /// (fully qualified field names, or <see>Field</see> instances),
3434
+ /// then the result will be an array of arrays, where each item
3435
+ /// in the top-level array is a "row", and each sub-array is the
3436
+ /// values for the columns (fields) specified. If a single field
3437
+ /// is specified, then an one dimensional array is returned, where
3438
+ /// each item is the column value for each row fetched.
3439
+ /// For example, if we call `pluck` like: `let result = await connection.pluck(User.where.firstName.EQ('Bob'), 'User:id')`
3440
+ /// then the `result` would look like `[ 'USER_id_1`, `USER_id_2`, ... ]`
3441
+ /// where each value in the array is a user id. If however we call pluck like:
3442
+ /// `let result = await connection.pluck(User.where.firstName.EQ('Bob'), [ 'User:id', 'User:firstName' ])`
3443
+ /// then the `result` would look like `[ [ 'USER_id_1', 'Bob' ], [ 'USER_id_2', 'Bob' ], ... ]`, because
3444
+ /// the provided `fields` is an array, an array of field values will be returned from each row.
3445
+ /// If the `option` `mapToObjects` is `true`, then an array of objects will be returned,
3446
+ /// where each object is a "row", and each property one of the specified fields. Note that
3447
+ /// the properties of each returned object will be the fully qualified field name of each field
3448
+ /// specified. So, for example, specifying a pluck field of `User:id` means that each returned
3449
+ /// object in the array will look like `[ { 'User:id': 'USER_id_1' }, { 'User:id': 'USER_id_2' }, ... ]`.
3450
+ ///
3451
+ /// Arguments:
3452
+ /// query: <see>QueryEngine</see>
3453
+ /// The query used to select rows from which columns will be plucked.
3454
+ /// fields: <see>Field</see> | string | Array<Field> | Array<string>
3455
+ /// Which fields to "pluck" from the matching rows. If an array is provided (even if
3456
+ /// it only contains a single field), then an array of arrays will be returned
3457
+ /// as the result. If a single field is provided (not an array), then an array
3458
+ /// of raw plucked column values will be returned instead.
3459
+ /// options?: object
3460
+ /// Operation specified options. These might change based on the database driver you
3461
+ /// are using, so please refer to your specific database driver documentation. One
3462
+ /// option that is common across all drivers is the `mapToObjects` boolean option.
3463
+ /// If `true`, then each row in the returned array will be an object instead of
3464
+ /// raw column values, where the property of each "row object" will be the fully
3465
+ /// qualified names of each field provided as the `fields` argument.
1312
3466
  // eslint-disable-next-line no-unused-vars
1313
3467
  async pluck(_queryEngine, _fields, _options) {
1314
3468
  throw new Error(`${this.constructor.name}::pluck: This operation is not supported for this connection type.`);
1315
3469
  }
1316
3470
 
3471
+ /// Check if any rows match the provided `query`.
3472
+ ///
3473
+ /// Return: boolean
3474
+ /// `true` if one or more rows match the provided query, or `false` otherwise.
3475
+ ///
3476
+ /// Arguments:
3477
+ /// query: <see>QueryEngine</see>
3478
+ /// The query used to select rows, to check if said rows exist in the database.
3479
+ /// options?: object
3480
+ /// Operation specific options. These will change depending on the database
3481
+ /// driver you are using. Please refer to the documentation for your specific
3482
+ /// driver for more information.
1317
3483
  // eslint-disable-next-line no-unused-vars
1318
3484
  async exists(queryEngine, options) {
1319
3485
  throw new Error(`${this.constructor.name}::exists: This operation is not supported for this connection type.`);
1320
3486
  }
1321
3487
 
3488
+ /// Truncate (erase/clear) the entire table/bucket defined
3489
+ /// by the provided `Model`. All rows/objects from the underlying
3490
+ /// table/bucket will be destroyed, leaving you with an empty
3491
+ /// table/bucket.
3492
+ ///
3493
+ /// Return: Promise<void>
3494
+ ///
3495
+ /// Arguments:
3496
+ /// Model: class <see>Model</see>
3497
+ /// The model class that defines the underlying table/bucket to wipe clean/erase.
3498
+ /// options?: object
3499
+ /// Operation specific options. These will change depending on the database
3500
+ /// driver you are using. Please refer to the documentation for your specific
3501
+ /// driver for more information.
1322
3502
  // eslint-disable-next-line no-unused-vars
1323
3503
  async truncate(Model, options) {
1324
3504
  throw new Error(`${this.constructor.name}::truncate: This operation is not supported for this connection type.`);
1325
3505
  }
1326
3506
 
3507
+ /// "raw" database/driver specific query interface.
3508
+ ///
3509
+ /// For SQL based databases, this would send a direct
3510
+ /// query string to the database. For other drivers, the
3511
+ /// arguments and operations that are executed might change.
3512
+ ///
3513
+ /// Use this method to directly interact with the underlying
3514
+ /// database, in its own native query language.
3515
+ ///
3516
+ /// The arguments and return value from this method is database/driver
3517
+ /// specific. Any `options` argument the database provides are also
3518
+ /// specific to the underlying database driver. However, one option
3519
+ /// is common across all drivers, and this is the `logger` option. If
3520
+ /// set, it is expected to have a `log` method in the provided `logger`
3521
+ /// object. Oftentimes, this will simply be `{ logger: console }`, but
3522
+ /// you can provided any custom `logger` instance you want, as long as it
3523
+ /// has a `log` method that can be called to log the results of the query.
3524
+ /// Most drivers also support a `{ logger }` option as a connection option
3525
+ /// when the connection is first instantiated, which will provided logging
3526
+ /// to every query that goes through the connection.
3527
+ ///
3528
+ /// Return: database specific
3529
+ /// A database/driver specific return value, based on the query provided.
1327
3530
  // eslint-disable-next-line no-unused-vars
1328
3531
  async query(sql, options) {
1329
3532
  throw new Error(`${this.constructor.name}::query: This operation is not supported for this connection type.`);
1330
3533
  }
1331
3534
 
3535
+ /// Initiate a transaction (or snapshot/sub-transaction)
3536
+ /// if the database driver supports transactions.
3537
+ ///
3538
+ /// This will initiate a transaction in the underlying database,
3539
+ /// if the database supports it. For SQL type databases this would
3540
+ /// be a `BEGIN/COMMIT/ROLLBACK` block. If this method is called
3541
+ /// when a transaction is already in-progress, then a snapshot/sub-transaction
3542
+ /// will be started instead.
3543
+ ///
3544
+ /// This method will start a transaction in the underlying database,
3545
+ /// and call the provided asynchronous callback. If the callback throws
3546
+ /// an error, then the transaction (or snapshot) will be automatically
3547
+ /// rolled-back. There is a single `connection` argument that will be
3548
+ /// provided to the callback function when called. This `connection` argument
3549
+ /// will be the transaction connection itself, which for many database drivers is simply
3550
+ /// the same connection the transaction was started from. At a lower-level,
3551
+ /// Mythix ORM will use an [AsyncLocalStorage](https://nodejs.org/docs/latest-v16.x/api/async_context.html)
3552
+ /// context to provide the transaction connection to all code executed inside
3553
+ /// the callback, so the provided `connection` argument can generally be ignored.
3554
+ /// However, if [AsyncLocalStorage](https://nodejs.org/docs/latest-v16.x/api/async_context.html) isn't
3555
+ /// supported in your environment, or the specific driver you are using requires that you use
3556
+ /// the supplied `connection` argument, then you must use the supplied `connection`
3557
+ /// for all your operations, and provide it as the `{ connection }` option to all Mythix ORM
3558
+ /// calls made inside the callback.
3559
+ ///
3560
+ /// Return: Promise<any>
3561
+ /// Return whatever return value is returned from the provided callback.
3562
+ ///
3563
+ /// Arguments:
3564
+ /// callback: (connection: <see>Connection</see>) => any
3565
+ /// The async callback to call for the transaction operation. This should return as quickly as
3566
+ /// possible to avoid deadlocks in the underlying database. Whatever value this method
3567
+ /// returns will be the return value from the `transaction` call itself. If an exception is
3568
+ /// thrown in this method, then the transaction will be automatically rolled-back. If no
3569
+ /// exception is thrown from this method, then when done executing, a `COMMIT` will be sent
3570
+ /// to the underlying database automatically for you.
3571
+ /// options?: object
3572
+ /// Optional database specific options to supply for the transaction operation. There are two
3573
+ /// options that are supported across most database drivers, and those are `connection` and `lock`.
3574
+ /// The `connection` option supplies the the connection to initiate the transaction from, which for
3575
+ /// example might be another transaction connection (initiating a sub-transaction). If no `connection`
3576
+ /// option is supplied, then the `connection` this method was called from is used instead. The second
3577
+ /// common option is `lock`, which specifies how/if to lock the table for the transaction.
3578
+ /// See <see>ConnectionBase.getLockMode</see> for your specific driver for more information on this `lock` option.
1332
3579
  // eslint-disable-next-line no-unused-vars
1333
3580
  async transaction(callback, options) {
1334
3581
  throw new Error(`${this.constructor.name}::transaction: This operation is not supported for this connection type.`);
1335
3582
  }
1336
3583
 
3584
+ /// Translate the `defaultValue` as provided by each models field's
3585
+ /// into a `DEFAULT` value for the underlying database.
3586
+ ///
3587
+ /// For example, if you were to specify a `defaultValue` for one of your
3588
+ /// models field's, such as `defaultValue: Types.INTEGER.Defaults.AUTOINCREMENT`,
3589
+ /// then this method would return a database specific `autoincrement` default for
3590
+ /// the column defined by your field. For date/time fields, this might be a database
3591
+ /// specific `NOW()` default. For non-database specific "defaults", the default value
3592
+ /// as defined by your `defaultValue` on your fields is simply returned.
3593
+ ///
3594
+ /// Return: any
3595
+ /// Your defined field's `defaultValue`, intercepted, and possibly translated to
3596
+ /// a proper value for the underlying database. It is common for many database
3597
+ /// drivers that only date/time and auto-increment default values are intercepted
3598
+ /// and translated. Most other "default values" are simply returned.
3599
+ ///
3600
+ /// Arguments:
3601
+ /// type: <see>Type</see>
3602
+ /// The field's `type` instance, as defined by the field itself.
3603
+ /// context: DefaultValueContext
3604
+ /// A `DefaultValueContext` as is normally provided to all `defaultValue` methods
3605
+ /// defined on <see name="fields">Field</see>. See the documentation for the `defaultValue`
3606
+ /// property of <see>Field</see> for more information.
1337
3607
  // eslint-disable-next-line no-unused-vars
1338
3608
  async getDefaultFieldValue(type, context) {
1339
3609
  throw new Error(`${this.constructor.name}::getDefaultFieldValue: This operation is not supported for this connection type.`);
1340
3610
  }
1341
3611
 
3612
+ /// A connection specific "helper" to force "dirty" fields
3613
+ /// for all models.
3614
+ ///
3615
+ /// This method, when present on a connection, will assist in
3616
+ /// defining which fields are "dirty" for a model. It is used,
3617
+ /// for example, by `Types.DATETIME.Defaults.NOW.UPDATED` default values.
3618
+ /// In the case presented, DATETIME fields that should always be updated
3619
+ /// are always marked dirty by this method, even though they might
3620
+ /// not actually be "dirty" client-side.
3621
+ ///
3622
+ /// If this method returns _any_ value other than `undefined`, then
3623
+ /// the field being operated upon will be marked "dirty" with the value
3624
+ /// returned, even if the field would otherwise not be marked "dirty".
3625
+ ///
3626
+ /// Return: any
3627
+ /// If _any_ value is returned that is not `undefined`, then the field
3628
+ /// will be marked "dirty" with the value returned. Return `undefined`
3629
+ /// if you do not wish to mark a specific field as "dirty". Note however
3630
+ /// that the field _might_ actually still be dirty because the user modified
3631
+ /// the field, in which case this method will not even be called for that field.
3632
+ /// In short, this method is only called for fields that are not already marked
3633
+ /// as dirty, and will force a field to be marked dirty if any value other than
3634
+ /// `undefined` is returned.
3635
+ ///
3636
+ /// Arguments:
3637
+ /// context: DefaultValueContext
3638
+ /// See <see>Field</see> for documentation on the `DefaultValueContext` interface.
1342
3639
  // eslint-disable-next-line no-unused-vars
1343
3640
  dirtyFieldHelper(context) {
1344
3641