mythix-orm 1.12.0 → 1.13.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.
- package/README.md +6 -4
- package/lib/connection/connection-base.js +3 -5
- package/lib/connection/literals/literal-base.js +14 -2
- package/lib/model.js +16 -2
- package/lib/query-engine/model-scope.js +12 -12
- package/lib/utils/index.js +2 -2
- package/lib/utils/misc-utils.js +38 -0
- package/lib/utils/query-utils.d.ts +1 -1
- package/lib/utils/query-utils.js +129 -37
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# mythix-orm
|
|
2
2
|
|
|
3
|
-
Mythix
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
Mythix ORM aims to replace Sequelize, Prisma, and the few other terrible solutions that the poor destitute Node community has to work with. Mythix ORM has been designed to replace all current ORMs for Node, with a focus on what is lacking in the community, namely good engineering, good documentation, and ease of use.
|
|
4
6
|
|
|
5
7
|
Mythix ORMs feature set includes:
|
|
6
8
|
1. An advanced, seamless, and powerful (yet simple) query engine that is easy to use, and works across database drivers, even for No-SQL databases.
|
|
@@ -28,7 +30,7 @@ Just start creating models!
|
|
|
28
30
|
|
|
29
31
|
```javascript
|
|
30
32
|
const { Model, Types } = require('mythix-orm');
|
|
31
|
-
const SQLiteConnection = require('mythix-orm-sqlite');
|
|
33
|
+
const { SQLiteConnection } = require('mythix-orm-sqlite');
|
|
32
34
|
|
|
33
35
|
class User extends Model {
|
|
34
36
|
static fields = {
|
|
@@ -97,10 +99,10 @@ class User extends Model {
|
|
|
97
99
|
|
|
98
100
|
## Notes
|
|
99
101
|
|
|
100
|
-
1. The [WIKI](https://github.com/th317erd/mythix-orm/wiki) is still being worked on. Most of the documentation is complete, but there is still a lot more to write. Documentation is the main focus right now.
|
|
102
|
+
1. The [WIKI](https://github.com/th317erd/mythix-orm/wiki) is still being worked on. Most of the documentation is complete, but there is still a lot more to write. Documentation is the main focus right now. If you have any questions, feel free to drop a line, or open an issue! We will be happy to answer any questions. We aren't "done" until our documentation is pristine.
|
|
101
103
|
2. Right now there are only database drivers for [SQLite](https://www.npmjs.com/package/mythix-orm-sqlite) and [PostgreSQL](https://www.npmjs.com/package/mythix-orm-postgresql). More are planned, with a Mongo driver likely to land next, followed by MySQL. Help wanted!
|
|
102
104
|
3. Check out the [Mythix](https://www.npmjs.com/package/mythix) web-app framework. It is also still in active development, and the documentation is poor (to say the least), but it is up and coming, and will soon have fantastic documentation, and even though still in active development is fully functional. To get started try `npx mythix-cli create 'Test App'`
|
|
103
105
|
|
|
104
106
|
## Goals
|
|
105
107
|
|
|
106
|
-
The `Mythix` suite of technologies are being developed to give a rock-solid full-stack to build web-apps on Node. I got tired of the piecemeal garbage that currently exist in the Node ecosystem for building apps. My end goal is to have `Mythix` technologies take the Node community by storm, providing top-notch technologies for developers to create amazing things. Get involved with me, and let's change the world for the better!
|
|
108
|
+
The `Mythix` suite of technologies are being developed to give a rock-solid full-stack framework to build web-apps on Node. I got tired of the piecemeal garbage that currently exist in the Node ecosystem for building apps. My end goal is to have `Mythix` technologies take the Node community by storm, providing top-notch technologies for developers to create amazing things. Get involved with me, and let's change the world for the better!
|
|
@@ -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:
|
|
400
|
-
///
|
|
401
|
-
///
|
|
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
|
|
248
|
-
|
|
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
|
|
1776
|
-
|
|
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.
|
|
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
|
-
|
|
338
|
-
return QueryUtils.
|
|
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.
|
|
491
|
-
/// 3) `ORDER.DESC` - Follow the rules of <see>ModelScope.
|
|
492
|
-
/// 4) `ORDER.ADD` - **DO NOT** follow the rules of <see>ModelScope.
|
|
493
|
-
/// 5) `ORDER.REPLACE` - **DO NOT** follow the rules of <see>ModelScope.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
694
|
+
let projection = this.mergeFields(
|
|
695
695
|
context.projection,
|
|
696
696
|
entities,
|
|
697
697
|
{},
|
package/lib/utils/index.js
CHANGED
|
@@ -30,7 +30,7 @@ const {
|
|
|
30
30
|
const {
|
|
31
31
|
parseFilterFieldAndOperator,
|
|
32
32
|
generateQueryFromFilter,
|
|
33
|
-
|
|
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
|
-
|
|
72
|
+
mergeFields,
|
|
73
73
|
|
|
74
74
|
// AsyncStore
|
|
75
75
|
getContextStore,
|
package/lib/utils/misc-utils.js
CHANGED
|
@@ -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
|
|
14
|
+
export declare function mergeFields(
|
|
15
15
|
connection: ConnectionBase,
|
|
16
16
|
currentFields: Map<string, any>,
|
|
17
17
|
incomingFields: Array<any>
|
package/lib/utils/query-utils.js
CHANGED
|
@@ -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,75 @@ 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
|
+
/// | -------- | ----------- |
|
|
122
|
+
/// | `=` | Equality operator. If an `Array` of values is provided, then this will turn into a `IN` operation in the underlying database. |
|
|
123
|
+
/// | `!=` | Inverse (not) equality operator. If an `Array` of values is provided, then this will turn into a `NOT IN` operation in the underlying database. |
|
|
124
|
+
/// | `>` | Greater than operator. |
|
|
125
|
+
/// | `>=` | Greater than or equal to operator. |
|
|
126
|
+
/// | `<` | Less than operator. |
|
|
127
|
+
/// | `<=` | Less than or equal to operator. |
|
|
128
|
+
/// | `><` | Between operator. This operator requires that the provided value be an array with exactly two elements: `[ min, max ]`. |
|
|
129
|
+
/// | `<>` | Inverse (not) between operator. This operator requires that the provided value be an array with exactly two elements: `[ min, max ]`. |
|
|
130
|
+
/// | `*` | A `LIKE` wildcard matching operator. The provided value should use `%` for "zero or more" matches, and `_` for "any single character" match. |
|
|
131
|
+
/// | `!*` | A `NOT LIKE` wildcard matching operator. The provided value should use `%` for "zero or more" matches, and `_` for "any single character" match. |
|
|
132
|
+
///
|
|
133
|
+
/// Note:
|
|
134
|
+
/// This is a simple interface to take an "object" and turn it into
|
|
135
|
+
/// a <see>QueryEngine</see>. It doesn't allow multiple models
|
|
136
|
+
/// to be defined at once (table-joins), nor other complex operations.
|
|
137
|
+
/// If you need more complex operations on your query, you will need
|
|
138
|
+
/// to manually create your query... though this method can be used
|
|
139
|
+
/// as a starting point.
|
|
140
|
+
///
|
|
141
|
+
/// Arguments:
|
|
142
|
+
/// connection: <see>Connection</see>
|
|
143
|
+
/// The connection used to create the <see>QueryEngine</see>.
|
|
144
|
+
/// Model: class <see>Model</see>
|
|
145
|
+
/// The model the query is being generated for. The specified
|
|
146
|
+
/// fields provided via the `filter` argument should all be from
|
|
147
|
+
/// this model.
|
|
148
|
+
/// filter: object | Array
|
|
149
|
+
/// An object or an array of objects to build a query from. Any
|
|
150
|
+
/// object will have all its properties `AND`ed together... whereas
|
|
151
|
+
/// any array will have its sub-objects `OR`ed together. i.e.
|
|
152
|
+
/// `[ { prop1 AND prop2 AND prop3 } OR { prop1 AND prop2 AND prop3 } ]`.
|
|
153
|
+
///
|
|
154
|
+
/// Return: <see>QueryEngine</see>
|
|
155
|
+
/// The new query for the `Model` provided, generated from the
|
|
156
|
+
/// provided `filter` argument.
|
|
93
157
|
function generateQueryFromFilter(connection, Model, _filter, _depth) {
|
|
94
158
|
const getOperator = (name) => {
|
|
95
159
|
let func = FILTER_OPERATORS[name];
|
|
@@ -180,7 +244,35 @@ function generateQueryFromFilter(connection, Model, _filter, _depth) {
|
|
|
180
244
|
return query;
|
|
181
245
|
}
|
|
182
246
|
|
|
183
|
-
|
|
247
|
+
/// Merge fields for a `PROJECT`, `ORDER`,
|
|
248
|
+
/// or `GROUP_BY` <see>QueryEngine</see> operation.
|
|
249
|
+
///
|
|
250
|
+
/// See <see>ModelScope.mergeFields</see> for a more detailed description
|
|
251
|
+
/// of what this method does and how it is used.
|
|
252
|
+
///
|
|
253
|
+
/// Arguments:
|
|
254
|
+
/// queryEngine: <see>QueryEngine</see>
|
|
255
|
+
/// The <see>QueryEngine</see> instance that the `PROJECT`,
|
|
256
|
+
/// `ORDER`, or `GROUP_BY` operation is being applied to.
|
|
257
|
+
/// currentFields: Map<string, object>
|
|
258
|
+
/// A map of the current fields that have been applied to the
|
|
259
|
+
/// given operation.
|
|
260
|
+
/// incomingFields: Array<string | Literal | Model | Field>
|
|
261
|
+
/// A list of all the incoming fields that are supplied to the
|
|
262
|
+
/// `PROJECT`, `ORDER`, or `GROUP_BY` operation that is being carried
|
|
263
|
+
/// out. This will either merge will `currentFields`, or replace
|
|
264
|
+
/// the `currentFields`, depending on the content of this argument.
|
|
265
|
+
/// extraData?: object
|
|
266
|
+
/// If supplied, then merge these extra properties into each field being
|
|
267
|
+
/// added to the list of fields. This is used for example by the `ORDER`
|
|
268
|
+
/// operation to define the `direction` property for each field added.
|
|
269
|
+
/// options?: object
|
|
270
|
+
/// Options for the operation. These are only used when stringifying
|
|
271
|
+
/// literals that are being added to the field list. See <see>LiteralBase.toString</see>
|
|
272
|
+
/// for more information.
|
|
273
|
+
///
|
|
274
|
+
/// See: ModelScope.mergeFields
|
|
275
|
+
function mergeFields(queryEngine, currentFields, _incomingFields, extraData, _options) {
|
|
184
276
|
const RESET = 0;
|
|
185
277
|
const ADD = 1;
|
|
186
278
|
const SUB = 2;
|
|
@@ -256,7 +348,7 @@ function margeFields(queryEngine, currentFields, _incomingFields, extraData, _op
|
|
|
256
348
|
|
|
257
349
|
if (typeof incomingField.isLiteral === 'function' && incomingField.isLiteral(incomingField)) {
|
|
258
350
|
if (!connection)
|
|
259
|
-
throw new Error('QueryUtils::
|
|
351
|
+
throw new Error('QueryUtils::mergeFields: "connection" is required, but not found.');
|
|
260
352
|
|
|
261
353
|
let result = incomingField.toString(connection, options);
|
|
262
354
|
addOrRemove(mode, result, result);
|
|
@@ -278,7 +370,7 @@ function margeFields(queryEngine, currentFields, _incomingFields, extraData, _op
|
|
|
278
370
|
continue;
|
|
279
371
|
|
|
280
372
|
if (!connection)
|
|
281
|
-
throw new Error('QueryUtils::
|
|
373
|
+
throw new Error('QueryUtils::mergeFields: "connection" is required, but not found.');
|
|
282
374
|
|
|
283
375
|
if (!incomingField)
|
|
284
376
|
continue;
|
|
@@ -330,7 +422,7 @@ function margeFields(queryEngine, currentFields, _incomingFields, extraData, _op
|
|
|
330
422
|
|
|
331
423
|
let Model = connection.getModel(def.modelName);
|
|
332
424
|
if (!Model)
|
|
333
|
-
throw new Error(`QueryUtils::
|
|
425
|
+
throw new Error(`QueryUtils::mergeFields: Model "${def.modelName}" not found.`);
|
|
334
426
|
|
|
335
427
|
if (Nife.isEmpty(def.fieldNames)) {
|
|
336
428
|
addOrRemoveAllModelFields(currentMode, Model);
|
|
@@ -341,7 +433,7 @@ function margeFields(queryEngine, currentFields, _incomingFields, extraData, _op
|
|
|
341
433
|
let fieldName = def.fieldNames[0];
|
|
342
434
|
let field = connection.getField(fieldName, modelName);
|
|
343
435
|
if (!field)
|
|
344
|
-
throw new Error(`QueryUtils::
|
|
436
|
+
throw new Error(`QueryUtils::mergeFields: Field "${fieldName}" not found.`);
|
|
345
437
|
|
|
346
438
|
let fullFieldName = `${modelName}:${fieldName}`;
|
|
347
439
|
addOrRemove(currentMode, fullFieldName, field);
|
|
@@ -353,5 +445,5 @@ function margeFields(queryEngine, currentFields, _incomingFields, extraData, _op
|
|
|
353
445
|
module.exports = {
|
|
354
446
|
parseFilterFieldAndOperator,
|
|
355
447
|
generateQueryFromFilter,
|
|
356
|
-
|
|
448
|
+
mergeFields,
|
|
357
449
|
};
|