mythix-orm 1.8.1 → 1.8.3

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.
@@ -7,6 +7,9 @@ const { Model: ModelBase } = require('../model');
7
7
  const LiteralBase = Literals.LiteralBase;
8
8
  const LITERAL_IS_DISTINCT_RE = (/^DISTINCT[\s(]/i);
9
9
 
10
+ /// The base query generator class.
11
+ ///
12
+ /// Alias: QueryGenerator
10
13
  class QueryGeneratorBase {
11
14
  constructor(connection) {
12
15
  Object.defineProperties(this, {
package/lib/model.js CHANGED
@@ -133,6 +133,8 @@ function bindStaticWhereToModelClass(ModelClass) {
133
133
  /// unless you know exactly what you are doing.
134
134
  /// It also implies that there is another method
135
135
  /// that can and should be overloaded instead.
136
+ ///
137
+ /// Alias: Models
136
138
  class Model {
137
139
  /// This property assists with type checking
138
140
  ///
@@ -1386,7 +1388,7 @@ class Model {
1386
1388
  /// sorted?: boolean = false
1387
1389
  /// If `true`, then sort the model fields before iterating. If `false`,
1388
1390
  /// simply iterate the model's fields in their defined order.
1389
- static iterateFields(callback, _fields, sorted) {
1391
+ static iterateFields(callback, _fields, sorted, stopOnTruthy) {
1390
1392
  if (typeof callback !== 'function')
1391
1393
  return [];
1392
1394
 
@@ -1394,12 +1396,24 @@ class Model {
1394
1396
  if (!fields)
1395
1397
  fields = (sorted !== false) ? this.getSortedFields() : this.getFields();
1396
1398
 
1397
- return Nife.iterate(fields, ({ value: field, index, context, stop, isStopped }) => {
1399
+ let finalValue;
1400
+
1401
+ let finalResult = Nife.iterate(fields, ({ value: field, index, context, stop, isStopped }) => {
1398
1402
  let result = callback({ field, fieldName: field.fieldName, fields, index, stop, isStopped });
1403
+ if (stopOnTruthy === true && result) {
1404
+ finalValue = field;
1405
+ stop();
1406
+ return;
1407
+ }
1399
1408
 
1400
1409
  if (!isStopped())
1401
1410
  context.push(result);
1402
1411
  }, []);
1412
+
1413
+ if (stopOnTruthy)
1414
+ return finalValue;
1415
+
1416
+ return finalResult;
1403
1417
  }
1404
1418
 
1405
1419
  iterateFields(callback, _fields, sorted) {
@@ -2226,7 +2240,7 @@ class Model {
2226
2240
  /// <see>Connection</see> then it will behave a little differently.
2227
2241
  ///
2228
2242
  /// The first difference is that if a model field is not marked
2229
- /// as dirty, then it will be passed through <see>connection.dirtyFieldHelper</see>.
2243
+ /// as dirty, then it will be passed through <see>Connection.dirtyFieldHelper</see>.
2230
2244
  /// If this method returns any value other than `undefined`, then
2231
2245
  /// that value will be used as the "dirty" value for the field, and
2232
2246
  /// the field will be marked as dirty. This can be used when you
@@ -107,10 +107,10 @@ class QueryEngineBase extends ProxyClass {
107
107
  }
108
108
 
109
109
  _newQueryEngineScope() {
110
- const QueryEngine = this.getQueryEngineClass();
111
- let context = this.currentContext;
112
- let newContext = this._inheritContext(context, 'queryEngine');
113
- let newScope = new QueryEngine(newContext);
110
+ const QueryEngineClass = this.getQueryEngineClass();
111
+ let context = this.currentContext;
112
+ let newContext = this._inheritContext(context, 'queryEngine');
113
+ let newScope = new QueryEngineClass(newContext);
114
114
 
115
115
  return newScope;
116
116
  }
@@ -179,7 +179,7 @@ class DateType extends Type {
179
179
  /// needs to be serialized. If a `connection` argument
180
180
  /// is provided, then this method will assume that the
181
181
  /// connection is serializing it for storage in the database.
182
- /// In this case, <see>connection.convertDateToDBTime</see> is
182
+ /// In this case, <see>Connection.convertDateToDBTime</see> is
183
183
  /// called and provided the `value` for the connection to turn
184
184
  /// the date into a proper value for the underlying database.
185
185
  ///
@@ -196,7 +196,7 @@ class DateType extends Type {
196
196
  /// connection?: <see>Connection</see>
197
197
  /// A connection instance, which if provided, will
198
198
  /// proxy serialization to the underlying database connection
199
- /// via the <see>connection.convertDateToDBTime</see> method.
199
+ /// via the <see>Connection.convertDateToDBTime</see> method.
200
200
  serialize(_value, connection) {
201
201
  let value = _value;
202
202
  if (value == null)
@@ -181,7 +181,7 @@ class DateTimeType extends Type {
181
181
  /// needs to be serialized. If a `connection` argument
182
182
  /// is provided, then this method will assume that the
183
183
  /// connection is serializing it for storage in the database.
184
- /// In this case, <see>connection.convertDateToDBTime</see> is
184
+ /// In this case, <see>Connection.convertDateToDBTime</see> is
185
185
  /// called and provided the `value` for the connection to turn
186
186
  /// the date into a proper value for the underlying database.
187
187
  ///
@@ -198,7 +198,7 @@ class DateTimeType extends Type {
198
198
  /// connection?: <see>Connection</see>
199
199
  /// A connection instance, which if provided, will
200
200
  /// proxy serialization to the underlying database connection
201
- /// via the <see>connection.convertDateToDBTime</see> method.
201
+ /// via the <see>Connection.convertDateToDBTime</see> method.
202
202
  serialize(_value, connection) {
203
203
  let value = _value;
204
204
  if (value == null)
@@ -12,7 +12,7 @@ const ModelUtils = require('../../utils/model-utils');
12
12
  /// type of integer.
13
13
  ///
14
14
  /// Foreign keys are one of the ways Mythix ORM knows that models
15
- /// are related, along with the `Model` and `Models` virtual types.
15
+ /// are related, along with the <see>ModelType</see> and <see>ModelsType</see> virtual types.
16
16
  /// The "target" field must be a fully qualified field name. A
17
17
  /// fully qualified field name is a name that is also prefixed by
18
18
  /// the model that owns it. For example, `'User:firstName'` would
@@ -284,7 +284,7 @@ class ForeignKeyType extends Type {
284
284
  /// across all model instances. `initialize` is still
285
285
  /// called for every model instance that is created however,
286
286
  /// because the type class can modify the model it
287
- /// exists on. For example, the `Model` and `Models` type
287
+ /// exists on. For example, the <see>ModelType</see> and <see>ModelsType</see> types
288
288
  /// inject custom relational methods onto each model instance.
289
289
  ///
290
290
  /// This specific `initialize` for the `ForeignKeyType`
package/lib/types/type.js CHANGED
@@ -244,7 +244,7 @@ class Type {
244
244
  /// across all model instances. `initialize` is still
245
245
  /// called for every model instance that is created however,
246
246
  /// because the type class can modify the model it
247
- /// exists on. For example, the `Model` and `Models` type
247
+ /// exists on. For example, the <see>ModelType</see> and <see>ModelsType</see> types
248
248
  /// inject custom relational methods onto each model instance.
249
249
  ///
250
250
  /// Return: undefined
@@ -122,11 +122,150 @@ const TYPE_OPERATIONS = {
122
122
  },
123
123
  };
124
124
 
125
+ /// A "virtual" field `type` that defines a one to one
126
+ /// relationship with another model.
127
+ ///
128
+ /// Relationships (also known as "associations") in Mythix ORM
129
+ /// are defined by virtual fields that return a "relationship query"
130
+ /// from a method known as a "query generator" that is defined along
131
+ /// with the type definition.
132
+ /// This "relationship query" itself defines the model relationship, no matter
133
+ /// how complicated. Other ORMs require that you define relationships
134
+ /// via their "through table", matching foreign key, type, etc...
135
+ /// In Mythix ORM, everything is simply defined as a query for the
136
+ /// relationship.
137
+ ///
138
+ /// This field `type` can be used to define a virtual field
139
+ /// that defines a relationship to another model. When your
140
+ /// model is instantiated into an instance, then this type will
141
+ /// inject the following methods into your model instance to
142
+ /// assist you in interacting with the relationship. The "injected methods"
143
+ /// listed below are "prefixes". The full names of these methods are
144
+ /// always postfixed with the actual name of your field that defines
145
+ /// the relationship. For example, if you had a `primaryRole` field
146
+ /// that defined this type of relationship, then the methods injected
147
+ /// would be `model.getPrimaryRole`, `model.destroyPrimaryRole`, `model.pluckPrimaryRole`, etc...
148
+ /// | Injected Method Prefix | Full Method Name | Signature | Description |
149
+ /// | ---------------------- | ---------------- | ----------- | --------- |
150
+ /// | `create` | `create${fieldName}` | `create(modelInstanceOrAttributes: Model \| object, options?: object, ...args: Array<any>): Promise<Model>` | Create the model defined by the relationship. Returns the newly created model. |
151
+ /// | `destroy` | `destroy${fieldName}` | `destroy(options?: object, ...args: Array<any>): Promise<number>` | Destroy the model defined by the relationship. Returns the number of models destroyed. |
152
+ /// | `get` | `get${fieldName}` | `get(userQuery?: QueryEngine, options?: object, ...args: Array<any>): Promise<Model>` | Get the model specified by the relationship, if one is found. Returned the related model (if there is any). |
153
+ /// | `has` | `has${fieldName}` | `has(options?: object, ...args: Array<any>): Promise<boolean>` | Check if the model defined by the relationship exists. |
154
+ /// | `pluck` | `pluck${fieldName}` | `pluck(fields: Field \| string \| Array<Field \| string>, options?: object, ...args: Array<any>): Promise<Array<any>>` | Pluck specific fields from the related model. |
155
+ /// | `queryFor` | `queryFor${fieldName}` | `queryFor(userQuery?: QueryEngine, options?: object, ...args: Array<any>): Promise<QueryEngine>` | Get the raw relationship query defined by this field type (returned by the "query generator"). |
156
+ /// | `update` | `update${fieldName}` | `update(modelInstanceOrAttributes: Model \| object, options?: object, ...args: Array<any>): Promise<Model>` | Update the model specified by the relationship. Return the updated model. |
157
+ ///
158
+ /// The `...args` provided for each injected method are pass-through args for the user, that allow
159
+ /// the user to pass any arguments they desire to the "relational query generator" method defined
160
+ /// by the field (the `...userArgs` as seen in the example below). One minor exception to this is
161
+ /// any method that has a `userQuery` argument. The `userQuery` argument will always also be passed as the
162
+ /// first argument to the `...userArgs` of the "query generator" method. The `userQuery` argument
163
+ /// is to allow the user to define an extra query to merge into the primary/root query of the relationship.
164
+ /// For example, in the example provided below, we could call `await user.getPrimaryRole(Role.where.name.EQ('admin'))`
165
+ /// to only get the primary role if it also had a `name` attribute with the value `'admin'`.
166
+ ///
167
+ /// This type also injects duplicates of each of these relational methods with an underscore prefix, i.e.
168
+ /// `_getPrimaryRole`. These are known as "root methods". They are provided so the user can overload the
169
+ /// default implementations. For example, you could define a `getPrimaryRole` method on your model class,
170
+ /// and from that call the "super" method simply by calling `this._getPrimaryRole`. This is required because
171
+ /// with method injection `super` doesn't actually exist, because the method was injected, instead of being
172
+ /// defined on the model class's prototype.
173
+ ///
174
+ /// Example:
175
+ /// class User extends Model {
176
+ /// static fields = {
177
+ /// ...,
178
+ /// // Target primary role ID
179
+ /// // as a foreign key
180
+ /// primaryRoleID: {
181
+ /// type: Types.FOREIGN_KEY('Role:id', {
182
+ /// // When the role is deleted then
183
+ /// // clear this attribute by setting it to NULL
184
+ /// onDelete: 'SET NULL',
185
+ /// onUpdate: 'CASCADE',
186
+ /// }),
187
+ /// allowNull: true,
188
+ /// index: true,
189
+ /// },
190
+ /// // Add a virtual relationship field, which creates a
191
+ /// // one to one relationship with the Role model.
192
+ /// //
193
+ /// // Notice how a "type" can always be used directly as
194
+ /// // the field definition, instead of defining an object
195
+ /// // with a "type" property.
196
+ /// primaryRole: Types.Model(
197
+ /// // Specify the target/root model.
198
+ /// 'Role',
199
+ /// // Define our "query generator" method that
200
+ /// // will return our "relationship query".
201
+ /// (context, connectionModels, ...userArgs) => {
202
+ /// // Get the model we need from the connection,
203
+ /// // which is conveniently passed to us as the
204
+ /// // `connectionModels` argument here.
205
+ /// let { Role } = connectionModels;
206
+ ///
207
+ /// // Get the "self", which is the model instance
208
+ /// // calling this method
209
+ /// // (i.e. with `model.getPrimaryRole()`, "self" would be "model")
210
+ /// let { self } = context;
211
+ ///
212
+ /// // Now return a relationship query
213
+ /// return Role.where
214
+ /// .id
215
+ /// .EQ(self.primaryRoleID)
216
+ /// .AND
217
+ /// .MERGE(userArgs[0]); // Apply the `userQuery` (will do nothing if nullish)
218
+ /// },
219
+ /// ),
220
+ /// };
221
+ /// }
222
+ ///
223
+ /// // ... later on
224
+ /// // get the "primary role" for the first user in the database
225
+ /// let user = await User.first();
226
+ /// // Call relationship method injected by the `Types.Model` type.
227
+ /// let primaryRole = await user.getPrimaryRole();
228
+ ///
229
+ /// Note:
230
+ /// See the [Associations](./Associations) article for a more in-depth discussion
231
+ /// of Mythix ORM model relationship/associations.
232
+ ///
233
+ /// Note:
234
+ /// The "query generator" method defined with the type can be an asynchronous method.
235
+ ///
236
+ /// Note:
237
+ /// Relational methods **will not** be injected into the model instance if a method of
238
+ /// the same name already exists on the model instance. For example, if you define a
239
+ /// `getPrimaryRole` method on your model class, then the default `get` relational method
240
+ /// (`getPrimaryRole` in our example) won't be injected. However, the "root method" `_getPrimaryRole`
241
+ /// will still be injected, allowing you to call that as the `super` method instead.
242
+ ///
243
+ /// Note:
244
+ /// Behind the scenes the relationship methods provided always apply a `LIMIT(1)` to
245
+ /// any query used. This is because this relationship type is singular, so it is expected
246
+ /// that only a single model can ever be returned.
125
247
  class ModelType extends RelationalTypeBase {
126
248
  isManyRelation() {
127
249
  return false;
128
250
  }
129
251
 
252
+ /// Internal method used to generate injected method names.
253
+ ///
254
+ /// This method likely should never be called by the user.
255
+ /// It is used to generate the injected method names.
256
+ ///
257
+ /// Return: string
258
+ /// The method name to inject into the model instance.
259
+ ///
260
+ /// Arguments:
261
+ /// field: <see>Field</see>
262
+ /// The parent field defining this relationship.
263
+ /// operation: string
264
+ /// The operation being injected into the model instance, which is one of
265
+ /// the prefixes listed above, i.e. `update`, `get`, `destroy`, etc...
266
+ /// rootMethod: boolean
267
+ /// If `true`, then return a generated "root method" name, instead of a "user method".
268
+ /// The only difference is that the "root method" name is prefixed with an underscore `_`.
130
269
  fieldNameToOperationName(field, operation, rootMethod) {
131
270
  if (rootMethod)
132
271
  return `_${operation}${Nife.capitalize(field.fieldName)}`;
@@ -134,6 +273,18 @@ class ModelType extends RelationalTypeBase {
134
273
  return `${operation}${Nife.capitalize(field.fieldName)}`;
135
274
  }
136
275
 
276
+ /// Inject type methods into the model instance.
277
+ ///
278
+ /// The <see>Type.initialize</see> method is always called
279
+ /// for each model that is instantiated. For this type, this
280
+ /// is used to inject the relational methods into the model
281
+ /// instance.
282
+ ///
283
+ /// Arguments:
284
+ /// connection: <see>Connection</see>
285
+ /// The connection for the model being instantiated.
286
+ /// self: <see>Model</see>
287
+ /// The model instance the type is being initialized for.
137
288
  initialize(connection, self) {
138
289
  return super.initialize(connection, self, TYPE_OPERATIONS);
139
290
  }
@@ -239,6 +239,124 @@ const TYPE_OPERATIONS = {
239
239
  },
240
240
  };
241
241
 
242
+ /// A "virtual" field `type` that defines a one to many,
243
+ /// or many to many relationship with other model(s).
244
+ ///
245
+ /// Many to many relationships interact with "sets", which in
246
+ /// Mythix ORM is the term used to define an "array of models",
247
+ /// in our N to many relationship.
248
+ ///
249
+ /// Relationships (also known as "associations") in Mythix ORM
250
+ /// are defined by virtual fields that return a "relationship query"
251
+ /// from a method known as a "query generator" that is defined along
252
+ /// with the type definition.
253
+ /// This "relationship query" itself defines the model relationship, no matter
254
+ /// how complicated. Other ORMs require that you define relationships
255
+ /// via their "through table", matching foreign key, type, etc...
256
+ /// In Mythix ORM, everything is simply defined as a query for the
257
+ /// relationship.
258
+ ///
259
+ /// This field `type` can be used to define a virtual field
260
+ /// that defines a relationship to other models. When your
261
+ /// model is instantiated into an instance, then this type will
262
+ /// inject the following methods into your model instance to
263
+ /// assist you in interacting with the relationship set. The "injected methods"
264
+ /// listed below are "prefixes". The full names of these methods are
265
+ /// always postfixed with the actual name of your field that defines
266
+ /// the relationship. For example, if you had a `roles` field
267
+ /// that defined this type of relationship, then the methods injected
268
+ /// would be `model.getRoles`, `model.destroyRoles`, `model.pluckRoles`, etc...
269
+ /// | Injected Method Prefix | Full Method Name | Signature | Description |
270
+ /// | ---------------------- | ---------------- | ----------- | --------- |
271
+ /// | `addTo` | `addTo${fieldName}` | `addTo(models: Array<Model \| object>, options?: object): Promise<Array<Model>>` | Create the models defined by the relationship, and add them to the set. Returns the newly created models. |
272
+ /// | `count` | `count${fieldName}` | `count(userQuery?: QueryEngine, options?: object, ...args: Array<any>): Promise<number>` | Count the number of models in the set. |
273
+ /// | `destroy` | `destroy${fieldName}` | `destroy(options?: object, ...args: Array<any>): Promise<number>` | Destroy the models defined by the relationship set. Returns the number of models destroyed. Note that this not only destroys the target models, but also any through-table relationships involved. |
274
+ /// | `get` | `get${fieldName}` | `get(userQuery?: QueryEngine, options?: object, ...args: Array<any>): Promise<Array<Model>>` | Get the models specified by the relationship set. Return the entire model set. |
275
+ /// | `has` | `has${fieldName}` | `has(userQuery?: QueryEngine, options?: object, ...args: Array<any>): Promise<boolean>` | Check if the relationship set contains any models. |
276
+ /// | `pluck` | `pluck${fieldName}` | `pluck(userQuery?: QueryEngine, fields: Field \| string \| Array<Field \| string>, options?: object, ...args: Array<any>): Promise<Array<any>>` | Pluck specific fields from the related models (set). |
277
+ /// | `queryFor` | `queryFor${fieldName}` | `queryFor(userQuery?: QueryEngine, options?: object, ...args: Array<any>): Promise<QueryEngine>` | Get the raw relationship query defined by this field type (returned by the "query generator"). |
278
+ /// | `removeFrom` | `removeFrom${fieldName}` | `removeFrom(models: Array<Model>, options?: object): Promise<number>` | Remove the specified models from the relationship set without destroying the target models (destroy only the through-table linking models). Returns the number of models remaining in the set after the operation. |
279
+ /// | `set` | `set${fieldName}` | `set(models: Array<Model \| object>, options?: object): Promise<Array<Model>>` | Overwrite the model set with the models specified, destroying models outside the specified set of models. Returns the new models. |
280
+ ///
281
+ /// The `...args` provided for each injected method are pass-through args for the user, that allow
282
+ /// the user to pass any arguments they desire to the "relational query generator" method defined
283
+ /// by the field (the `...userArgs` as seen in the example below). One minor exception to this is
284
+ /// any method that has a `userQuery` argument. The `userQuery` argument will always also be passed as the
285
+ /// first argument to the `...userArgs` of the "query generator" method. The `userQuery` argument
286
+ /// is to allow the user to define an extra query to merge into the primary/root query of the relationship.
287
+ /// For example, in the example provided below, we could call `await user.getRoles(Role.where.name.EQ('admin'))`
288
+ /// to only get the related roles where each role also has a `name` attribute with the value `'admin'`.
289
+ ///
290
+ /// This type also injects duplicates of each of these relational methods with an underscore prefix, i.e.
291
+ /// `_getRoles`. These are known as "root methods". They are provided so the user can overload the
292
+ /// default implementations. For example, you could define a `getRoles` method on your model class,
293
+ /// and from that call the "super" method simply by calling `this._getRoles`. This is required because
294
+ /// with method injection `super` doesn't actually exist, because the method was injected, instead of being
295
+ /// defined on the model class's prototype.
296
+ ///
297
+ /// Example:
298
+ /// class User extends Model {
299
+ /// static fields = {
300
+ /// ...,
301
+ /// // Add a virtual relationship field, which creates a
302
+ /// // one to many relationship with the Role model.
303
+ /// //
304
+ /// // Notice how a "type" can always be used directly as
305
+ /// // the field definition, instead of defining an object
306
+ /// // with a "type" property.
307
+ /// roles: Types.Models(
308
+ /// // Specify the target/root model.
309
+ /// 'Role',
310
+ /// // Define our "query generator" method that
311
+ /// // will return our "relationship query".
312
+ /// (context, connectionModels, ...userArgs) => {
313
+ /// // Get the model we need from the connection,
314
+ /// // which is conveniently passed to us as the
315
+ /// // `connectionModels` argument here.
316
+ /// let { Role } = connectionModels;
317
+ ///
318
+ /// // Get the "self", which is the model instance
319
+ /// // calling this method
320
+ /// // (i.e. with `model.getRoles()`, "self" would be "model")
321
+ /// let { self } = context;
322
+ ///
323
+ /// // Now return a relationship query
324
+ /// return Role.where
325
+ /// .userID
326
+ /// .EQ(self.id)
327
+ /// .AND
328
+ /// .MERGE(userArgs[0]); // Apply the `userQuery` (will do nothing if nullish)
329
+ /// },
330
+ /// ),
331
+ /// };
332
+ /// }
333
+ ///
334
+ /// // ... later on
335
+ /// // get the "roles" for the first user in the database
336
+ /// let user = await User.first();
337
+ /// // Call relationship method injected by the `Types.Models` type.
338
+ /// let allUserRoles = await user.getRoles();
339
+ ///
340
+ /// Note:
341
+ /// See the [Associations](./Associations) article for a more in-depth discussion
342
+ /// of Mythix ORM model relationship/associations.
343
+ ///
344
+ /// Note:
345
+ /// The "query generator" method defined with the type can be an asynchronous method.
346
+ ///
347
+ /// Note:
348
+ /// Relational methods **will not** be injected into the model instance if a method of
349
+ /// the same name already exists on the model instance. For example, if you define a
350
+ /// `getRoles` method on your model class, then the default `get` relational method
351
+ /// (`getRoles` in our example) won't be injected. However, the "root method" `_getRoles`
352
+ /// will still be injected, allowing you to call that as the `super` method instead.
353
+ ///
354
+ /// Note:
355
+ /// This field type will not be "exposed" to models. This means that your model will not
356
+ /// have an attribute with the name provided by the field defining this type. The only
357
+ /// thing that will exist on your model will be the relational methods injected by this type.
358
+ /// Said another way, and using the examples provided, there will be no "roles" attribute
359
+ /// on your `User` model.
242
360
  class ModelsType extends RelationalTypeBase {
243
361
  static exposeToModel() {
244
362
  return false;
@@ -248,6 +366,23 @@ class ModelsType extends RelationalTypeBase {
248
366
  return true;
249
367
  }
250
368
 
369
+ /// Internal method used to generate injected method names.
370
+ ///
371
+ /// This method likely should never be called by the user.
372
+ /// It is used to generate the injected method names.
373
+ ///
374
+ /// Return: string
375
+ /// The method name to inject into the model instance.
376
+ ///
377
+ /// Arguments:
378
+ /// field: <see>Field</see>
379
+ /// The parent field defining this relationship.
380
+ /// operation: string
381
+ /// The operation being injected into the model instance, which is one of
382
+ /// the prefixes listed above, i.e. `addTo`, `get`, `destroy`, etc...
383
+ /// rootMethod: boolean
384
+ /// If `true`, then return a generated "root method" name, instead of a "user method".
385
+ /// The only difference is that the "root method" name is prefixed with an underscore `_`.
251
386
  fieldNameToOperationName(field, operation, rootMethod) {
252
387
  let fieldName = field.pluralName;
253
388
  if (!fieldName)
@@ -259,6 +394,18 @@ class ModelsType extends RelationalTypeBase {
259
394
  return `${operation}${fieldName}`;
260
395
  }
261
396
 
397
+ /// Inject type methods into the model instance.
398
+ ///
399
+ /// The <see>Type.initialize</see> method is always called
400
+ /// for each model that is instantiated. For this type, this
401
+ /// is used to inject the relational methods into the model
402
+ /// instance.
403
+ ///
404
+ /// Arguments:
405
+ /// connection: <see>Connection</see>
406
+ /// The connection for the model being instantiated.
407
+ /// self: <see>Model</see>
408
+ /// The model instance the type is being initialized for.
262
409
  initialize(connection, self) {
263
410
  return super.initialize(connection, self, TYPE_OPERATIONS);
264
411
  }
@@ -17,16 +17,6 @@ class RelationalTypeBase extends Type {
17
17
  return true;
18
18
  }
19
19
 
20
- // Model types work by specifying a "target"
21
- // and a "value provider" (source).
22
- // These are fully qualified names, meaning
23
- // they also point to the model as well as the field.
24
- // If no model is specified, then it always defaults to
25
- // "this" model. If no field is specified, then it always
26
- // defaults to "this PK" of the model.
27
- // Mythix ORM will recursively walk all models and fields
28
- // defined until it has the full relationships between
29
- // all fields.
30
20
  constructor(_targetModelName, queryFactory, _options) {
31
21
  let options = _options || {};
32
22
 
@@ -1,3 +1,5 @@
1
+ ///! DocScope: ModelUtils
2
+
1
3
  'use strict';
2
4
 
3
5
  const Nife = require('nife');
@@ -11,6 +13,48 @@ function sanitizeFieldString(str) {
11
13
  return ('' + str).replace(/[^\w:.]+/g, '').replace(/([:.])+/g, '$1');
12
14
  }
13
15
 
16
+ /// Parse a fully qualified field name.
17
+ ///
18
+ /// A fully qualified field name is a field name that
19
+ /// is prefixed by its parent model. For example,
20
+ /// `'User:email'` is a fully qualified field name,
21
+ /// whereas just `'email'` is not.
22
+ ///
23
+ /// This method will actually parse both formats however.
24
+ /// It will parse fully qualified field names, and short-hand
25
+ /// field names (just the field name). If just a field name
26
+ /// is provided with no model name prefix, then the resulting
27
+ /// `modelName` property of the returned object will be `undefined`.
28
+ ///
29
+ /// Field names themselves can be deeply nested. For example,
30
+ /// `'User:metadata.ipAddress'`. Field nesting was designed into
31
+ /// the Mythix ORM framework, and is partially supported in some
32
+ /// areas of the framework. However, field nesting likely won't work
33
+ /// in many places without further support being added. This feature
34
+ /// was left in-place for now, because it will likely be expanded upon
35
+ /// in the future.
36
+ /// Because of this "field nesting", instead of just one field name being
37
+ /// returned, an array of field names is *always* returned. Generally,
38
+ /// to get the field name you are looking for you would just get the first
39
+ /// index (`fieldNames[0]`) on the returned object.
40
+ ///
41
+ /// This method can and will also parse just a model name, so long as the provided
42
+ /// name starts with an upper-case letter. If no colon is found (meaning no model
43
+ /// prefix is present), then the provided value will be assumed to be a model name
44
+ /// only if it starts with an upper-case letter. Otherwise, it is treated as a field
45
+ /// name, and returned in the `fieldNames` array.
46
+ ///
47
+ /// Arguments:
48
+ /// fieldName: string
49
+ /// A model name, field name, or fully qualified field name to parse.
50
+ ///
51
+ /// Return: object
52
+ /// An object with the shape `{ modelName: string | undefined; fieldNames: Array<string> }`.
53
+ /// The `modelName` property will contain the name of the model for the field, if it is known,
54
+ /// which will only be the case for a fully qualified name (i.e. `'User:email'`), or a
55
+ /// model name alone (i.e. `'User'`). `fieldNames` will always be an array of field names
56
+ /// parsed, which will generally only be one. It can be empty if no field name was provided,
57
+ /// for example if only a model name was provided (i.e. `'User'`).
14
58
  function parseQualifiedName(str) {
15
59
  let fieldNames = [];
16
60
  let modelName;
@@ -62,11 +62,11 @@ const FILTER_OPERATORS = {
62
62
  },
63
63
  // Like
64
64
  '*': (Model, fieldName, query, value) => {
65
- return query.LIKE(value);
65
+ return query.AND[fieldName].LIKE(value);
66
66
  },
67
67
  // NOT Like
68
68
  '!*': (Model, fieldName, query, value) => {
69
- return query.NOT_LIKE(value);
69
+ return query.AND[fieldName].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.8.1",
3
+ "version": "1.8.3",
4
4
  "description": "ORM for Mythix framework",
5
5
  "main": "lib/index",
6
6
  "type": "commonjs",