mythix-orm 1.10.2 → 1.11.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.
- package/lib/connection/connection-base.js +4 -20
- package/lib/connection/literals/literal-base.js +20 -1
- package/lib/connection/query-generator-base.d.ts +1 -15
- package/lib/connection/query-generator-base.js +55 -583
- package/lib/field.js +9 -2
- package/lib/model.d.ts +0 -2
- package/lib/model.js +58 -71
- package/lib/query-engine/field-scope.js +57 -24
- package/lib/query-engine/model-scope.js +232 -35
- package/lib/query-engine/query-engine-base.js +46 -13
- package/lib/query-engine/query-engine.d.ts +18 -4
- package/lib/query-engine/query-engine.js +47 -54
- package/lib/types/concrete/datetime-type.js +1 -1
- package/lib/types/concrete/serialized-type.js +2 -2
- package/lib/types/virtual/model-type.js +3 -3
- package/lib/types/virtual/relational-type-base.js +24 -24
- package/lib/utils/index.js +2 -2
- package/lib/utils/misc-utils.d.ts +0 -1
- package/lib/utils/misc-utils.js +0 -24
- package/lib/utils/model-utils.js +17 -17
- package/lib/utils/query-utils.d.ts +7 -0
- package/lib/utils/query-utils.js +175 -4
- package/package.json +1 -1
package/lib/field.js
CHANGED
|
@@ -118,8 +118,8 @@
|
|
|
118
118
|
/// If `true`, then this field will be indexed in the database.
|
|
119
119
|
/// If an array is provided, then it must be an array containing
|
|
120
120
|
/// other field names to combine with this field to create a combined
|
|
121
|
-
/// index. For example: `index: [ true, 'firstName', 'lastName', [ 'firstName', 'lastName' ] ]`
|
|
122
|
-
///
|
|
121
|
+
/// index. For example: `index: [ true, 'firstName', 'lastName', [ 'firstName', 'lastName' ] ]` would create four indexes:
|
|
122
|
+
/// `true` means index this field, `firstName` would create
|
|
123
123
|
/// the combination index `this_field_first_name`, `lastName` would create the
|
|
124
124
|
/// combination index `this_field_last_name`, and `[ 'firstName', 'lastName ]` would
|
|
125
125
|
/// create the combination index `this_field_first_name_last_name`.
|
|
@@ -234,6 +234,13 @@ class Field {
|
|
|
234
234
|
return false;
|
|
235
235
|
}
|
|
236
236
|
|
|
237
|
+
isField(value) {
|
|
238
|
+
if (arguments.length === 0)
|
|
239
|
+
return true;
|
|
240
|
+
|
|
241
|
+
return this.constructor.isField(value);
|
|
242
|
+
}
|
|
243
|
+
|
|
237
244
|
/// Construct a Field.
|
|
238
245
|
///
|
|
239
246
|
/// This method will create a new instance of <see>Field</see>.
|
package/lib/model.d.ts
CHANGED
|
@@ -170,8 +170,6 @@ export declare class Model {
|
|
|
170
170
|
public static getConcreteFieldCount(): number;
|
|
171
171
|
public getConcreteFieldCount(): number;
|
|
172
172
|
|
|
173
|
-
public static defaultOrder(options?: GenericObject): Array<string>;
|
|
174
|
-
|
|
175
173
|
public static getWhereWithConnection(options?: { connection: ConnectionBase }): QueryEngine;
|
|
176
174
|
public getWhereWithConnection(options?: { connection: ConnectionBase }): QueryEngine;
|
|
177
175
|
|
package/lib/model.js
CHANGED
|
@@ -104,8 +104,8 @@ function bindStaticWhereToModelClass(ModelClass) {
|
|
|
104
104
|
};
|
|
105
105
|
|
|
106
106
|
Object.defineProperties(ModelClass, {
|
|
107
|
-
'where':
|
|
108
|
-
'$':
|
|
107
|
+
'where': whereProp,
|
|
108
|
+
'$': whereProp,
|
|
109
109
|
});
|
|
110
110
|
}
|
|
111
111
|
|
|
@@ -557,7 +557,7 @@ class Model {
|
|
|
557
557
|
get: () => {
|
|
558
558
|
return ModelClass.getQueryEngine(ModelClass._getConnection() || connection);
|
|
559
559
|
},
|
|
560
|
-
set:
|
|
560
|
+
set: () => {},
|
|
561
561
|
};
|
|
562
562
|
|
|
563
563
|
Object.defineProperties(ModelClass, {
|
|
@@ -567,8 +567,8 @@ class Model {
|
|
|
567
567
|
configurable: true,
|
|
568
568
|
value: connection,
|
|
569
569
|
},
|
|
570
|
-
'where':
|
|
571
|
-
'$':
|
|
570
|
+
'where': whereProp,
|
|
571
|
+
'$': whereProp,
|
|
572
572
|
});
|
|
573
573
|
|
|
574
574
|
return ModelClass;
|
|
@@ -637,14 +637,16 @@ class Model {
|
|
|
637
637
|
|
|
638
638
|
/// One can apply a "default scope" to any model simply
|
|
639
639
|
/// by providing this static method on the model. By
|
|
640
|
-
/// default it will simply return the <see
|
|
640
|
+
/// default it will simply return the <see>QueryEngine</see>
|
|
641
641
|
/// instance (a query) that it was provided.
|
|
642
642
|
///
|
|
643
643
|
/// The way this works is simple. The caller provides the
|
|
644
644
|
/// query as the first and only argument. The user, by
|
|
645
645
|
/// overloading this method can then easily modify this
|
|
646
646
|
/// provided query, changing the "default scope" whenever
|
|
647
|
-
/// the model is used for queries.
|
|
647
|
+
/// the model is used for queries. This can be used to set
|
|
648
|
+
/// a default order for a model, or to set default conditions
|
|
649
|
+
/// to apply to any query for the model. For example, let's say
|
|
648
650
|
/// you have a User model, and by default, you only want
|
|
649
651
|
/// to query against Users that have their `active` column
|
|
650
652
|
/// set to `true`. In order to do this, you could easily
|
|
@@ -656,7 +658,7 @@ class Model {
|
|
|
656
658
|
/// static defaultScope(baseQuery) {
|
|
657
659
|
/// // `baseQuery` is equal to `User.where`
|
|
658
660
|
/// // without a default scope.
|
|
659
|
-
/// return baseQuery.active.EQ(true);
|
|
661
|
+
/// return baseQuery.active.EQ(true).ORDER.ASC('User:createdAt');
|
|
660
662
|
/// }
|
|
661
663
|
/// }
|
|
662
664
|
///
|
|
@@ -1607,37 +1609,6 @@ class Model {
|
|
|
1607
1609
|
return this.constructor.getConcreteFieldCount();
|
|
1608
1610
|
}
|
|
1609
1611
|
|
|
1610
|
-
/// Specify the default SELECT "ORDER" for the model.
|
|
1611
|
-
/// This method will be called if no "ORDER" was specified
|
|
1612
|
-
/// for any given query. This method should return an
|
|
1613
|
-
/// Array of fully qualified field names. The `options`
|
|
1614
|
-
/// argument is the current options for the specific
|
|
1615
|
-
/// query being operated on.
|
|
1616
|
-
///
|
|
1617
|
-
/// Note:
|
|
1618
|
-
/// Internally, Mythix ORM will call `this.connection.defaultOrder(options)`,
|
|
1619
|
-
/// which--if not overloaded--will simply call this method from the
|
|
1620
|
-
/// model itself.
|
|
1621
|
-
///
|
|
1622
|
-
/// Note:
|
|
1623
|
-
/// A "fully qualified field name" in Mythix ORM means
|
|
1624
|
-
/// a full field definition, including the model name.
|
|
1625
|
-
/// An example of a fully qualified field name might be
|
|
1626
|
-
/// `"User:id"`. The model name is separated from the field
|
|
1627
|
-
/// name by a colon `:`. A short hand, *not fully qualified
|
|
1628
|
-
/// field name* example would be simply `"id"`. Since this
|
|
1629
|
-
/// contains no model prefix, this is "short hand", and is
|
|
1630
|
-
/// **not** a fully qualified field name.
|
|
1631
|
-
///
|
|
1632
|
-
/// Return: Array<string> | null
|
|
1633
|
-
/// An array of fully qualified field names to specify the ORDER.
|
|
1634
|
-
/// Prefix a field name with `+` to specify ASCending order, i.e.
|
|
1635
|
-
/// `[ "+User:id" ]`. Prefix the field name with `-` to specify
|
|
1636
|
-
/// DESCending order, i.e. `[ "-User:id" ]`.
|
|
1637
|
-
// eslint-disable-next-line no-unused-vars
|
|
1638
|
-
static defaultOrder(options) {
|
|
1639
|
-
}
|
|
1640
|
-
|
|
1641
1612
|
/// This method is called anytime a `Model.where` or `Model.$`
|
|
1642
1613
|
/// attribute is accessed. It will provide the instantiated
|
|
1643
1614
|
/// <see>QueryEngine</see> with the connection specified (if any).
|
|
@@ -1705,14 +1676,18 @@ class Model {
|
|
|
1705
1676
|
/// Return the number of models stored in the database for this model type.
|
|
1706
1677
|
///
|
|
1707
1678
|
/// Arguments:
|
|
1679
|
+
/// field?: string | Field
|
|
1680
|
+
/// A fully qualified field name, or a `Field` instance. If supplied, this is the
|
|
1681
|
+
/// column in the table that will be counted (i.e. `COUNT(column)`). If not supplied,
|
|
1682
|
+
/// then a `COUNT(*)` type operation will be carried out.
|
|
1708
1683
|
/// options?: object
|
|
1709
1684
|
/// An "options" object to pass off to the underlying <see>Connection</see> methods.
|
|
1710
1685
|
/// If a `connection` key is specified in this object, then that will be used
|
|
1711
1686
|
/// as the connection for the operation. This can be important if for example
|
|
1712
1687
|
/// you are calling this from a `transaction`, in which case you most certainly
|
|
1713
1688
|
/// would want to provide the `connection` for the transaction.
|
|
1714
|
-
static count(options) {
|
|
1715
|
-
return this.getWhereWithConnection(options).count(
|
|
1689
|
+
static count(field, options) {
|
|
1690
|
+
return this.getWhereWithConnection(options).count(field, options);
|
|
1716
1691
|
}
|
|
1717
1692
|
|
|
1718
1693
|
/// Fetch all models from the database for this model type.
|
|
@@ -1895,7 +1870,7 @@ class Model {
|
|
|
1895
1870
|
get: () => {
|
|
1896
1871
|
return this.constructor.where(this._getConnection());
|
|
1897
1872
|
},
|
|
1898
|
-
set:
|
|
1873
|
+
set: () => {},
|
|
1899
1874
|
};
|
|
1900
1875
|
|
|
1901
1876
|
Object.defineProperties(this, {
|
|
@@ -1965,10 +1940,10 @@ class Model {
|
|
|
1965
1940
|
get: () => {
|
|
1966
1941
|
return this._getDirtyFields();
|
|
1967
1942
|
},
|
|
1968
|
-
set:
|
|
1943
|
+
set: () => {},
|
|
1969
1944
|
},
|
|
1970
|
-
'where':
|
|
1971
|
-
'$':
|
|
1945
|
+
'where': whereProp,
|
|
1946
|
+
'$': whereProp,
|
|
1972
1947
|
});
|
|
1973
1948
|
|
|
1974
1949
|
this._constructor(data);
|
|
@@ -2037,12 +2012,12 @@ class Model {
|
|
|
2037
2012
|
_constructField(fieldName, field) {
|
|
2038
2013
|
Object.defineProperties(this, {
|
|
2039
2014
|
[fieldName]: {
|
|
2040
|
-
enumerable:
|
|
2015
|
+
enumerable: true,
|
|
2041
2016
|
configurable: true,
|
|
2042
2017
|
get: () => {
|
|
2043
2018
|
return this._getFieldValue(fieldName, field);
|
|
2044
2019
|
},
|
|
2045
|
-
set:
|
|
2020
|
+
set: (value) => {
|
|
2046
2021
|
this._setFieldValue(fieldName, field, value);
|
|
2047
2022
|
},
|
|
2048
2023
|
},
|
|
@@ -2087,9 +2062,12 @@ class Model {
|
|
|
2087
2062
|
_initializeModelData(_data) {
|
|
2088
2063
|
let dirtyFieldData = this._dirtyFieldData;
|
|
2089
2064
|
let data = _data || {};
|
|
2065
|
+
let fieldNames = new Set();
|
|
2090
2066
|
|
|
2091
2067
|
// First initialize field values from data
|
|
2092
2068
|
this.iterateFields(({ field, fieldName }) => {
|
|
2069
|
+
fieldNames.add(fieldName);
|
|
2070
|
+
|
|
2093
2071
|
if (!field.type.exposeToModel())
|
|
2094
2072
|
return;
|
|
2095
2073
|
|
|
@@ -2109,6 +2087,15 @@ class Model {
|
|
|
2109
2087
|
let fieldValue = (data) ? data[fieldName] : undefined;
|
|
2110
2088
|
this._initializeFieldData(fieldName, field, fieldValue, data);
|
|
2111
2089
|
});
|
|
2090
|
+
|
|
2091
|
+
let keys = Object.keys(data);
|
|
2092
|
+
for (let i = 0, il = keys.length; i < il; i++) {
|
|
2093
|
+
let key = keys[i];
|
|
2094
|
+
if (fieldNames.has(key))
|
|
2095
|
+
continue;
|
|
2096
|
+
|
|
2097
|
+
this[key] = data[key];
|
|
2098
|
+
}
|
|
2112
2099
|
}
|
|
2113
2100
|
|
|
2114
2101
|
/// This casts a field value to its type,
|
|
@@ -2134,9 +2121,9 @@ class Model {
|
|
|
2134
2121
|
return value;
|
|
2135
2122
|
|
|
2136
2123
|
return type.castToType({
|
|
2137
|
-
connection:
|
|
2138
|
-
Model:
|
|
2139
|
-
self:
|
|
2124
|
+
connection: this.getConnection(),
|
|
2125
|
+
Model: this.getModel(),
|
|
2126
|
+
self: this,
|
|
2140
2127
|
field,
|
|
2141
2128
|
value,
|
|
2142
2129
|
});
|
|
@@ -2194,10 +2181,10 @@ class Model {
|
|
|
2194
2181
|
|
|
2195
2182
|
if (shouldRunDefaultValueOnInitialize()) {
|
|
2196
2183
|
defaultValue = defaultValue({
|
|
2197
|
-
model:
|
|
2198
|
-
self:
|
|
2199
|
-
connection:
|
|
2200
|
-
_initial:
|
|
2184
|
+
model: this,
|
|
2185
|
+
self: this,
|
|
2186
|
+
connection: this.getConnection(),
|
|
2187
|
+
_initial: true,
|
|
2201
2188
|
field,
|
|
2202
2189
|
fieldName,
|
|
2203
2190
|
fieldValue,
|
|
@@ -2223,8 +2210,8 @@ class Model {
|
|
|
2223
2210
|
}
|
|
2224
2211
|
|
|
2225
2212
|
field.type.onSetFieldValue({
|
|
2226
|
-
self:
|
|
2227
|
-
value:
|
|
2213
|
+
self: this,
|
|
2214
|
+
value: initialValue,
|
|
2228
2215
|
field,
|
|
2229
2216
|
fieldName,
|
|
2230
2217
|
});
|
|
@@ -2327,8 +2314,8 @@ class Model {
|
|
|
2327
2314
|
|
|
2328
2315
|
// Does the type itself report that we are dirty?
|
|
2329
2316
|
let value = field.type.isDirty({
|
|
2330
|
-
self:
|
|
2331
|
-
value:
|
|
2317
|
+
self: this,
|
|
2318
|
+
value: this.getDataValue(fieldName),
|
|
2332
2319
|
field,
|
|
2333
2320
|
fieldName,
|
|
2334
2321
|
connection,
|
|
@@ -2427,10 +2414,10 @@ class Model {
|
|
|
2427
2414
|
|
|
2428
2415
|
if (typeof field.get === 'function') {
|
|
2429
2416
|
return field.get.call(this, {
|
|
2430
|
-
model:
|
|
2431
|
-
self:
|
|
2432
|
-
set:
|
|
2433
|
-
get:
|
|
2417
|
+
model: this,
|
|
2418
|
+
self: this,
|
|
2419
|
+
set: this.setDataValue.bind(this, fieldName),
|
|
2420
|
+
get: this.getDataValue.bind(this, fieldName),
|
|
2434
2421
|
value,
|
|
2435
2422
|
field,
|
|
2436
2423
|
fieldName,
|
|
@@ -2473,10 +2460,10 @@ class Model {
|
|
|
2473
2460
|
_setFieldValue(fieldName, field, value) {
|
|
2474
2461
|
if (typeof field.set === 'function') {
|
|
2475
2462
|
field.set.call(this, {
|
|
2476
|
-
model:
|
|
2477
|
-
self:
|
|
2478
|
-
set:
|
|
2479
|
-
get:
|
|
2463
|
+
model: this,
|
|
2464
|
+
self: this,
|
|
2465
|
+
set: this.setDataValue.bind(this, fieldName),
|
|
2466
|
+
get: this.getDataValue.bind(this, fieldName),
|
|
2480
2467
|
value,
|
|
2481
2468
|
field,
|
|
2482
2469
|
fieldName,
|
|
@@ -2675,8 +2662,8 @@ class Model {
|
|
|
2675
2662
|
let newValue = this._castFieldValue(field, value);
|
|
2676
2663
|
|
|
2677
2664
|
field.type.onSetFieldValue({
|
|
2678
|
-
self:
|
|
2679
|
-
value:
|
|
2665
|
+
self: this,
|
|
2666
|
+
value: newValue,
|
|
2680
2667
|
field,
|
|
2681
2668
|
fieldName,
|
|
2682
2669
|
});
|
|
@@ -2815,9 +2802,9 @@ class Model {
|
|
|
2815
2802
|
return false;
|
|
2816
2803
|
|
|
2817
2804
|
return pkField.type.isValidValue(pkValue, {
|
|
2818
|
-
connection:
|
|
2819
|
-
Model:
|
|
2820
|
-
self:
|
|
2805
|
+
connection: this.getConnection(),
|
|
2806
|
+
Model: this.getModel(),
|
|
2807
|
+
self: this,
|
|
2821
2808
|
});
|
|
2822
2809
|
}
|
|
2823
2810
|
|
|
@@ -9,11 +9,13 @@ function addOperatorToQuery(name, inverseName, value, extraOptions) {
|
|
|
9
9
|
throw new Error(`QueryEngine::addOperatorToQuery: ${name}(${value}) makes no sense...`);
|
|
10
10
|
|
|
11
11
|
let conditionalParams = {
|
|
12
|
-
condition:
|
|
13
|
-
operator:
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
condition: true,
|
|
13
|
+
operator: name,
|
|
14
|
+
queryProp: name,
|
|
15
|
+
queryExtraArgs: (extraOptions) ? [ extraOptions ] : [],
|
|
16
|
+
inverseOperator: inverseName,
|
|
17
|
+
value: this._fetchOperatorValue(value),
|
|
18
|
+
hasCondition: true,
|
|
17
19
|
};
|
|
18
20
|
|
|
19
21
|
if (extraOptions)
|
|
@@ -24,9 +26,40 @@ function addOperatorToQuery(name, inverseName, value, extraOptions) {
|
|
|
24
26
|
return this._fetchScope('model');
|
|
25
27
|
}
|
|
26
28
|
|
|
29
|
+
function wrapAnyAll(func) {
|
|
30
|
+
const checkQueryProjection = (_query, subType) => {
|
|
31
|
+
let query = _query;
|
|
32
|
+
let queryContext = (QueryEngineBase.isQuery(query)) ? query.getOperationContext() : null;
|
|
33
|
+
if (!queryContext || !queryContext.hasCondition)
|
|
34
|
+
throw new Error(`QueryEngine::FieldScope::${subType}: Provided value must be a query with conditions.`);
|
|
35
|
+
|
|
36
|
+
if (!queryContext.projection) {
|
|
37
|
+
let Model = queryContext.Model;
|
|
38
|
+
let pkField = Model.getPrimaryKeyField();
|
|
39
|
+
|
|
40
|
+
if (pkField)
|
|
41
|
+
query = query.clone().PROJECT(pkField);
|
|
42
|
+
else
|
|
43
|
+
throw new Error(`QueryEngine::FieldScope::${subType}: Provided query must have only a single field projected.`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return query;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
func.ANY = (_query) => {
|
|
50
|
+
return func.call(this, checkQueryProjection(_query, 'ANY'), { subType: 'ANY' });
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
func.ALL = (_query) => {
|
|
54
|
+
return func.call(this, checkQueryProjection(_query, 'ANY'), { subType: 'ALL' });
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
return func;
|
|
58
|
+
}
|
|
59
|
+
|
|
27
60
|
class FieldScope extends QueryEngineBase {
|
|
28
61
|
NOT = ProxyClass.autoCall(function() {
|
|
29
|
-
this._pushOperationOntoStack({ logical: true, operator: 'NOT', not: !this.currentContext.not });
|
|
62
|
+
this._pushOperationOntoStack({ logical: true, operator: 'NOT', queryProp: 'NOT', not: !this.currentContext.not });
|
|
30
63
|
return this[ProxyClass.PROXY];
|
|
31
64
|
});
|
|
32
65
|
|
|
@@ -65,29 +98,29 @@ class FieldScope extends QueryEngineBase {
|
|
|
65
98
|
return value;
|
|
66
99
|
}
|
|
67
100
|
|
|
68
|
-
EQ(value) {
|
|
69
|
-
return addOperatorToQuery.call(this, 'EQ', 'NEQ', value);
|
|
70
|
-
}
|
|
101
|
+
EQ = wrapAnyAll.call(this, (value, options) => {
|
|
102
|
+
return addOperatorToQuery.call(this, 'EQ', 'NEQ', value, options);
|
|
103
|
+
});
|
|
71
104
|
|
|
72
|
-
NEQ(value) {
|
|
73
|
-
return addOperatorToQuery.call(this, 'NEQ', 'EQ', value);
|
|
74
|
-
}
|
|
105
|
+
NEQ = wrapAnyAll.call(this, (value, options) => {
|
|
106
|
+
return addOperatorToQuery.call(this, 'NEQ', 'EQ', value, options);
|
|
107
|
+
});
|
|
75
108
|
|
|
76
|
-
GT(value) {
|
|
77
|
-
return addOperatorToQuery.call(this, 'GT', 'LTE', value);
|
|
78
|
-
}
|
|
109
|
+
GT = wrapAnyAll.call(this, (value, options) => {
|
|
110
|
+
return addOperatorToQuery.call(this, 'GT', 'LTE', value, options);
|
|
111
|
+
});
|
|
79
112
|
|
|
80
|
-
GTE(value) {
|
|
81
|
-
return addOperatorToQuery.call(this, 'GTE', 'LT', value);
|
|
82
|
-
}
|
|
113
|
+
GTE = wrapAnyAll.call(this, (value, options) => {
|
|
114
|
+
return addOperatorToQuery.call(this, 'GTE', 'LT', value, options);
|
|
115
|
+
});
|
|
83
116
|
|
|
84
|
-
LT(value) {
|
|
85
|
-
return addOperatorToQuery.call(this, 'LT', 'GTE', value);
|
|
86
|
-
}
|
|
117
|
+
LT = wrapAnyAll.call(this, (value, options) => {
|
|
118
|
+
return addOperatorToQuery.call(this, 'LT', 'GTE', value, options);
|
|
119
|
+
});
|
|
87
120
|
|
|
88
|
-
LTE(value) {
|
|
89
|
-
return addOperatorToQuery.call(this, 'LTE', 'GT', value);
|
|
90
|
-
}
|
|
121
|
+
LTE = wrapAnyAll.call(this, (value, options) => {
|
|
122
|
+
return addOperatorToQuery.call(this, 'LTE', 'GT', value, options);
|
|
123
|
+
});
|
|
91
124
|
|
|
92
125
|
LIKE(value, options) {
|
|
93
126
|
let caseSensitive = ((options && options.caseSensitive) === true);
|