mythix-orm 1.12.0 → 1.13.1

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.
@@ -396,11 +396,9 @@ class ConnectionBase extends EventEmitter {
396
396
  /// options: object
397
397
  /// Operation specific options (i.e. options for a "select" call)
398
398
  ///
399
- /// Return: Array<string>
400
- /// An array of fully qualified field names for this model should
401
- /// be returned by this method. An empty array, `null`, or `undefined`
402
- /// are also valid return values (in which case no order will be
403
- /// applied to the given operation).
399
+ /// Return: Map<string, { value: Field | Literal | string; direction?: '+' | '-'; ... }>
400
+ /// Return the field-set for the default ordering to apply to the operation taking place.
401
+ /// This `Map` should have the same format as is returned by <see>ModelScope.mergeFields</see>.
404
402
  getDefaultOrder(Model, options) {
405
403
  }
406
404
 
@@ -243,9 +243,21 @@ class LiteralBase {
243
243
  /// Convert the literal value provided to the `constructor`
244
244
  /// to a string.
245
245
  ///
246
+ /// Arguments:
247
+ /// connection?: <see>Connection</see>
248
+ /// The connection to use to stringify the literal. If
249
+ /// not provided, then a "representation" of the literal
250
+ /// (for logging/debugging) will be returned instead.
251
+ /// options?: object
252
+ /// Any options needed to stringify the literal. These are often
253
+ /// literal and/or database specific.
254
+ ///
246
255
  /// Return: string
247
- /// The value provided to the `constructor` as a string.
248
- toString() {
256
+ /// The stringified literal, ready to be used in the underlying database
257
+ /// (if a `connection` was provided), or a logging/debugging representation
258
+ /// of the literal if no `connection` is provided.
259
+ // eslint-disable-next-line no-unused-vars
260
+ toString(connection, options) {
249
261
  if (!this.literal)
250
262
  return ('' + this.literal);
251
263
 
package/lib/model.js CHANGED
@@ -1772,8 +1772,22 @@ class Model {
1772
1772
  return this.getWhereWithConnection(options).all(options);
1773
1773
  }
1774
1774
 
1775
- static fetchAll(options) {
1776
- return this.all({ ...(options || {}), stream: true });
1775
+ /// This method is similar in nature to <see>Model.static all</see>,
1776
+ /// except that instead of collecting all results into an
1777
+ /// array before returning, it will instead "stream" the results
1778
+ /// from the database using an async generator.
1779
+ ///
1780
+ /// Arguments:
1781
+ /// options?: object
1782
+ /// Options for the operation. These are generally
1783
+ /// database specific. Like the <see>Model.static all</see>
1784
+ /// method you can supply a `batchSize`.
1785
+ ///
1786
+ /// Return: async * iterator
1787
+ /// An async generator iterator that will "stream" the
1788
+ /// results from the database.
1789
+ static cursor(options) {
1790
+ return this.cursor(options);
1777
1791
  }
1778
1792
 
1779
1793
  /// Get the first (limit) rows from the database for this model type.
@@ -31,7 +31,7 @@ function applyOrderClause(extraData, ...args) {
31
31
  }).filter(Boolean);
32
32
 
33
33
  let context = this.getOperationContext();
34
- let order = this.margeFields(
34
+ let order = this.mergeFields(
35
35
  context.order,
36
36
  entities,
37
37
  extraData,
@@ -334,8 +334,8 @@ class ModelScope extends QueryEngineBase {
334
334
  /// Return: Map<string, { value: Field | Literal | string; direction?: '+' | '-'; ... }>
335
335
  /// Return the new field set. A `Map` will always be returned, but it is possible
336
336
  /// for the `Map` to be empty.
337
- margeFields(currentFields, incomingFields, extraData, options) {
338
- return QueryUtils.margeFields(this, currentFields, incomingFields, extraData, options);
337
+ mergeFields(currentFields, incomingFields, extraData, options) {
338
+ return QueryUtils.mergeFields(this, currentFields, incomingFields, extraData, options);
339
339
  }
340
340
 
341
341
  /// Invert the logic of the following operator.
@@ -487,12 +487,12 @@ class ModelScope extends QueryEngineBase {
487
487
  /// There are five variants to this method, `ORDER.ASC`,
488
488
  /// `ORDER.DESC`, `ORDER.ADD`, `ORDER.REPLACE`, and `ORDER` (which is an alias for `ORDER.ASC`), .
489
489
  /// 1) `ORDER` - Alias for `ORDER.ASC`.
490
- /// 2) `ORDER.ASC` - Follow the rules of <see>ModelScope.margeFields</see>. Each field/literal added is in `ASC` order.
491
- /// 3) `ORDER.DESC` - Follow the rules of <see>ModelScope.margeFields</see>. Each field/literal added is in `DESC` order.
492
- /// 4) `ORDER.ADD` - **DO NOT** follow the rules of <see>ModelScope.margeFields</see>, and instead **add** all fields specified, with their sort order being specified instead by the `+` or `-` prefixes on each field.
493
- /// 5) `ORDER.REPLACE` - **DO NOT** follow the rules of <see>ModelScope.margeFields</see>, and instead **replace** the operation fields to the fields specified, with their sort order being specified instead by the `+` or `-` prefixes on each field.
490
+ /// 2) `ORDER.ASC` - Follow the rules of <see>ModelScope.mergeFields</see>. Each field/literal added is in `ASC` order.
491
+ /// 3) `ORDER.DESC` - Follow the rules of <see>ModelScope.mergeFields</see>. Each field/literal added is in `DESC` order.
492
+ /// 4) `ORDER.ADD` - **DO NOT** follow the rules of <see>ModelScope.mergeFields</see>, and instead **add** all fields specified, with their sort order being specified instead by the `+` or `-` prefixes on each field.
493
+ /// 5) `ORDER.REPLACE` - **DO NOT** follow the rules of <see>ModelScope.mergeFields</see>, and instead **replace** the operation fields to the fields specified, with their sort order being specified instead by the `+` or `-` prefixes on each field.
494
494
  ///
495
- /// See <see>ModelScope.margeFields</see> to better understand how this method works.
495
+ /// See <see>ModelScope.mergeFields</see> to better understand how this method works.
496
496
  ///
497
497
  /// SyntaxType: FunctionDeclaration
498
498
  ///
@@ -513,7 +513,7 @@ class ModelScope extends QueryEngineBase {
513
513
 
514
514
  /// Apply a `GROUP BY` clause to the query.
515
515
  ///
516
- /// See <see>ModelScope.margeFields</see> to better understand how this method works.
516
+ /// See <see>ModelScope.mergeFields</see> to better understand how this method works.
517
517
  ///
518
518
  /// Note:
519
519
  /// This method will flatten all provided arguments into a one dimensional array,
@@ -548,7 +548,7 @@ class ModelScope extends QueryEngineBase {
548
548
  }).filter(Boolean);
549
549
 
550
550
  let context = this.getOperationContext();
551
- let groupBy = this.margeFields(
551
+ let groupBy = this.mergeFields(
552
552
  context.groupBy,
553
553
  entities,
554
554
  {},
@@ -643,7 +643,7 @@ class ModelScope extends QueryEngineBase {
643
643
 
644
644
  /// Replace, add to, or subtract from the projection of the query.
645
645
  ///
646
- /// See <see>ModelScope.margeFields</see> to better understand how this method works.
646
+ /// See <see>ModelScope.mergeFields</see> to better understand how this method works.
647
647
  ///
648
648
  /// Note:
649
649
  /// This method will flatten all provided arguments into a one dimensional array,
@@ -691,7 +691,7 @@ class ModelScope extends QueryEngineBase {
691
691
  }).filter(Boolean);
692
692
 
693
693
  let context = this.getOperationContext();
694
- let projection = this.margeFields(
694
+ let projection = this.mergeFields(
695
695
  context.projection,
696
696
  entities,
697
697
  {},
@@ -30,7 +30,7 @@ const {
30
30
  const {
31
31
  parseFilterFieldAndOperator,
32
32
  generateQueryFromFilter,
33
- margeFields,
33
+ mergeFields,
34
34
  } = QueryUtils;
35
35
 
36
36
  const {
@@ -69,7 +69,7 @@ module.exports = {
69
69
  // QueryUtils
70
70
  parseFilterFieldAndOperator,
71
71
  generateQueryFromFilter,
72
- margeFields,
72
+ mergeFields,
73
73
 
74
74
  // AsyncStore
75
75
  getContextStore,
@@ -1,8 +1,25 @@
1
+ ///! import `var { Utils: { MiscUtils } } = require('mythix-orm');`
2
+ ///!
3
+ ///! MiscUtils utilities provide some miscellaneous utility
4
+ ///! functions for assisting with some common operations.
5
+ ///!
6
+ ///! DocScope: MiscUtils
7
+
1
8
  'use strict';
2
9
 
3
10
  const Nife = require('nife');
4
11
  const { DateTime } = require('luxon');
5
12
 
13
+ /// When provided an async iterator,
14
+ /// collect all results from the iterator
15
+ /// until the iterator is exhausted.
16
+ ///
17
+ /// Arguments:
18
+ /// iterator: async * iterator
19
+ /// The async generator iterator to collect items from.
20
+ ///
21
+ /// Return: Promise<Array<any>>
22
+ /// Return the collected results as an array.
6
23
  async function collect(iterator) {
7
24
  let items = [];
8
25
 
@@ -12,6 +29,27 @@ async function collect(iterator) {
12
29
  return items;
13
30
  }
14
31
 
32
+ /// Take a timestamp, a string, a Date instance,
33
+ /// or a Luxon [DateTime](https://moment.github.io/luxon/#/) instance
34
+ /// and convert the input to a Luxon [DateTime](https://moment.github.io/luxon/#/) instance.
35
+ ///
36
+ /// Arguments:
37
+ /// value: number | string | Date | [DateTime](https://moment.github.io/luxon/#/)
38
+ /// The value to use to create a new Luxon [DateTime](https://moment.github.io/luxon/#/) instance.
39
+ /// If this is a `number` or `bigint`, then it will be assumed this is a timestamp, and
40
+ /// the provided `format` will be ignored. If this is a `string`, the provided `format`
41
+ /// will be used to parse it into a `DateTime` instance. If this is a `Date` instance, then
42
+ /// it will be converted to a Luxon `DateTime` instance. A Luxon `DateTime` instance will
43
+ /// simply be returned.
44
+ /// format?: string
45
+ /// The Luxon [DateTime](https://moment.github.io/luxon/#/) format used to parse
46
+ /// the date/time if the provided `value` is a string. This is only required if
47
+ /// the format is something that Luxon doesn't natively understand (i.e. an ISO format).
48
+ ///
49
+ /// Return: [DateTime](https://moment.github.io/luxon/#/)
50
+ /// The newly created Luxon `DateTime` instance. If a Luxon `DateTime`
51
+ /// instance was provided as the `value` argument, then it will simply
52
+ /// be returned.
15
53
  function valueToDateTime(value, format) {
16
54
  if (DateTime.isDateTime(value)) {
17
55
  return value;
@@ -11,7 +11,7 @@ export declare function generateQueryFromFilter(
11
11
  filter: Array<GenericObject | Model> | GenericObject | Model,
12
12
  ): QueryEngine;
13
13
 
14
- export declare function margeFields(
14
+ export declare function mergeFields(
15
15
  connection: ConnectionBase,
16
16
  currentFields: Map<string, any>,
17
17
  incomingFields: Array<any>
@@ -1,38 +1,15 @@
1
+ ///! import `var { Utils: { QueryUtils } } = require('mythix-orm');`
2
+ ///!
3
+ ///! QueryUtils provide utility functions
4
+ ///! for creating and interacting with queries
5
+ ///! ([QueryEngine](https://github.com/th317erd/mythix-orm/wiki/QueryEngine)).
6
+ ///!
7
+ ///! DocScope: QueryUtils
8
+
1
9
  'use strict';
2
10
 
3
11
  const Nife = require('nife');
4
12
 
5
- // The code below will take a "query object"
6
- // and convert it into Mythix ORM query.
7
- //
8
- // "query objects" are objects with a simple
9
- // structure and convention to build complex queries.
10
- //
11
- // Fields inside these objects can have operators,
12
- // which are postfixed to the field name. For example,
13
- // you could create a filter to find a user by name
14
- // with the following query object:
15
- // { "firstName=": "John", "lastName!=": "Bob" }
16
- // which would find all users with the first name
17
- // of "John", and any last name except "Bob".
18
- //
19
- // AND and OR conditions are also supported. These
20
- // work based of the structure of the object itself.
21
- // If an array is used, then OR is in effect.
22
- // If an object is used, then AND is in effect.
23
- // For example, the following query object:
24
- // [ { firstName: "John", lastName: "Brown" }, { firstName: "Mary", lastName: "Smith" } ]
25
- // would result in the following query:
26
- // WHERE ((firstName = 'John' AND lastName = 'Brown') OR (firstName = 'Mary' AND lastName = 'Smith')),
27
- // finding either user John Brown, or Mary Smith.
28
- //
29
- // IN and NOT IN operators are handled automatically
30
- // when the operator is either "=" or "!=", and the
31
- // provided value is an array. For example:
32
- // { firstName: [ 'John', 'Bob', 'Mary' ] }
33
- // would result in the following query:
34
- // WHERE firstName IN ('John', 'Bob', 'Mary')
35
-
36
13
  const FILTER_OPERATORS = {
37
14
  '=': (Model, fieldName, query, value) => {
38
15
  return query.AND[fieldName].EQ(value);
@@ -70,6 +47,24 @@ const FILTER_OPERATORS = {
70
47
  },
71
48
  };
72
49
 
50
+ /// Take the provided `fieldName`, which might include
51
+ /// an operator as a postfix, and return the operator
52
+ /// and `fieldName` found. If no operator postfix is
53
+ /// present, then the default `=` operator is returned.
54
+ ///
55
+ /// Refer to <see>QueryUtils.generateQueryFromFilter</see> for
56
+ /// a better understanding of what this does and why it is needed.
57
+ ///
58
+ /// Arguments:
59
+ /// fieldName: string
60
+ /// The field name to parse, with an optional operator postfix added.
61
+ ///
62
+ /// Return: { field: string; operator: string; }
63
+ /// Return the parsed `field`, and the parsed `operator`. If no
64
+ /// operator postfix is on the field, then the default is the `=`
65
+ /// operator.
66
+ ///
67
+ /// See: QueryUtils.generateQueryFromFilter
73
68
  function parseFilterFieldAndOperator(fieldName) {
74
69
  let operator = '=';
75
70
  let field;
@@ -90,6 +85,74 @@ function parseFilterFieldAndOperator(fieldName) {
90
85
  return { field, operator };
91
86
  }
92
87
 
88
+ /// Take a "query object" and convert it into Mythix ORM query.
89
+ ///
90
+ /// "query objects" are objects with a simple
91
+ /// structure and convention to build complex queries.
92
+ ///
93
+ /// Fields inside these objects can have operators,
94
+ /// which are postfixed to the field name. For example,
95
+ /// you could create a filter to find a user by name
96
+ /// with the following query object:
97
+ /// `{ "firstName=": "John", "lastName!=": "Bob" }`
98
+ /// which would find all users with the first name
99
+ /// of "John", and any last name except "Bob".
100
+ ///
101
+ /// `AND` and `OR` conditions are also supported. These
102
+ /// work based of the structure of the object itself.
103
+ /// If an array is used, then `OR` is in effect.
104
+ /// If an object is used, then `AND` is in effect.
105
+ /// For example, the following query object:
106
+ /// `[ { firstName: "John", lastName: "Brown" }, { firstName: "Mary", lastName: "Smith" } ]`
107
+ /// would result in the following SQL query:
108
+ /// `WHERE ((firstName = 'John' AND lastName = 'Brown') OR (firstName = 'Mary' AND lastName = 'Smith'))`,
109
+ /// finding either user John Brown, or Mary Smith.
110
+ ///
111
+ /// `IN` and `NOT IN` operators are handled automatically
112
+ /// when the operator is either `=` or `!=`, and the
113
+ /// provided value is an array. For example:
114
+ /// `{ firstName: [ 'John', 'Bob', 'Mary' ] }`
115
+ /// would result in the following SQL query:
116
+ /// `WHERE firstName IN ('John', 'Bob', 'Mary')`.
117
+ ///
118
+ /// Operators that can be postfixed to field names
119
+ /// in the provided `filter` object are as follows:
120
+ /// | Operator | Description |
121
+ /// | `=` | Equality operator. If an `Array` of values is provided, then this will turn into a `IN` operation in the underlying database. |
122
+ /// | `!=` | Inverse (not) equality operator. If an `Array` of values is provided, then this will turn into a `NOT IN` operation in the underlying database. |
123
+ /// | `>` | Greater than operator. |
124
+ /// | `>=` | Greater than or equal to operator. |
125
+ /// | `<` | Less than operator. |
126
+ /// | `<=` | Less than or equal to operator. |
127
+ /// | `><` | Between operator. This operator requires that the provided value be an array with exactly two elements: `[ min, max ]`. |
128
+ /// | `<>` | Inverse (not) between operator. This operator requires that the provided value be an array with exactly two elements: `[ min, max ]`. |
129
+ /// | `*` | A `LIKE` wildcard matching operator. The provided value should use `%` for "zero or more" matches, and `_` for "any single character" match. |
130
+ /// | `!*` | A `NOT LIKE` wildcard matching operator. The provided value should use `%` for "zero or more" matches, and `_` for "any single character" match. |
131
+ ///
132
+ /// Note:
133
+ /// This is a simple interface to take an "object" and turn it into
134
+ /// a <see>QueryEngine</see>. It doesn't allow multiple models
135
+ /// to be defined at once (table-joins), nor other complex operations.
136
+ /// If you need more complex operations on your query, you will need
137
+ /// to manually create your query... though this method can be used
138
+ /// as a starting point.
139
+ ///
140
+ /// Arguments:
141
+ /// connection: <see>Connection</see>
142
+ /// The connection used to create the <see>QueryEngine</see>.
143
+ /// Model: class <see>Model</see>
144
+ /// The model the query is being generated for. The specified
145
+ /// fields provided via the `filter` argument should all be from
146
+ /// this model.
147
+ /// filter: object | Array
148
+ /// An object or an array of objects to build a query from. Any
149
+ /// object will have all its properties `AND`ed together... whereas
150
+ /// any array will have its sub-objects `OR`ed together. i.e.
151
+ /// `[ { prop1 AND prop2 AND prop3 } OR { prop1 AND prop2 AND prop3 } ]`.
152
+ ///
153
+ /// Return: <see>QueryEngine</see>
154
+ /// The new query for the `Model` provided, generated from the
155
+ /// provided `filter` argument.
93
156
  function generateQueryFromFilter(connection, Model, _filter, _depth) {
94
157
  const getOperator = (name) => {
95
158
  let func = FILTER_OPERATORS[name];
@@ -180,7 +243,35 @@ function generateQueryFromFilter(connection, Model, _filter, _depth) {
180
243
  return query;
181
244
  }
182
245
 
183
- function margeFields(queryEngine, currentFields, _incomingFields, extraData, _options) {
246
+ /// Merge fields for a `PROJECT`, `ORDER`,
247
+ /// or `GROUP_BY` <see>QueryEngine</see> operation.
248
+ ///
249
+ /// See <see>ModelScope.mergeFields</see> for a more detailed description
250
+ /// of what this method does and how it is used.
251
+ ///
252
+ /// Arguments:
253
+ /// queryEngine: <see>QueryEngine</see>
254
+ /// The <see>QueryEngine</see> instance that the `PROJECT`,
255
+ /// `ORDER`, or `GROUP_BY` operation is being applied to.
256
+ /// currentFields: Map<string, object>
257
+ /// A map of the current fields that have been applied to the
258
+ /// given operation.
259
+ /// incomingFields: Array<string | Literal | Model | Field>
260
+ /// A list of all the incoming fields that are supplied to the
261
+ /// `PROJECT`, `ORDER`, or `GROUP_BY` operation that is being carried
262
+ /// out. This will either merge will `currentFields`, or replace
263
+ /// the `currentFields`, depending on the content of this argument.
264
+ /// extraData?: object
265
+ /// If supplied, then merge these extra properties into each field being
266
+ /// added to the list of fields. This is used for example by the `ORDER`
267
+ /// operation to define the `direction` property for each field added.
268
+ /// options?: object
269
+ /// Options for the operation. These are only used when stringifying
270
+ /// literals that are being added to the field list. See <see>LiteralBase.toString</see>
271
+ /// for more information.
272
+ ///
273
+ /// See: ModelScope.mergeFields
274
+ function mergeFields(queryEngine, currentFields, _incomingFields, extraData, _options) {
184
275
  const RESET = 0;
185
276
  const ADD = 1;
186
277
  const SUB = 2;
@@ -256,7 +347,7 @@ function margeFields(queryEngine, currentFields, _incomingFields, extraData, _op
256
347
 
257
348
  if (typeof incomingField.isLiteral === 'function' && incomingField.isLiteral(incomingField)) {
258
349
  if (!connection)
259
- throw new Error('QueryUtils::margeFields: "connection" is required, but not found.');
350
+ throw new Error('QueryUtils::mergeFields: "connection" is required, but not found.');
260
351
 
261
352
  let result = incomingField.toString(connection, options);
262
353
  addOrRemove(mode, result, result);
@@ -278,7 +369,7 @@ function margeFields(queryEngine, currentFields, _incomingFields, extraData, _op
278
369
  continue;
279
370
 
280
371
  if (!connection)
281
- throw new Error('QueryUtils::margeFields: "connection" is required, but not found.');
372
+ throw new Error('QueryUtils::mergeFields: "connection" is required, but not found.');
282
373
 
283
374
  if (!incomingField)
284
375
  continue;
@@ -330,7 +421,7 @@ function margeFields(queryEngine, currentFields, _incomingFields, extraData, _op
330
421
 
331
422
  let Model = connection.getModel(def.modelName);
332
423
  if (!Model)
333
- throw new Error(`QueryUtils::margeFields: Model "${def.modelName}" not found.`);
424
+ throw new Error(`QueryUtils::mergeFields: Model "${def.modelName}" not found.`);
334
425
 
335
426
  if (Nife.isEmpty(def.fieldNames)) {
336
427
  addOrRemoveAllModelFields(currentMode, Model);
@@ -341,7 +432,7 @@ function margeFields(queryEngine, currentFields, _incomingFields, extraData, _op
341
432
  let fieldName = def.fieldNames[0];
342
433
  let field = connection.getField(fieldName, modelName);
343
434
  if (!field)
344
- throw new Error(`QueryUtils::margeFields: Field "${fieldName}" not found.`);
435
+ throw new Error(`QueryUtils::mergeFields: Field "${fieldName}" not found.`);
345
436
 
346
437
  let fullFieldName = `${modelName}:${fieldName}`;
347
438
  addOrRemove(currentMode, fullFieldName, field);
@@ -353,5 +444,5 @@ function margeFields(queryEngine, currentFields, _incomingFields, extraData, _op
353
444
  module.exports = {
354
445
  parseFilterFieldAndOperator,
355
446
  generateQueryFromFilter,
356
- margeFields,
447
+ mergeFields,
357
448
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mythix-orm",
3
- "version": "1.12.0",
3
+ "version": "1.13.1",
4
4
  "description": "ORM for Mythix framework",
5
5
  "main": "lib/index",
6
6
  "type": "commonjs",