@nocobase/database 0.9.4-alpha.2 → 0.10.0-alpha.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.
Files changed (44) hide show
  1. package/lib/database.js +2 -1
  2. package/lib/eager-loading/eager-loading-tree.d.ts +4 -0
  3. package/lib/eager-loading/eager-loading-tree.js +79 -16
  4. package/lib/fields/belongs-to-field.d.ts +1 -1
  5. package/lib/fields/belongs-to-field.js +3 -2
  6. package/lib/fields/belongs-to-many-field.js +1 -1
  7. package/lib/fields/has-many-field.js +1 -1
  8. package/lib/fields/has-one-field.js +1 -1
  9. package/lib/listeners/append-child-collection-name-after-repository-find.js +5 -1
  10. package/lib/options-parser.d.ts +1 -1
  11. package/lib/options-parser.js +15 -11
  12. package/lib/relation-repository/multiple-relation-repository.js +2 -1
  13. package/lib/relation-repository/single-relation-repository.js +2 -1
  14. package/lib/repository.d.ts +12 -0
  15. package/lib/repository.js +166 -66
  16. package/lib/update-associations.js +1 -1
  17. package/lib/view/field-type-map.d.ts +2 -0
  18. package/lib/view/field-type-map.js +2 -0
  19. package/lib/view/view-inference.js +52 -26
  20. package/lib/view-collection.js +0 -1
  21. package/package.json +4 -4
  22. package/src/__tests__/eager-loading/eager-loading-tree.test.ts +149 -0
  23. package/src/__tests__/field-options/sort-by.test.ts +3 -1
  24. package/src/__tests__/inhertits/collection-inherits.test.ts +165 -0
  25. package/src/__tests__/repository/create.test.ts +129 -6
  26. package/src/__tests__/repository/find.test.ts +11 -0
  27. package/src/__tests__/repository.test.ts +24 -0
  28. package/src/__tests__/update-associations.test.ts +109 -1
  29. package/src/__tests__/view/view-inference.test.ts +1 -0
  30. package/src/database.ts +4 -1
  31. package/src/eager-loading/eager-loading-tree.ts +92 -17
  32. package/src/fields/belongs-to-field.ts +6 -4
  33. package/src/fields/belongs-to-many-field.ts +2 -1
  34. package/src/fields/has-many-field.ts +1 -1
  35. package/src/fields/has-one-field.ts +1 -1
  36. package/src/listeners/append-child-collection-name-after-repository-find.ts +9 -5
  37. package/src/options-parser.ts +25 -19
  38. package/src/relation-repository/multiple-relation-repository.ts +1 -0
  39. package/src/relation-repository/single-relation-repository.ts +1 -0
  40. package/src/repository.ts +84 -0
  41. package/src/update-associations.ts +3 -0
  42. package/src/view/field-type-map.ts +2 -0
  43. package/src/view/view-inference.ts +75 -43
  44. package/src/view-collection.ts +0 -1
package/lib/database.js CHANGED
@@ -593,7 +593,8 @@ class Database extends _events().EventEmitter {
593
593
  if (!Field) {
594
594
  throw Error(`unsupported field type ${type}`);
595
595
  }
596
- if (options.field && context.collection.options.underscored) {
596
+ const collection = context.collection;
597
+ if (options.field && collection.options.underscored && !collection.isView()) {
597
598
  options.field = (0, _utils2.snakeCase)(options.field);
598
599
  }
599
600
  return new Field(options, context);
@@ -1,4 +1,5 @@
1
1
  import { Association, Includeable, Model, ModelStatic, Transaction } from 'sequelize';
2
+ import Database from '../database';
2
3
  interface EagerLoadingNode {
3
4
  model: ModelStatic<any>;
4
5
  association: Association;
@@ -8,15 +9,18 @@ interface EagerLoadingNode {
8
9
  parent?: EagerLoadingNode;
9
10
  instances?: Array<Model>;
10
11
  order?: any;
12
+ inspectInheritAttribute?: boolean;
11
13
  }
12
14
  export declare class EagerLoadingTree {
13
15
  root: EagerLoadingNode;
16
+ db: Database;
14
17
  constructor(root: EagerLoadingNode);
15
18
  static buildFromSequelizeOptions(options: {
16
19
  model: ModelStatic<any>;
17
20
  rootAttributes: Array<string>;
18
21
  rootOrder?: any;
19
22
  includeOption: Includeable | Includeable[];
23
+ db: Database;
20
24
  }): EagerLoadingTree;
21
25
  load(pks: Array<string | number>, transaction?: Transaction): Promise<{}>;
22
26
  }
@@ -4,6 +4,13 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.EagerLoadingTree = void 0;
7
+ function _sequelize() {
8
+ const data = require("sequelize");
9
+ _sequelize = function _sequelize() {
10
+ return data;
11
+ };
12
+ return data;
13
+ }
7
14
  function _lodash() {
8
15
  const data = _interopRequireDefault(require("lodash"));
9
16
  _lodash = function _lodash() {
@@ -11,6 +18,8 @@ function _lodash() {
11
18
  };
12
19
  return data;
13
20
  }
21
+ var _optionsParser = require("../options-parser");
22
+ var _appendChildCollectionNameAfterRepositoryFind = require("../listeners/append-child-collection-name-after-repository-find");
14
23
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
15
24
  function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
16
25
  function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
@@ -22,28 +31,49 @@ function _asyncToGenerator(fn) { return function () { var self = this, args = ar
22
31
  function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
23
32
  function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
24
33
  function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
34
+ const pushAttribute = (node, attribute) => {
35
+ if (_lodash().default.isArray(node.attributes) && !node.attributes.includes(attribute)) {
36
+ node.attributes.push(attribute);
37
+ }
38
+ };
39
+ const EagerLoadingNodeProto = {
40
+ afterBuild(db) {
41
+ const collection = db.modelCollection.get(this.model);
42
+ if (collection && collection.isParent()) {
43
+ if (!this.attributes) {
44
+ this.attributes = {
45
+ include: []
46
+ };
47
+ }
48
+ _optionsParser.OptionsParser.appendInheritInspectAttribute(_lodash().default.isArray(this.attributes) ? this.attributes : this.attributes.include, collection);
49
+ this.inspectInheritAttribute = true;
50
+ }
51
+ }
52
+ };
25
53
  class EagerLoadingTree {
26
54
  constructor(root) {
27
55
  this.root = void 0;
56
+ this.db = void 0;
28
57
  this.root = root;
29
58
  }
30
59
  static buildFromSequelizeOptions(options) {
31
60
  const model = options.model,
32
61
  rootAttributes = options.rootAttributes,
33
- includeOption = options.includeOption;
34
- const root = {
62
+ includeOption = options.includeOption,
63
+ db = options.db;
64
+ const buildNode = node => {
65
+ Object.setPrototypeOf(node, EagerLoadingNodeProto);
66
+ node.afterBuild(db);
67
+ return node;
68
+ };
69
+ const root = buildNode({
35
70
  model,
36
71
  association: null,
37
72
  rawAttributes: _lodash().default.cloneDeep(rootAttributes),
38
73
  attributes: _lodash().default.cloneDeep(rootAttributes),
39
74
  order: options.rootOrder,
40
75
  children: []
41
- };
42
- const pushAttribute = (node, attribute) => {
43
- if (_lodash().default.isArray(node.attributes) && !node.attributes.includes(attribute)) {
44
- node.attributes.push(attribute);
45
- }
46
- };
76
+ });
47
77
  const traverseIncludeOption = (includeOption, eagerLoadingTreeParent) => {
48
78
  const includeOptions = _lodash().default.castArray(includeOption);
49
79
  if (includeOption.length > 0) {
@@ -61,14 +91,14 @@ class EagerLoadingTree {
61
91
  }
62
92
  const association = eagerLoadingTreeParent.model.associations[include.association];
63
93
  const associationType = association.associationType;
64
- const child = {
94
+ const child = buildNode({
65
95
  model: association.target,
66
96
  association,
67
97
  rawAttributes: _lodash().default.cloneDeep(include.attributes),
68
98
  attributes: _lodash().default.cloneDeep(include.attributes),
69
99
  parent: eagerLoadingTreeParent,
70
100
  children: []
71
- };
101
+ });
72
102
  if (associationType == 'HasOne' || associationType == 'HasMany') {
73
103
  const sourceKey = association.sourceKey,
74
104
  foreignKey = association.foreignKey;
@@ -97,12 +127,22 @@ class EagerLoadingTree {
97
127
  }
98
128
  };
99
129
  traverseIncludeOption(includeOption, root);
100
- return new EagerLoadingTree(root);
130
+ const tree = new EagerLoadingTree(root);
131
+ tree.db = db;
132
+ return tree;
101
133
  }
102
134
  load(pks, transaction) {
103
135
  var _this = this;
104
136
  return _asyncToGenerator(function* () {
105
137
  const result = {};
138
+ const orderOption = association => {
139
+ const targetModel = association.target;
140
+ const order = [];
141
+ if (targetModel.primaryKeyAttribute && targetModel.rawAttributes[targetModel.primaryKeyAttribute].autoIncrement) {
142
+ order.push([targetModel.primaryKeyAttribute, 'ASC']);
143
+ }
144
+ return order;
145
+ };
106
146
  const loadRecursive = /*#__PURE__*/function () {
107
147
  var _ref = _asyncToGenerator(function* (node, ids) {
108
148
  const modelPrimaryKey = node.model.primaryKeyAttribute;
@@ -132,6 +172,7 @@ class EagerLoadingTree {
132
172
  [foreignKey]: foreignKeyValues
133
173
  },
134
174
  attributes: node.attributes,
175
+ order: orderOption(association),
135
176
  transaction
136
177
  };
137
178
  instances = yield node.model.findAll(findOptions);
@@ -149,15 +190,21 @@ class EagerLoadingTree {
149
190
  }
150
191
  if (associationType == 'BelongsToMany') {
151
192
  const foreignKeyValues = node.parent.instances.map(instance => instance.get(association.sourceKey));
193
+ const pivotAssoc = new (_sequelize().HasOne)(association.target, association.through.model, {
194
+ as: '_pivot_',
195
+ foreignKey: association.otherKey,
196
+ sourceKey: association.targetKey
197
+ });
152
198
  instances = yield node.model.findAll({
153
199
  transaction,
154
200
  attributes: node.attributes,
155
201
  include: [{
156
- association: association.oneFromTarget,
202
+ association: pivotAssoc,
157
203
  where: {
158
204
  [association.foreignKey]: foreignKeyValues
159
205
  }
160
- }]
206
+ }],
207
+ order: orderOption(association)
161
208
  });
162
209
  }
163
210
  }
@@ -252,13 +299,17 @@ class EagerLoadingTree {
252
299
  if (associationType == 'BelongsToMany') {
253
300
  const sourceKey = association.sourceKey;
254
301
  const foreignKey = association.foreignKey;
255
- const oneFromTarget = association.oneFromTarget;
302
+ const as = association.oneFromTarget.as;
256
303
  var _iterator6 = _createForOfIteratorHelper(node.instances),
257
304
  _step6;
258
305
  try {
259
306
  for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) {
260
307
  const instance = _step6.value;
261
- const parentInstance = node.parent.instances.find(parentInstance => parentInstance.get(sourceKey) == instance[oneFromTarget.as].get(foreignKey));
308
+ // set instance accessor
309
+ instance[as] = instance.dataValues[as] = instance['_pivot_'];
310
+ delete instance.dataValues['_pivot_'];
311
+ delete instance['_pivot_'];
312
+ const parentInstance = node.parent.instances.find(parentInstance => parentInstance.get(sourceKey) == instance.dataValues[as].get(foreignKey));
262
313
  if (parentInstance) {
263
314
  const children = parentInstance.getDataValue(association.as);
264
315
  if (!children) {
@@ -293,13 +344,25 @@ class EagerLoadingTree {
293
344
  };
294
345
  }();
295
346
  yield loadRecursive(_this.root, pks);
347
+ const appendChildCollectionName = (0, _appendChildCollectionNameAfterRepositoryFind.appendChildCollectionNameAfterRepositoryFind)(_this.db);
296
348
  const setInstanceAttributes = node => {
297
- const nodeRawAttributes = node.rawAttributes;
349
+ if (node.inspectInheritAttribute) {
350
+ appendChildCollectionName({
351
+ findOptions: {},
352
+ data: node.instances,
353
+ dataCollection: _this.db.modelCollection.get(node.model)
354
+ });
355
+ }
356
+ // if no attributes are specified, return empty fields
357
+ const nodeRawAttributes = node.rawAttributes || [];
298
358
  if (!_lodash().default.isArray(nodeRawAttributes)) {
299
359
  return;
300
360
  }
301
361
  const nodeChildrenAs = node.children.map(child => child.association.as);
302
362
  const includeAttributes = [...nodeRawAttributes, ...nodeChildrenAs];
363
+ if (node.inspectInheritAttribute) {
364
+ includeAttributes.push('__schemaName', '__tableName', '__collection');
365
+ }
303
366
  var _iterator8 = _createForOfIteratorHelper(node.instances),
304
367
  _step8;
305
368
  try {
@@ -2,8 +2,8 @@ import { BelongsToOptions as SequelizeBelongsToOptions } from 'sequelize';
2
2
  import { Reference } from '../features/ReferencesMap';
3
3
  import { BaseRelationFieldOptions, RelationField } from './relation-field';
4
4
  export declare class BelongsToField extends RelationField {
5
- get dataType(): string;
6
5
  static type: string;
6
+ get dataType(): string;
7
7
  get target(): any;
8
8
  static toReference(db: any, association: any, onDelete: any): {
9
9
  sourceCollectionName: any;
@@ -85,7 +85,8 @@ class BelongsToField extends _relationField.RelationField {
85
85
  this.options.sourceKey = association.sourceKey;
86
86
  }
87
87
  this.collection.addIndex([this.options.foreignKey]);
88
- this.database.referenceMap.addReference(this.reference(association));
88
+ const reference = this.reference(association);
89
+ this.database.referenceMap.addReference(reference);
89
90
  return true;
90
91
  }
91
92
  unbind() {
@@ -105,7 +106,7 @@ class BelongsToField extends _relationField.RelationField {
105
106
  collection.model.removeAttribute(foreignKey);
106
107
  }
107
108
  const association = collection.model.associations[this.name];
108
- if (association) {
109
+ if (association && !this.options.inherit) {
109
110
  const reference = this.reference(association);
110
111
  this.database.referenceMap.removeReference(reference);
111
112
  }
@@ -119,7 +119,7 @@ class BelongsToManyField extends _relationField.RelationField {
119
119
  database.removePendingField(this);
120
120
  // 删掉 model 的关联字段
121
121
  const association = collection.model.associations[this.name];
122
- if (association) {
122
+ if (association && !this.options.inherit) {
123
123
  this.references(association).forEach(reference => this.database.referenceMap.removeReference(reference));
124
124
  }
125
125
  this.clearAccessors();
@@ -114,7 +114,7 @@ class HasManyField extends _relationField.RelationField {
114
114
  }
115
115
  }
116
116
  const association = collection.model.associations[this.name];
117
- if (association) {
117
+ if (association && !this.options.inherit) {
118
118
  this.database.referenceMap.removeReference(this.reference(association));
119
119
  }
120
120
  this.clearAccessors();
@@ -118,7 +118,7 @@ class HasOneField extends _relationField.RelationField {
118
118
  }
119
119
  }
120
120
  const association = collection.model.associations[this.name];
121
- if (association) {
121
+ if (association && !this.options.inherit) {
122
122
  this.database.referenceMap.removeReference(this.reference(association));
123
123
  }
124
124
  this.clearAccessors();
@@ -19,7 +19,11 @@ const appendChildCollectionNameAfterRepositoryFind = db => {
19
19
  try {
20
20
  for (_iterator.s(); !(_step = _iterator.n()).done;) {
21
21
  const row = _step.value;
22
- const rowCollection = db.tableNameCollectionMap.get(findOptions.raw ? `${row['__schemaName']}.${row['__tableName']}` : `${row.get('__schemaName')}.${row.get('__tableName')}`);
22
+ if (row.__collection) {
23
+ continue;
24
+ }
25
+ const fullTableName = findOptions.raw ? `${row['__schemaName']}.${row['__tableName']}` : `${row.get('__schemaName')}.${row.get('__tableName')}`;
26
+ const rowCollection = db.tableNameCollectionMap.get(fullTableName);
23
27
  if (!rowCollection) {
24
28
  db.logger.warn(`Can not find collection by table name ${JSON.stringify(row)}, current collections: ${Array.from(db.tableNameCollectionMap.keys()).join(', ')}`);
25
29
  return;
@@ -15,6 +15,7 @@ export declare class OptionsParser {
15
15
  filterParser: FilterParser;
16
16
  context: OptionsParserContext;
17
17
  constructor(options: FindOptions, context: OptionsParserContext);
18
+ static appendInheritInspectAttribute(include: any, collection: any): any;
18
19
  isAssociation(key: string): boolean;
19
20
  isAssociationPath(path: string): boolean;
20
21
  toSequelizeParams(): any;
@@ -24,7 +25,6 @@ export declare class OptionsParser {
24
25
  * @protected
25
26
  */
26
27
  protected parseSort(filterParams: any): any;
27
- protected inheritFromSubQuery(include: any): any;
28
28
  protected parseFields(filterParams: any): any;
29
29
  protected parseExcept(except: Except, filterParams: any): any;
30
30
  protected parseAppends(appends: Appends, filterParams: any): any;
@@ -43,6 +43,19 @@ class OptionsParser {
43
43
  });
44
44
  this.context = context;
45
45
  }
46
+ static appendInheritInspectAttribute(include, collection) {
47
+ // if include already has __tableName, __schemaName, skip
48
+ if (include.find(item => (item === null || item === void 0 ? void 0 : item[1]) === '__tableName')) {
49
+ return;
50
+ }
51
+ include.push([_sequelize().Sequelize.literal(`(select relname from pg_class where pg_class.oid = "${collection.name}".tableoid)`), '__tableName']);
52
+ include.push([_sequelize().Sequelize.literal(`
53
+ (SELECT n.nspname
54
+ FROM pg_class c
55
+ JOIN pg_namespace n ON n.oid = c.relnamespace
56
+ WHERE c.oid = "${collection.name}".tableoid)
57
+ `), '__schemaName']);
58
+ }
46
59
  isAssociation(key) {
47
60
  return this.model.associations[key] !== undefined;
48
61
  }
@@ -113,15 +126,6 @@ class OptionsParser {
113
126
  }
114
127
  return filterParams;
115
128
  }
116
- inheritFromSubQuery(include) {
117
- include.push([_sequelize().Sequelize.literal(`(select relname from pg_class where pg_class.oid = "${this.collection.name}".tableoid)`), '__tableName']);
118
- include.push([_sequelize().Sequelize.literal(`
119
- (SELECT n.nspname
120
- FROM pg_class c
121
- JOIN pg_namespace n ON n.oid = c.relnamespace
122
- WHERE c.oid = "${this.collection.name}".tableoid)
123
- `), '__schemaName']);
124
- }
125
129
  parseFields(filterParams) {
126
130
  var _this$options3, _this$options4, _this$options5, _this$options6;
127
131
  const appends = ((_this$options3 = this.options) === null || _this$options3 === void 0 ? void 0 : _this$options3.appends) || [];
@@ -136,12 +140,12 @@ class OptionsParser {
136
140
  exclude: []
137
141
  }; // out put all fields by default
138
142
  if (this.collection.isParent()) {
139
- this.inheritFromSubQuery(attributes.include);
143
+ OptionsParser.appendInheritInspectAttribute(attributes.include, this.collection);
140
144
  }
141
145
  if ((_this$options5 = this.options) === null || _this$options5 === void 0 ? void 0 : _this$options5.fields) {
142
146
  attributes = [];
143
147
  if (this.collection.isParent()) {
144
- this.inheritFromSubQuery(attributes);
148
+ OptionsParser.appendInheritInspectAttribute(attributes, this.collection);
145
149
  }
146
150
  // 将fields拆分为 attributes 和 appends
147
151
  var _iterator2 = _createForOfIteratorHelper(this.options.fields),
@@ -65,7 +65,8 @@ class MultipleRelationRepository extends _relationRepository.RelationRepository
65
65
  model: _this.targetModel,
66
66
  rootAttributes: findOptions.attributes,
67
67
  includeOption: findOptions.include,
68
- rootOrder: findOptions.order
68
+ rootOrder: findOptions.order,
69
+ db: _this.db
69
70
  });
70
71
  yield eagerLoadingTree.load(ids.map(i => i.pk), transaction);
71
72
  return eagerLoadingTree.root.instances;
@@ -71,7 +71,8 @@ class SingleRelationRepository extends _relationRepository.RelationRepository {
71
71
  const eagerLoadingTree = _eagerLoadingTree.EagerLoadingTree.buildFromSequelizeOptions({
72
72
  model: _this3.targetModel,
73
73
  rootAttributes: findOptions.attributes,
74
- includeOption: findOptions.include
74
+ includeOption: findOptions.include,
75
+ db: _this3.db
75
76
  });
76
77
  yield eagerLoadingTree.load([templateModel.get(_this3.targetModel.primaryKeyAttribute)], transaction);
77
78
  return eagerLoadingTree.root.instances[0];
@@ -119,11 +119,18 @@ export interface AggregateOptions {
119
119
  filter?: Filter;
120
120
  distinct?: boolean;
121
121
  }
122
+ interface FirstOrCreateOptions extends Transactionable {
123
+ filterKeys: string[];
124
+ values?: Values;
125
+ }
122
126
  export declare class Repository<TModelAttributes extends {} = any, TCreationAttributes extends {} = TModelAttributes> implements IRepository {
123
127
  database: Database;
124
128
  collection: Collection;
125
129
  model: ModelStatic<Model>;
126
130
  constructor(collection: Collection);
131
+ static valuesToFilter(values: Values, filterKeys: Array<string>): {
132
+ $and: any[];
133
+ };
127
134
  /**
128
135
  * return count by filter
129
136
  */
@@ -152,6 +159,11 @@ export declare class Repository<TModelAttributes extends {} = any, TCreationAttr
152
159
  * @param options
153
160
  */
154
161
  findOne(options?: FindOneOptions): Promise<any>;
162
+ /**
163
+ * Get the first record matching the attributes or create it.
164
+ */
165
+ firstOrCreate(options: FirstOrCreateOptions): Promise<any>;
166
+ updateOrCreate(options: FirstOrCreateOptions): Promise<any>;
155
167
  /**
156
168
  * Save instance to database
157
169
  *