mythix-orm 1.4.6 → 1.5.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.
@@ -68,8 +68,9 @@ class QueryEngine extends QueryEngineBase {
68
68
  if (!QueryEngine.isQuery(queryEngine) && Nife.instanceOf(queryEngine, 'array', 'object', 'map', 'set'))
69
69
  queryEngine = Utils.generateQueryFromFilter(this.getConnection(), thisQueryContext.rootModel, queryEngine);
70
70
 
71
- let sourceQuery = queryEngine._getRawQuery();
72
- let currentModel = thisQueryContext.Model;
71
+ let sourceQuery = queryEngine._getRawQuery();
72
+ let currentModel = thisQueryContext.Model;
73
+ let skipLogicalOperators = true;
73
74
 
74
75
  for (let i = 0, il = sourceQuery.length; i < il; i++) {
75
76
  let queryPart = sourceQuery[i];
@@ -88,12 +89,29 @@ class QueryEngine extends QueryEngineBase {
88
89
  'rootModelName',
89
90
  ]);
90
91
 
91
- // Skip unneeded duplicate model entries
92
- if (mergeContext.operator === 'MODEL') {
93
- if (mergeContext.Model === currentModel)
92
+ // For merges, we want to skip the first logical operators
93
+ // found before any other operation.
94
+ // This is because one might do a: Model.where.OR.MERGE(Model.AND.field.EQ()).
95
+ // This query, once merged, would be: Model.where.OR.Model.AND.field.EQ(),
96
+ // which is not what we want... instead we want: Model.where.OR.Model.field.EQ().
97
+ // Since the result we want here is OR merge, not AND merge
98
+ // we skip the first "AND" we encounter, leaving the "OR" as
99
+ // the current logical operator.
100
+ if (skipLogicalOperators && Object.prototype.hasOwnProperty.call(mergeContext, 'logical') && mergeContext.logical) {
101
+ if (mergeContext.value == null && (mergeContext.operator === 'AND' || mergeContext.operator === 'OR'))
94
102
  continue;
103
+ }
104
+
105
+ if (Object.prototype.hasOwnProperty.call(mergeContext, 'operator')) {
106
+ // Skip unneeded duplicate model entries
107
+ if (mergeContext.operator === 'MODEL') {
108
+ if (mergeContext.Model === currentModel)
109
+ continue;
95
110
 
96
- currentModel = mergeContext.Model;
111
+ currentModel = mergeContext.Model;
112
+ } else if (mergeContext.operator !== 'FIELD') {
113
+ skipLogicalOperators = false;
114
+ }
97
115
  }
98
116
 
99
117
  this._addToQuery(Object.assign({
@@ -26,11 +26,11 @@ class DateType extends Type {
26
26
  this.format = format || 'YYYY-MM-DD';
27
27
  }
28
28
 
29
- castToType({ value }) {
29
+ castToType({ value, connection }) {
30
30
  if (value == null)
31
31
  return value;
32
32
 
33
- let dateTime = this.deserialize(value);
33
+ let dateTime = this.deserialize(value, connection);
34
34
  if (!dateTime.isValid())
35
35
  throw new TypeError(`DateType::castToType: Value provided ("${value}") can not be cast into a date.`);
36
36
 
@@ -60,7 +60,8 @@ class DateType extends Type {
60
60
  return value.toISOString();
61
61
  }
62
62
 
63
- deserialize(_value) {
63
+ // eslint-disable-next-line no-unused-vars
64
+ deserialize(_value, connection) {
64
65
  let value = _value;
65
66
  if (value == null)
66
67
  return value;
@@ -27,11 +27,11 @@ class DateTimeType extends Type {
27
27
  this.format = format;
28
28
  }
29
29
 
30
- castToType({ value }) {
30
+ castToType({ value, connection }) {
31
31
  if (value == null)
32
32
  return value;
33
33
 
34
- let dateTime = this.deserialize(value);
34
+ let dateTime = this.deserialize(value, connection);
35
35
  if (!dateTime.isValid())
36
36
  throw new TypeError(`DateTimeType::castToType: Value provided ("${value}") can not be cast into a date.`);
37
37
 
@@ -65,7 +65,8 @@ class DateTimeType extends Type {
65
65
  return value.toISOString();
66
66
  }
67
67
 
68
- deserialize(_value) {
68
+ // eslint-disable-next-line no-unused-vars
69
+ deserialize(_value, connection) {
69
70
  let value = _value;
70
71
  if (value == null)
71
72
  return value;
@@ -6,9 +6,11 @@ const { BOOLEAN, BooleanType } = require('./boolean-type');
6
6
  const { CHAR, CharType } = require('./char-type');
7
7
  const { DATE, DateType } = require('./date-type');
8
8
  const { DATETIME, DateTimeType } = require('./datetime-type');
9
- const { FLOAT, FloatType } = require('./float-type');
10
9
  const { FOREIGN_KEY, ForeignKeyType } = require('./foreign-key-type');
11
10
  const { INTEGER, IntegerType } = require('./integer-type');
11
+ const { NUMERIC, NumericType } = require('./numeric-type');
12
+ const { REAL, RealType } = require('./real-type');
13
+ const { SERIALIZED, SerializedType } = require('./serialized-type');
12
14
  const { STRING, StringType } = require('./string-type');
13
15
  const { TEXT, TextType } = require('./text-type');
14
16
  const { UUIDV1, UUIDV1Type } = require('./uuid-v1-type');
@@ -24,9 +26,11 @@ module.exports = {
24
26
  CharType,
25
27
  DateTimeType,
26
28
  DateType,
27
- FloatType,
29
+ NumericType,
30
+ RealType,
28
31
  ForeignKeyType,
29
32
  IntegerType,
33
+ SerializedType,
30
34
  StringType,
31
35
  TextType,
32
36
  UUIDV1Type,
@@ -40,9 +44,11 @@ module.exports = {
40
44
  CHAR,
41
45
  DATE,
42
46
  DATETIME,
43
- FLOAT,
47
+ NUMERIC,
48
+ REAL,
44
49
  FOREIGN_KEY,
45
50
  INTEGER,
51
+ SERIALIZED,
46
52
  STRING,
47
53
  TEXT,
48
54
  UUIDV1,
@@ -3,20 +3,23 @@
3
3
  const Nife = require('nife');
4
4
  const Type = require('../type');
5
5
 
6
- class FloatType extends Type {
6
+ const DEFAULT_TOTAL_DIGITS = 20;
7
+ const DEFAULT_DECIMAL_PLACES = 6;
8
+
9
+ class NumericType extends Type {
7
10
  static getDisplayName() {
8
- return 'FLOAT';
11
+ return 'NUMERIC';
9
12
  }
10
13
 
11
14
  getDisplayName() {
12
15
  return this.constructor.getDisplayName();
13
16
  }
14
17
 
15
- constructor(length, precision) {
16
- super(length, precision);
18
+ constructor(precision, scale) {
19
+ super(precision, scale);
17
20
 
18
- this.length = length;
19
- this.precision = precision;
21
+ this.precision = precision || DEFAULT_TOTAL_DIGITS;
22
+ this.scale = scale || DEFAULT_DECIMAL_PLACES;
20
23
  }
21
24
 
22
25
  castToType({ value }) {
@@ -37,11 +40,11 @@ class FloatType extends Type {
37
40
  toString(connection) {
38
41
  return (connection)
39
42
  ? this.toConnectionType(connection)
40
- : 'FLOAT';
43
+ : 'NUMERIC';
41
44
  }
42
45
  }
43
46
 
44
47
  module.exports = {
45
- FLOAT: Type.wrapConstructor(FloatType),
46
- FloatType,
48
+ NUMERIC: Type.wrapConstructor(NumericType),
49
+ NumericType,
47
50
  };
@@ -0,0 +1,47 @@
1
+ 'use strict';
2
+
3
+ const Nife = require('nife');
4
+ const Type = require('../type');
5
+
6
+ class RealType extends Type {
7
+ static getDisplayName() {
8
+ return 'REAL';
9
+ }
10
+
11
+ getDisplayName() {
12
+ return this.constructor.getDisplayName();
13
+ }
14
+
15
+ constructor(length, scale) {
16
+ super(length, scale);
17
+
18
+ this.length = length;
19
+ this.scale = scale;
20
+ }
21
+
22
+ castToType({ value }) {
23
+ if (value == null)
24
+ return value;
25
+
26
+ let number = parseFloat(('' + value));
27
+ if (!isFinite(number))
28
+ throw new TypeError(`RealType::castToType: Value provided ("${value}") can not be cast into an floating point number.`);
29
+
30
+ return number;
31
+ }
32
+
33
+ isValidValue(value) {
34
+ return (Nife.instanceOf(value, 'number') && isFinite(value));
35
+ }
36
+
37
+ toString(connection) {
38
+ return (connection)
39
+ ? this.toConnectionType(connection)
40
+ : 'FLOAT';
41
+ }
42
+ }
43
+
44
+ module.exports = {
45
+ REAL: Type.wrapConstructor(RealType),
46
+ RealType,
47
+ };
@@ -0,0 +1,134 @@
1
+ 'use strict';
2
+
3
+ const Type = require('../type');
4
+
5
+ class SerializedType extends Type {
6
+ static getDisplayName() {
7
+ return 'SERIALIZED';
8
+ }
9
+
10
+ getDisplayName() {
11
+ return this.constructor.getDisplayName();
12
+ }
13
+
14
+ constructor(_options) {
15
+ let options = _options;
16
+ if (Type.isTypeClass(options) || Type.isType(options))
17
+ options = { type: options };
18
+
19
+ options = {
20
+ serialize: ({ value }) => {
21
+ if (value == null)
22
+ return value;
23
+
24
+ return JSON.stringify(value);
25
+ },
26
+ deserialize: ({ value }) => {
27
+ if (value == null)
28
+ return value;
29
+
30
+ return JSON.parse(value);
31
+ },
32
+ ...(options || {}),
33
+ };
34
+
35
+ if (!options.type)
36
+ throw new TypeError('SerializedType::constructor: "type" must be specified on the "options" for this type. "type" defines the storage type for this field. i.e. Types.SERIALIZED({ type: Types.STRING(1024) })');
37
+
38
+ super(options);
39
+
40
+ options.type = Type.instantiateType(options.type);
41
+
42
+ Object.defineProperties(this, {
43
+ 'options': {
44
+ writable: true,
45
+ enumberable: false,
46
+ configurable: true,
47
+ value: options,
48
+ },
49
+ });
50
+ }
51
+
52
+ getOptions() {
53
+ return this.options;
54
+ }
55
+
56
+ initialize(connection, self) {
57
+ let options = this.getOptions();
58
+ options.type.initialize(connection, self);
59
+ }
60
+
61
+ castToType({ value, connection }) {
62
+ return this.deserialize(value, connection);
63
+ }
64
+
65
+ onSetFieldValue({ value, fieldName, self }) {
66
+ // we make a copy here
67
+ // so that when the value is modified
68
+ // we can detect the changes later
69
+ let clonedValue = this.deserialize(this.serialize(value, self.getConnection() || {}));
70
+ self._typeData[fieldName] = clonedValue;
71
+ }
72
+
73
+ isDirty(context) {
74
+ let options = this.getOptions();
75
+ if (typeof options.isDirty === 'function')
76
+ return options.isDirty.call(this, context);
77
+
78
+ let { value, fieldName, self, connection } = context;
79
+ let currentValue = self._typeData[fieldName];
80
+ if (value == null && currentValue == null)
81
+ return;
82
+
83
+ if (this.serialize(value, connection) !== this.serialize(currentValue, connection))
84
+ return value;
85
+ }
86
+
87
+ isValidValue() {
88
+ return true;
89
+ }
90
+
91
+ serialize(value, connection) {
92
+ if (!connection)
93
+ return value;
94
+
95
+ let options = this.getOptions();
96
+ let innerType = options.type;
97
+
98
+ if (value != null && innerType.isValidValue(value))
99
+ return value;
100
+
101
+ return options.serialize({ value, connection });
102
+ }
103
+
104
+ deserialize(value, connection) {
105
+ let options = this.getOptions();
106
+ let innerType = options.type;
107
+
108
+ if (value != null && !innerType.isValidValue(value))
109
+ return value;
110
+
111
+ return options.deserialize({ value, connection });
112
+ }
113
+
114
+ toString(connection) {
115
+ let options = this.getOptions();
116
+ let innerType = options.type;
117
+ return innerType.toString(connection);
118
+ }
119
+
120
+ toConnectionType(connection, options) {
121
+ let typeOptions = this.getOptions();
122
+ let innerType = typeOptions.type;
123
+
124
+ if (!connection)
125
+ return innerType.toString();
126
+
127
+ return connection.typeToString(innerType, options);
128
+ }
129
+ }
130
+
131
+ module.exports = {
132
+ SERIALIZED: Type.wrapConstructor(SerializedType),
133
+ SerializedType,
134
+ };
@@ -12,9 +12,11 @@ const {
12
12
  CharType,
13
13
  DateTimeType,
14
14
  DateType,
15
- FloatType,
15
+ NumericType,
16
+ RealType,
16
17
  ForeignKeyType,
17
18
  IntegerType,
19
+ SerializedType,
18
20
  StringType,
19
21
  TextType,
20
22
  UUIDV1Type,
@@ -28,9 +30,11 @@ const {
28
30
  CHAR,
29
31
  DATE,
30
32
  DATETIME,
31
- FLOAT,
33
+ NUMERIC,
34
+ REAL,
32
35
  FOREIGN_KEY,
33
36
  INTEGER,
37
+ SERIALIZED,
34
38
  STRING,
35
39
  TEXT,
36
40
  UUIDV1,
@@ -59,9 +63,11 @@ module.exports = {
59
63
  CharType,
60
64
  DateTimeType,
61
65
  DateType,
62
- FloatType,
66
+ NumericType,
67
+ RealType,
63
68
  ForeignKeyType,
64
69
  IntegerType,
70
+ SerializedType,
65
71
  StringType,
66
72
  TextType,
67
73
  UUIDV1Type,
@@ -75,9 +81,11 @@ module.exports = {
75
81
  CHAR,
76
82
  DATE,
77
83
  DATETIME,
78
- FLOAT,
84
+ NUMERIC,
85
+ REAL,
79
86
  FOREIGN_KEY,
80
87
  INTEGER,
88
+ SERIALIZED,
81
89
  STRING,
82
90
  TEXT,
83
91
  UUIDV1,
package/lib/types/type.js CHANGED
@@ -229,6 +229,12 @@ class Type {
229
229
  initialize(connection, self) {
230
230
  }
231
231
 
232
+ isDirty() {
233
+ }
234
+
235
+ onSetFieldValue() {
236
+ }
237
+
232
238
  serialize(value) {
233
239
  return value;
234
240
  }
@@ -81,32 +81,52 @@ function fieldToFullyQualifiedName(field, Model) {
81
81
  return `${def.modelName}:${def.fieldNames[0]}`;
82
82
  }
83
83
 
84
- function sortModelNamesByDependencyOrder(connection, modelNames, dependencyHelper) {
85
- let modelOrderMap = {};
86
- for (let i = 0, il = modelNames.length; i < il; i++) {
87
- let modelName = modelNames[i];
88
- let Model = connection.getModel(modelName);
89
- let dependentModelNames = dependencyHelper(Model, modelName);
84
+ function sortModelNamesByDependencyOrder(connection, _modelNames, dependencyHelper) {
85
+ const recursiveAdd = (modelName, _depth) => {
86
+ if (modelNames.indexOf(modelName) < 0)
87
+ return;
90
88
 
91
- modelOrderMap[modelName] = dependentModelNames;
92
- }
89
+ let depth = _depth || 0;
93
90
 
94
- let sortedModelNames = modelNames.sort((a, b) => {
95
- if (a === b)
96
- return 0;
91
+ // eslint-disable-next-line no-magic-numbers
92
+ if (depth > 10)
93
+ throw new Error(`ModelUtils::sortModelNamesByDependencyOrder: Cyclic dependency detected with model "${modelName}".`);
97
94
 
98
- let orderA = modelOrderMap[a];
99
- let orderB = modelOrderMap[b];
95
+ if (finalOrder.has(modelName))
96
+ return;
100
97
 
101
- if (orderA.indexOf(b) >= 0)
102
- return 1;
98
+ let dependentModelNames = modelOrderMap[modelName];
99
+ if (dependentModelNames == null) {
100
+ let Model = connection.getModel(modelName);
101
+ dependentModelNames = modelOrderMap[modelName] = (dependencyHelper(Model, modelName) || []);
102
+ }
103
103
 
104
- if (orderB.indexOf(a) >= 0)
105
- return -1;
104
+ if (dependentModelNames.length === 0) {
105
+ finalOrder.set(modelName, dependentModelNames);
106
+ return;
107
+ }
106
108
 
107
- return Math.sign(orderA.length - orderB.length);
108
- });
109
+ for (let j = 0, jl = dependentModelNames.length; j < jl; j++) {
110
+ let dependentModelName = dependentModelNames[j];
111
+ recursiveAdd(dependentModelName, depth + 1);
112
+ }
113
+
114
+ finalOrder.set(modelName, dependentModelNames);
115
+ };
116
+
117
+ let modelOrderMap = {};
118
+ let modelNames = Nife.toArray(_modelNames).slice().sort();
119
+ let finalOrder = new Map();
120
+
121
+ for (let i = 0, il = modelNames.length; i < il; i++) {
122
+ let modelName = modelNames[i];
123
+ if (!modelName)
124
+ continue;
125
+
126
+ recursiveAdd(modelName);
127
+ }
109
128
 
129
+ let sortedModelNames = Array.from(finalOrder.keys());
110
130
  return sortedModelNames;
111
131
  }
112
132
 
@@ -60,13 +60,13 @@ const FILTER_OPERATORS = {
60
60
  '<>': (Model, fieldName, query, value) => {
61
61
  return query.AND(Model.where[fieldName].LT(value[0]).OR[fieldName].GT(value[1]));
62
62
  },
63
- // Like (TODO: Mythix ORM needs to support this)
63
+ // Like
64
64
  '*': (Model, fieldName, query, value) => {
65
- return query.EQ(value);
65
+ return query.LIKE(value);
66
66
  },
67
- // NOT Like (TODO: Mythix ORM needs to support this)
67
+ // NOT Like
68
68
  '!*': (Model, fieldName, query, value) => {
69
- return query.EQ(value);
69
+ return query.NOT_LIKE(value);
70
70
  },
71
71
  };
72
72
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mythix-orm",
3
- "version": "1.4.6",
3
+ "version": "1.5.1",
4
4
  "description": "ORM for Mythix framework",
5
5
  "main": "lib/index.js",
6
6
  "type": "commonjs",